diff options
Diffstat (limited to 'saltoqd/musicmanager.cpp')
-rw-r--r-- | saltoqd/musicmanager.cpp | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/saltoqd/musicmanager.cpp b/saltoqd/musicmanager.cpp new file mode 100644 index 0000000..8b1e9a9 --- /dev/null +++ b/saltoqd/musicmanager.cpp @@ -0,0 +1,199 @@ +#include <QtDBus/QDBusConnectionInterface> +#include "musicmanager.h" + +MusicManager::MusicManager(ToqManager *toq) : + QObject(toq), _toq(toq), _watcher(new QDBusServiceWatcher(this)), _isPlayerVisible(false) +{ + 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))); + + _toq->setEndpointListener(ToqConnection::MusicEndpoint, this); +} + +void MusicManager::handleMessage(const ToqConnection::Message &msg) +{ + Q_ASSERT(msg.destination == ToqConnection::MusicEndpoint); + switch (msg.type) { + case 0: + handleGetPlayerStatusMessage(msg); + _isPlayerVisible = true; + break; + case 1: + callMprisMethod("Play"); + // TODO Reply with new metadata! + break; + case 2: + callMprisMethod("Pause"); + break; + case 3: + callMprisMethod("Next"); + break; + case 4: + callMprisMethod("Previous"); + break; + case 0x8004: // Player closing? + _isPlayerVisible = false; + break; + case 5: //Volume up/down + case 6: + // Not yet implemented + default: + qWarning() << "Unknown message type" << msg.type; + break; + } +} + +void MusicManager::handleGetPlayerStatusMessage(const ToqConnection::Message &msg) +{ + QJsonObject reply; + reply.insert("result", int(0)); + reply.insert("description", QLatin1String("PlayerStatus Request received")); + reply.insert("artist", _curMetadata.value("xesam:artist").toString()); + reply.insert("album", _curMetadata.value("xesam:album").toString()); + reply.insert("title", _curMetadata.value("xesam:title").toString()); + reply.insert("state", _curPlaybackStatus == "Playing" ? QLatin1String("playing") : QLatin1String("paused")); + reply.insert("album-art", QLatin1String("")); + reply.insert("playlist", int(1)); // TODO + reply.insert("player", _curService); + reply.insert("version", QLatin1String("1.0")); + reply.insert("controller", QLatin1String("SERIAL")); + + _toq->sendReply(msg, 0x4000, reply); +} + +void MusicManager::switchToService(const QString &service) +{ + if (_curService != service) { + qDebug() << "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()) { + qDebug() << "got mpris metadata from service" << _curService; + _curMetadata = qdbus_cast<QVariantMap>(reply.value().variant().value<QDBusArgument>()); + } else { + qWarning() << reply.error().message(); + } + } +} + +void MusicManager::sendCurrentMprisMetadata() +{ + Q_ASSERT(_toq->isConnected()); + + QJsonObject obj; + fillWithMetadata(obj); + + _toq->sendMessage(ToqConnection::MusicEndpoint, ToqConnection::MusicEndpoint + 1, 0x8003, obj); +} + +void MusicManager::fillWithMetadata(QJsonObject &obj) +{ + obj.insert("artist", _curMetadata.value("xesam:artist").toString()); + obj.insert("album", _curMetadata.value("xesam:album").toString()); + obj.insert("title", _curMetadata.value("xesam:title").toString()); + obj.insert("state", _curPlaybackStatus == "Playing" ? QLatin1String("playing") : QLatin1String("paused")); + obj.insert("album-art", QLatin1String("")); + obj.insert("playlist", int(1)); // TODO + obj.insert("player", _curService); + obj.insert("version", QLatin1String("1.0")); + obj.insert("controller", QLatin1String("SERIAL")); +} + +void MusicManager::callMprisMethod(const QString &method) +{ + Q_ASSERT(!method.isEmpty()); + Q_ASSERT(!_curService.isEmpty()); + + qDebug() << _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()) { + qWarning() << "while calling mpris method on" << _curService << ":" << err.message(); + } +} + +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 (_toq->isConnected() && _isPlayerVisible) { + 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>()); + qDebug() << "received new metadata" << metadata; + _curMetadata = metadata; + } + + if (changed.contains("PlaybackStatus")) { + _curPlaybackStatus = changed.value("PlaybackStatus").toString(); + if (_curPlaybackStatus == "Stopped") { + _curMetadata.clear(); + } + } + + if (_toq->isConnected() && _isPlayerVisible) { + sendCurrentMprisMetadata(); + } + + switchToService(message().service()); +} |