summaryrefslogtreecommitdiff
path: root/daemon/musicmanager.cpp
blob: 05e372751e965106e65cea322f6e6cecbb4c4ae8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#include <QtDBus>
#include "musicmanager.h"

MusicManager::MusicManager(WatchConnector *watch, QObject *parent)
    : QObject(parent), watch(watch)
{
    QDBusConnection bus = QDBusConnection::sessionBus();
    QDBusConnectionInterface *bus_iface = bus.interface();

    // Listen for MPRIS signals from every player
    bus.connect("", "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged",
                this, SLOT(handleMprisPropertiesChanged(QString,QMap<QString,QVariant>,QStringList)));

    // Listen for D-Bus name registered signals to see if a MPRIS service comes up
    connect(bus_iface, &QDBusConnectionInterface::serviceOwnerChanged,
            this, &MusicManager::handleServiceOwnerChanged);

    // But also try to find an already active MPRIS service
    const QStringList &services = bus_iface->registeredServiceNames();
    foreach (QString service, services) {
        if (service.startsWith("org.mpris.MediaPlayer2.")) {
            switchToService(service);
            break;
        }
    }

    // Set up watch endpoint handler for music control
    watch->setEndpointHandler(WatchConnector::watchMUSIC_CONTROL, [this](const QByteArray& data) {
        musicControl(WatchConnector::MusicControl(data.at(0)));
        return true;
    });
    connect(watch, &WatchConnector::connectedChanged,
            this, &MusicManager::handleWatchConnected);
}

void MusicManager::musicControl(WatchConnector::MusicControl operation)
{
    logger()->debug() << "operation from watch:" << operation;

    if (_curService.isEmpty()) {
        logger()->info() << "No mpris interface active";
        return;
    }

    QString method;

    switch(operation) {
    case WatchConnector::musicPLAY_PAUSE:
        method = "PlayPause";
        break;
    case WatchConnector::musicPAUSE:
        method = "Pause";
        break;
    case WatchConnector::musicPLAY:
        method = "Play";
        break;
    case WatchConnector::musicNEXT:
        method = "Next";
        break;
    case WatchConnector::musicPREVIOUS:
        method = "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;
            }
            logger()->debug() << "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()) {
                logger()->error() << err.message();
            }
        } else {
            logger()->error() << volumeReply.error().message();
        }
        }
        return;
    case WatchConnector::musicGET_NOW_PLAYING:
        setMprisMetadata(_curMetadata);
        return;

    case WatchConnector::musicSEND_NOW_PLAYING:
    default:
        logger()->warn() << "Operation" << operation << "not supported";
        return;
    }

    if (method.isEmpty()) {
        logger()->error() << "Requested unsupported operation" << operation;
        return;
    }

    logger()->debug() << operation << "->" << method;

    QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method);
    QDBusError err = QDBusConnection::sessionBus().call(call);
    if (err.isValid()) {
        logger()->error() << err.message();
    }
}

void MusicManager::switchToService(const QString &service)
{
    if (_curService != service) {
        logger()->debug() << "switching to mpris service" << service;
        _curService = service;
    }
}

void MusicManager::setMprisMetadata(const QVariantMap &metadata)
{
    _curMetadata = metadata;
    QString track = metadata.value("xesam:title").toString();
    QString album = metadata.value("xesam:album").toString();
    QString artist = metadata.value("xesam:artist").toString();

    logger()->debug() << "new mpris metadata:" << track << album << artist;

    if (watch->isConnected()) {
        watch->sendMusicNowPlaying(track, album, artist);
    }
}

void MusicManager::handleServiceRegistered(const QString &service)
{
    if (service.startsWith("org.mpris.MediaPlayer2.")) {
        if (_curService.isEmpty()) {
            switchToService(service);
        }
    }
}

void MusicManager::handleServiceUnregistered(const QString &service)
{
    if (service == _curService) {
        // Oops! Losing the current MPRIS service
        // We must assume it's been closed and thus remove current metadata
        setMprisMetadata(QVariantMap());
        switchToService(QString());
    }
}

void MusicManager::handleServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
{
    Q_UNUSED(oldOwner);
    if (newOwner.isEmpty()) {
        handleServiceUnregistered(name);
    } else {
        handleServiceRegistered(name);
    }
}

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>());
        logger()->debug() << "received new metadata" << metadata;
        setMprisMetadata(metadata);
    }

    if (changed.contains("PlaybackStatus")) {
        QString status = changed.value("PlaybackStatus").toString();
        if (status == "Stopped") {
            setMprisMetadata(QVariantMap());
        }
    }

    switchToService(message().service());
}

void MusicManager::handleWatchConnected()
{
    if (watch->isConnected()) {
        if (!_curService.isEmpty()) {
            QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get");
            call << "org.mpris.MediaPlayer2.Player" << "Metadata";
            QDBusReply<QDBusVariant> metadata = QDBusConnection::sessionBus().call(call);
            if (metadata.isValid()) {
                setMprisMetadata(qdbus_cast<QVariantMap>(metadata.value().variant().value<QDBusArgument>()));
            } else {
                logger()->error() << metadata.error().message();
                setMprisMetadata(QVariantMap());
            }
        }
    }
}