Add MPRIS2 and gnome sound menu support
This commit is contained in:
parent
eb818a2cd2
commit
626c75cd45
|
@ -26,6 +26,7 @@ endif(APPLE)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
find_package(Boost REQUIRED)
|
find_package(Boost REQUIRED)
|
||||||
find_package(Gettext REQUIRED)
|
find_package(Gettext REQUIRED)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
|
pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
|
||||||
pkg_check_modules(GSTREAMER gstreamer-0.10)
|
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(PLIST libplist)
|
||||||
pkg_check_modules(USBMUXD libusbmuxd)
|
pkg_check_modules(USBMUXD libusbmuxd)
|
||||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||||
|
pkg_check_modules(INDICATEQT indicate-qt)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
@ -123,6 +125,7 @@ option(ENABLE_LIBMTP "MTP support" ON)
|
||||||
option(ENABLE_GIO "GIO backend" ON)
|
option(ENABLE_GIO "GIO backend" ON)
|
||||||
option(ENABLE_VISUALISATIONS "Use libprojectm visualisations" 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(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)
|
if(NOT APPLE AND NOT WIN32)
|
||||||
option(ENABLE_WIIMOTEDEV "Enable Wii remote support in Clementine" ON)
|
option(ENABLE_WIIMOTEDEV "Enable Wii remote support in Clementine" ON)
|
||||||
|
@ -144,6 +147,10 @@ if(ENABLE_LIBMTP AND LIBMTP_FOUND)
|
||||||
set(HAVE_LIBMTP ON)
|
set(HAVE_LIBMTP ON)
|
||||||
endif(ENABLE_LIBMTP AND LIBMTP_FOUND)
|
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)
|
if(ENABLE_VISUALISATIONS)
|
||||||
# When/if upstream accepts our patches then these options can be used to link
|
# When/if upstream accepts our patches then these options can be used to link
|
||||||
# to system installed projectM instead.
|
# 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: iPod Touch, iPhone, iPad support" HAVE_IMOBILEDEVICE)
|
||||||
summary_add("devices: MTP support" HAVE_LIBMTP)
|
summary_add("devices: MTP support" HAVE_LIBMTP)
|
||||||
summary_add("devices: GIO backend" HAVE_GIO)
|
summary_add("devices: GIO backend" HAVE_GIO)
|
||||||
|
summary_add("Gnome sound menu integration" HAVE_LIBINDICATE)
|
||||||
summary_add("Wiimote support" ENABLE_WIIMOTEDEV)
|
summary_add("Wiimote support" ENABLE_WIIMOTEDEV)
|
||||||
summary_add("Visualisations" ENABLE_VISUALISATIONS)
|
summary_add("Visualisations" ENABLE_VISUALISATIONS)
|
||||||
summary_show()
|
summary_show()
|
||||||
|
|
|
@ -19,6 +19,11 @@ include_directories(${QTIOCOMPRESSOR_INCLUDE_DIRS})
|
||||||
include_directories(${QXT_INCLUDE_DIRS})
|
include_directories(${QXT_INCLUDE_DIRS})
|
||||||
include_directories(${ECHONEST_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)
|
cmake_policy(SET CMP0011 NEW)
|
||||||
include(../cmake/AddEngine.cmake)
|
include(../cmake/AddEngine.cmake)
|
||||||
include(../cmake/ParseArguments.cmake)
|
include(../cmake/ParseArguments.cmake)
|
||||||
|
@ -529,13 +534,24 @@ if(NOT APPLE AND NOT WIN32)
|
||||||
# MPRIS DBUS interfaces
|
# MPRIS DBUS interfaces
|
||||||
qt4_add_dbus_adaptor(SOURCES
|
qt4_add_dbus_adaptor(SOURCES
|
||||||
dbus/org.freedesktop.MediaPlayer.player.xml
|
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
|
qt4_add_dbus_adaptor(SOURCES
|
||||||
dbus/org.freedesktop.MediaPlayer.root.xml
|
dbus/org.freedesktop.MediaPlayer.root.xml
|
||||||
core/mpris.h MPRIS core/mpris_root MprisRoot)
|
core/mpris.h MPRIS core/mpris_root MprisRoot)
|
||||||
qt4_add_dbus_adaptor(SOURCES
|
qt4_add_dbus_adaptor(SOURCES
|
||||||
dbus/org.freedesktop.MediaPlayer.tracklist.xml
|
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
|
# org.freedesktop.Notifications DBUS interface
|
||||||
qt4_add_dbus_interface(SOURCES
|
qt4_add_dbus_interface(SOURCES
|
||||||
|
@ -551,8 +567,8 @@ if(NOT APPLE AND NOT WIN32)
|
||||||
dbus/udisksdevice)
|
dbus/udisksdevice)
|
||||||
|
|
||||||
# MPRIS source
|
# MPRIS source
|
||||||
list(APPEND SOURCES core/mpris.cpp)
|
list(APPEND SOURCES core/mpris.cpp core/mpris2.cpp)
|
||||||
list(APPEND HEADERS core/mpris.h)
|
list(APPEND HEADERS core/mpris.h core/mpris2.h)
|
||||||
|
|
||||||
# Wiimotedev interface classes
|
# Wiimotedev interface classes
|
||||||
if(ENABLE_WIIMOTEDEV)
|
if(ENABLE_WIIMOTEDEV)
|
||||||
|
@ -651,6 +667,10 @@ list(APPEND OTHER_SOURCES
|
||||||
core/macglobalshortcutbackend.h
|
core/macglobalshortcutbackend.h
|
||||||
core/macglobalshortcutbackend.mm
|
core/macglobalshortcutbackend.mm
|
||||||
core/modelfuturewatcher.h
|
core/modelfuturewatcher.h
|
||||||
|
core/mpris.cpp
|
||||||
|
core/mpris.h
|
||||||
|
core/mpris2.cpp
|
||||||
|
core/mpris2.h
|
||||||
devices/afcdevice.cpp
|
devices/afcdevice.cpp
|
||||||
devices/afcdevice.h
|
devices/afcdevice.h
|
||||||
devices/afcfile.cpp
|
devices/afcfile.cpp
|
||||||
|
@ -718,6 +738,7 @@ add_library(clementine_lib STATIC
|
||||||
|
|
||||||
add_dependencies(clementine_lib pot)
|
add_dependencies(clementine_lib pot)
|
||||||
|
|
||||||
|
|
||||||
target_link_libraries(clementine_lib
|
target_link_libraries(clementine_lib
|
||||||
chardet
|
chardet
|
||||||
echonest
|
echonest
|
||||||
|
@ -761,6 +782,10 @@ if(HAVE_LIBMTP)
|
||||||
target_link_libraries(clementine_lib ${LIBMTP_LIBRARIES})
|
target_link_libraries(clementine_lib ${LIBMTP_LIBRARIES})
|
||||||
endif(HAVE_LIBMTP)
|
endif(HAVE_LIBMTP)
|
||||||
|
|
||||||
|
if(HAVE_LIBINDICATE)
|
||||||
|
target_link_libraries(clementine_lib ${INDICATEQT_LIBRARIES})
|
||||||
|
endif(HAVE_LIBINDICATE)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
target_link_libraries(clementine_lib
|
target_link_libraries(clementine_lib
|
||||||
${GROWL}
|
${GROWL}
|
||||||
|
|
|
@ -45,4 +45,6 @@
|
||||||
#cmakedefine HAVE_IMOBILEDEVICE
|
#cmakedefine HAVE_IMOBILEDEVICE
|
||||||
#cmakedefine HAVE_LIBMTP
|
#cmakedefine HAVE_LIBMTP
|
||||||
|
|
||||||
|
#cmakedefine HAVE_LIBINDICATE
|
||||||
|
|
||||||
#endif // CONFIG_H_IN
|
#endif // CONFIG_H_IN
|
||||||
|
|
|
@ -18,8 +18,14 @@
|
||||||
#include "mpris.h"
|
#include "mpris.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QDBusConnection>
|
||||||
|
|
||||||
|
#include "core/mpris_player.h"
|
||||||
#include "core/mpris_root.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) {
|
QDBusArgument& operator<< (QDBusArgument& arg, const Version& version) {
|
||||||
arg.beginStructure();
|
arg.beginStructure();
|
||||||
|
@ -35,10 +41,19 @@ const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version) {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
MPRIS::MPRIS(QObject* parent)
|
MPRIS::MPRIS(Player* player, QObject* parent)
|
||||||
: QObject(parent) {
|
: QObject(parent),
|
||||||
new MprisRoot(this);
|
player_(player)
|
||||||
QDBusConnection::sessionBus().registerObject("/", this);
|
{
|
||||||
|
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() {
|
QString MPRIS::Identity() {
|
||||||
|
@ -57,3 +72,193 @@ Version MPRIS::MprisVersion() {
|
||||||
void MPRIS::Quit() {
|
void MPRIS::Quit() {
|
||||||
qApp->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<PlaylistItem> 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;
|
||||||
|
}
|
||||||
|
|
127
src/core/mpris.h
127
src/core/mpris.h
|
@ -18,25 +18,148 @@
|
||||||
#ifndef MPRIS_H
|
#ifndef MPRIS_H
|
||||||
#define MPRIS_H
|
#define MPRIS_H
|
||||||
|
|
||||||
|
#include "core/player.h"
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
#include <QDBusArgument>
|
#include <QDBusArgument>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
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 {
|
struct Version {
|
||||||
quint16 minor;
|
quint16 minor;
|
||||||
quint16 major;
|
quint16 major;
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(Version)
|
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>
|
||||||
|
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);
|
QDBusArgument& operator<< (QDBusArgument& arg, const Version& version);
|
||||||
const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version);
|
const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version);
|
||||||
|
|
||||||
class MPRIS : public QObject {
|
class MPRIS : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
|
||||||
MPRIS(QObject* parent = 0);
|
public:
|
||||||
|
// Root interface
|
||||||
|
MPRIS(Player* player, QObject* parent);
|
||||||
|
|
||||||
QString Identity();
|
QString Identity();
|
||||||
void Quit();
|
void Quit();
|
||||||
Version MprisVersion();
|
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
|
#endif // MPRIS_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,395 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
|
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 "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 <QApplication>
|
||||||
|
#include <QDBusConnection>
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBINDICATE
|
||||||
|
# include <qindicateserver.h>
|
||||||
|
#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<AlbumCoverLoader, AlbumCoverLoader>(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
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
|
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 MPRIS2_H
|
||||||
|
#define MPRIS2_H
|
||||||
|
|
||||||
|
#include "core/backgroundthread.h"
|
||||||
|
#include "playlist/playlistitem.h"
|
||||||
|
|
||||||
|
#include <QMetaObject>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtDBus>
|
||||||
|
|
||||||
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
|
||||||
|
class AlbumCoverLoader;
|
||||||
|
class MainWindow;
|
||||||
|
class Player;
|
||||||
|
|
||||||
|
class QTemporaryFile;
|
||||||
|
|
||||||
|
typedef QList<QVariantMap> TrackMetadata;
|
||||||
|
typedef QList<QDBusObjectPath> 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<QTemporaryFile> temp_art_;
|
||||||
|
BackgroundThread<AlbumCoverLoader>* cover_loader_;
|
||||||
|
quint64 art_request_id_;
|
||||||
|
PlaylistItemPtr art_request_item_;
|
||||||
|
|
||||||
|
QVariantMap last_metadata_;
|
||||||
|
|
||||||
|
MainWindow* ui_;
|
||||||
|
Player* player_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -37,9 +37,10 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_WS_X11
|
#ifdef Q_WS_X11
|
||||||
# include "core/mpris_player.h"
|
# include "mpris.h"
|
||||||
# include "core/mpris_tracklist.h"
|
# include "mpris2.h"
|
||||||
# include <QDBusConnection>
|
# include <QDBusConnection>
|
||||||
|
# include <QImage>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
@ -73,9 +74,11 @@ const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Player::Player(PlaylistManager* playlists, LastFMService* lastfm,
|
Player::Player(MainWindow* main_window, PlaylistManager* playlists,
|
||||||
Engine::Type engine, QObject* parent)
|
LastFMService* lastfm, Engine::Type engine, QObject* parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
|
mpris_(NULL),
|
||||||
|
mpris2_(NULL),
|
||||||
playlists_(playlists),
|
playlists_(playlists),
|
||||||
lastfm_(lastfm),
|
lastfm_(lastfm),
|
||||||
engine_(CreateEngine(engine)),
|
engine_(CreateEngine(engine)),
|
||||||
|
@ -84,21 +87,25 @@ Player::Player(PlaylistManager* playlists, LastFMService* lastfm,
|
||||||
toad_stream_(-1),
|
toad_stream_(-1),
|
||||||
volume_before_mute_(0)
|
volume_before_mute_(0)
|
||||||
{
|
{
|
||||||
|
#ifdef Q_WS_X11
|
||||||
|
// MPRIS DBus interface.
|
||||||
|
qDBusRegisterMetaType<DBusStatus>();
|
||||||
|
qDBusRegisterMetaType<Version>();
|
||||||
|
qDBusRegisterMetaType<QImage>();
|
||||||
|
qDBusRegisterMetaType<TrackMetadata>();
|
||||||
|
qDBusRegisterMetaType<TrackIds>();
|
||||||
|
//MPRIS 1.0 implementation
|
||||||
|
mpris_ = new MPRIS(this, this);
|
||||||
|
//MPRIS 2.0 implementation
|
||||||
|
mpris2_ = new MPRIS2(main_window, this, this);
|
||||||
|
#endif
|
||||||
|
|
||||||
settings_.beginGroup("Player");
|
settings_.beginGroup("Player");
|
||||||
|
|
||||||
SetVolume(settings_.value("volume", 50).toInt());
|
SetVolume(settings_.value("volume", 50).toInt());
|
||||||
|
|
||||||
connect(engine_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString)));
|
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() {
|
Player::~Player() {
|
||||||
|
@ -265,6 +272,9 @@ void Player::PlayPause() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mpris2_)
|
||||||
|
mpris2_->emitNotification("PlaybackStatus");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::Stop() {
|
void Player::Stop() {
|
||||||
|
@ -291,8 +301,14 @@ void Player::EngineStateChanged(Engine::State state) {
|
||||||
case Engine::Empty:
|
case Engine::Empty:
|
||||||
case Engine::Idle: emit Stopped(); break;
|
case Engine::Idle: emit Stopped(); break;
|
||||||
}
|
}
|
||||||
emit StatusChange(GetStatus());
|
if (mpris_) {
|
||||||
emit CapsChange(GetCaps());
|
mpris_->EmitStatusChange(mpris_->GetStatus());
|
||||||
|
mpris_->EmitCapsChange(mpris_->GetCaps());
|
||||||
|
}
|
||||||
|
if (mpris2_) {
|
||||||
|
mpris2_->emitNotification("PlaybackStatus");
|
||||||
|
mpris2_->emitNotification("Metadata");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::SetVolume(int value) {
|
void Player::SetVolume(int value) {
|
||||||
|
@ -302,8 +318,12 @@ void Player::SetVolume(int value) {
|
||||||
settings_.setValue("volume", volume);
|
settings_.setValue("volume", volume);
|
||||||
engine_->SetVolume(volume);
|
engine_->SetVolume(volume);
|
||||||
|
|
||||||
if (volume != old_volume)
|
if (volume != old_volume){
|
||||||
emit VolumeChanged(volume);
|
emit VolumeChanged(volume);
|
||||||
|
if (mpris2_)
|
||||||
|
mpris2_->emitNotification("Volume");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Player::GetVolume() const {
|
int Player::GetVolume() const {
|
||||||
|
@ -337,12 +357,23 @@ void Player::PlayAt(int index, Engine::TrackChangeType change, bool reshuffle) {
|
||||||
lastfm_->NowPlaying(current_item_->Metadata());
|
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);
|
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) {
|
void Player::Seek(int seconds) {
|
||||||
|
@ -354,7 +385,7 @@ void Player::Seek(int seconds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
|
void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
|
||||||
shared_ptr<PlaylistItem> item = playlists_->active()->current_item();
|
PlaylistItemPtr item = playlists_->active()->current_item();
|
||||||
if (!item)
|
if (!item)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -383,102 +414,10 @@ void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
|
||||||
playlists_->active()->SetStreamMetadata(item->Url(), song);
|
playlists_->active()->SetStreamMetadata(item->Url(), song);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Player::GetCaps() const {
|
PlaylistItemPtr Player::GetItemAt(int pos) const {
|
||||||
int caps = CAN_HAS_TRACKLIST;
|
if (pos < 0 || pos >= playlists_->active()->rowCount())
|
||||||
if (current_item_) { caps |= CAN_PROVIDE_METADATA; }
|
return PlaylistItemPtr();
|
||||||
if (GetState() == Engine::Playing && current_item_->options() & PlaylistItem::PauseDisabled) {
|
return playlists_->active()->item_at(pos);
|
||||||
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<PlaylistItem> 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::Mute() {
|
void Player::Mute() {
|
||||||
|
@ -527,10 +466,6 @@ int Player::PositionGet() const {
|
||||||
return engine_->position();
|
return engine_->position();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::PositionSet(int x) {
|
|
||||||
Seek(x / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::Repeat(bool enable) {
|
void Player::Repeat(bool enable) {
|
||||||
playlists_->sequence()->SetRepeatMode(
|
playlists_->sequence()->SetRepeatMode(
|
||||||
enable ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off);
|
enable ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off);
|
||||||
|
@ -553,10 +488,6 @@ int Player::VolumeGet() const {
|
||||||
return GetVolume();
|
return GetVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::VolumeSet(int volume) {
|
|
||||||
SetVolume(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Player::AddTrack(const QString& track, bool play_now) {
|
int Player::AddTrack(const QString& track, bool play_now) {
|
||||||
playlists_->active()->InsertUrls(QList<QUrl>() << QUrl(track), play_now);
|
playlists_->active()->InsertUrls(QList<QUrl>() << QUrl(track), play_now);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -584,12 +515,9 @@ void Player::SetRandom(bool enable) {
|
||||||
enable ? PlaylistSequence::Shuffle_All : PlaylistSequence::Shuffle_Off);
|
enable ? PlaylistSequence::Shuffle_All : PlaylistSequence::Shuffle_Off);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::PlayTrack(int index) {
|
|
||||||
PlayAt(index, Engine::Manual, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::PlaylistChanged() {
|
void Player::PlaylistChanged() {
|
||||||
emit TrackListChange(GetLength());
|
if (mpris_)
|
||||||
|
mpris_->EmitTrackListChange(GetLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::TrackAboutToEnd() {
|
void Player::TrackAboutToEnd() {
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <boost/scoped_ptr.hpp>
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
|
||||||
|
#include "core/albumcoverloader.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "engines/engine_fwd.h"
|
#include "engines/engine_fwd.h"
|
||||||
#include "playlist/playlistitem.h"
|
#include "playlist/playlistitem.h"
|
||||||
|
@ -30,33 +31,23 @@
|
||||||
class PlaylistManager;
|
class PlaylistManager;
|
||||||
class Settings;
|
class Settings;
|
||||||
class LastFMService;
|
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
|
#ifdef Q_WS_X11
|
||||||
#include <QDBusArgument>
|
# include <QDBusArgument>
|
||||||
QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status);
|
QDBusArgument& operator<< (QDBusArgument& arg, const QImage& image);
|
||||||
const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status);
|
const QDBusArgument& operator>> (const QDBusArgument& arg, QImage& image);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Player : public QObject {
|
class Player : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Player(PlaylistManager* playlists, LastFMService* lastfm, Engine::Type engine,
|
Player(MainWindow* main_window, PlaylistManager* playlists,
|
||||||
QObject* parent = 0);
|
LastFMService* lastfm, Engine::Type engine, QObject* parent = 0);
|
||||||
~Player();
|
~Player();
|
||||||
|
|
||||||
EngineBase* CreateEngine(Engine::Type engine);
|
EngineBase* CreateEngine(Engine::Type engine);
|
||||||
|
@ -67,18 +58,8 @@ class Player : public QObject {
|
||||||
int GetVolume() const;
|
int GetVolume() const;
|
||||||
|
|
||||||
PlaylistItemPtr GetCurrentItem() const { return current_item_; }
|
PlaylistItemPtr GetCurrentItem() const { return current_item_; }
|
||||||
|
PlaylistItemPtr GetItemAt(int pos) const;
|
||||||
// MPRIS
|
PlaylistManager* GetPlaylists() {return playlists_; }
|
||||||
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:
|
public slots:
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
@ -108,16 +89,12 @@ class Player : public QObject {
|
||||||
void AllHail(bool hypnotoad);
|
void AllHail(bool hypnotoad);
|
||||||
|
|
||||||
// MPRIS /Player
|
// MPRIS /Player
|
||||||
int GetCaps() const;
|
|
||||||
DBusStatus GetStatus() const;
|
|
||||||
QVariantMap GetMetadata() const;
|
|
||||||
void Mute();
|
void Mute();
|
||||||
void Pause();
|
void Pause();
|
||||||
void Stop();
|
void Stop();
|
||||||
void Play();
|
void Play();
|
||||||
void Prev();
|
void Prev();
|
||||||
int PositionGet() const;
|
int PositionGet() const;
|
||||||
void PositionSet(int);
|
|
||||||
void Repeat(bool);
|
void Repeat(bool);
|
||||||
void ShowOSD();
|
void ShowOSD();
|
||||||
void VolumeDown(int);
|
void VolumeDown(int);
|
||||||
|
@ -125,20 +102,15 @@ class Player : public QObject {
|
||||||
void VolumeDown() { VolumeDown(4); }
|
void VolumeDown() { VolumeDown(4); }
|
||||||
void VolumeUp() { VolumeUp(4); }
|
void VolumeUp() { VolumeUp(4); }
|
||||||
int VolumeGet() const;
|
int VolumeGet() const;
|
||||||
void VolumeSet(int);
|
|
||||||
|
|
||||||
// MPRIS /Tracklist
|
// MPRIS /Tracklist
|
||||||
int AddTrack(const QString&, bool);
|
int AddTrack(const QString&, bool);
|
||||||
void DelTrack(int index);
|
void DelTrack(int index);
|
||||||
int GetCurrentTrack() const;
|
int GetCurrentTrack() const;
|
||||||
int GetLength() const;
|
int GetLength() const;
|
||||||
QVariantMap GetMetadata(int) const;
|
|
||||||
void SetLoop(bool enable);
|
void SetLoop(bool enable);
|
||||||
void SetRandom(bool enable);
|
void SetRandom(bool enable);
|
||||||
|
|
||||||
// Amarok extension.
|
|
||||||
void PlayTrack(int index);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void Playing();
|
void Playing();
|
||||||
void Paused();
|
void Paused();
|
||||||
|
@ -150,20 +122,11 @@ class Player : public QObject {
|
||||||
|
|
||||||
void ForceShowOSD(Song);
|
void ForceShowOSD(Song);
|
||||||
|
|
||||||
// MPRIS
|
|
||||||
// Player
|
|
||||||
void CapsChange(int);
|
|
||||||
void TrackChange(QVariantMap);
|
|
||||||
void StatusChange(DBusStatus);
|
|
||||||
// TrackList
|
|
||||||
void TrackListChange(int i);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void EngineStateChanged(Engine::State);
|
void EngineStateChanged(Engine::State);
|
||||||
void EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle);
|
void EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle);
|
||||||
void TrackAboutToEnd();
|
void TrackAboutToEnd();
|
||||||
void TrackEnded();
|
void TrackEnded();
|
||||||
|
|
||||||
// Play the next item on the playlist - disregarding radio stations like
|
// Play the next item on the playlist - disregarding radio stations like
|
||||||
// last.fm that might have more tracks.
|
// last.fm that might have more tracks.
|
||||||
void NextItem(Engine::TrackChangeType change);
|
void NextItem(Engine::TrackChangeType change);
|
||||||
|
@ -171,7 +134,8 @@ class Player : public QObject {
|
||||||
void NextInternal(Engine::TrackChangeType);
|
void NextInternal(Engine::TrackChangeType);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariantMap GetMetadata(const PlaylistItem& item) const;
|
MPRIS* mpris_;
|
||||||
|
MPRIS2* mpris2_;
|
||||||
|
|
||||||
PlaylistManager* playlists_;
|
PlaylistManager* playlists_;
|
||||||
LastFMService* lastfm_;
|
LastFMService* lastfm_;
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<!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.mpris.MediaPlayer2.Player'>
|
||||||
|
<method name='Next'/>
|
||||||
|
<method name='Previous'/>
|
||||||
|
<method name='Pause'/>
|
||||||
|
<method name='PlayPause'/>
|
||||||
|
<method name='Stop'/>
|
||||||
|
<method name='Play'/>
|
||||||
|
<method name='Seek'>
|
||||||
|
<arg direction='in' name='Offset' type='x'/>
|
||||||
|
</method>
|
||||||
|
<method name='SetPosition'>
|
||||||
|
<arg direction='in' name='TrackId' type='o'/>
|
||||||
|
<arg direction='in' name='Position' type='x'/>
|
||||||
|
</method>
|
||||||
|
<method name='OpenUri'>
|
||||||
|
<arg direction='in' name='Uri' type='s'/>
|
||||||
|
</method>
|
||||||
|
<signal name='Seeked'>
|
||||||
|
<arg name='Position' type='x'/>
|
||||||
|
</signal>
|
||||||
|
<property name='PlaybackStatus' type='s' access='read'/>
|
||||||
|
<property name='LoopStatus' type='s' access='readwrite'/>
|
||||||
|
<property name='Rate' type='d' access='readwrite'/>
|
||||||
|
<property name='Shuffle' type='b' access='readwrite'/>
|
||||||
|
<property name='Metadata' type='a{sv}' access='read'>
|
||||||
|
<annotation name="com.trolltech.QtDBus.QtTypeName" value="QVariantMap"/>
|
||||||
|
</property>
|
||||||
|
<property name='Volume' type='d' access='readwrite'/>
|
||||||
|
<property name='Position' type='x' access='read'/>
|
||||||
|
<property name='MinimumRate' type='d' access='read'/>
|
||||||
|
<property name='MaximumRate' type='d' access='read'/>
|
||||||
|
<property name='CanGoNext' type='b' access='read'/>
|
||||||
|
<property name='CanGoPrevious' type='b' access='read'/>
|
||||||
|
<property name='CanPlay' type='b' access='read'/>
|
||||||
|
<property name='CanPause' type='b' access='read'/>
|
||||||
|
<property name='CanSeek' type='b' access='read'/>
|
||||||
|
<property name='CanControl' type='b' access='read'/>
|
||||||
|
</interface>
|
||||||
|
</node>
|
|
@ -0,0 +1,45 @@
|
||||||
|
<!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.mpris.MediaPlayer2.TrackList'>
|
||||||
|
<method name='GetTracksMetadata'>
|
||||||
|
<arg direction='in' name='TrackIds' type='ao'/>
|
||||||
|
<arg direction='out' name='Metadata' type='aa{sv}'>
|
||||||
|
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="TrackMetadata" />
|
||||||
|
</arg>
|
||||||
|
</method>
|
||||||
|
<method name='AddTrack'>
|
||||||
|
<arg direction='in' name='Uri' type='s'/>
|
||||||
|
<arg direction='in' name='AfterTrack' type='o'/>
|
||||||
|
<arg direction='in' name='SetAsCurrent' type='b'/>
|
||||||
|
</method>
|
||||||
|
<method name='RemoveTrack'>
|
||||||
|
<arg direction='in' name='TrackId' type='o'/>
|
||||||
|
</method>
|
||||||
|
<method name='GoTo'>
|
||||||
|
<arg direction='in' name='TrackId' type='o'/>
|
||||||
|
</method>
|
||||||
|
<signal name='TrackListReplaced'>
|
||||||
|
<arg name='Tracks' type='ao'/>
|
||||||
|
<arg name='CurrentTrack' type='o'/>
|
||||||
|
</signal>
|
||||||
|
<signal name='TrackAdded'>
|
||||||
|
<arg name='Metadata' type='a{sv}'>
|
||||||
|
<annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="TrackMetadata"/>
|
||||||
|
</arg>
|
||||||
|
<arg name='AfterTrack' type='o'/>
|
||||||
|
</signal>
|
||||||
|
<signal name='TrackRemoved'>
|
||||||
|
<arg name='TrackId' type='o'/>
|
||||||
|
</signal>
|
||||||
|
<signal name='TrackMetadataChanged'>
|
||||||
|
<arg name='TrackId' type='o'/>
|
||||||
|
<arg name='Metadata' type='a{sv}'>
|
||||||
|
<annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="TrackMetadata"/>
|
||||||
|
</arg>
|
||||||
|
</signal>
|
||||||
|
<property name='Tracks' type='ao' access='read'/>
|
||||||
|
<property name='CanEditTracks' type='b' access='read'/>
|
||||||
|
</interface>
|
||||||
|
</node>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!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.mpris.MediaPlayer2'>
|
||||||
|
<method name='Raise'/>
|
||||||
|
<method name='Quit'/>
|
||||||
|
<property name='CanQuit' type='b' access='read'/>
|
||||||
|
<property name='CanRaise' type='b' access='read'/>
|
||||||
|
<property name='HasTrackList' type='b' access='read'/>
|
||||||
|
<property name='Identity' type='s' access='read'/>
|
||||||
|
<property name='DesktopEntry' type='s' access='read'/>
|
||||||
|
<property name='SupportedUriSchemes' type='as' access='read'/>
|
||||||
|
<property name='SupportedMimeTypes' type='as' access='read'/>
|
||||||
|
</interface>
|
||||||
|
</node>
|
16
src/main.cpp
16
src/main.cpp
|
@ -50,12 +50,7 @@
|
||||||
|
|
||||||
#include <echonest/Config.h>
|
#include <echonest/Config.h>
|
||||||
|
|
||||||
#ifdef Q_WS_X11
|
|
||||||
# include <QDBusConnection>
|
|
||||||
# include <QDBusMetaType>
|
|
||||||
# include "core/mpris.h"
|
|
||||||
# include "widgets/osd.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_GSTREAMER
|
#ifdef HAVE_GSTREAMER
|
||||||
# include <gst/gst.h>
|
# include <gst/gst.h>
|
||||||
|
@ -204,15 +199,6 @@ int main(int argc, char *argv[]) {
|
||||||
Echonest::Config::instance()->setAPIKey("DFLFLJBUF4EGTXHIG");
|
Echonest::Config::instance()->setAPIKey("DFLFLJBUF4EGTXHIG");
|
||||||
Echonest::Config::instance()->setNetworkAccessManager(new NetworkAccessManager);
|
Echonest::Config::instance()->setNetworkAccessManager(new NetworkAccessManager);
|
||||||
|
|
||||||
// MPRIS DBus interface.
|
|
||||||
#ifdef Q_WS_X11
|
|
||||||
qDBusRegisterMetaType<DBusStatus>();
|
|
||||||
qDBusRegisterMetaType<Version>();
|
|
||||||
qDBusRegisterMetaType<QImage>();
|
|
||||||
QDBusConnection::sessionBus().registerService("org.mpris.clementine");
|
|
||||||
MPRIS mpris;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Seed the random number generator
|
// Seed the random number generator
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ MainWindow::MainWindow(Engine::Type engine, QWidget *parent)
|
||||||
|
|
||||||
// Create stuff that needs the database
|
// Create stuff that needs the database
|
||||||
radio_model_ = new RadioModel(database_, task_manager_, this);
|
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);
|
library_ = new Library(database_, task_manager_, this);
|
||||||
devices_ = new DeviceManager(database_, task_manager_, this);
|
devices_ = new DeviceManager(database_, task_manager_, this);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue