aboutsummaryrefslogtreecommitdiffstats
path: root/src/modules/mediaplayer/MpMprisInterface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/mediaplayer/MpMprisInterface.cpp')
-rw-r--r--src/modules/mediaplayer/MpMprisInterface.cpp579
1 files changed, 579 insertions, 0 deletions
diff --git a/src/modules/mediaplayer/MpMprisInterface.cpp b/src/modules/mediaplayer/MpMprisInterface.cpp
new file mode 100644
index 000000000..560d03c93
--- /dev/null
+++ b/src/modules/mediaplayer/MpMprisInterface.cpp
@@ -0,0 +1,579 @@
+//=============================================================================
+//
+// File : MpMprisInterface.cpp
+// Creation date : Thu 06 Dec 2007 14:20:02 by Tomasz Moń
+//
+// This file is part of the KVIrc IRC client distribution
+// Copyright (C) 2007-2009 Tomasz Moń <desowin@gmail.com>
+//
+// This program 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 2
+// of the License, or (at your opinion) any later version.
+//
+// This program 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 this program. If not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=============================================================================
+
+#include "MpMprisInterface.h"
+#if (defined(COMPILE_DBUS_SUPPORT) && !defined(COMPILE_ON_WINDOWS) && !defined(COMPILE_ON_MAC) && !defined(COMPILE_ON_MINGW))
+#include "KviLocale.h"
+
+/*
+ according to MPRIS 1.0, GetStatus returns struct of 4 integers.
+
+ First integer: 0 = Playing, 1 = Paused, 2 = Stopped.
+ Second interger: 0 = Playing linearly, 1 = Playing randomly.
+ Third integer: 0 = Go to the next element once the current has finished playing, 1 = Repeat the current element
+ Fourth integer: 0 = Stop playing once the last element has been played, 1 = Never give up playing
+*/
+
+struct MPRISPlayerStatus
+{
+ int Play;
+ int Random;
+ int RepeatCurrent;
+ int RepeatPlaylist;
+};
+Q_DECLARE_METATYPE( MPRISPlayerStatus )
+
+QDBusArgument &operator<<(QDBusArgument &argument, const MPRISPlayerStatus &status)
+{
+ argument.beginStructure();
+ argument << status.Play << status.Random << status.RepeatCurrent << status.RepeatPlaylist;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, MPRISPlayerStatus &status)
+{
+ argument.beginStructure();
+ argument >> status.Play >> status.Random >> status.RepeatCurrent >> status.RepeatPlaylist;
+ argument.endStructure();
+ return argument;
+}
+
+MpMprisInterface::MpMprisInterface()
+: MpInterface()
+{
+ qDBusRegisterMetaType<MPRISPlayerStatus>();
+}
+
+MpMprisInterface::~MpMprisInterface()
+{
+}
+
+int MpMprisInterface::detect(bool)
+{
+ QDBusReply<QStringList> reply = QDBusConnection::sessionBus().interface()->registeredServiceNames();
+ if (!reply.isValid()) /* something fishy with dbus, it won't work */
+ return 0;
+
+ foreach (QString name, reply.value())
+ if (name == m_szServiceName) /* player is running */
+ return 100;
+
+ return 1; /* dbus works, player may be closed */
+}
+
+#define MPRIS_SIMPLE_CALL(__path, __action) \
+ QDBusInterface dbus_iface(m_szServiceName, __path, \
+ "org.freedesktop.MediaPlayer", QDBusConnection::sessionBus()); \
+ QDBusMessage reply = dbus_iface.call(QDBus::Block, __action); \
+ if (reply.type() == QDBusMessage::ErrorMessage) { \
+ QDBusError err = reply; \
+ qDebug("Error: %s\n%s\n", qPrintable(err.name()), qPrintable(err.message())); \
+ return FALSE; \
+ } \
+ return TRUE;
+
+bool MpMprisInterface::prev()
+{
+ MPRIS_SIMPLE_CALL("/Player", "Prev")
+}
+
+bool MpMprisInterface::next()
+{
+ MPRIS_SIMPLE_CALL("/Player", "Next")
+}
+
+bool MpMprisInterface::play()
+{
+ MPRIS_SIMPLE_CALL("/Player", "Play")
+}
+
+bool MpMprisInterface::stop()
+{
+ MPRIS_SIMPLE_CALL("/Player", "Stop")
+}
+
+bool MpMprisInterface::pause()
+{
+ MPRIS_SIMPLE_CALL("/Player", "Pause")
+}
+
+bool MpMprisInterface::quit()
+{
+ MPRIS_SIMPLE_CALL("/", "Quit")
+}
+
+#define MPRIS_CALL_METHOD(__method, __return_if_fail) \
+ QDBusInterface dbus_iface(m_szServiceName, "/Player", \
+ "org.freedesktop.MediaPlayer", QDBusConnection::sessionBus()); \
+ QDBusMessage reply = dbus_iface.call(QDBus::Block, __method); \
+ if (reply.type() == QDBusMessage::ErrorMessage) { \
+ QDBusError err = reply; \
+ qDebug("Error: %s\n%s\n", qPrintable(err.name()), qPrintable(err.message())); \
+ return __return_if_fail; \
+ }
+
+#define MPRIS_GET_METADATA_FIELD(__field, __return_type, __return_if_fail) \
+ if (this->status() != MpInterface::Playing) \
+ return __return_if_fail; \
+ MPRIS_CALL_METHOD("GetMetadata", __return_if_fail) \
+ foreach (QVariant v, reply.arguments()) { \
+ QDBusArgument arg = qvariant_cast<QDBusArgument>(v); \
+ QVariant v = qdbus_cast<QVariantMap>(arg); \
+ if (v.userType() == QVariant::Map) { \
+ const QVariantMap map = v.toMap(); \
+ QVariantMap::ConstIterator it = map.find(__field); \
+ if (it != map.end() && it.key() == __field) { \
+ return it.value().value< __return_type >(); \
+ } \
+ } \
+ } \
+ return __return_if_fail;
+
+#define MPRIS_CALL_METHOD_WITH_ARG(__method, __arg, __return_if_fail) \
+ QDBusInterface dbus_iface(m_szServiceName, "/Player", \
+ "org.freedesktop.MediaPlayer", QDBusConnection::sessionBus()); \
+ QDBusMessage reply = dbus_iface.call(QDBus::Block, __method, __arg); \
+ if (reply.type() == QDBusMessage::ErrorMessage) { \
+ QDBusError err = reply; \
+ qDebug("Error: %s\n%s\n", qPrintable(err.name()), qPrintable(err.message())); \
+ return __return_if_fail; \
+ }
+
+MpInterface::PlayerStatus MpMprisInterface::status()
+{
+ MPRIS_CALL_METHOD("GetStatus", MpInterface::Unknown)
+
+ MPRISPlayerStatus status;
+
+ if(reply.arguments().isEmpty())
+ return MpInterface::Unknown;
+
+ status = qdbus_cast<MPRISPlayerStatus>(reply.arguments().first());
+
+ switch (status.Play) {
+ case 0: return MpInterface::Playing;
+ case 1: return MpInterface::Paused;
+ case 2: return MpInterface::Stopped;
+ default: return MpInterface::Unknown;
+ }
+}
+
+QString MpMprisInterface::nowPlaying()
+{
+ if (this->status() != MpInterface::Playing)
+ return "";
+
+ MPRIS_CALL_METHOD("GetMetadata", "")
+
+ QString artist;
+ QString title;
+ foreach (QVariant v, reply.arguments()) {
+ QDBusArgument arg = qvariant_cast<QDBusArgument>(v);
+ QVariant v = qdbus_cast<QVariantMap>(arg);
+ if (v.userType() == QVariant::Map) {
+ const QVariantMap map = v.toMap();
+ QVariantMap::ConstIterator it = map.constBegin();
+ for ( ; it != map.constEnd(); ++it) { /* maybe do some configureable formatting */
+ if (it.key() == "artist")
+ artist = it.value().toString();
+ else if (it.key() == "title")
+ title = it.value().toString();
+ }
+ }
+ }
+ if (artist.length() && title.length())
+ return artist + " - " + title;
+ else
+ return "";
+}
+
+QString MpMprisInterface::mrl()
+{
+ MPRIS_CALL_METHOD("GetMetadata", "")
+
+ foreach (QVariant v, reply.arguments()) {
+ QDBusArgument arg = qvariant_cast<QDBusArgument>(v);
+ QVariant v = qdbus_cast<QVariantMap>(arg);
+ if (v.userType() == QVariant::Map) {
+ const QVariantMap map = v.toMap();
+ QVariantMap::ConstIterator it = map.find("location");
+ if (it != map.end() && it.key() == "location") {
+ return it.value().toString();
+ }
+ }
+ }
+ return "";
+}
+
+QString MpMprisInterface::title()
+{
+ MPRIS_GET_METADATA_FIELD("title", QString, "")
+}
+
+QString MpMprisInterface::artist()
+{
+ MPRIS_GET_METADATA_FIELD("artist", QString, "")
+}
+
+QString MpMprisInterface::genre()
+{
+ MPRIS_GET_METADATA_FIELD("genre", QString, "")
+}
+
+QString MpMprisInterface::comment()
+{
+ MPRIS_GET_METADATA_FIELD("comment", QString, "")
+}
+
+QString MpMprisInterface::year()
+{
+ MPRIS_GET_METADATA_FIELD("year", QString, "")
+}
+
+QString MpMprisInterface::album()
+{
+ MPRIS_GET_METADATA_FIELD("album", QString, "")
+}
+
+int MpMprisInterface::bitRate()
+{
+ MPRIS_GET_METADATA_FIELD("audio-bitrate", int, -1)
+}
+
+int MpMprisInterface::sampleRate()
+{
+ MPRIS_GET_METADATA_FIELD("audio-samplerate", int, -1)
+}
+
+bool MpMprisInterface::setVol(kvs_int_t &iVol)
+{
+ MPRIS_CALL_METHOD_WITH_ARG("VolumeSet", QVariant((int)(100*iVol/255)), false);
+ return true;
+}
+
+int MpMprisInterface::getVol()
+{
+ MPRIS_CALL_METHOD("VolumeGet", -1)
+
+ int iVol = reply.arguments().first().toInt();
+ return iVol * 255 /100;
+}
+
+int MpMprisInterface::position()
+{
+ MPRIS_CALL_METHOD("PositionGet", -1)
+ return reply.arguments().first().toInt();
+}
+
+int MpMprisInterface::length()
+{
+ MPRIS_CALL_METHOD("GetMetadata", -1)
+
+ foreach (QVariant v, reply.arguments()) {
+ QDBusArgument arg = qvariant_cast<QDBusArgument>(v);
+ QVariant v = qdbus_cast<QVariantMap>(arg);
+ if (v.userType() == QVariant::Map) {
+ const QVariantMap map = v.toMap();
+ QVariantMap::ConstIterator it = map.constBegin();
+ for ( ; it != map.constEnd(); ++it) {
+ if (it.key() == "mtime")
+ return it.value().toInt();
+ }
+ }
+ }
+ return -1;
+}
+
+bool MpMprisInterface::jumpTo(kvs_int_t &iPos)
+{
+ MPRIS_CALL_METHOD_WITH_ARG("PositionSet", QVariant((int)iPos), false)
+ return true;
+}
+
+/* audacious interface */
+MP_IMPLEMENT_DESCRIPTOR(
+ MpAudaciousInterface,
+ "audacious",
+ __tr2qs_ctx(
+ "An interface to the popular audacious media player.\n" \
+ "Download it from http://audacious-media-player.org\n"
+ ,
+ "mediaplayer"
+ )
+)
+
+MpAudaciousInterface::MpAudaciousInterface()
+: MpMprisInterface()
+{
+ m_szServiceName = "org.mpris.audacious";
+}
+
+int MpAudaciousInterface::getPlayListPos()
+{
+ QDBusInterface dbus_iface("org.mpris.audacious", "/org/atheme/audacious",
+ "org.atheme.audacious", QDBusConnection::sessionBus());
+ QDBusReply<uint> pos = dbus_iface.call(QDBus::Block, "Position");
+ return pos;
+}
+
+
+bool MpAudaciousInterface::quit()
+{
+ if (MpMprisInterface::quit())
+ return TRUE;
+
+ /* compability with older versions */
+ MPRIS_SIMPLE_CALL("/Player", "Quit")
+}
+
+QString MpAudaciousInterface::mrl()
+{
+ MPRIS_CALL_METHOD("GetMetadata", "")
+
+ foreach (QVariant v, reply.arguments()) {
+ QDBusArgument arg = qvariant_cast<QDBusArgument>(v);
+ QVariant v = qdbus_cast<QVariantMap>(arg);
+ if (v.userType() == QVariant::Map) {
+ const QVariantMap map = v.toMap();
+ QVariantMap::ConstIterator it = map.find("location");
+ if (it != map.end() && it.key() == "location") {
+ return it.value().toString();
+ }
+ /* Some audacious versions send URI instead of location */
+ it = map.find("URI");
+ if (it != map.end() && it.key() == "URI") {
+ return it.value().toString();
+ }
+ }
+ }
+ return "";
+}
+
+MpInterface::PlayerStatus MpAudaciousInterface::status()
+{
+ MpInterface::PlayerStatus status;
+ status = MpMprisInterface::status();
+ if (status != MpInterface::Unknown)
+ return status;
+
+ /* compability with older versions */
+ QDBusInterface dbus_iface(m_szServiceName, "/Player",
+ "org.freedesktop.MediaPlayer", QDBusConnection::sessionBus());
+ if (!dbus_iface.isValid())
+ return MpInterface::Unknown;
+
+ QDBusMessage reply = dbus_iface.call(QDBus::Block, "GetStatus");
+
+ switch (reply.arguments().first().toInt()) {
+ case 0: return MpInterface::Playing;
+ case 1: return MpInterface::Paused;
+ case 2: return MpInterface::Stopped;
+ default: return MpInterface::Unknown;
+ }
+}
+
+int MpAudaciousInterface::length()
+{
+ int length = MpMprisInterface::length();
+ if (length != -1)
+ return length;
+
+ /* compability with older versions */
+ MPRIS_CALL_METHOD("GetMetadata", -1)
+
+ foreach (QVariant v, reply.arguments()) {
+ QDBusArgument arg = qvariant_cast<QDBusArgument>(v);
+ QVariant v = qdbus_cast<QVariantMap>(arg);
+ if (v.userType() == QVariant::Map) {
+ const QVariantMap map = v.toMap();
+ QVariantMap::ConstIterator it = map.constBegin();
+ for ( ; it != map.constEnd(); ++it) {
+ if (it.key() == "length")
+ return it.value().toInt();
+ }
+ }
+ }
+ return -1;
+}
+
+#define AUDACIOUS_GET_TUPLE_FIELD(__field) \
+ if (this->status() != MpInterface::Playing) \
+ return ""; \
+ QDBusInterface dbus_iface("org.mpris.audacious", "/org/atheme/audacious", \
+ "org.atheme.audacious", QDBusConnection::sessionBus()); \
+ QList<QVariant> args; \
+ args << (uint)this->getPlayListPos() << QString(__field); \
+ QDBusReply<QDBusVariant> reply = dbus_iface.callWithArgumentList(QDBus::Block, "SongTuple", args); \
+ return reply.value().variant().toString(); \
+
+QString MpAudaciousInterface::year()
+{
+ AUDACIOUS_GET_TUPLE_FIELD("year")
+}
+
+QString MpAudaciousInterface::mediaType()
+{
+ AUDACIOUS_GET_TUPLE_FIELD("codec")
+}
+
+/* BMPx interface */
+MP_IMPLEMENT_DESCRIPTOR(
+ MpBmpxInterface,
+ "bmpx",
+ __tr2qs_ctx(
+ "An interface to BMPx.\n" \
+ "Download it from http://sourceforge.net/projects/beepmp\n"
+ ,
+ "mediaplayer"
+ )
+)
+
+MpBmpxInterface::MpBmpxInterface()
+: MpMprisInterface()
+{
+ m_szServiceName = "org.mpris.bmp";
+}
+
+/* BMPx doesn't provide GetStatus method, always assume Playing status */
+MpInterface::PlayerStatus MpBmpxInterface::status()
+{
+ return MpInterface::Playing;
+}
+
+/* Amarok2 interface */
+MP_IMPLEMENT_DESCRIPTOR(
+ MpAmarok2Interface,
+ "amarok2",
+ __tr2qs_ctx(
+ "An interface to Amarok2.\n" \
+ "Download it from http://amarok.kde.org\n"
+ ,
+ "mediaplayer"
+ )
+)
+
+MpAmarok2Interface::MpAmarok2Interface()
+: MpMprisInterface()
+{
+ m_szServiceName = "org.mpris.amarok";
+}
+
+/* Qmmp interface */
+MP_IMPLEMENT_DESCRIPTOR(
+ MpQmmpInterface,
+ "Qmmp",
+ __tr2qs_ctx(
+ "An interface to Qmmp.\n" \
+ "Download it from http://qmmp.ylsoftware.com\n"
+ ,
+ "mediaplayer"
+ )
+)
+
+MpQmmpInterface::MpQmmpInterface()
+: MpMprisInterface()
+{
+ m_szServiceName = "org.mpris.qmmp";
+}
+
+/* xmms2 interface */
+MP_IMPLEMENT_DESCRIPTOR(
+ MpXmms2Interface,
+ "xmms2",
+ __tr2qs_ctx(
+ "An interface to the Xmms2 media player.\n" \
+ "Download it from http://xmms2.org\n",
+ "mediaplayer"
+ )
+)
+
+MpXmms2Interface::MpXmms2Interface()
+: MpMprisInterface()
+{
+ m_szServiceName = "org.mpris.xmms2";
+}
+
+/* mozilla songbird interface */
+MP_IMPLEMENT_DESCRIPTOR(
+ MpSongbirdInterface,
+ "songbird",
+ __tr2qs_ctx(
+ "An interface to the Mozilla Songbird media player.\n" \
+ "Download it from http://www.getsongbird.com.\n" \
+ "To use it you have to install also the MPRIS addon " \
+ "available at http://addons.songbirdnest.com/addon/1626.\n",
+ "mediaplayer"
+ )
+)
+
+MpSongbirdInterface::MpSongbirdInterface()
+: MpMprisInterface()
+{
+ m_szServiceName = "org.mpris.songbird";
+}
+
+MpInterface::PlayerStatus MpSongbirdInterface::status()
+{
+ return MpInterface::Playing;
+}
+
+/* Totem interface */
+MP_IMPLEMENT_DESCRIPTOR(
+ MpTotemInterface,
+ "totem",
+ __tr2qs_ctx(
+ "An interface to Totem.\n" \
+ "Download it from http://projects.gnome.org/totem/\n"
+ ,
+ "mediaplayer"
+ )
+)
+
+MpTotemInterface::MpTotemInterface()
+: MpMprisInterface()
+{
+ m_szServiceName = "org.mpris.Totem";
+}
+
+/* Vlc interface */
+MP_IMPLEMENT_DESCRIPTOR(
+ MpVlcInterface,
+ "vlc",
+ __tr2qs_ctx(
+ "An interface to Vlc.\n" \
+ "Download it from http://www.videolan.org/\n" \
+ "You need to manually enable the DBus control\n" \
+ "interface in the Vlc preferences\n" \
+ ,
+ "mediaplayer"
+ )
+)
+
+MpVlcInterface::MpVlcInterface()
+: MpMprisInterface()
+{
+ m_szServiceName = "org.mpris.vlc";
+}
+#endif //COMPILE_ON_WINDOWS