diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 41446a725..fe44fd8b0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -866,6 +866,11 @@ if(HAVE_DBUS) dbus/org.mpris.MediaPlayer2.TrackList.xml core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList) + # MPRIS 2.1 DBUS interfaces + qt4_add_dbus_adaptor(SOURCES + dbus/org.mpris.MediaPlayer2.Playlists.xml + core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists) + # org.freedesktop.Notifications DBUS interface qt4_add_dbus_interface(SOURCES dbus/org.freedesktop.Notifications.xml diff --git a/src/config.h.in b/src/config.h.in index ac2acda8a..0c13abd4e 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -30,7 +30,6 @@ #cmakedefine HAVE_IMOBILEDEVICE #cmakedefine HAVE_LIBARCHIVE #cmakedefine HAVE_LIBGPOD -#cmakedefine HAVE_LIBINDICATE #cmakedefine HAVE_LIBLASTFM #cmakedefine HAVE_LIBLASTFM1 #cmakedefine HAVE_LIBMTP diff --git a/src/core/metatypes.cpp b/src/core/metatypes.cpp index 1882f4bcf..b3975824d 100644 --- a/src/core/metatypes.cpp +++ b/src/core/metatypes.cpp @@ -73,5 +73,8 @@ void RegisterMetaTypes() { qDBusRegisterMetaType(); qDBusRegisterMetaType(); qDBusRegisterMetaType >(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); #endif } diff --git a/src/core/mpris.cpp b/src/core/mpris.cpp index 3fcf57874..da297cec7 100644 --- a/src/core/mpris.cpp +++ b/src/core/mpris.cpp @@ -27,7 +27,6 @@ Mpris::Mpris(Application* app, QObject* parent) mpris2_(new mpris::Mpris2(app, mpris1_, this)) { connect(mpris2_, SIGNAL(RaiseMainWindow()), SIGNAL(RaiseMainWindow())); - mpris2_->InitLibIndicate(); } } // namespace mpris diff --git a/src/core/mpris2.cpp b/src/core/mpris2.cpp index 8146f74d5..97f4d8291 100644 --- a/src/core/mpris2.cpp +++ b/src/core/mpris2.cpp @@ -15,13 +15,17 @@ along with Clementine. If not, see . */ +#include "mpris2.h" + +#include + #include "config.h" #include "mpris_common.h" #include "mpris1.h" -#include "mpris2.h" #include "core/application.h" #include "core/logging.h" #include "core/mpris2_player.h" +#include "core/mpris2_playlists.h" #include "core/mpris2_root.h" #include "core/mpris2_tracklist.h" #include "core/player.h" @@ -37,9 +41,36 @@ #include #include -#ifdef HAVE_LIBINDICATE -# include -#endif +QDBusArgument& operator<< (QDBusArgument& arg, const MprisPlaylist& playlist) { + arg.beginStructure(); + arg << playlist.id << playlist.name << playlist.icon; + arg.endStructure(); + return arg; +} + +const QDBusArgument& operator>> ( + const QDBusArgument& arg, MprisPlaylist& playlist) { + arg.beginStructure(); + arg >> playlist.id >> playlist.name >> playlist.icon; + arg.endStructure(); + return arg; +} + +QDBusArgument& operator<< (QDBusArgument& arg, const MaybePlaylist& playlist) { + arg.beginStructure(); + arg << playlist.valid; + arg << playlist.playlist; + arg.endStructure(); + return arg; +} + +const QDBusArgument& operator>> ( + const QDBusArgument& arg, MaybePlaylist& playlist) { + arg.beginStructure(); + arg >> playlist.valid >> playlist.playlist; + arg.endStructure(); + return arg; +} namespace mpris { @@ -55,6 +86,7 @@ Mpris2::Mpris2(Application* app, Mpris1* mpris1, QObject* parent) new Mpris2Root(this); new Mpris2TrackList(this); new Mpris2Player(this); + new Mpris2Playlists(this); if (!QDBusConnection::sessionBus().registerService(kServiceName)) { qLog(Warning) << "Failed to register" << QString(kServiceName) << "on the session bus"; @@ -71,15 +103,7 @@ Mpris2::Mpris2(Application* app, Mpris1* mpris1, QObject* parent) connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized())); connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song))); -} - -void Mpris2::InitLibIndicate() { -#ifdef HAVE_LIBINDICATE - QIndicate::Server* indicate_server = QIndicate::Server::defaultInstance(); - indicate_server->setType("music.clementine"); - indicate_server->setDesktopFile(DesktopEntryAbsolutePath()); - indicate_server->show(); -#endif + connect(app_->playlist_manager(), SIGNAL(PlaylistChanged(Playlist*)), SLOT(PlaylistChanged())); } // when PlaylistManager gets it ready, we connect PlaylistSequence with this @@ -474,4 +498,75 @@ void Mpris2::GoTo(const QDBusObjectPath &trackId) { //TODO } +quint32 Mpris2::PlaylistCount() const { + return app_->playlist_manager()->GetAllPlaylists().size(); +} + +QStringList Mpris2::Orderings() const { + return QStringList() << "User"; +} + +namespace { + +QDBusObjectPath MakePlaylistPath(int id) { + return QDBusObjectPath(QString( + "/org/mpris/MediaPlayer2/Playlists/%1").arg(id)); +} + +} + +MaybePlaylist Mpris2::ActivePlaylist() const { + MaybePlaylist maybe_playlist; + Playlist* current_playlist = app_->playlist_manager()->current(); + maybe_playlist.valid = current_playlist; + if (!current_playlist) { + return maybe_playlist; + } + + maybe_playlist.playlist.id = MakePlaylistPath(current_playlist->id()); + maybe_playlist.playlist.name = + app_->playlist_manager()->GetPlaylistName(current_playlist->id()); + return maybe_playlist; +} + +void Mpris2::ActivatePlaylist(const QDBusObjectPath& playlist_id) { + QStringList split_path = playlist_id.path().split('/'); + qLog(Debug) << Q_FUNC_INFO << playlist_id.path() << split_path; + if (split_path.isEmpty()) { + return; + } + bool ok = false; + int p = split_path.last().toInt(&ok); + if (!ok) { + return; + } + app_->playlist_manager()->SetActivePlaylist(p); + app_->player()->Next(); +} + +// TODO: Support sort orders. +MprisPlaylistList Mpris2::GetPlaylists( + quint32 index, quint32 max_count, const QString& order, bool reverse_order) { + MprisPlaylistList ret; + foreach (Playlist* p, app_->playlist_manager()->GetAllPlaylists()) { + MprisPlaylist mpris_playlist; + mpris_playlist.id = MakePlaylistPath(p->id()); + mpris_playlist.name = app_->playlist_manager()->GetPlaylistName(p->id()); + ret << mpris_playlist; + } + + if (reverse_order) { + std::reverse(ret.begin(), ret.end()); + } + + return ret.mid(index, max_count); +} + +void Mpris2::PlaylistChanged(Playlist* playlist) { + MprisPlaylist mpris_playlist; + mpris_playlist.id = MakePlaylistPath(playlist->id()); + mpris_playlist.name = app_->playlist_manager()->GetPlaylistName(playlist->id()); + emit PlaylistChanged(mpris_playlist); +} + } // namespace mpris diff --git a/src/core/mpris2.h b/src/core/mpris2.h index f67e2e180..999994cd4 100644 --- a/src/core/mpris2.h +++ b/src/core/mpris2.h @@ -28,11 +28,36 @@ class Application; class MainWindow; +class Playlist; typedef QList TrackMetadata; typedef QList TrackIds; Q_DECLARE_METATYPE(TrackMetadata) +struct MprisPlaylist { + QDBusObjectPath id; + QString name; + QString icon; // Uri +}; +typedef QList MprisPlaylistList; +Q_DECLARE_METATYPE(MprisPlaylist); +Q_DECLARE_METATYPE(MprisPlaylistList); + +struct MaybePlaylist { + bool valid; + MprisPlaylist playlist; +}; +Q_DECLARE_METATYPE(MaybePlaylist); + +QDBusArgument& operator<< (QDBusArgument& arg, const MprisPlaylist& playlist); +const QDBusArgument& operator>> ( + const QDBusArgument& arg, MprisPlaylist& playlist); + +QDBusArgument& operator<< (QDBusArgument& arg, const MaybePlaylist& playlist); +const QDBusArgument& operator>> ( + const QDBusArgument& arg, MaybePlaylist& playlist); + + namespace mpris { class Mpris1; @@ -40,6 +65,7 @@ class Mpris1; class Mpris2 : public QObject { Q_OBJECT + public: //org.mpris.MediaPlayer2 MPRIS 2.0 Root interface Q_PROPERTY( bool CanQuit READ CanQuit ) Q_PROPERTY( bool CanRaise READ CanRaise ) @@ -74,10 +100,12 @@ class Mpris2 : public QObject { Q_PROPERTY( TrackIds Tracks READ Tracks ) Q_PROPERTY( bool CanEditTracks READ CanEditTracks ) -public: - Mpris2(Application* app, Mpris1* mpris1, QObject* parent = 0); + //org.mpris.MediaPlayer2.Playlists MPRIS 2.1 Playlists interface + Q_PROPERTY( quint32 PlaylistCount READ PlaylistCount ) + Q_PROPERTY( QStringList Orderings READ Orderings ) + Q_PROPERTY( MaybePlaylist ActivePlaylist READ ActivePlaylist ) - void InitLibIndicate(); + Mpris2(Application* app, Mpris1* mpris1, QObject* parent = 0); // Root Properties bool CanQuit() const; @@ -139,6 +167,16 @@ public: void RemoveTrack(const QDBusObjectPath& trackId); void GoTo(const QDBusObjectPath& trackId); + // Playlist Properties + quint32 PlaylistCount() const; + QStringList Orderings() const; + MaybePlaylist ActivePlaylist() const; + + // Methods + void ActivatePlaylist(const QDBusObjectPath& playlist_id); + QList GetPlaylists( + quint32 index, quint32 max_count, const QString& order, bool reverse_order); + signals: // Player void Seeked(qlonglong position); @@ -151,6 +189,9 @@ signals: void RaiseMainWindow(); + // Playlist + void PlaylistChanged(const MprisPlaylist& playlist); + private slots: void ArtLoaded(const Song& song, const QString& art_uri); void EngineStateChanged(Engine::State newState); @@ -160,6 +201,7 @@ private slots: void CurrentSongChanged(const Song& song); void ShuffleModeChanged(); void RepeatModeChanged(); + void PlaylistChanged(Playlist* playlist); private: void EmitNotification(const QString& name); diff --git a/src/dbus/org.mpris.MediaPlayer2.Playlists.xml b/src/dbus/org.mpris.MediaPlayer2.Playlists.xml new file mode 100644 index 000000000..f94b31f2f --- /dev/null +++ b/src/dbus/org.mpris.MediaPlayer2.Playlists.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +