Implement MediaPlayer2.Playlists from MPRIS2.

Fixes issue #3194
This commit is contained in:
John Maguire 2012-10-11 15:38:51 +02:00
parent 4de0b93305
commit d674d9ecf7
7 changed files with 189 additions and 18 deletions

View File

@ -866,6 +866,11 @@ if(HAVE_DBUS)
dbus/org.mpris.MediaPlayer2.TrackList.xml
core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
# MPRIS 2.1 DBUS interfaces
qt4_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Playlists.xml
core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
# org.freedesktop.Notifications DBUS interface
qt4_add_dbus_interface(SOURCES
dbus/org.freedesktop.Notifications.xml

View File

@ -30,7 +30,6 @@
#cmakedefine HAVE_IMOBILEDEVICE
#cmakedefine HAVE_LIBARCHIVE
#cmakedefine HAVE_LIBGPOD
#cmakedefine HAVE_LIBINDICATE
#cmakedefine HAVE_LIBLASTFM
#cmakedefine HAVE_LIBLASTFM1
#cmakedefine HAVE_LIBMTP

View File

@ -73,5 +73,8 @@ void RegisterMetaTypes() {
qDBusRegisterMetaType<TrackMetadata>();
qDBusRegisterMetaType<TrackIds>();
qDBusRegisterMetaType<QList<QByteArray> >();
qDBusRegisterMetaType<MprisPlaylist>();
qDBusRegisterMetaType<MaybePlaylist>();
qDBusRegisterMetaType<MprisPlaylistList>();
#endif
}

View File

@ -27,7 +27,6 @@ Mpris::Mpris(Application* app, QObject* parent)
mpris2_(new mpris::Mpris2(app, mpris1_, this))
{
connect(mpris2_, SIGNAL(RaiseMainWindow()), SIGNAL(RaiseMainWindow()));
mpris2_->InitLibIndicate();
}
} // namespace mpris

View File

@ -15,13 +15,17 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mpris2.h"
#include <algorithm>
#include "config.h"
#include "mpris_common.h"
#include "mpris1.h"
#include "mpris2.h"
#include "core/application.h"
#include "core/logging.h"
#include "core/mpris2_player.h"
#include "core/mpris2_playlists.h"
#include "core/mpris2_root.h"
#include "core/mpris2_tracklist.h"
#include "core/player.h"
@ -37,9 +41,36 @@
#include <QDBusConnection>
#include <QtConcurrentRun>
#ifdef HAVE_LIBINDICATE
# include <qindicateserver.h>
#endif
QDBusArgument& operator<< (QDBusArgument& arg, const MprisPlaylist& playlist) {
arg.beginStructure();
arg << playlist.id << playlist.name << playlist.icon;
arg.endStructure();
return arg;
}
const QDBusArgument& operator>> (
const QDBusArgument& arg, MprisPlaylist& playlist) {
arg.beginStructure();
arg >> playlist.id >> playlist.name >> playlist.icon;
arg.endStructure();
return arg;
}
QDBusArgument& operator<< (QDBusArgument& arg, const MaybePlaylist& playlist) {
arg.beginStructure();
arg << playlist.valid;
arg << playlist.playlist;
arg.endStructure();
return arg;
}
const QDBusArgument& operator>> (
const QDBusArgument& arg, MaybePlaylist& playlist) {
arg.beginStructure();
arg >> playlist.valid >> playlist.playlist;
arg.endStructure();
return arg;
}
namespace mpris {
@ -55,6 +86,7 @@ Mpris2::Mpris2(Application* app, Mpris1* mpris1, QObject* parent)
new Mpris2Root(this);
new Mpris2TrackList(this);
new Mpris2Player(this);
new Mpris2Playlists(this);
if (!QDBusConnection::sessionBus().registerService(kServiceName)) {
qLog(Warning) << "Failed to register" << QString(kServiceName) << "on the session bus";
@ -71,15 +103,7 @@ Mpris2::Mpris2(Application* app, Mpris1* mpris1, QObject* parent)
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized()));
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song)));
}
void Mpris2::InitLibIndicate() {
#ifdef HAVE_LIBINDICATE
QIndicate::Server* indicate_server = QIndicate::Server::defaultInstance();
indicate_server->setType("music.clementine");
indicate_server->setDesktopFile(DesktopEntryAbsolutePath());
indicate_server->show();
#endif
connect(app_->playlist_manager(), SIGNAL(PlaylistChanged(Playlist*)), SLOT(PlaylistChanged()));
}
// when PlaylistManager gets it ready, we connect PlaylistSequence with this
@ -474,4 +498,75 @@ void Mpris2::GoTo(const QDBusObjectPath &trackId) {
//TODO
}
quint32 Mpris2::PlaylistCount() const {
return app_->playlist_manager()->GetAllPlaylists().size();
}
QStringList Mpris2::Orderings() const {
return QStringList() << "User";
}
namespace {
QDBusObjectPath MakePlaylistPath(int id) {
return QDBusObjectPath(QString(
"/org/mpris/MediaPlayer2/Playlists/%1").arg(id));
}
}
MaybePlaylist Mpris2::ActivePlaylist() const {
MaybePlaylist maybe_playlist;
Playlist* current_playlist = app_->playlist_manager()->current();
maybe_playlist.valid = current_playlist;
if (!current_playlist) {
return maybe_playlist;
}
maybe_playlist.playlist.id = MakePlaylistPath(current_playlist->id());
maybe_playlist.playlist.name =
app_->playlist_manager()->GetPlaylistName(current_playlist->id());
return maybe_playlist;
}
void Mpris2::ActivatePlaylist(const QDBusObjectPath& playlist_id) {
QStringList split_path = playlist_id.path().split('/');
qLog(Debug) << Q_FUNC_INFO << playlist_id.path() << split_path;
if (split_path.isEmpty()) {
return;
}
bool ok = false;
int p = split_path.last().toInt(&ok);
if (!ok) {
return;
}
app_->playlist_manager()->SetActivePlaylist(p);
app_->player()->Next();
}
// TODO: Support sort orders.
MprisPlaylistList Mpris2::GetPlaylists(
quint32 index, quint32 max_count, const QString& order, bool reverse_order) {
MprisPlaylistList ret;
foreach (Playlist* p, app_->playlist_manager()->GetAllPlaylists()) {
MprisPlaylist mpris_playlist;
mpris_playlist.id = MakePlaylistPath(p->id());
mpris_playlist.name = app_->playlist_manager()->GetPlaylistName(p->id());
ret << mpris_playlist;
}
if (reverse_order) {
std::reverse(ret.begin(), ret.end());
}
return ret.mid(index, max_count);
}
void Mpris2::PlaylistChanged(Playlist* playlist) {
MprisPlaylist mpris_playlist;
mpris_playlist.id = MakePlaylistPath(playlist->id());
mpris_playlist.name = app_->playlist_manager()->GetPlaylistName(playlist->id());
emit PlaylistChanged(mpris_playlist);
}
} // namespace mpris

