Music Hub ..
A session-wide music playback service
 
Loading...
Searching...
No Matches
track_list.cpp
Go to the documentation of this file.
1/*
2 * Copyright © 2021-2022 UBports Foundation.
3 *
4 * Contact: Alberto Mardegan <mardy@users.sourceforge.net>
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License version 3,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "track_list_p.h"
20
21#include "dbus_constants.h"
22#include "dbus_utils.h"
23
24#include <QDBusAbstractInterface>
25#include <QDBusPendingCall>
26#include <QDBusPendingCallWatcher>
27#include <QDBusPendingReply>
28#include <QDebug>
29
30using namespace lomiri::MediaHub;
31
33{
34 Q_OBJECT
35
36public:
37 DBusTrackList(const QDBusConnection &conn, const QString &path,
39
40private Q_SLOTS:
41 void onTrackAdded(const QString &id) { d->onTrackAdded(id); }
42 void onTracksAdded(const QStringList &ids) { d->onTracksAdded(ids); }
43 void onTrackMoved(const QString &id, const QString &to) {
44 d->onTrackMoved(id, to);
45 }
46 void onTrackRemoved(const QString &id) { d->onTrackRemoved(id); }
47 void onTrackListReset() { d->onTrackListReset(); }
48 void onTrackChanged(const QString &id) { d->onTrackChanged(id); }
49
50private:
52};
53
54DBusTrackList::DBusTrackList(const QDBusConnection &conn,
55 const QString &path,
58 path,
60 conn, nullptr),
61 d(d)
62{
63 QDBusConnection c(conn);
64
65 c.connect(service(), path, interface(), QStringLiteral("TrackAdded"),
66 this, SLOT(onTrackAdded(QString)));
67 c.connect(service(), path, interface(), QStringLiteral("TracksAdded"),
68 this, SLOT(onTracksAdded(QStringList)));
69 c.connect(service(), path, interface(), QStringLiteral("TrackRemoved"),
70 this, SLOT(onTrackRemoved(QString)));
71 c.connect(service(), path, interface(), QStringLiteral("TrackMoved"),
72 this, SLOT(onTrackMoved(QString,QString)));
73 c.connect(service(), path, interface(), QStringLiteral("TrackListReset"),
74 this, SLOT(onTrackListReset()));
75
76 c.connect(service(), path, interface(), QStringLiteral("TrackChanged"),
77 this, SLOT(onTrackChanged(QString)));
78
79 // Blocking call to get the initial properties
80 QDBusMessage msg = QDBusMessage::createMethodCall(
81 QStringLiteral(MEDIAHUB_SERVICE_NAME),
82 path,
83 QStringLiteral(FDO_PROPERTIES_INTERFACE),
84 QStringLiteral("GetAll"));
85 msg.setArguments({ QStringLiteral(MPRIS_TRACKLIST_INTERFACE) });
86 QDBusMessage reply = c.call(msg);
87 if (Q_UNLIKELY(reply.type()) == QDBusMessage::ErrorMessage) {
88 qWarning() << "Cannot get tracklist properties:" <<
89 reply.errorMessage();
90 } else {
91 QDBusArgument arg = reply.arguments().first().value<QDBusArgument>();
92 d->initialize(qdbus_cast<QVariantMap>(arg));
93 }
94}
95
97 m_canEditTracks(true),
98 m_currentTrack(-1),
99 q_ptr(q)
100{
101}
102
103void TrackListPrivate::createProxy(const QDBusConnection &conn,
104 const QString &objectPath)
105{
106 m_proxy.reset(new DBusTrackList(conn, objectPath, this));
107}
108
110{
111 if (!m_proxy) {
112 qWarning() << "Operating on disconnected playlist not supported!";
113 return false;
114 }
115 return true;
116}
117
118QString TrackListPrivate::remotePos(int index) const
119{
120 return (index >= 0 && index < m_trackIds.count()) ?
121 m_trackIds[index] :
122 QStringLiteral("/org/mpris/MediaPlayer2/TrackList/NoTrack");
123}
124
125void TrackListPrivate::initialize(const QVariantMap &properties)
126{
127 m_canEditTracks =
128 properties.value(QStringLiteral("CanEditTracks"), false).toBool();
129 // The tracklist is always empty here, no need to retrieve it
130}
131
132void TrackListPrivate::addTrackWithUriAt(const QUrl &uri, int position,
133 bool makeCurrent)
134{
135 if (!ensureProxy()) return;
136
137 QDBusPendingCall call =
138 m_proxy->asyncCall(QStringLiteral("AddTrack"),
139 uri.toString(), remotePos(position), makeCurrent);
140 m_indexForNextAddition = position;
141 m_urisForNextAddition = { uri };
143}
144
145void TrackListPrivate::addTracksWithUriAt(const QVector<QUrl> &uris,
146 int position)
147{
148 if (!ensureProxy()) return;
149
150 QStringList urisAsStrings;
151 for (const QUrl &uri: uris) {
152 urisAsStrings.append(uri.toString());
153 }
154 QDBusPendingCall call =
155 m_proxy->asyncCall(QStringLiteral("AddTracks"),
156 urisAsStrings, remotePos(position));
157 m_indexForNextAddition = position;
158 m_urisForNextAddition = uris;
160}
161
162void TrackListPrivate::moveTrack(int index, int to)
163{
164 if (!ensureProxy()) return;
165
166 QDBusPendingCall call =
167 m_proxy->asyncCall(QStringLiteral("MoveTrack"),
168 remotePos(index), remotePos(to));
170}
171
173{
174 if (!ensureProxy()) return;
175
176 QDBusPendingCall call =
177 m_proxy->asyncCall(QStringLiteral("RemoveTrack"), remotePos(index));
179}
180
182{
183 if (!ensureProxy()) return;
184
185 QDBusPendingCall call =
186 m_proxy->asyncCall(QStringLiteral("GoTo"), remotePos(index));
188}
189
191{
192 if (!ensureProxy()) return;
193
194 QDBusPendingCall call = m_proxy->asyncCall(QStringLiteral("Reset"));
196}
197
198void TrackListPrivate::onTrackAdded(const QString &id)
199{
200 onTracksAdded({id});
201}
202
203void TrackListPrivate::onTracksAdded(const QStringList &ids)
204{
205 Q_Q(TrackList);
206 // TODO: rewrite once we change the service to be spec-compliant
207 if (ids.count() != m_urisForNextAddition.count()) {
208 qWarning() << "Mismatching counters for TrackAdded signal";
209 return;
210 }
211 if (m_indexForNextAddition < 0 || m_indexForNextAddition > m_trackIds.count()) {
212 m_indexForNextAddition = m_trackIds.count();
213 }
214 for (int i = 0; i < m_urisForNextAddition.count(); i++) {
215 const QUrl &uri = m_urisForNextAddition[i];
216 m_tracks.insert(m_indexForNextAddition + i, Track(uri));
217 m_trackIds.insert(m_indexForNextAddition + i, ids[i]);
218 }
219
220 Q_EMIT q->tracksAdded(m_indexForNextAddition,
221 m_indexForNextAddition + ids.count() - 1);
222}
223
224void TrackListPrivate::onTrackMoved(const QString &id, const QString &to)
225{
226 Q_Q(TrackList);
227 int idIndex = m_trackIds.indexOf(id);
228 int toIndex = m_trackIds.indexOf(to);
229 m_trackIds.move(idIndex, toIndex);
230 m_tracks.move(idIndex, toIndex);
231 Q_EMIT q->trackMoved(idIndex, toIndex);
232}
233
234void TrackListPrivate::onTrackRemoved(const QString &id)
235{
236 Q_Q(TrackList);
237 int idIndex = m_trackIds.indexOf(id);
238 m_trackIds.remove(idIndex);
239 m_tracks.remove(idIndex);
240 Q_EMIT q->trackRemoved(idIndex);
241}
242
244{
245 Q_Q(TrackList);
246 m_trackIds.clear();
247 m_tracks.clear();
248 m_currentTrack = -1;
249 Q_EMIT q->trackListReset();
250}
251
252void TrackListPrivate::onTrackChanged(const QString &id)
253{
254 Q_Q(TrackList);
255 m_currentTrack = m_trackIds.indexOf(id);
256 Q_EMIT q->currentTrackChanged();
257}
258
260 QObject(parent),
261 d_ptr(new TrackListPrivate(this))
262{
263}
264
265TrackList::~TrackList() = default;
266
267const QVector<Track> &TrackList::tracks() const
268{
269 Q_D(const TrackList);
270 return d->m_tracks;
271}
272
274{
275 Q_D(const TrackList);
276 return d->m_canEditTracks;
277}
278
280{
281 Q_D(const TrackList);
282 return d->m_currentTrack;
283}
284
285void TrackList::addTrackWithUriAt(const QUrl &uri, int position,
286 bool makeCurrent)
287{
288 Q_D(TrackList);
289 d->addTrackWithUriAt(uri, position, makeCurrent);
290}
291
292void TrackList::addTracksWithUriAt(const QVector<QUrl> &uris, int position)
293{
294 Q_D(TrackList);
295 d->addTracksWithUriAt(uris, position);
296}
297
298void TrackList::moveTrack(int index, int to)
299{
300 Q_D(TrackList);
301 d->moveTrack(index, to);
302}
303
305{
306 Q_D(TrackList);
307 d->removeTrack(index);
308}
309
310void TrackList::goTo(int index)
311{
312 Q_D(TrackList);
313 d->goTo(index);
314}
315
317{
318 Q_D(TrackList);
319 d->reset();
320}
321
322#include "track_list.moc"
DBusTrackList(const QDBusConnection &conn, const QString &path, TrackListPrivate *d)
void onTrackChanged(const QString &id)
void moveTrack(int index, int to)
void addTrackWithUriAt(const QUrl &uri, int position, bool makeCurrent)
void onTrackRemoved(const QString &id)
void createProxy(const QDBusConnection &conn, const QString &objectPath)
void onTracksAdded(const QStringList &ids)
void initialize(const QVariantMap &properties)
void addTracksWithUriAt(const QVector< QUrl > &uris, int position)
QString remotePos(int index) const
void onTrackAdded(const QString &id)
void onTrackMoved(const QString &id, const QString &to)
void addTrackWithUriAt(const QUrl &uri, int position, bool makeCurrent)
const QVector< Track > & tracks() const
void addTracksWithUriAt(const QVector< QUrl > &uris, int position)
TrackList(QObject *parent=nullptr)
void moveTrack(int index, int to)
#define MEDIAHUB_SERVICE_NAME
#define MPRIS_TRACKLIST_INTERFACE
#define FDO_PROPERTIES_INTERFACE
static bool waitForFinished(const QDBusPendingCall &call)