From 626c75cd45c594a96d8099328c7eb8119c4e2dbb Mon Sep 17 00:00:00 2001 From: David Sansome Date: Sun, 21 Nov 2010 15:13:26 +0000 Subject: [PATCH] Add MPRIS2 and gnome sound menu support --- CMakeLists.txt | 8 + src/CMakeLists.txt | 33 +- src/config.h.in | 2 + src/core/mpris.cpp | 213 +++++++++- src/core/mpris.h | 127 +++++- src/core/mpris2.cpp | 395 ++++++++++++++++++ src/core/mpris2.h | 166 ++++++++ src/core/player.cpp | 186 +++------ src/core/player.h | 62 +-- src/dbus/org.mpris.MediaPlayer2.Player.xml | 43 ++ src/dbus/org.mpris.MediaPlayer2.TrackList.xml | 45 ++ src/dbus/org.mpris.MediaPlayer2.xml | 16 + src/main.cpp | 16 +- src/ui/mainwindow.cpp | 2 +- 14 files changed, 1110 insertions(+), 204 deletions(-) create mode 100644 src/core/mpris2.cpp create mode 100644 src/core/mpris2.h create mode 100644 src/dbus/org.mpris.MediaPlayer2.Player.xml create mode 100644 src/dbus/org.mpris.MediaPlayer2.TrackList.xml create mode 100644 src/dbus/org.mpris.MediaPlayer2.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index cf4ad6708..449d920e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ endif(APPLE) find_package(OpenGL REQUIRED) find_package(Boost REQUIRED) find_package(Gettext REQUIRED) +find_package(PkgConfig REQUIRED) pkg_check_modules(TAGLIB REQUIRED taglib>=1.6) pkg_check_modules(GSTREAMER gstreamer-0.10) @@ -41,6 +42,7 @@ pkg_check_modules(IMOBILEDEVICE libimobiledevice-1.0) pkg_check_modules(PLIST libplist) pkg_check_modules(USBMUXD libusbmuxd) pkg_check_modules(LIBMTP libmtp>=1.0) +pkg_check_modules(INDICATEQT indicate-qt) if (WIN32) find_package(ZLIB REQUIRED) @@ -123,6 +125,7 @@ option(ENABLE_LIBMTP "MTP support" ON) option(ENABLE_GIO "GIO backend" ON) option(ENABLE_VISUALISATIONS "Use libprojectm visualisations" ON) option(BUNDLE_PROJECTM_PRESETS "Install Clementine's own copies of libprojectm presets - disable this if you want to use a system package instead" ON) +option(ENABLE_SOUNDMENU "Add Clementine to the Gnome sound menu" ON) if(NOT APPLE AND NOT WIN32) option(ENABLE_WIIMOTEDEV "Enable Wii remote support in Clementine" ON) @@ -144,6 +147,10 @@ if(ENABLE_LIBMTP AND LIBMTP_FOUND) set(HAVE_LIBMTP ON) endif(ENABLE_LIBMTP AND LIBMTP_FOUND) +if(ENABLE_SOUNDMENU AND INDICATEQT_FOUND) + set(HAVE_LIBINDICATE ON) +endif(ENABLE_SOUNDMENU AND INDICATEQT_FOUND) + if(ENABLE_VISUALISATIONS) # When/if upstream accepts our patches then these options can be used to link # to system installed projectM instead. @@ -245,6 +252,7 @@ summary_add("devices: iPod classic support" HAVE_LIBGPOD) summary_add("devices: iPod Touch, iPhone, iPad support" HAVE_IMOBILEDEVICE) summary_add("devices: MTP support" HAVE_LIBMTP) summary_add("devices: GIO backend" HAVE_GIO) +summary_add("Gnome sound menu integration" HAVE_LIBINDICATE) summary_add("Wiimote support" ENABLE_WIIMOTEDEV) summary_add("Visualisations" ENABLE_VISUALISATIONS) summary_show() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 564f1dceb..aa6ac3ab3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,11 @@ include_directories(${QTIOCOMPRESSOR_INCLUDE_DIRS}) include_directories(${QXT_INCLUDE_DIRS}) include_directories(${ECHONEST_INCLUDE_DIRS}) +if(HAVE_LIBINDICATE) + link_directories(${INDICATEQT_LIBRARY_DIRS}) + include_directories(${INDICATEQT_INCLUDE_DIRS}) +endif(HAVE_LIBINDICATE) + cmake_policy(SET CMP0011 NEW) include(../cmake/AddEngine.cmake) include(../cmake/ParseArguments.cmake) @@ -529,13 +534,24 @@ if(NOT APPLE AND NOT WIN32) # MPRIS DBUS interfaces qt4_add_dbus_adaptor(SOURCES dbus/org.freedesktop.MediaPlayer.player.xml - core/player.h Player core/mpris_player MprisPlayer) + core/mpris.h MPRIS core/mpris_player MprisPlayer) qt4_add_dbus_adaptor(SOURCES dbus/org.freedesktop.MediaPlayer.root.xml core/mpris.h MPRIS core/mpris_root MprisRoot) qt4_add_dbus_adaptor(SOURCES dbus/org.freedesktop.MediaPlayer.tracklist.xml - core/player.h Player core/mpris_tracklist MprisTrackList) + core/mpris.h MPRIS core/mpris_tracklist MprisTrackList) + + # MPRIS 2.0 DBUS interfaces + qt4_add_dbus_adaptor(SOURCES + dbus/org.mpris.MediaPlayer2.Player.xml + core/mpris2.h MPRIS2 core/mpris2_player Mpris2Player) + qt4_add_dbus_adaptor(SOURCES + dbus/org.mpris.MediaPlayer2.xml + core/mpris2.h MPRIS2 core/mpris2_root Mpris2Root) + qt4_add_dbus_adaptor(SOURCES + dbus/org.mpris.MediaPlayer2.TrackList.xml + core/mpris2.h MPRIS2 core/mpris2_tracklist Mpris2TrackList) # org.freedesktop.Notifications DBUS interface qt4_add_dbus_interface(SOURCES @@ -551,8 +567,8 @@ if(NOT APPLE AND NOT WIN32) dbus/udisksdevice) # MPRIS source - list(APPEND SOURCES core/mpris.cpp) - list(APPEND HEADERS core/mpris.h) + list(APPEND SOURCES core/mpris.cpp core/mpris2.cpp) + list(APPEND HEADERS core/mpris.h core/mpris2.h) # Wiimotedev interface classes if(ENABLE_WIIMOTEDEV) @@ -651,6 +667,10 @@ list(APPEND OTHER_SOURCES core/macglobalshortcutbackend.h core/macglobalshortcutbackend.mm core/modelfuturewatcher.h + core/mpris.cpp + core/mpris.h + core/mpris2.cpp + core/mpris2.h devices/afcdevice.cpp devices/afcdevice.h devices/afcfile.cpp @@ -718,6 +738,7 @@ add_library(clementine_lib STATIC add_dependencies(clementine_lib pot) + target_link_libraries(clementine_lib chardet echonest @@ -761,6 +782,10 @@ if(HAVE_LIBMTP) target_link_libraries(clementine_lib ${LIBMTP_LIBRARIES}) endif(HAVE_LIBMTP) +if(HAVE_LIBINDICATE) + target_link_libraries(clementine_lib ${INDICATEQT_LIBRARIES}) +endif(HAVE_LIBINDICATE) + if (APPLE) target_link_libraries(clementine_lib ${GROWL} diff --git a/src/config.h.in b/src/config.h.in index 4158873b6..5ec3eefcf 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -45,4 +45,6 @@ #cmakedefine HAVE_IMOBILEDEVICE #cmakedefine HAVE_LIBMTP +#cmakedefine HAVE_LIBINDICATE + #endif // CONFIG_H_IN diff --git a/src/core/mpris.cpp b/src/core/mpris.cpp index 3e055b69a..a01bd97bd 100644 --- a/src/core/mpris.cpp +++ b/src/core/mpris.cpp @@ -18,8 +18,14 @@ #include "mpris.h" #include +#include +#include "core/mpris_player.h" #include "core/mpris_root.h" +#include "core/mpris_tracklist.h" +#include "playlist/playlist.h" +#include "playlist/playlistmanager.h" +#include "playlist/playlistsequence.h" QDBusArgument& operator<< (QDBusArgument& arg, const Version& version) { arg.beginStructure(); @@ -35,10 +41,19 @@ const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version) { return arg; } -MPRIS::MPRIS(QObject* parent) - : QObject(parent) { - new MprisRoot(this); - QDBusConnection::sessionBus().registerObject("/", this); +MPRIS::MPRIS(Player* player, QObject* parent) + : QObject(parent), + player_(player) +{ + MprisRoot* mpris_root = new MprisRoot(this); + MprisPlayer* mpris_player = new MprisPlayer(this); + MprisTrackList* mpris_tracklist = new MprisTrackList(this); + + QDBusConnection bus(QDBusConnection::sessionBus()); + bus.registerService("org.mpris.clementine"); + bus.registerObject("/", mpris_root, QDBusConnection::ExportAllContents); + bus.registerObject("/Player", mpris_player, QDBusConnection::ExportAllContents); + bus.registerObject("/TrackList", mpris_tracklist, QDBusConnection::ExportAllContents); } QString MPRIS::Identity() { @@ -57,3 +72,193 @@ Version MPRIS::MprisVersion() { void MPRIS::Quit() { qApp->quit(); } + +void MPRIS::Pause() { + player_->Pause(); +} + +void MPRIS::Stop() { + player_->Stop(); +} + +void MPRIS::Prev() { + player_->Prev(); +} + +void MPRIS::Play() { + player_->Prev(); +} + +void MPRIS::Next() { + player_->Next(); +} + +void MPRIS::Repeat(bool repeat) { + player_->Repeat(repeat); +} + +DBusStatus MPRIS::GetStatus() const { + DBusStatus status; + switch (player_->GetState()) { + case Engine::Empty: + case Engine::Idle: + status.play = DBusStatus::Mpris_Stopped; + break; + case Engine::Playing: + status.play = DBusStatus::Mpris_Playing; + break; + case Engine::Paused: + status.play = DBusStatus::Mpris_Paused; + break; + } + + PlaylistManager* playlists_ = player_->GetPlaylists(); + PlaylistSequence::RepeatMode repeat_mode = playlists_->sequence()->repeat_mode(); + + status.random = playlists_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off ? 0 : 1; + status.repeat = repeat_mode == PlaylistSequence::Repeat_Track ? 1 : 0; + status.repeat_playlist = (repeat_mode == PlaylistSequence::Repeat_Album || + repeat_mode == PlaylistSequence::Repeat_Playlist) ? 1 : 0; + return status; + +} +void MPRIS::VolumeSet(int volume) { + player_->SetVolume(volume); +} + +int MPRIS::VolumeGet() const { + return player_->VolumeGet(); +} +void MPRIS::PositionSet(int pos) { + player_->Seek(pos/1000); +} + +int MPRIS::PositionGet() const { + return player_->PositionGet()*1000; +} + +QVariantMap MPRIS::GetMetadata() const { + return GetMetadata(player_->GetCurrentItem()); +} + +int MPRIS::GetCaps() const { + int caps = CAN_HAS_TRACKLIST; + Engine::State state = player_->GetState(); + boost::shared_ptr current_item_ = player_->GetCurrentItem(); + PlaylistManager* playlists_ = player_->GetPlaylists(); + + if (state == Engine::Paused) { + caps |= CAN_PLAY; + } + + if (current_item_) { + caps |= CAN_PROVIDE_METADATA; + if (state == Engine::Playing && current_item_->options() & PlaylistItem::PauseDisabled) { + caps |= CAN_PAUSE; + } + if (state != Engine::Empty && current_item_->Metadata().filetype() != Song::Type_Stream) { + caps |= CAN_SEEK; + } + } + + if (playlists_->active()->next_index() != -1 || + playlists_->active()->current_item_options() & PlaylistItem::ContainsMultipleTracks) { + caps |= CAN_GO_NEXT; + } + if (playlists_->active()->previous_index() != -1) { + caps |= CAN_GO_PREV; + } + + return caps; +} + +void MPRIS::VolumeUp(int vol) { + player_->VolumeUp(vol); +} + +void MPRIS::VolumeDown(int vol) { + player_->VolumeDown(vol); +} + +void MPRIS::Mute() { + player_->Mute(); +} + +void MPRIS::ShowOSD() { + player_->ShowOSD(); +} + +int MPRIS::AddTrack(const QString& track, bool play) { + return player_->AddTrack(track, play); +} + +void MPRIS::DelTrack(int index) { + player_->DelTrack(index); +} + +int MPRIS::GetCurrentTrack() const { + return player_->GetCurrentTrack(); +} + +int MPRIS::GetLength() const { + return player_->GetLength(); +} + +QVariantMap MPRIS::GetMetadata(int pos) const { + return GetMetadata(player_->GetItemAt(pos)); +} + +void MPRIS::SetLoop(bool enable) { + player_->SetLoop(enable); +} + +void MPRIS::SetRandom(bool enable) { + player_->SetRandom(enable); +} + +void MPRIS::PlayTrack(int index) { + player_->PlayAt(index, Engine::Manual, true); +} + +void MPRIS::EmitCapsChange(int param) { + emit CapsChange(param); +} + +void MPRIS::EmitTrackChange(QVariantMap param) { + emit TrackChange(param); +} + +void MPRIS::EmitStatusChange(DBusStatus param) { + emit StatusChange(param); +} + +void MPRIS::EmitTrackListChange(int i) { + emit TrackListChange(i); +} + + +QVariantMap MPRIS::GetMetadata(PlaylistItemPtr item) const { + using metadata::AddMetadata; + QVariantMap ret; + + if (!item) + return ret; + + Song song = item->Metadata(); + 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); + AddMetadata("year", song.year(), &ret); + AddMetadata("genre", song.genre(), &ret); + AddMetadata("disc", song.disc(), &ret); + AddMetadata("comment", song.comment(), &ret); + AddMetadata("bitrate", song.bitrate(), &ret); + AddMetadata("samplerate", song.samplerate(), &ret); + AddMetadata("bpm", song.bpm(), &ret); + AddMetadata("composer", song.composer(), &ret); + + return ret; +} diff --git a/src/core/mpris.h b/src/core/mpris.h index b7bfd6623..ebf822ca6 100644 --- a/src/core/mpris.h +++ b/src/core/mpris.h @@ -18,25 +18,148 @@ #ifndef MPRIS_H #define MPRIS_H +#include "core/player.h" + +#include #include #include +class Player; + + +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 repeat_playlist; // Stop_When_Finished = 0, Never_Give_Up_Playing = 1, Never_Let_You_Down = 42 + + enum MprisPlayState { + Mpris_Playing = 0, + Mpris_Paused = 1, + Mpris_Stopped = 2, + }; +}; +Q_DECLARE_METATYPE(DBusStatus); + struct Version { quint16 minor; quint16 major; }; Q_DECLARE_METATYPE(Version) +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, +}; + + +#ifdef Q_WS_X11 +# include + QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status); + const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status); +#endif + + +namespace metadata { + +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; + } +} + +inline void AddMetadata(const QString& key, const QDateTime& metadata, QVariantMap* map) { + if (metadata.isValid()) { + (*map)[key] = metadata; + } +} + +inline void AddMetadata(const QString &key, const QStringList& metadata, QVariantMap *map) { + if (!metadata.isEmpty()) { + (*map)[key] = metadata; + } +} + +} // namespace metadata + 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); + +public: + // Root interface + MPRIS(Player* player, QObject* parent); + QString Identity(); void Quit(); Version MprisVersion(); + + // Player Interface + void Pause(); + void Stop(); + void Prev(); + void Play(); + void Next(); + void Repeat(bool); + DBusStatus GetStatus() const; + void VolumeSet(int volume); + int VolumeGet() const; + void PositionSet(int pos); + int PositionGet() const; + QVariantMap GetMetadata() const; + QVariantMap GetMetadata(PlaylistItemPtr item) const; + int GetCaps() const; + + // Amarok Extensions for Player + void VolumeUp(int vol); + void VolumeDown(int vol); + void Mute(); + void ShowOSD(); + + // Tracklist Interface + 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 Emitters + void EmitCapsChange(int param); + void EmitTrackChange(QVariantMap param); + void EmitStatusChange(DBusStatus); + void EmitTrackListChange(int i); + +signals: + void CapsChange(int); + void TrackChange(QVariantMap); + void StatusChange(DBusStatus); + // TrackList + void TrackListChange(int i); + +private: + Player* player_; }; #endif // MPRIS_H + + + diff --git a/src/core/mpris2.cpp b/src/core/mpris2.cpp new file mode 100644 index 000000000..16f52b968 --- /dev/null +++ b/src/core/mpris2.cpp @@ -0,0 +1,395 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + 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 . +*/ + +#include "config.h" +#include "mpris.h" +#include "mpris2.h" +#include "core/albumcoverloader.h" +#include "core/mpris2_player.h" +#include "core/mpris2_root.h" +#include "core/mpris2_tracklist.h" +#include "core/player.h" +#include "playlist/playlistmanager.h" +#include "playlist/playlistsequence.h" +#include "ui/mainwindow.h" + +#include +#include + +#ifdef HAVE_LIBINDICATE +# include +#endif + +const char* MPRIS2::kMprisObjectPath = "/org/mpris/MediaPlayer2"; +const char* MPRIS2::kServiceName = "org.mpris.MediaPlayer2.clementine"; +const char* MPRIS2::kFreedesktopPath = "org.freedesktop.DBus.Properties"; + +MPRIS2::MPRIS2(MainWindow* main_window, Player* player, QObject* parent) + : QObject(parent), + cover_loader_(new BackgroundThreadImplementation(this)), + art_request_id_(0), + ui_(main_window), + player_(player) +{ + cover_loader_->Start(); + connect(cover_loader_, SIGNAL(Initialised()), SLOT(Initialised())); + + new Mpris2Root(this); + new Mpris2TrackList(this); + new Mpris2Player(this); + + QDBusConnection::sessionBus().registerService(kServiceName); + QDBusConnection::sessionBus().registerObject(kMprisObjectPath, this); + +#ifdef HAVE_LIBINDICATE + QIndicate::Server* indicate_server = QIndicate::Server::defaultInstance(); + indicate_server->setType("music.clementine"); + indicate_server->setDesktopFile(DesktopEntry()); + indicate_server->show(); +#endif +} + +void MPRIS2::emitNotification(const QString& name, const QVariant& val) { + QDBusMessage msg = QDBusMessage::createSignal( + kMprisObjectPath, kFreedesktopPath, "PropertiesChanged"); + QVariantMap map; + map.insert(name, val); + QVariantList args = QVariantList() + << "org.mpris.MediaPlayer2.Player" + << map + << QStringList(); + msg.setArguments(args); + QDBusConnection::sessionBus().send(msg); +} + +void MPRIS2::emitNotification(const QString& name) { + QVariant value; + if (name == "PlaybackStatus") value = PlaybackStatus(); + else if (name == "LoopStatus") value = LoopStatus(); + else if (name == "Shuffle") value = Shuffle(); + else if (name == "Metadata") value = Metadata(); + else if (name == "Volume") value = Volume(); + else if (name == "Position") value = Position(); + + if (value.isValid()) + emitNotification(name, value); +} + +//------------------Root Interface--------------------------// + +bool MPRIS2::CanQuit() const { + return true; +} + +bool MPRIS2::CanRaise() const { + return true; +} + +bool MPRIS2::HasTrackList() const { + return true; +} + +QString MPRIS2::Identity() const { + return QString("%1 %2").arg( + QCoreApplication::applicationName(), + QCoreApplication::applicationVersion()); +} + +QString MPRIS2::DesktopEntry() const { + QStringList xdg_data_dirs = QString(getenv("XDG_DATA_DIRS")).split(":"); + xdg_data_dirs.append("/usr/local/share/"); + xdg_data_dirs.append("/usr/share/"); + + foreach (const QString& directory, xdg_data_dirs) { + QString path = QString("%1/applications/%2.desktop"). + arg(directory, QApplication::applicationName().toLower()); + if (QFile::exists(path)) + return path; + } + return QString(); +} + +QStringList MPRIS2::SupportedUriSchemes() const { + static QStringList res = QStringList() + << "file" + << "http" + << "cdda" + << "smb" + << "sftp"; + return res; +} + +QStringList MPRIS2::SupportedMimeTypes() const { + static QStringList res = QStringList() + << "application/ogg" + << "application/x-ogg" + << "application/x-ogm-audio" + << "audio/aac" + << "audio/mp4" + << "audio/mpeg" + << "audio/mpegurl" + << "audio/ogg" + << "audio/vnd.rn-realaudio" + << "audio/vorbis" + << "audio/x-flac" + << "audio/x-mp3" + << "audio/x-mpeg" + << "audio/x-mpegurl" + << "audio/x-ms-wma" + << "audio/x-musepack" + << "audio/x-oggflac" + << "audio/x-pn-realaudio" + << "audio/x-scpls" + << "audio/x-speex" + << "audio/x-vorbis" + << "audio/x-vorbis+ogg" + << "audio/x-wav" + << "video/x-ms-asf" + << "x-content/audio-player"; + return res; +} + +void MPRIS2::Raise() { + ui_->show(); + ui_->activateWindow(); +} + +void MPRIS2::Quit() { + qApp->quit(); +} + +QString MPRIS2::PlaybackStatus() const { + switch (player_->GetState()) { + case Engine::Playing: return "Playing"; + case Engine::Paused: return "Paused"; + default: return "Stopped"; + } +} + +QString MPRIS2::LoopStatus() const { + switch (player_->GetPlaylists()->sequence()->repeat_mode()) { + case PlaylistSequence::Repeat_Album: + case PlaylistSequence::Repeat_Playlist: return "Playlist"; + case PlaylistSequence::Repeat_Track: return "Track"; + default: return "None"; + } +} + +void MPRIS2::SetLoopStatus(const QString& value) { + if (value == "None") { + player_->SetLoop(PlaylistSequence::Repeat_Off); + } else if (value == "Track") { + player_->SetLoop(PlaylistSequence::Repeat_Track); + } else if (value == "Playlist") { + player_->SetLoop(PlaylistSequence::Repeat_Playlist); + } + emitNotification("LoopStatus", value); +} + +double MPRIS2::Rate() const { + return 1.0; +} + +void MPRIS2::SetRate(double) { + // Do nothing +} + +bool MPRIS2::Shuffle() const { + return player_->GetPlaylists()->sequence()->shuffle_mode() != + PlaylistSequence::Shuffle_Off; +} + +void MPRIS2::SetShuffle(bool value) { + player_->SetRandom(value); + emitNotification("Shuffle", value); +} + +QVariantMap MPRIS2::Metadata() const { + return last_metadata_; +} + +void MPRIS2::UpdateMetadata(PlaylistItemPtr item) { + last_metadata_ = QVariantMap(); + + // Load the cover art + art_request_item_ = item; + art_request_id_ = cover_loader_->Worker()->LoadImageAsync(item->Metadata()); +} + +void MPRIS2::Initialised() { + cover_loader_->Worker()->SetPadOutputImage(true); + cover_loader_->Worker()->SetDefaultOutputImage(QImage(":nocover.png")); + connect(cover_loader_->Worker().get(), SIGNAL(ImageLoaded(quint64,QImage)), + SLOT(TempArtLoaded(quint64,QImage))); +} + +void MPRIS2::TempArtLoaded(quint64 id, const QImage& image) { + if (id != art_request_id_ || !art_request_item_) + return; + art_request_id_ = 0; + last_metadata_ = QVariantMap(); + + Song song = art_request_item_->Metadata(); + + QString track_id = QString("/org/mpris/MediaPlayer2/Track/%1"). + arg(QString::number(player_->GetCurrentTrack())); + + using metadata::AddMetadata; + AddMetadata("mpris:trackid", track_id, &last_metadata_); + AddMetadata("xesam:url", art_request_item_->Url().toString(), &last_metadata_); + AddMetadata("xesam:title", song.PrettyTitle(), &last_metadata_); + AddMetadata("xesam:artist", QStringList() << song.artist(), &last_metadata_); + AddMetadata("xesam:album", song.album(), &last_metadata_); + AddMetadata("xesam:albumArtist", song.albumartist(), &last_metadata_); + AddMetadata("mpris:length", song.length()*1e6, &last_metadata_); + AddMetadata("xesam:trackNumber", song.track(), &last_metadata_); + AddMetadata("xesam:genre", song.genre(), &last_metadata_); + AddMetadata("xesam:discNumber", song.disc(), &last_metadata_); + AddMetadata("xesam:comment", song.comment(), &last_metadata_); + AddMetadata("xesam:contentCreated", QDateTime(QDate(song.year(),1,1)), &last_metadata_); + AddMetadata("xesam:audioBPM", song.bpm(), &last_metadata_); + AddMetadata("xesam:composer", song.composer(), &last_metadata_); + + if (!image.isNull()) { + temp_art_.reset(new QTemporaryFile(QDir::tempPath() + "/clementine-art-XXXXXX.jpg")); + temp_art_->open(); + image.save(temp_art_->fileName(), "JPEG"); + AddMetadata("mpris:artUrl", "file://" + temp_art_->fileName(), &last_metadata_); + } + + emitNotification("Metadata", last_metadata_); + art_request_item_.reset(); +} + +double MPRIS2::Volume() const { + return player_->VolumeGet() / 100; +} + +void MPRIS2::SetVolume(double value) { + player_->SetVolume(value * 100); + emitNotification("Volume",value); +} + +qlonglong MPRIS2::Position() const { + return player_->PositionGet() * 1e6; +} + +double MPRIS2::MaximumRate() const { + return 1.0; +} + +double MPRIS2::MinimumRate() const { + return 1.0; +} + +bool MPRIS2::CanGoNext() const { + return true; +} + +bool MPRIS2::CanGoPrevious() const { + return true; +} + +bool MPRIS2::CanPlay() const { + return true; +} + +bool MPRIS2::CanPause() const { + return true; +} + +bool MPRIS2::CanSeek() const { + return true; +} + +bool MPRIS2::CanControl() const { + return true; +} + +void MPRIS2::Next() { + player_->Next(); + emitNotification("PlaybackStatus",PlaybackStatus()); + emitNotification("Metadata",Metadata()); +} + +void MPRIS2::Previous() { + player_->Previous(); + emitNotification("PlaybackStatus",PlaybackStatus()); + emitNotification("Metadata",Metadata()); +} + +void MPRIS2::Pause() { + player_->Pause(); + emitNotification("PlaybackStatus",PlaybackStatus()); + emitNotification("Metadata",Metadata()); +} + +void MPRIS2::PlayPause() { + player_->PlayPause(); + emitNotification("PlaybackStatus",PlaybackStatus()); + emitNotification("Metadata",Metadata()); +} + +void MPRIS2::Stop() { + player_->Stop(); + emitNotification("PlaybackStatus",PlaybackStatus()); + emitNotification("Metadata",Metadata()); +} + +void MPRIS2::Play() { + player_->Play(); + emitNotification("PlaybackStatus",PlaybackStatus()); + emitNotification("Metadata",Metadata()); +} + +void MPRIS2::Seek(qlonglong offset) { + player_->Seek(offset*1e6); +} + +void MPRIS2::SetPosition(const QDBusObjectPath& trackId, qlonglong offset) { + //TODO +} + +void MPRIS2::OpenUri(const QString &uri) { + player_->AddTrack(uri,true); +} + +TrackIds MPRIS2::Tracks() const { + //TODO + return TrackIds(); +} + +bool MPRIS2::CanEditTracks() const { + return false; +} + +TrackMetadata MPRIS2::GetTracksMetadata(const TrackIds &tracks) const { + //TODO + return TrackMetadata(); +} + +void MPRIS2::AddTrack(const QString &uri, const QDBusObjectPath &afterTrack, bool setAsCurrent) { + //TODO +} + +void MPRIS2::RemoveTrack(const QDBusObjectPath &trackId) { + //TODO +} + +void MPRIS2::GoTo(const QDBusObjectPath &trackId) { + //TODO +} diff --git a/src/core/mpris2.h b/src/core/mpris2.h new file mode 100644 index 000000000..6905094f2 --- /dev/null +++ b/src/core/mpris2.h @@ -0,0 +1,166 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + 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 . +*/ + +#ifndef MPRIS2_H +#define MPRIS2_H + +#include "core/backgroundthread.h" +#include "playlist/playlistitem.h" + +#include +#include +#include + +#include + +class AlbumCoverLoader; +class MainWindow; +class Player; + +class QTemporaryFile; + +typedef QList TrackMetadata; +typedef QList TrackIds; +Q_DECLARE_METATYPE(TrackMetadata) + +class MPRIS2 : public QObject { + Q_OBJECT + + //org.mpris.MediaPlayer2 MPRIS 2.0 Root interface + Q_PROPERTY( bool CanQuit READ CanQuit ) + Q_PROPERTY( bool CanRaise READ CanRaise ) + Q_PROPERTY( bool HasTrackList READ HasTrackList ) + Q_PROPERTY( QString Identity READ Identity ) + Q_PROPERTY( QString DesktopEntry READ DesktopEntry ) + Q_PROPERTY( QStringList SupportedUriSchemes READ SupportedUriSchemes ) + Q_PROPERTY( QStringList SupportedMimeTypes READ SupportedMimeTypes ) + + //org.mpris.MediaPlayer2.Player MPRIS 2.0 Player interface + Q_PROPERTY( QString PlaybackStatus READ PlaybackStatus ) + Q_PROPERTY( QString LoopStatus READ LoopStatus WRITE SetLoopStatus ) + Q_PROPERTY( double Rate READ Rate WRITE SetRate ) + Q_PROPERTY( bool Shuffle READ Shuffle WRITE SetShuffle ) + Q_PROPERTY( QVariantMap Metadata READ Metadata ) + Q_PROPERTY( double Volume READ Volume WRITE SetVolume ) + Q_PROPERTY( qlonglong Position READ Position ) + Q_PROPERTY( double MinimumRate READ MinimumRate ) + Q_PROPERTY( double MaximumRate READ MaximumRate ) + Q_PROPERTY( bool CanGoNext READ CanGoNext ) + Q_PROPERTY( bool CanGoPrevious READ CanGoPrevious ) + Q_PROPERTY( bool CanPlay READ CanPlay ) + Q_PROPERTY( bool CanPause READ CanPause ) + Q_PROPERTY( bool CanSeek READ CanSeek ) + Q_PROPERTY( bool CanControl READ CanControl ) + + //org.mpris.MediaPlayer2.TrackList MPRIS 2.0 Player interface + Q_PROPERTY( TrackIds Tracks READ Tracks ) + Q_PROPERTY( bool CanEditTracks READ CanEditTracks ) + +public: + MPRIS2(MainWindow* main_window, Player* player, QObject* parent); + void emitNotification(const QString& name); + void emitNotification(const QString& name, const QVariant& val); + + // Root Properties + bool CanQuit() const; + bool CanRaise() const; + bool HasTrackList() const; + QString Identity() const; + QString DesktopEntry() const; + QStringList SupportedUriSchemes() const; + QStringList SupportedMimeTypes() const; + + // Methods + void Raise(); + void Quit(); + + // Player Properties + QString PlaybackStatus() const; + QString LoopStatus() const; + void SetLoopStatus(const QString& value); + double Rate() const; + void SetRate(double value); + bool Shuffle() const; + void SetShuffle(bool value); + QVariantMap Metadata() const; + double Volume() const; + void SetVolume(double value); + qlonglong Position() const; + double MaximumRate() const; + double MinimumRate() const; + bool CanGoNext() const; + bool CanGoPrevious() const; + bool CanPlay() const; + bool CanPause() const; + bool CanSeek() const; + bool CanControl() const; + + // Methods + void Next(); + void Previous(); + void Pause(); + void PlayPause(); + void Stop(); + void Play(); + void Seek(qlonglong offset); + void SetPosition(const QDBusObjectPath& trackId, qlonglong offset); + void OpenUri(const QString& uri); + + // TrackList Properties + TrackIds Tracks() const; + bool CanEditTracks() const; + + // Methods + TrackMetadata GetTracksMetadata(const TrackIds& tracks) const; + void AddTrack(const QString& uri, const QDBusObjectPath& afterTrack, bool setAsCurrent); + void RemoveTrack(const QDBusObjectPath& trackId); + void GoTo(const QDBusObjectPath& trackId); + +public slots: + void UpdateMetadata(PlaylistItemPtr item); + +signals: + // Player + void Seeked(qlonglong position); + + // TrackList + void TrackListReplaced(const TrackIds& Tracks, QDBusObjectPath CurrentTrack); + void TrackAdded(const TrackMetadata& Metadata, QDBusObjectPath AfterTrack); + void TrackRemoved(const QDBusObjectPath& trackId); + void TrackMetadataChanged(const QDBusObjectPath& trackId, const TrackMetadata& metadata); + +private slots: + void Initialised(); + void TempArtLoaded(quint64 id, const QImage& image); + +private: + static const char* kMprisObjectPath; + static const char* kServiceName; + static const char* kFreedesktopPath; + + boost::scoped_ptr temp_art_; + BackgroundThread* cover_loader_; + quint64 art_request_id_; + PlaylistItemPtr art_request_item_; + + QVariantMap last_metadata_; + + MainWindow* ui_; + Player* player_; +}; + +#endif diff --git a/src/core/player.cpp b/src/core/player.cpp index 139d57435..542be1e94 100644 --- a/src/core/player.cpp +++ b/src/core/player.cpp @@ -37,9 +37,10 @@ #endif #ifdef Q_WS_X11 -# include "core/mpris_player.h" -# include "core/mpris_tracklist.h" +# include "mpris.h" +# include "mpris2.h" # include +# include #endif #include @@ -73,9 +74,11 @@ const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status) { } #endif -Player::Player(PlaylistManager* playlists, LastFMService* lastfm, - Engine::Type engine, QObject* parent) +Player::Player(MainWindow* main_window, PlaylistManager* playlists, + LastFMService* lastfm, Engine::Type engine, QObject* parent) : QObject(parent), + mpris_(NULL), + mpris2_(NULL), playlists_(playlists), lastfm_(lastfm), engine_(CreateEngine(engine)), @@ -84,21 +87,25 @@ Player::Player(PlaylistManager* playlists, LastFMService* lastfm, toad_stream_(-1), volume_before_mute_(0) { +#ifdef Q_WS_X11 + // MPRIS DBus interface. + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + //MPRIS 1.0 implementation + mpris_ = new MPRIS(this, this); + //MPRIS 2.0 implementation + mpris2_ = new MPRIS2(main_window, this, this); +#endif + settings_.beginGroup("Player"); SetVolume(settings_.value("volume", 50).toInt()); connect(engine_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString))); - // MPRIS DBus interface. -#ifdef Q_WS_X11 - 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); -#endif } Player::~Player() { @@ -265,6 +272,9 @@ void Player::PlayPause() { break; } } + + if (mpris2_) + mpris2_->emitNotification("PlaybackStatus"); } void Player::Stop() { @@ -291,8 +301,14 @@ void Player::EngineStateChanged(Engine::State state) { case Engine::Empty: case Engine::Idle: emit Stopped(); break; } - emit StatusChange(GetStatus()); - emit CapsChange(GetCaps()); + if (mpris_) { + mpris_->EmitStatusChange(mpris_->GetStatus()); + mpris_->EmitCapsChange(mpris_->GetCaps()); + } + if (mpris2_) { + mpris2_->emitNotification("PlaybackStatus"); + mpris2_->emitNotification("Metadata"); + } } void Player::SetVolume(int value) { @@ -302,8 +318,12 @@ void Player::SetVolume(int value) { settings_.setValue("volume", volume); engine_->SetVolume(volume); - if (volume != old_volume) + if (volume != old_volume){ emit VolumeChanged(volume); + if (mpris2_) + mpris2_->emitNotification("Volume"); + } + } int Player::GetVolume() const { @@ -337,12 +357,23 @@ void Player::PlayAt(int index, Engine::TrackChangeType change, bool reshuffle) { lastfm_->NowPlaying(current_item_->Metadata()); } - emit CapsChange(GetCaps()); + if (mpris_) { + mpris_->EmitCapsChange(mpris_->GetCaps()); + } + if (mpris2_) { + mpris2_->emitNotification("PlaybackStatus"); + mpris2_->emitNotification("Metadata"); + } } -void Player::CurrentMetadataChanged(const Song &metadata) { +void Player::CurrentMetadataChanged(const Song& metadata) { lastfm_->NowPlaying(metadata); - emit TrackChange(GetMetadata()); + + PlaylistItemPtr item = playlists_->active()->current_item(); + if (mpris_) + mpris_->EmitTrackChange(mpris_->GetMetadata(item)); + if (mpris2_) + mpris2_->UpdateMetadata(item); } void Player::Seek(int seconds) { @@ -354,7 +385,7 @@ void Player::Seek(int seconds) { } void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) { - shared_ptr item = playlists_->active()->current_item(); + PlaylistItemPtr item = playlists_->active()->current_item(); if (!item) return; @@ -383,102 +414,10 @@ void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) { playlists_->active()->SetStreamMetadata(item->Url(), song); } -int Player::GetCaps() const { - int caps = CAN_HAS_TRACKLIST; - if (current_item_) { caps |= CAN_PROVIDE_METADATA; } - if (GetState() == Engine::Playing && current_item_->options() & PlaylistItem::PauseDisabled) { - caps |= CAN_PAUSE; - } - if (GetState() == Engine::Paused) { - caps |= CAN_PLAY; - } - if (GetState() != Engine::Empty && current_item_->Metadata().filetype() != Song::Type_Stream) { - caps |= CAN_SEEK; - } - if (playlists_->active()->next_index() != -1 || - playlists_->active()->current_item_options() & PlaylistItem::ContainsMultipleTracks) { - caps |= CAN_GO_NEXT; - } - if (playlists_->active()->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 = DBusStatus::Mpris_Stopped; - break; - case Engine::Playing: - status.play = DBusStatus::Mpris_Playing; - break; - case Engine::Paused: - status.play = DBusStatus::Mpris_Paused; - break; - } - status.random = playlists_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off ? 0 : 1; - PlaylistSequence::RepeatMode repeat_mode = playlists_->sequence()->repeat_mode(); - status.repeat = repeat_mode == PlaylistSequence::Repeat_Track ? 1 : 0; - status.repeat_playlist = (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; - - const Song& song = item.Metadata(); - 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); - AddMetadata("year", song.year(), &ret); - AddMetadata("genre", song.genre(), &ret); - AddMetadata("disc", song.disc(), &ret); - AddMetadata("comment", song.comment(), &ret); - AddMetadata("bitrate", song.bitrate(), &ret); - AddMetadata("samplerate", song.samplerate(), &ret); - AddMetadata("bpm", song.bpm(), &ret); - AddMetadata("composer", song.composer(), &ret); - AddMetadata("mtime", song.length() * 1000, &ret); - - return ret; -} - -QVariantMap Player::GetMetadata() const { - shared_ptr item = playlists_->active()->current_item(); - if (item) { - return GetMetadata(*item); - } - return QVariantMap(); -} - -QVariantMap Player::GetMetadata(int track) const { - if (track >= playlists_->active()->rowCount() || track < 0) { - return QVariantMap(); - } - const PlaylistItem& item = *(playlists_->active()->item_at(track)); - return GetMetadata(item); +PlaylistItemPtr Player::GetItemAt(int pos) const { + if (pos < 0 || pos >= playlists_->active()->rowCount()) + return PlaylistItemPtr(); + return playlists_->active()->item_at(pos); } void Player::Mute() { @@ -527,10 +466,6 @@ int Player::PositionGet() const { return engine_->position(); } -void Player::PositionSet(int x) { - Seek(x / 1000); -} - void Player::Repeat(bool enable) { playlists_->sequence()->SetRepeatMode( enable ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off); @@ -553,10 +488,6 @@ int Player::VolumeGet() const { return GetVolume(); } -void Player::VolumeSet(int volume) { - SetVolume(volume); -} - int Player::AddTrack(const QString& track, bool play_now) { playlists_->active()->InsertUrls(QList() << QUrl(track), play_now); return 0; @@ -584,12 +515,9 @@ void Player::SetRandom(bool enable) { enable ? PlaylistSequence::Shuffle_All : PlaylistSequence::Shuffle_Off); } -void Player::PlayTrack(int index) { - PlayAt(index, Engine::Manual, true); -} - void Player::PlaylistChanged() { - emit TrackListChange(GetLength()); + if (mpris_) + mpris_->EmitTrackListChange(GetLength()); } void Player::TrackAboutToEnd() { diff --git a/src/core/player.h b/src/core/player.h index aceaab8d0..2cf91297d 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -23,6 +23,7 @@ #include +#include "core/albumcoverloader.h" #include "core/song.h" #include "engines/engine_fwd.h" #include "playlist/playlistitem.h" @@ -30,33 +31,23 @@ class PlaylistManager; class Settings; class LastFMService; +class MainWindow; +class MPRIS; +class MPRIS2; -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 repeat_playlist; // Stop_When_Finished = 0, Never_Give_Up_Playing = 1, Never_Let_You_Down = 42 - - enum MprisPlayState { - Mpris_Playing = 0, - Mpris_Paused = 1, - Mpris_Stopped = 2, - }; -}; -Q_DECLARE_METATYPE(DBusStatus); #ifdef Q_WS_X11 -#include -QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status); -const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status); +# include + QDBusArgument& operator<< (QDBusArgument& arg, const QImage& image); + const QDBusArgument& operator>> (const QDBusArgument& arg, QImage& image); #endif class Player : public QObject { Q_OBJECT public: - Player(PlaylistManager* playlists, LastFMService* lastfm, Engine::Type engine, - QObject* parent = 0); + Player(MainWindow* main_window, PlaylistManager* playlists, + LastFMService* lastfm, Engine::Type engine, QObject* parent = 0); ~Player(); EngineBase* CreateEngine(Engine::Type engine); @@ -67,18 +58,8 @@ class Player : public QObject { int GetVolume() const; PlaylistItemPtr 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, - }; + PlaylistItemPtr GetItemAt(int pos) const; + PlaylistManager* GetPlaylists() {return playlists_; } public slots: void ReloadSettings(); @@ -108,16 +89,12 @@ class Player : public QObject { void AllHail(bool hypnotoad); // MPRIS /Player - int GetCaps() const; - DBusStatus GetStatus() const; - QVariantMap GetMetadata() const; void Mute(); void Pause(); void Stop(); void Play(); void Prev(); int PositionGet() const; - void PositionSet(int); void Repeat(bool); void ShowOSD(); void VolumeDown(int); @@ -125,20 +102,15 @@ class Player : public QObject { void VolumeDown() { VolumeDown(4); } void VolumeUp() { VolumeUp(4); } 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 Playing(); void Paused(); @@ -150,20 +122,11 @@ class Player : public QObject { void ForceShowOSD(Song); - // MPRIS - // Player - void CapsChange(int); - void TrackChange(QVariantMap); - void StatusChange(DBusStatus); - // TrackList - void TrackListChange(int i); - private slots: void EngineStateChanged(Engine::State); void EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle); void TrackAboutToEnd(); void TrackEnded(); - // Play the next item on the playlist - disregarding radio stations like // last.fm that might have more tracks. void NextItem(Engine::TrackChangeType change); @@ -171,7 +134,8 @@ class Player : public QObject { void NextInternal(Engine::TrackChangeType); private: - QVariantMap GetMetadata(const PlaylistItem& item) const; + MPRIS* mpris_; + MPRIS2* mpris2_; PlaylistManager* playlists_; LastFMService* lastfm_; diff --git a/src/dbus/org.mpris.MediaPlayer2.Player.xml b/src/dbus/org.mpris.MediaPlayer2.Player.xml new file mode 100644 index 000000000..3c11567fa --- /dev/null +++ b/src/dbus/org.mpris.MediaPlayer2.Player.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/dbus/org.mpris.MediaPlayer2.TrackList.xml b/src/dbus/org.mpris.MediaPlayer2.TrackList.xml new file mode 100644 index 000000000..51b4bbc6a --- /dev/null +++ b/src/dbus/org.mpris.MediaPlayer2.TrackList.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/dbus/org.mpris.MediaPlayer2.xml b/src/dbus/org.mpris.MediaPlayer2.xml new file mode 100644 index 000000000..74208a733 --- /dev/null +++ b/src/dbus/org.mpris.MediaPlayer2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/src/main.cpp b/src/main.cpp index 99971f3c6..28f9f8113 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -50,12 +50,7 @@ #include -#ifdef Q_WS_X11 -# include -# include -# include "core/mpris.h" -# include "widgets/osd.h" -#endif + #ifdef HAVE_GSTREAMER # include @@ -204,15 +199,6 @@ int main(int argc, char *argv[]) { Echonest::Config::instance()->setAPIKey("DFLFLJBUF4EGTXHIG"); Echonest::Config::instance()->setNetworkAccessManager(new NetworkAccessManager); - // MPRIS DBus interface. -#ifdef Q_WS_X11 - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - QDBusConnection::sessionBus().registerService("org.mpris.clementine"); - MPRIS mpris; -#endif - // Seed the random number generator srand(time(NULL)); diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index 442ad0668..de0c4ff8c 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -170,7 +170,7 @@ MainWindow::MainWindow(Engine::Type engine, QWidget *parent) // Create stuff that needs the database radio_model_ = new RadioModel(database_, task_manager_, this); - player_ = new Player(playlists_, radio_model_->GetLastFMService(), engine, this); + player_ = new Player(this, playlists_, radio_model_->GetLastFMService(), engine, this); library_ = new Library(database_, task_manager_, this); devices_ = new DeviceManager(database_, task_manager_, this);