View File

@ -28,11 +28,36 @@
class Application;
class MainWindow;
class Playlist;
typedef QList<QVariantMap> TrackMetadata;
typedef QList<QDBusObjectPath> TrackIds;
Q_DECLARE_METATYPE(TrackMetadata)
struct MprisPlaylist {
QDBusObjectPath id;
QString name;
QString icon; // Uri
};
typedef QList<MprisPlaylist> MprisPlaylistList;
Q_DECLARE_METATYPE(MprisPlaylist);
Q_DECLARE_METATYPE(MprisPlaylistList);
struct MaybePlaylist {
bool valid;
MprisPlaylist playlist;
};
Q_DECLARE_METATYPE(MaybePlaylist);
QDBusArgument& operator<< (QDBusArgument& arg, const MprisPlaylist& playlist);
const QDBusArgument& operator>> (
const QDBusArgument& arg, MprisPlaylist& playlist);
QDBusArgument& operator<< (QDBusArgument& arg, const MaybePlaylist& playlist);
const QDBusArgument& operator>> (
const QDBusArgument& arg, MaybePlaylist& playlist);
namespace mpris {
class Mpris1;
@ -40,6 +65,7 @@ class Mpris1;
class Mpris2 : public QObject {
Q_OBJECT
public:
//org.mpris.MediaPlayer2 MPRIS 2.0 Root interface
Q_PROPERTY( bool CanQuit READ CanQuit )
Q_PROPERTY( bool CanRaise READ CanRaise )
@ -74,10 +100,12 @@ class Mpris2 : public QObject {
Q_PROPERTY( TrackIds Tracks READ Tracks )
Q_PROPERTY( bool CanEditTracks READ CanEditTracks )
public:
Mpris2(Application* app, Mpris1* mpris1, QObject* parent = 0);
//org.mpris.MediaPlayer2.Playlists MPRIS 2.1 Playlists interface
Q_PROPERTY( quint32 PlaylistCount READ PlaylistCount )
Q_PROPERTY( QStringList Orderings READ Orderings )
Q_PROPERTY( MaybePlaylist ActivePlaylist READ ActivePlaylist )
void InitLibIndicate();
Mpris2(Application* app, Mpris1* mpris1, QObject* parent = 0);
// Root Properties
bool CanQuit() const;
@ -139,6 +167,16 @@ public:
void RemoveTrack(const QDBusObjectPath& trackId);
void GoTo(const QDBusObjectPath& trackId);
// Playlist Properties
quint32 PlaylistCount() const;
QStringList Orderings() const;
MaybePlaylist ActivePlaylist() const;
// Methods
void ActivatePlaylist(const QDBusObjectPath& playlist_id);
QList<MprisPlaylist> GetPlaylists(
quint32 index, quint32 max_count, const QString& order, bool reverse_order);
signals:
// Player
void Seeked(qlonglong position);
@ -151,6 +189,9 @@ signals:
void RaiseMainWindow();
// Playlist
void PlaylistChanged(const MprisPlaylist& playlist);
private slots:
void ArtLoaded(const Song& song, const QString& art_uri);
void EngineStateChanged(Engine::State newState);
@ -160,6 +201,7 @@ private slots:
void CurrentSongChanged(const Song& song);
void ShuffleModeChanged();
void RepeatModeChanged();
void PlaylistChanged(Playlist* playlist);
private:
void EmitNotification(const QString& name);

View File

@ -0,0 +1,28 @@
<!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.Playlists'>
<property name='PlaylistCount' type='u' access='read' />
<property name='Orderings' type='as' access='read' />
<property name='ActivePlaylist' type='(b(oss))' access='read'>
<annotation name="com.trolltech.QtDBus.QtTypeName" value="MaybePlaylist"/>
</property>
<method name='ActivatePlaylist'>
<arg direction='in' name='PlaylistId' type='o' />
</method>
<method name='GetPlaylists'>
<arg direction='in' name='Index' type='u' />
<arg direction='in' name='MaxCount' type='u' />
<arg direction='in' name='Order' type='s' />
<arg direction='in' name='ReverseOrder' type='b' />
<arg direction='out' name='Playlists' type='a(oss)' />
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="MprisPlaylistList"/>
</method>
<signal name='PlaylistChanged'>
<arg name='Playlist' type='(oss)' />
<annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="MprisPlaylist" />
</signal>
<annotation name="com.trolltech.QtDBus.QtTypeName" value="MaybePlaylist" />
</interface>
</node>