#include #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,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: if (!_curService.isEmpty()) callMprisMethod("Play"); // TODO Reply with new metadata! break; case 2: if (!_curService.isEmpty()) callMprisMethod("Pause"); break; case 3: if (!_curService.isEmpty()) callMprisMethod("Next"); break; case 4: if (!_curService.isEmpty()) 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 reply = QDBusConnection::sessionBus().call(call); if (reply.isValid()) { qDebug() << "got mpris metadata from service" << _curService; _curMetadata = qdbus_cast(reply.value().variant().value()); } 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 &changed, const QStringList &invalidated) { Q_ASSERT(calledFromDBus()); Q_UNUSED(interface); Q_UNUSED(invalidated); if (changed.contains("Metadata")) { QVariantMap metadata = qdbus_cast(changed.value("Metadata").value()); 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()); }