summaryrefslogtreecommitdiff
path: root/daemon/musicmanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/musicmanager.cpp')
-rw-r--r--daemon/musicmanager.cpp221
1 files changed, 221 insertions, 0 deletions
diff --git a/daemon/musicmanager.cpp b/daemon/musicmanager.cpp
new file mode 100644
index 0000000..385abbf
--- /dev/null
+++ b/daemon/musicmanager.cpp
@@ -0,0 +1,221 @@
+#include <QDBusConnection>
+#include <QDBusConnectionInterface>
+#include "musicmanager.h"
+
+MusicManager::MusicManager(WatchConnector *watch, QObject *parent)
+ : QObject(parent), l(metaObject()->className()),
+ watch(watch), _watcher(new QDBusServiceWatcher(this))
+{
+ QDBusConnection bus = QDBusConnection::sessionBus();
+ QDBusConnectionInterface *bus_iface = bus.interface();
+
+ // This watcher will be used to find when the current MPRIS service dies
+ // (and thus we must clear the metadata)
+ _watcher->setConnection(bus);
+ connect(_watcher, &QDBusServiceWatcher::serviceOwnerChanged,
+ this, &MusicManager::handleMprisServiceOwnerChanged);
+
+ // Try to find an active MPRIS service to initially connect to
+ const QStringList &services = bus_iface->registeredServiceNames();
+ foreach (QString service, services) {
+ if (service.startsWith("org.mpris.MediaPlayer2.")) {
+ switchToService(service);
+ fetchMetadataFromService();
+ // The watch is not connected by this point,
+ // so we don't send the current metadata.
+ break;
+ }
+ }
+
+ // Even if we didn't find any service, we still listen for metadataChanged signals
+ // from every MPRIS-compatible player
+ // If such a signal comes in, we will connect to the source service for that signal
+ bus.connect("", "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged",
+ this, SLOT(handleMprisPropertiesChanged(QString,QMap<QString,QVariant>,QStringList)));
+
+ // Now set up the Pebble endpoint handler for music control commands
+ watch->setEndpointHandler(WatchConnector::watchMUSIC_CONTROL, [this](const QByteArray& data) {
+ handleMusicControl(WatchConnector::MusicControl(data.at(0)));
+ return true;
+ });
+
+ // If the watch disconnects, we will send the current metadata when it comes back.
+ connect(watch, &WatchConnector::connectedChanged,
+ this, &MusicManager::handleWatchConnected);
+}
+
+void MusicManager::switchToService(const QString &service)
+{
+ if (_curService != service) {
+ qCDebug(l) << "switching to mpris service" << service;
+ _curService = service;
+
+ if (_curService.isEmpty()) {
+ _watcher->setWatchedServices(QStringList());
+ } else {
+ _watcher->setWatchedServices(QStringList(_curService));
+ }
+ }
+}
+
+void MusicManager::fetchMetadataFromService()
+{
+ _curMetadata.clear();
+
+ if (!_curService.isEmpty()) {
+ QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get");
+ call << "org.mpris.MediaPlayer2.Player" << "Metadata";
+ QDBusReply<QDBusVariant> reply = QDBusConnection::sessionBus().call(call);
+ if (reply.isValid()) {
+ qCDebug(l) << "got mpris metadata from service" << _curService;
+ _curMetadata = qdbus_cast<QVariantMap>(reply.value().variant().value<QDBusArgument>());
+ } else {
+ qCWarning(l) << reply.error().message();
+ }
+ }
+}
+
+void MusicManager::sendCurrentMprisMetadata()
+{
+ Q_ASSERT(watch->isConnected());
+
+ QString track = _curMetadata.value("xesam:title").toString().left(30);
+ QString album = _curMetadata.value("xesam:album").toString().left(30);
+ QString artist = _curMetadata.value("xesam:artist").toString().left(30);
+
+ qCDebug(l) << "sending mpris metadata:" << track << album << artist;
+
+ watch->sendMusicNowPlaying(track, album, artist);
+}
+
+void MusicManager::callMprisMethod(const QString &method)
+{
+ Q_ASSERT(!method.isEmpty());
+ Q_ASSERT(!_curService.isEmpty());
+
+ qCDebug(l) << _curService << "->" << method;
+
+ QDBusConnection bus = QDBusConnection::sessionBus();
+ QDBusMessage call = QDBusMessage::createMethodCall(_curService,
+ "/org/mpris/MediaPlayer2",
+ "org.mpris.MediaPlayer2.Player",
+ method);
+
+ QDBusError err = bus.call(call);
+
+ if (err.isValid()) {
+ qCWarning(l) << "while calling mpris method on" << _curService << ":" << err.message();
+ }
+}
+
+void MusicManager::handleMusicControl(WatchConnector::MusicControl operation)
+{
+ qCDebug(l) << "operation from watch:" << operation;
+
+ if (_curService.isEmpty()) {
+ qCDebug(l) << "can't do any music operation, no mpris interface active";
+ return;
+ }
+
+ switch (operation) {
+ case WatchConnector::musicPLAY_PAUSE:
+ callMprisMethod("PlayPause");
+ break;
+ case WatchConnector::musicPAUSE:
+ callMprisMethod("Pause");
+ break;
+ case WatchConnector::musicPLAY:
+ callMprisMethod("Play");
+ break;
+ case WatchConnector::musicNEXT:
+ callMprisMethod("Next");
+ break;
+ case WatchConnector::musicPREVIOUS:
+ callMprisMethod("Previous");
+ break;
+
+ case WatchConnector::musicVOLUME_UP:
+ case WatchConnector::musicVOLUME_DOWN: {
+ QDBusConnection bus = QDBusConnection::sessionBus();
+ QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2",
+ "org.freedesktop.DBus.Properties", "Get");
+ call << "org.mpris.MediaPlayer2.Player" << "Volume";
+ QDBusReply<QDBusVariant> volumeReply = bus.call(call);
+ if (volumeReply.isValid()) {
+ double volume = volumeReply.value().variant().toDouble();
+ if (operation == WatchConnector::musicVOLUME_UP) {
+ volume += 0.1;
+ }
+ else {
+ volume -= 0.1;
+ }
+ qCDebug(l) << "Setting volume" << volume;
+
+ call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Set");
+ call << "org.mpris.MediaPlayer2.Player" << "Volume" << QVariant::fromValue(QDBusVariant(volume));
+
+ QDBusError err = QDBusConnection::sessionBus().call(call);
+ if (err.isValid()) {
+ qCWarning(l) << err.message();
+ }
+ } else {
+ qCWarning(l) << volumeReply.error().message();
+ }
+ }
+ break;
+
+ case WatchConnector::musicGET_NOW_PLAYING:
+ sendCurrentMprisMetadata();
+ break;
+
+ default:
+ qCWarning(l) << "Operation" << operation << "not supported";
+ break;
+ }
+}
+
+void MusicManager::handleMprisServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
+{
+ Q_UNUSED(oldOwner);
+ if (name == _curService && newOwner.isEmpty()) {
+ // Oops, current service is going away
+ switchToService(QString());
+ _curMetadata.clear();
+ if (watch->isConnected()) {
+ sendCurrentMprisMetadata();
+ }
+ }
+}
+
+void MusicManager::handleMprisPropertiesChanged(const QString &interface, const QMap<QString, QVariant> &changed, const QStringList &invalidated)
+{
+ Q_ASSERT(calledFromDBus());
+ Q_UNUSED(interface);
+ Q_UNUSED(invalidated);
+
+ if (changed.contains("Metadata")) {
+ QVariantMap metadata = qdbus_cast<QVariantMap>(changed.value("Metadata").value<QDBusArgument>());
+ qCDebug(l) << "received new metadata" << metadata;
+ _curMetadata = metadata;
+ }
+
+ if (changed.contains("PlaybackStatus")) {
+ QString status = changed.value("PlaybackStatus").toString();
+ if (status == "Stopped") {
+ _curMetadata.clear();
+ }
+ }
+
+ if (watch->isConnected()) {
+ sendCurrentMprisMetadata();
+ }
+
+ switchToService(message().service());
+}
+
+void MusicManager::handleWatchConnected()
+{
+ if (watch->isConnected()) {
+ sendCurrentMprisMetadata();
+ }
+}