summaryrefslogtreecommitdiff
path: root/saltoqd/musicmanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'saltoqd/musicmanager.cpp')
-rw-r--r--saltoqd/musicmanager.cpp199
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());
+}