2011-01-08 16:31:14 +01:00
|
|
|
/* 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 "mpris1.h"
|
|
|
|
#include "mpris_common.h"
|
2012-02-12 14:41:50 +01:00
|
|
|
#include "core/application.h"
|
2011-04-22 18:50:29 +02:00
|
|
|
#include "core/logging.h"
|
2012-02-13 21:44:04 +01:00
|
|
|
#include "covers/currentartloader.h"
|
2011-01-08 16:31:14 +01:00
|
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QDBusConnection>
|
|
|
|
|
|
|
|
#include "core/mpris_player.h"
|
|
|
|
#include "core/mpris_root.h"
|
|
|
|
#include "core/mpris_tracklist.h"
|
2011-02-14 20:34:37 +01:00
|
|
|
#include "core/timeconstants.h"
|
2011-01-08 16:31:14 +01:00
|
|
|
#include "engines/enginebase.h"
|
|
|
|
#include "playlist/playlist.h"
|
|
|
|
#include "playlist/playlistmanager.h"
|
|
|
|
#include "playlist/playlistsequence.h"
|
|
|
|
|
|
|
|
namespace mpris {
|
|
|
|
|
2011-02-13 19:37:21 +01:00
|
|
|
const char* Mpris1::kDefaultDbusServiceName = "org.mpris.clementine";
|
|
|
|
|
2012-02-13 21:44:04 +01:00
|
|
|
Mpris1::Mpris1(Application* app, QObject* parent,
|
2011-02-13 19:37:21 +01:00
|
|
|
const QString& dbus_service_name)
|
|
|
|
: QObject(parent),
|
|
|
|
dbus_service_name_(dbus_service_name),
|
|
|
|
root_(NULL),
|
|
|
|
player_(NULL),
|
|
|
|
tracklist_(NULL)
|
2011-01-08 16:31:14 +01:00
|
|
|
{
|
|
|
|
qDBusRegisterMetaType<DBusStatus>();
|
|
|
|
qDBusRegisterMetaType<Version>();
|
|
|
|
|
2011-02-13 19:37:21 +01:00
|
|
|
if (dbus_service_name_.isEmpty()) {
|
|
|
|
dbus_service_name_ = kDefaultDbusServiceName;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!QDBusConnection::sessionBus().registerService(dbus_service_name_)) {
|
2011-04-22 18:50:29 +02:00
|
|
|
qLog(Warning) << "Failed to register" << dbus_service_name_ << "on the session bus";
|
2011-02-13 19:37:21 +01:00
|
|
|
return;
|
|
|
|
}
|
2011-01-08 16:31:14 +01:00
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
root_ = new Mpris1Root(app, this);
|
|
|
|
player_ = new Mpris1Player(app, this);
|
|
|
|
tracklist_ = new Mpris1TrackList(app, this);
|
2011-01-08 16:31:14 +01:00
|
|
|
|
2012-02-13 21:44:04 +01:00
|
|
|
connect(app->current_art_loader(), SIGNAL(ArtLoaded(const Song&, const QString&, const QImage&)),
|
2011-02-23 12:11:17 +01:00
|
|
|
player_, SLOT(CurrentSongChanged(const Song&, const QString&, const QImage&)));
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
2011-02-13 19:37:21 +01:00
|
|
|
Mpris1::~Mpris1() {
|
|
|
|
QDBusConnection::sessionBus().unregisterService(dbus_service_name_);
|
|
|
|
}
|
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
Mpris1Root::Mpris1Root(Application* app, QObject* parent)
|
|
|
|
: QObject(parent),
|
|
|
|
app_(app) {
|
2011-01-08 16:31:14 +01:00
|
|
|
new MprisRoot(this);
|
|
|
|
QDBusConnection::sessionBus().registerObject("/", this);
|
|
|
|
}
|
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
Mpris1Player::Mpris1Player(Application* app, QObject* parent)
|
|
|
|
: QObject(parent),
|
|
|
|
app_(app) {
|
2011-01-08 16:31:14 +01:00
|
|
|
new MprisPlayer(this);
|
|
|
|
QDBusConnection::sessionBus().registerObject("/Player", this);
|
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
connect(app_->player()->engine(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State)));
|
|
|
|
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized()));
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// when PlaylistManager gets it ready, we connect PlaylistSequence with this
|
|
|
|
void Mpris1Player::PlaylistManagerInitialized() {
|
2012-02-12 14:41:50 +01:00
|
|
|
connect(app_->playlist_manager()->sequence(), SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
|
2011-01-08 16:31:14 +01:00
|
|
|
SLOT(ShuffleModeChanged()));
|
2012-02-12 14:41:50 +01:00
|
|
|
connect(app_->playlist_manager()->sequence(), SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)),
|
2011-01-08 16:31:14 +01:00
|
|
|
SLOT(RepeatModeChanged()));
|
|
|
|
}
|
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
Mpris1TrackList::Mpris1TrackList(Application* app, QObject* parent)
|
|
|
|
: QObject(parent), app_(app) {
|
2011-01-08 16:31:14 +01:00
|
|
|
new MprisTrackList(this);
|
|
|
|
QDBusConnection::sessionBus().registerObject("/TrackList", this);
|
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
connect(app_->playlist_manager(), SIGNAL(PlaylistChanged(Playlist*)), SLOT(PlaylistChanged(Playlist*)));
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1TrackList::PlaylistChanged(Playlist* playlist) {
|
|
|
|
emit TrackListChange(playlist->rowCount());
|
|
|
|
}
|
|
|
|
|
|
|
|
// we use the state from event and don't try to obtain it from Player
|
|
|
|
// later because only the event's version is really the current one
|
|
|
|
void Mpris1Player::EngineStateChanged(Engine::State state) {
|
|
|
|
emit StatusChange(GetStatus(state));
|
|
|
|
emit CapsChange(GetCaps(state));
|
|
|
|
}
|
|
|
|
|
2011-02-23 12:11:17 +01:00
|
|
|
void Mpris1Player::CurrentSongChanged(
|
|
|
|
const Song& song, const QString& art_uri, const QImage&) {
|
2011-01-08 16:31:14 +01:00
|
|
|
last_metadata_ = Mpris1::GetMetadata(song);
|
|
|
|
|
|
|
|
if (!art_uri.isEmpty()) {
|
|
|
|
AddMetadata("arturl", art_uri, &last_metadata_);
|
|
|
|
}
|
|
|
|
|
|
|
|
emit TrackChange(last_metadata_);
|
|
|
|
emit StatusChange(GetStatus());
|
|
|
|
emit CapsChange(GetCaps());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString Mpris1Root::Identity() {
|
|
|
|
return QString("%1 %2").arg(
|
|
|
|
QCoreApplication::applicationName(),
|
|
|
|
QCoreApplication::applicationVersion());
|
|
|
|
}
|
|
|
|
|
|
|
|
Version Mpris1Root::MprisVersion() {
|
|
|
|
Version version;
|
|
|
|
version.major = 1;
|
|
|
|
version.minor = 0;
|
|
|
|
return version;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Root::Quit() {
|
|
|
|
qApp->quit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::Pause() {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->PlayPause();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::Stop() {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->Stop();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::Prev() {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->Previous();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::Play() {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->Play();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::Next() {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->Next();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::Repeat(bool repeat) {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->playlist_manager()->sequence()->SetRepeatMode(
|
2011-01-08 16:31:14 +01:00
|
|
|
repeat ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::ShuffleModeChanged() {
|
|
|
|
emit StatusChange(GetStatus());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::RepeatModeChanged() {
|
|
|
|
emit StatusChange(GetStatus());
|
|
|
|
}
|
|
|
|
|
|
|
|
DBusStatus Mpris1Player::GetStatus() const {
|
2012-02-12 14:41:50 +01:00
|
|
|
return GetStatus(app_->player()->GetState());
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DBusStatus Mpris1Player::GetStatus(Engine::State state) const {
|
|
|
|
DBusStatus status;
|
|
|
|
switch (state) {
|
|
|
|
case Engine::Playing:
|
|
|
|
status.play = DBusStatus::Mpris_Playing;
|
|
|
|
break;
|
|
|
|
case Engine::Paused:
|
|
|
|
status.play = DBusStatus::Mpris_Paused;
|
|
|
|
break;
|
2011-03-29 21:05:04 +02:00
|
|
|
case Engine::Empty:
|
|
|
|
case Engine::Idle:
|
|
|
|
default:
|
|
|
|
status.play = DBusStatus::Mpris_Stopped;
|
|
|
|
break;
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
2012-08-17 15:35:24 +02:00
|
|
|
if (app_->playlist_manager()->sequence()) {
|
|
|
|
PlaylistManagerInterface* playlists_ = app_->playlist_manager();
|
|
|
|
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 ||
|
|
|
|
repeat_mode == PlaylistSequence::Repeat_Track) ? 1 : 0;
|
|
|
|
}
|
2011-01-08 16:31:14 +01:00
|
|
|
return status;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::VolumeSet(int volume) {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->SetVolume(volume);
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int Mpris1Player::VolumeGet() const {
|
2012-02-12 14:41:50 +01:00
|
|
|
return app_->player()->GetVolume();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
2011-02-13 19:29:27 +01:00
|
|
|
void Mpris1Player::PositionSet(int pos_msec) {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->SeekTo(pos_msec / kMsecPerSec);
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int Mpris1Player::PositionGet() const {
|
2012-02-12 14:41:50 +01:00
|
|
|
return app_->player()->engine()->position_nanosec() / kNsecPerMsec;
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariantMap Mpris1Player::GetMetadata() const {
|
|
|
|
return last_metadata_;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Mpris1Player::GetCaps() const {
|
2012-02-12 14:41:50 +01:00
|
|
|
return GetCaps(app_->player()->GetState());
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int Mpris1Player::GetCaps(Engine::State state) const {
|
|
|
|
int caps = CAN_HAS_TRACKLIST;
|
2012-02-12 14:41:50 +01:00
|
|
|
PlaylistItemPtr current_item = app_->player()->GetCurrentItem();
|
|
|
|
PlaylistManagerInterface* playlists = app_->playlist_manager();
|
2011-01-08 16:31:14 +01:00
|
|
|
|
2012-08-17 15:35:24 +02:00
|
|
|
if (playlists->active()) {
|
|
|
|
// play is disabled when playlist is empty or when last.fm stream is already playing
|
|
|
|
if (playlists->active() && playlists->active()->rowCount() != 0
|
|
|
|
&& !(state == Engine::Playing && (app_->player()->GetCurrentItem()->options() & PlaylistItem::LastFMControls))) {
|
|
|
|
caps |= CAN_PLAY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (playlists->active()->next_row() != -1) {
|
|
|
|
caps |= CAN_GO_NEXT;
|
|
|
|
}
|
|
|
|
if (playlists->active()->previous_row() != -1) {
|
|
|
|
caps |= CAN_GO_PREV;
|
|
|
|
}
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (current_item) {
|
|
|
|
caps |= CAN_PROVIDE_METADATA;
|
|
|
|
if (state == Engine::Playing && !(current_item->options() & PlaylistItem::PauseDisabled)) {
|
|
|
|
caps |= CAN_PAUSE;
|
|
|
|
}
|
2011-03-20 13:43:10 +01:00
|
|
|
if (state != Engine::Empty && !current_item->Metadata().is_stream()) {
|
2011-01-08 16:31:14 +01:00
|
|
|
caps |= CAN_SEEK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return caps;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::VolumeUp(int change) {
|
|
|
|
VolumeSet(VolumeGet() + change);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::VolumeDown(int change) {
|
|
|
|
VolumeSet(VolumeGet() - change);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::Mute() {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->Mute();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1Player::ShowOSD() {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->ShowOSD();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int Mpris1TrackList::AddTrack(const QString& track, bool play) {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->playlist_manager()->active()->InsertUrls(
|
2011-03-31 20:47:25 +02:00
|
|
|
QList<QUrl>() << QUrl(track), -1, play);
|
2011-01-08 16:31:14 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1TrackList::DelTrack(int index) {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->playlist_manager()->active()->removeRows(index, 1);
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int Mpris1TrackList::GetCurrentTrack() const {
|
2012-02-12 14:41:50 +01:00
|
|
|
return app_->playlist_manager()->active()->current_row();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int Mpris1TrackList::GetLength() const {
|
2012-02-12 14:41:50 +01:00
|
|
|
return app_->playlist_manager()->active()->rowCount();
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariantMap Mpris1TrackList::GetMetadata(int pos) const {
|
2012-02-12 14:41:50 +01:00
|
|
|
PlaylistItemPtr item = app_->player()->GetItemAt(pos);
|
2011-01-08 16:31:14 +01:00
|
|
|
if (!item)
|
|
|
|
return QVariantMap();
|
|
|
|
|
|
|
|
return Mpris1::GetMetadata(item->Metadata());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1TrackList::SetLoop(bool enable) {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->playlist_manager()->active()->sequence()->SetRepeatMode(
|
2011-01-08 16:31:14 +01:00
|
|
|
enable ? PlaylistSequence::Repeat_Playlist : PlaylistSequence::Repeat_Off);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1TrackList::SetRandom(bool enable) {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->playlist_manager()->active()->sequence()->SetShuffleMode(
|
2011-01-08 16:31:14 +01:00
|
|
|
enable ? PlaylistSequence::Shuffle_All : PlaylistSequence::Shuffle_Off);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mpris1TrackList::PlayTrack(int index) {
|
2012-02-12 14:41:50 +01:00
|
|
|
app_->player()->PlayAt(index, Engine::Manual, true);
|
2011-01-08 16:31:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariantMap Mpris1::GetMetadata(const Song& song) {
|
|
|
|
QVariantMap ret;
|
|
|
|
|
2011-04-28 14:27:53 +02:00
|
|
|
AddMetadata("location", song.url().toString(), &ret);
|
2011-01-08 16:31:14 +01:00
|
|
|
AddMetadata("title", song.PrettyTitle(), &ret);
|
|
|
|
AddMetadata("artist", song.artist(), &ret);
|
|
|
|
AddMetadata("album", song.album(), &ret);
|
2011-02-14 20:34:37 +01:00
|
|
|
AddMetadata("time", song.length_nanosec() / kNsecPerSec, &ret);
|
2011-02-14 22:23:01 +01:00
|
|
|
AddMetadata("mtime", song.length_nanosec() / kNsecPerMsec, &ret);
|
2011-01-08 16:31:14 +01:00
|
|
|
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("audio-bitrate", song.bitrate(), &ret);
|
|
|
|
AddMetadata("audio-samplerate", song.samplerate(), &ret);
|
|
|
|
AddMetadata("bpm", song.bpm(), &ret);
|
|
|
|
AddMetadata("composer", song.composer(), &ret);
|
2013-03-03 13:00:24 +01:00
|
|
|
AddMetadata("performer", song.performer(), &ret);
|
|
|
|
AddMetadata("grouping", song.grouping(), &ret);
|
2011-01-08 16:31:14 +01:00
|
|
|
if (song.rating() != -1.0) {
|
|
|
|
AddMetadata("rating", song.rating() * 5, &ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mpris
|
|
|
|
|
|
|
|
|
|
|
|
QDBusArgument& operator<< (QDBusArgument& arg, const Version& version) {
|
|
|
|
arg.beginStructure();
|
|
|
|
arg << version.major << version.minor;
|
|
|
|
arg.endStructure();
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version) {
|
|
|
|
arg.beginStructure();
|
|
|
|
arg >> version.major >> version.minor;
|
|
|
|
arg.endStructure();
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) {
|
|
|
|
arg.beginStructure();
|
|
|
|
arg << status.play;
|
|
|
|
arg << status.random;
|
|
|
|
arg << status.repeat;
|
|
|
|
arg << status.repeat_playlist;
|
|
|
|
arg.endStructure();
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status) {
|
|
|
|
arg.beginStructure();
|
|
|
|
arg >> status.play;
|
|
|
|
arg >> status.random;
|
|
|
|
arg >> status.repeat;
|
|
|
|
arg >> status.repeat_playlist;
|
|
|
|
arg.endStructure();
|
|
|
|
return arg;
|
|
|
|
}
|