Add MPRIS support (thanks Amarok ;-)
Fixes issue #29 Example command lines: dbus-send --print-reply --dest=org.mpris.clementine /Player org.freedesktop.MediaPlayer.Play dbus-send --print-reply --dest=org.mpris.clementine / org.freedesktop.MediaPlayer.Identity dbus-send --print-reply --dest=org.mpris.clementine /TrackList org.freedesktop.MediaPlayer.GetCurrentTrack dbus-send --print-reply --dest=org.mpris.clementine /TrackList org.freedesktop.MediaPlayer.GetMetadata int32:0
This commit is contained in:
parent
ceb225c236
commit
a8a37264f7
|
@ -0,0 +1,87 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
|
||||
|
||||
<node>
|
||||
<interface name="org.freedesktop.MediaPlayer">
|
||||
|
||||
<method name="Pause">
|
||||
</method>
|
||||
|
||||
<method name="Stop">
|
||||
</method>
|
||||
|
||||
<method name="Play">
|
||||
</method>
|
||||
|
||||
<method name="Prev">
|
||||
</method>
|
||||
|
||||
<method name="Next">
|
||||
</method>
|
||||
|
||||
<method name="Repeat">
|
||||
<arg type="b" direction="in"/>
|
||||
</method>
|
||||
|
||||
<method name="GetStatus">
|
||||
<arg type="(iiii)" direction="out"/>
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="DBusStatus"/>
|
||||
</method>
|
||||
|
||||
<method name="VolumeSet">
|
||||
<arg type="i" direction="in"/>
|
||||
</method>
|
||||
|
||||
<method name="VolumeGet">
|
||||
<arg type="i" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="PositionSet">
|
||||
<arg type="i" direction="in"/>
|
||||
</method>
|
||||
|
||||
<method name="PositionGet">
|
||||
<arg type="i" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="GetMetadata">
|
||||
<arg type="a{sv}" direction="out"/>
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
</method>
|
||||
|
||||
<method name="GetCaps">
|
||||
<arg type="i" direction="out" />
|
||||
</method>
|
||||
|
||||
<signal name="TrackChange">
|
||||
<arg type="a{sv}"/>
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
</signal>
|
||||
|
||||
<signal name="StatusChange">
|
||||
<arg type="(iiii)"/>
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="DBusStatus"/>
|
||||
</signal>
|
||||
|
||||
<signal name="CapsChange">
|
||||
<arg type="i" />
|
||||
</signal>
|
||||
|
||||
<!-- NB: Amarok extensions to the mpris spec -->
|
||||
<method name="VolumeUp">
|
||||
<arg type="i" drection="in"/>
|
||||
</method>
|
||||
|
||||
<method name="VolumeDown">
|
||||
<arg type="i" drection="in"/>
|
||||
</method>
|
||||
|
||||
<method name="Mute">
|
||||
</method>
|
||||
|
||||
<method name="ShowOSD">
|
||||
</method>
|
||||
|
||||
</interface>
|
||||
</node>
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
|
||||
|
||||
<node>
|
||||
<interface name="org.freedesktop.MediaPlayer">
|
||||
<method name="Identity">
|
||||
<arg type="s" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="Quit">
|
||||
</method>
|
||||
|
||||
<method name="MprisVersion">
|
||||
<arg type="(qq)" direction="out"/>
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="Version"/>
|
||||
</method>
|
||||
|
||||
</interface>
|
||||
</node>
|
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
|
||||
<node>
|
||||
<interface name="org.freedesktop.MediaPlayer">
|
||||
|
||||
<method name="GetMetadata">
|
||||
<arg type="i" direction="in" />
|
||||
<arg type="a{sv}" direction="out" />
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
</method>
|
||||
|
||||
<method name="GetCurrentTrack">
|
||||
<arg type="i" direction="out" />
|
||||
</method>
|
||||
|
||||
<method name="GetLength">
|
||||
<arg type="i" direction="out" />
|
||||
</method>
|
||||
|
||||
<method name="AddTrack">
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="b" direction="in" />
|
||||
<arg type="i" direction="out" />
|
||||
</method>
|
||||
|
||||
<method name="DelTrack">
|
||||
<arg type="i" />
|
||||
</method>
|
||||
|
||||
<method name="SetLoop">
|
||||
<arg type="b" />
|
||||
</method>
|
||||
|
||||
<method name="SetRandom">
|
||||
<arg type="b" />
|
||||
</method>
|
||||
|
||||
<method name="PlayTrack">
|
||||
<arg type="i" />
|
||||
</method>
|
||||
|
||||
<signal name="TrackListChange">
|
||||
<arg type="i" />
|
||||
</signal>
|
||||
|
||||
</interface>
|
||||
</node>
|
|
@ -64,6 +64,7 @@ set(CLEMENTINE-SOURCES
|
|||
globalshortcuts/globalshortcuts.cpp
|
||||
fixlastfm.cpp
|
||||
backgroundthread.cpp
|
||||
mpris.cpp
|
||||
)
|
||||
|
||||
# Header files that have Q_OBJECT in
|
||||
|
@ -120,6 +121,7 @@ set(CLEMENTINE-MOC-HEADERS
|
|||
analyzers/sonogram.h
|
||||
analyzers/turbine.h
|
||||
globalshortcuts/globalshortcuts.h
|
||||
mpris.h
|
||||
)
|
||||
|
||||
# UI files
|
||||
|
@ -184,6 +186,15 @@ else(APPLE)
|
|||
set(CLEMENTINE-SOURCES ${CLEMENTINE-SOURCES} osd_win.cpp)
|
||||
else(WIN32)
|
||||
set(CLEMENTINE-SOURCES ${CLEMENTINE-SOURCES} osd_x11.cpp)
|
||||
qt4_add_dbus_adaptor(MPRIS-PLAYER-SOURCES
|
||||
../data/org.freedesktop.MediaPlayer.player.xml
|
||||
player.h Player mpris_player MprisPlayer)
|
||||
qt4_add_dbus_adaptor(MPRIS-ROOT-SOURCES
|
||||
../data/org.freedesktop.MediaPlayer.root.xml
|
||||
mpris.h MPRIS mpris_root MprisRoot)
|
||||
qt4_add_dbus_adaptor(MPRIS-TRACKLIST-SOURCES
|
||||
../data/org.freedesktop.MediaPlayer.tracklist.xml
|
||||
player.h Player mpris_tracklist MprisTrackList)
|
||||
endif(WIN32)
|
||||
endif(APPLE)
|
||||
|
||||
|
@ -209,6 +220,9 @@ add_library(clementine_lib
|
|||
${CLEMENTINE-SOURCES-UI}
|
||||
${CLEMENTINE-SOURCES-RESOURCE}
|
||||
${CLEMENTINE-QM-FILES}
|
||||
${MPRIS-ROOT-SOURCES}
|
||||
${MPRIS-PLAYER-SOURCES}
|
||||
${MPRIS-TRACKLIST-SOURCES}
|
||||
)
|
||||
target_link_libraries(clementine_lib
|
||||
qtsingleapplication
|
||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -23,7 +23,9 @@
|
|||
#include "directory.h"
|
||||
#include "lastfmservice.h"
|
||||
#include "mainwindow.h"
|
||||
#include "player.h"
|
||||
#include "song.h"
|
||||
#include "mpris.h"
|
||||
|
||||
#include <QtSingleApplication>
|
||||
#include <QtDebug>
|
||||
|
@ -31,6 +33,8 @@
|
|||
#include <QTranslator>
|
||||
#include <QDir>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMetaType>
|
||||
|
||||
// Load sqlite plugin on windows
|
||||
#ifdef WIN32
|
||||
|
@ -61,6 +65,9 @@ int main(int argc, char *argv[]) {
|
|||
qRegisterMetaType<DirectoryList>("DirectoryList");
|
||||
qRegisterMetaType<SongList>("SongList");
|
||||
|
||||
qDBusRegisterMetaType<DBusStatus>();
|
||||
qDBusRegisterMetaType<Version>();
|
||||
|
||||
lastfm::ws::ApiKey = LastFMService::kApiKey;
|
||||
lastfm::ws::SharedSecret = LastFMService::kSecret;
|
||||
|
||||
|
@ -85,6 +92,9 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
QNetworkAccessManager network;
|
||||
|
||||
QDBusConnection::sessionBus().registerService("org.mpris.clementine");
|
||||
MPRIS mpris;
|
||||
|
||||
// Window
|
||||
MainWindow w(&network);;
|
||||
a.setActivationWindow(&w);
|
||||
|
|
|
@ -181,8 +181,10 @@ MainWindow::MainWindow(QNetworkAccessManager* network, QWidget *parent)
|
|||
connect(player_, SIGNAL(Paused()), osd_, SLOT(Paused()));
|
||||
connect(player_, SIGNAL(Stopped()), osd_, SLOT(Stopped()));
|
||||
connect(player_, SIGNAL(VolumeChanged(int)), osd_, SLOT(VolumeChanged(int)));
|
||||
connect(player_, SIGNAL(ForceShowOSD(Song)), osd_, SLOT(SongChanged(Song)));
|
||||
connect(playlist_, SIGNAL(CurrentSongChanged(Song)), osd_, SLOT(SongChanged(Song)));
|
||||
connect(playlist_, SIGNAL(CurrentSongChanged(Song)), player_, SLOT(CurrentMetadataChanged(Song)));
|
||||
connect(playlist_, SIGNAL(PlaylistChanged()), player_, SLOT(PlaylistChanged()));
|
||||
|
||||
connect(ui_.playlist, SIGNAL(doubleClicked(QModelIndex)), SLOT(PlayIndex(QModelIndex)));
|
||||
connect(ui_.playlist, SIGNAL(PlayPauseItem(QModelIndex)), SLOT(PlayIndex(QModelIndex)));
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* This file is part of Clementine.
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mpris.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "mpris_root.h"
|
||||
|
||||
QDBusArgument& operator<< (QDBusArgument& arg, const Version& version) {
|
||||
arg.beginStructure();
|
||||
arg << version.major << version.minor;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version) {
|
||||
arg.beginStructure();
|
||||
arg >> version.major >> version.minor;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
MPRIS::MPRIS(QObject* parent)
|
||||
: QObject(parent) {
|
||||
new MprisRoot(this);
|
||||
QDBusConnection::sessionBus().registerObject("/", this);
|
||||
}
|
||||
|
||||
QString MPRIS::Identity() {
|
||||
return "Clementine 0.2";
|
||||
}
|
||||
|
||||
Version MPRIS::MprisVersion() {
|
||||
Version version;
|
||||
version.major = 1;
|
||||
version.minor = 0;
|
||||
return version;
|
||||
}
|
||||
|
||||
void MPRIS::Quit() {
|
||||
qApp->quit();
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* This file is part of Clementine.
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MPRIS_H
|
||||
#define MPRIS_H
|
||||
|
||||
#include <QDBusArgument>
|
||||
#include <QObject>
|
||||
|
||||
struct Version {
|
||||
quint16 minor;
|
||||
quint16 major;
|
||||
};
|
||||
Q_DECLARE_METATYPE(Version)
|
||||
|
||||
QDBusArgument& operator<< (QDBusArgument& arg, const Version& version);
|
||||
const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version);
|
||||
|
||||
class MPRIS : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MPRIS(QObject* parent = 0);
|
||||
QString Identity();
|
||||
void Quit();
|
||||
Version MprisVersion();
|
||||
};
|
||||
|
||||
#endif // MPRIS_H
|
249
src/player.cpp
249
src/player.cpp
|
@ -17,6 +17,8 @@
|
|||
#include "player.h"
|
||||
#include "playlist.h"
|
||||
#include "lastfmservice.h"
|
||||
#include "mpris_player.h"
|
||||
#include "mpris_tracklist.h"
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
# include "phononengine.h"
|
||||
|
@ -26,9 +28,30 @@
|
|||
|
||||
#include <QtDebug>
|
||||
#include <QtConcurrentRun>
|
||||
#include <QDBusConnection>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) {
|
||||
arg.beginStructure();
|
||||
arg << status.Play;
|
||||
arg << status.Random;
|
||||
arg << status.Repeat;
|
||||
arg << status.RepeatPlaylist;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status) {
|
||||
arg.beginStructure();
|
||||
arg >> status.Play;
|
||||
arg >> status.Random;
|
||||
arg >> status.Repeat;
|
||||
arg >> status.RepeatPlaylist;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
Player::Player(Playlist* playlist, LastFMService* lastfm, QObject* parent)
|
||||
: QObject(parent),
|
||||
playlist_(playlist),
|
||||
|
@ -49,6 +72,13 @@ Player::Player(Playlist* playlist, LastFMService* lastfm, QObject* parent)
|
|||
|
||||
connect(init_engine_watcher_, SIGNAL(finished()), SLOT(EngineInitFinished()));
|
||||
connect(engine_, SIGNAL(error(QString)), SIGNAL(Error(QString)));
|
||||
|
||||
MprisPlayer* mpris = new MprisPlayer(this);
|
||||
// Hack so the next registerObject() doesn't override this one.
|
||||
QDBusConnection::sessionBus().registerObject(
|
||||
"/Player", mpris, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals);
|
||||
new MprisTrackList(this);
|
||||
QDBusConnection::sessionBus().registerObject("/TrackList", this);
|
||||
}
|
||||
|
||||
void Player::Init() {
|
||||
|
@ -168,6 +198,8 @@ void Player::EngineStateChanged(Engine::State state) {
|
|||
case Engine::Empty:
|
||||
case Engine::Idle: emit Stopped(); break;
|
||||
}
|
||||
emit StatusChange(GetStatus());
|
||||
emit CapsChange(GetCaps());
|
||||
}
|
||||
|
||||
void Player::SetVolume(int value) {
|
||||
|
@ -202,6 +234,8 @@ void Player::PlayAt(int index) {
|
|||
if (lastfm_->IsScrobblingEnabled())
|
||||
lastfm_->NowPlaying(item->Metadata());
|
||||
}
|
||||
|
||||
emit CapsChange(GetCaps());
|
||||
}
|
||||
|
||||
void Player::StreamReady(const QUrl& original_url, const QUrl& media_url) {
|
||||
|
@ -225,6 +259,7 @@ void Player::StreamReady(const QUrl& original_url, const QUrl& media_url) {
|
|||
void Player::CurrentMetadataChanged(const Song &metadata) {
|
||||
lastfm_->NowPlaying(metadata);
|
||||
current_item_ = metadata;
|
||||
emit TrackChange(GetMetadata());
|
||||
}
|
||||
|
||||
void Player::Seek(int seconds) {
|
||||
|
@ -251,3 +286,217 @@ void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
|
|||
|
||||
playlist_->SetStreamMetadata(item->Url(), song);
|
||||
}
|
||||
|
||||
int Player::GetCaps() const {
|
||||
int caps = CAN_HAS_TRACKLIST;
|
||||
if (current_item_.is_valid()) { caps |= CAN_PROVIDE_METADATA; }
|
||||
if (GetState() == Engine::Playing && current_item_.filetype() != Song::Type_Stream) {
|
||||
caps |= CAN_PAUSE;
|
||||
}
|
||||
if (GetState() == Engine::Paused) {
|
||||
caps |= CAN_PLAY;
|
||||
}
|
||||
if (GetState() != Engine::Empty && current_item_.filetype() != Song::Type_Stream) {
|
||||
caps |= CAN_SEEK;
|
||||
}
|
||||
if (playlist_->next_index() != -1 ||
|
||||
playlist_->current_item_options() & PlaylistItem::ContainsMultipleTracks) {
|
||||
caps |= CAN_GO_NEXT;
|
||||
}
|
||||
if (playlist_->previous_index() != -1) {
|
||||
caps |= CAN_GO_PREV;
|
||||
}
|
||||
return caps;
|
||||
}
|
||||
|
||||
DBusStatus Player::GetStatus() const {
|
||||
DBusStatus status;
|
||||
switch (GetState()) {
|
||||
case Engine::Empty:
|
||||
case Engine::Idle:
|
||||
status.Play = 2;
|
||||
break;
|
||||
case Engine::Playing:
|
||||
status.Play = 0;
|
||||
break;
|
||||
case Engine::Paused:
|
||||
status.Play = 1;
|
||||
break;
|
||||
}
|
||||
status.Random = playlist_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off ? 0 : 1;
|
||||
PlaylistSequence::RepeatMode repeat_mode = playlist_->sequence()->repeat_mode();
|
||||
status.Repeat = repeat_mode == PlaylistSequence::Repeat_Track ? 1 : 0;
|
||||
status.RepeatPlaylist = (repeat_mode == PlaylistSequence::Repeat_Album ||
|
||||
repeat_mode == PlaylistSequence::Repeat_Playlist) ? 1 : 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
namespace {
|
||||
inline void AddMetadata(const QString& key, const QString& metadata, QVariantMap* map) {
|
||||
if (!metadata.isEmpty()) {
|
||||
(*map)[key] = metadata;
|
||||
}
|
||||
}
|
||||
|
||||
inline void AddMetadata(const QString& key, int metadata, QVariantMap* map) {
|
||||
if (metadata > 0) {
|
||||
(*map)[key] = metadata;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QVariantMap Player::GetMetadata(const PlaylistItem& item) const {
|
||||
QVariantMap ret;
|
||||
if (item.type() == PlaylistItem::Type_Song) {
|
||||
const Song& song = item.Metadata();
|
||||
if (song.is_valid()) {
|
||||
AddMetadata("location", item.Url().toString(), &ret);
|
||||
AddMetadata("title", song.PrettyTitle(), &ret);
|
||||
AddMetadata("artist", song.artist(), &ret);
|
||||
AddMetadata("album", song.album(), &ret);
|
||||
AddMetadata("time", song.length(), &ret);
|
||||
AddMetadata("tracknumber", song.track(), &ret);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
AddMetadata("location", item.Url().toString(), &ret);
|
||||
const Song& song = item.Metadata();
|
||||
AddMetadata("title", song.PrettyTitle(), &ret);
|
||||
AddMetadata("artist", song.artist(), &ret);
|
||||
AddMetadata("album", song.album(), &ret);
|
||||
AddMetadata("time", song.length(), &ret);
|
||||
AddMetadata("tracknumber", song.track(), &ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap Player::GetMetadata() const {
|
||||
return GetMetadata(*(playlist_->current_item()));
|
||||
}
|
||||
|
||||
QVariantMap Player::GetMetadata(int track) const {
|
||||
if (track >= playlist_->rowCount()) {
|
||||
return QVariantMap();
|
||||
}
|
||||
const PlaylistItem& item = *(playlist_->item_at(track));
|
||||
return GetMetadata(item);
|
||||
}
|
||||
|
||||
void Player::Mute() {
|
||||
SetVolume(0);
|
||||
}
|
||||
|
||||
void Player::Pause() {
|
||||
switch (GetState()) {
|
||||
case Engine::Playing:
|
||||
engine_->pause();
|
||||
break;
|
||||
case Engine::Paused:
|
||||
engine_->pause();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::Play() {
|
||||
switch (GetState()) {
|
||||
case Engine::Playing:
|
||||
Seek(0);
|
||||
break;
|
||||
case Engine::Paused:
|
||||
engine_->unpause();
|
||||
break;
|
||||
default:
|
||||
Next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::Prev() {
|
||||
Previous();
|
||||
}
|
||||
|
||||
int Player::PositionGet() const {
|
||||
return engine_->position();
|
||||
}
|
||||
|
||||
void Player::PositionSet(int x) {
|
||||
Seek(x / 1000);
|
||||
}
|
||||
|
||||
void Player::Repeat(bool enable) {
|
||||
playlist_->sequence()->SetRepeatMode(
|
||||
enable ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off);
|
||||
}
|
||||
|
||||
void Player::ShowOSD() {
|
||||
emit ForceShowOSD(current_item_);
|
||||
}
|
||||
|
||||
void Player::VolumeDown(int change) {
|
||||
SetVolume(GetVolume() - change);
|
||||
}
|
||||
|
||||
void Player::VolumeUp(int change) {
|
||||
SetVolume(GetVolume() + change);
|
||||
}
|
||||
|
||||
int Player::VolumeGet() const {
|
||||
return GetVolume();
|
||||
}
|
||||
|
||||
void Player::VolumeSet(int volume) {
|
||||
SetVolume(volume);
|
||||
}
|
||||
|
||||
int Player::AddTrack(const QString& track, bool play_now) {
|
||||
QUrl url(track);
|
||||
QList<QUrl> list;
|
||||
list << url;
|
||||
QModelIndex index;
|
||||
if (url.scheme() == "file") {
|
||||
index = playlist_->InsertPaths(list, play_now ? playlist_->current_index() + 1 : -1);
|
||||
} else {
|
||||
index = playlist_->InsertStreamUrls(list, play_now ? playlist_->current_index() + 1: -1);
|
||||
}
|
||||
|
||||
if (index.isValid()) {
|
||||
if (play_now) {
|
||||
Next();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Player::DelTrack(int index) {
|
||||
playlist_->removeRows(index, 1);
|
||||
}
|
||||
|
||||
int Player::GetCurrentTrack() const {
|
||||
return playlist_->current_index();
|
||||
}
|
||||
|
||||
int Player::GetLength() const {
|
||||
return playlist_->rowCount();
|
||||
}
|
||||
|
||||
void Player::SetLoop(bool enable) {
|
||||
playlist_->sequence()->SetRepeatMode(
|
||||
enable ? PlaylistSequence::Repeat_Playlist : PlaylistSequence::Repeat_Off);
|
||||
}
|
||||
|
||||
void Player::SetRandom(bool enable) {
|
||||
playlist_->sequence()->SetShuffleMode(
|
||||
enable ? PlaylistSequence::Shuffle_All : PlaylistSequence::Shuffle_Off);
|
||||
}
|
||||
|
||||
void Player::PlayTrack(int index) {
|
||||
PlayAt(index);
|
||||
}
|
||||
|
||||
void Player::PlaylistChanged() {
|
||||
emit TrackListChange(GetLength());
|
||||
}
|
||||
|
|
71
src/player.h
71
src/player.h
|
@ -21,6 +21,7 @@
|
|||
#include <QSettings>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QDBusArgument>
|
||||
|
||||
#include "engine_fwd.h"
|
||||
#include "playlistitem.h"
|
||||
|
@ -30,6 +31,17 @@ class Playlist;
|
|||
class Settings;
|
||||
class LastFMService;
|
||||
|
||||
struct DBusStatus { // From Amarok.
|
||||
int Play; // Playing = 0, Paused = 1, Stopped = 2
|
||||
int Random; // Linearly = 0, Randomly = 1
|
||||
int Repeat; // Go_To_Next = 0, Repeat_Current = 1
|
||||
int RepeatPlaylist; // Stop_When_Finished = 0, Never_Give_Up_Playing = 1
|
||||
};
|
||||
Q_DECLARE_METATYPE(DBusStatus);
|
||||
|
||||
QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status);
|
||||
const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status);
|
||||
|
||||
class Player : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -45,15 +57,25 @@ class Player : public QObject {
|
|||
PlaylistItem::Options GetCurrentItemOptions() const { return current_item_options_; }
|
||||
Song GetCurrentItem() const { return current_item_; }
|
||||
|
||||
// MPRIS
|
||||
enum DBusCaps {
|
||||
NONE = 0,
|
||||
CAN_GO_NEXT = 1 << 0,
|
||||
CAN_GO_PREV = 1 << 1,
|
||||
CAN_PAUSE = 1 << 2,
|
||||
CAN_PLAY = 1 << 3,
|
||||
CAN_SEEK = 1 << 4,
|
||||
CAN_PROVIDE_METADATA = 1 << 5,
|
||||
CAN_HAS_TRACKLIST = 1 << 6,
|
||||
};
|
||||
|
||||
public slots:
|
||||
void ReloadSettings();
|
||||
|
||||
void PlayAt(int index);
|
||||
void PlayPause();
|
||||
void Next();
|
||||
void NextItem();
|
||||
void Previous();
|
||||
void Stop();
|
||||
void SetVolume(int value);
|
||||
void Seek(int seconds);
|
||||
|
||||
|
@ -61,6 +83,39 @@ class Player : public QObject {
|
|||
void StreamReady(const QUrl& original_url, const QUrl& media_url);
|
||||
void CurrentMetadataChanged(const Song& metadata);
|
||||
|
||||
void PlaylistChanged();
|
||||
|
||||
// MPRIS /Player
|
||||
int GetCaps() const;
|
||||
DBusStatus GetStatus() const;
|
||||
QVariantMap GetMetadata() const;
|
||||
void Mute();
|
||||
void Pause();
|
||||
void Stop();
|
||||
void Play();
|
||||
void Next();
|
||||
void Prev();
|
||||
int PositionGet() const;
|
||||
void PositionSet(int);
|
||||
void Repeat(bool);
|
||||
void ShowOSD();
|
||||
void VolumeDown(int);
|
||||
void VolumeUp(int);
|
||||
int VolumeGet() const;
|
||||
void VolumeSet(int);
|
||||
|
||||
// MPRIS /Tracklist
|
||||
int AddTrack(const QString&, bool);
|
||||
void DelTrack(int index);
|
||||
int GetCurrentTrack() const;
|
||||
int GetLength() const;
|
||||
QVariantMap GetMetadata(int) const;
|
||||
void SetLoop(bool enable);
|
||||
void SetRandom(bool enable);
|
||||
|
||||
// Amarok extension.
|
||||
void PlayTrack(int index);
|
||||
|
||||
signals:
|
||||
void InitFinished();
|
||||
|
||||
|
@ -70,12 +125,24 @@ class Player : public QObject {
|
|||
void VolumeChanged(int volume);
|
||||
void Error(const QString& message);
|
||||
|
||||
void ForceShowOSD(Song);
|
||||
|
||||
// MPRIS
|
||||
// Player
|
||||
void CapsChange(int);
|
||||
void TrackChange(QVariantMap);
|
||||
void StatusChange(DBusStatus);
|
||||
// TrackList
|
||||
void TrackListChange(int i);
|
||||
|
||||
private slots:
|
||||
void EngineInitFinished();
|
||||
void EngineStateChanged(Engine::State);
|
||||
void EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle);
|
||||
|
||||
private:
|
||||
QVariantMap GetMetadata(const PlaylistItem& item) const;
|
||||
|
||||
Playlist* playlist_;
|
||||
LastFMService* lastfm_;
|
||||
QSettings settings_;
|
||||
|
|
|
@ -47,6 +47,8 @@ Playlist::Playlist(QObject *parent) :
|
|||
playlist_sequence_(NULL),
|
||||
ignore_sorting_(false)
|
||||
{
|
||||
connect(this, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SIGNAL(PlaylistChanged()));
|
||||
connect(this, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SIGNAL(PlaylistChanged()));
|
||||
}
|
||||
|
||||
Playlist::~Playlist() {
|
||||
|
@ -569,6 +571,9 @@ void Playlist::Restore() {
|
|||
}
|
||||
|
||||
bool Playlist::removeRows(int row, int count, const QModelIndex& parent) {
|
||||
if (row < 0 || row >= items_.size() || row + count > items_.size()) {
|
||||
return false;
|
||||
}
|
||||
beginRemoveRows(parent, row, row+count-1);
|
||||
|
||||
// Remove items
|
||||
|
|
|
@ -119,6 +119,7 @@ class Playlist : public QAbstractListModel {
|
|||
void sort(int column, Qt::SortOrder order);
|
||||
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||
|
||||
|
||||
public slots:
|
||||
void set_current_index(int index);
|
||||
void Paused();
|
||||
|
@ -137,6 +138,8 @@ class Playlist : public QAbstractListModel {
|
|||
signals:
|
||||
void CurrentSongChanged(const Song& metadata);
|
||||
|
||||
void PlaylistChanged();
|
||||
|
||||
private:
|
||||
void SetCurrentIsPaused(bool paused);
|
||||
void UpdateScrobblePoint();
|
||||
|
|
|
@ -62,7 +62,7 @@ class PlaylistItem {
|
|||
// return true. If it returns false then the URL from Url() will be passed
|
||||
// directly to xine instead.
|
||||
virtual void StartLoading() {}
|
||||
virtual QUrl Url() = 0;
|
||||
virtual QUrl Url() const = 0;
|
||||
|
||||
// If the item is a radio station that can play another song after one has
|
||||
// finished then it should do so and return true
|
||||
|
|
|
@ -80,7 +80,7 @@ void RadioPlaylistItem::LoadNext() {
|
|||
service_->LoadNext(url_);
|
||||
}
|
||||
|
||||
QUrl RadioPlaylistItem::Url() {
|
||||
QUrl RadioPlaylistItem::Url() const {
|
||||
return url_;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class RadioPlaylistItem : public PlaylistItem {
|
|||
Song Metadata() const;
|
||||
|
||||
void StartLoading();
|
||||
QUrl Url();
|
||||
QUrl Url() const;
|
||||
|
||||
void LoadNext();
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ void SongPlaylistItem::RestoreStream(const QSettings& settings) {
|
|||
settings.value("length", -1).toInt());
|
||||
}
|
||||
|
||||
QUrl SongPlaylistItem::Url() {
|
||||
QUrl SongPlaylistItem::Url() const {
|
||||
if (QFile::exists(song_.filename())) {
|
||||
return QUrl::fromLocalFile(song_.filename());
|
||||
} else {
|
||||
|
|
|
@ -33,7 +33,7 @@ class SongPlaylistItem : public PlaylistItem {
|
|||
|
||||
Song Metadata() const { return song_; }
|
||||
|
||||
QUrl Url();
|
||||
QUrl Url() const;
|
||||
|
||||
private:
|
||||
void SaveFile(QSettings& settings) const;
|
||||
|
|
Loading…
Reference in New Issue