Music Hub  ..
A session-wide music playback service
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 
30 using namespace lomiri::MediaHub;
31 
33 {
34  Q_OBJECT
35 
36 public:
37  DBusTrackList(const QDBusConnection &conn, const QString &path,
38  TrackListPrivate *d);
39 
40 private 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 
50 private:
52 };
53 
54 DBusTrackList::DBusTrackList(const QDBusConnection &conn,
55  const QString &path,
56  TrackListPrivate *d):
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 
96 TrackListPrivate::TrackListPrivate(TrackList *q):
97  m_canEditTracks(true),
98  m_currentTrack(-1),
99  q_ptr(q)
100 {
101 }
102 
103 void 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 
118 QString 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 
125 void 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 
132 void 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 
145 void 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 
162 void 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 
181 void TrackListPrivate::goTo(int index)
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 
198 void TrackListPrivate::onTrackAdded(const QString &id)
199 {
200  onTracksAdded({id});
201 }
202 
203 void 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 
224 void 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 
234 void 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 
252 void 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 
265 TrackList::~TrackList() = default;
266 
267 const QVector<Track> &TrackList::tracks() const
268 {
269  Q_D(const TrackList);
270  return d->m_tracks;
271 }
272 
273 bool TrackList::canEditTracks() const
274 {
275  Q_D(const TrackList);
276  return d->m_canEditTracks;
277 }
278 
279 int TrackList::currentTrack() const
280 {
281  Q_D(const TrackList);
282  return d->m_currentTrack;
283 }
284 
285 void TrackList::addTrackWithUriAt(const QUrl &uri, int position,
286  bool makeCurrent)
287 {
288  Q_D(TrackList);
289  d->addTrackWithUriAt(uri, position, makeCurrent);
290 }
291 
292 void TrackList::addTracksWithUriAt(const QVector<QUrl> &uris, int position)
293 {
294  Q_D(TrackList);
295  d->addTracksWithUriAt(uris, position);
296 }
297 
298 void TrackList::moveTrack(int index, int to)
299 {
300  Q_D(TrackList);
301  d->moveTrack(index, to);
302 }
303 
304 void TrackList::removeTrack(int index)
305 {
306  Q_D(TrackList);
307  d->removeTrack(index);
308 }
309 
310 void 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"
lomiri::MediaHub
Definition: dbus_utils.h:25
QObject
DBusTrackList
Definition: track_list.cpp:32
lomiri::MediaHub::TrackListPrivate
Definition: track_list_p.h:32
lomiri::MediaHub::TrackListPrivate::onTrackAdded
void onTrackAdded(const QString &id)
Definition: track_list.cpp:198
lomiri::MediaHub::TrackList::tracks
const QVector< Track > & tracks() const
Definition: track_list.cpp:267
mpris::objectPath
const QString objectPath
Definition: media_player2.cpp:39
lomiri::MediaHub::TrackListPrivate::onTrackMoved
void onTrackMoved(const QString &id, const QString &to)
Definition: track_list.cpp:224
FDO_PROPERTIES_INTERFACE
#define FDO_PROPERTIES_INTERFACE
Definition: dbus_constants.h:41
QDBusAbstractInterface
lomiri::MediaHub::TrackList::moveTrack
void moveTrack(int index, int to)
Definition: track_list.cpp:298
lomiri::MediaHub::TrackList::canEditTracks
bool canEditTracks
Definition: track_list.h:46
lomiri::MediaHub::TrackList::goTo
void goTo(int index)
Definition: track_list.cpp:310
dbus_constants.h
lomiri::MediaHub::TrackList::addTrackWithUriAt
void addTrackWithUriAt(const QUrl &uri, int position, bool makeCurrent)
Definition: track_list.cpp:285
lomiri::MediaHub::TrackList::addTracksWithUriAt
void addTracksWithUriAt(const QVector< QUrl > &uris, int position)
Definition: track_list.cpp:292
lomiri::MediaHub::TrackListPrivate::ensureProxy
bool ensureProxy() const
Definition: track_list.cpp:109
lomiri::MediaHub::TrackListPrivate::reset
void reset()
Definition: track_list.cpp:190
lomiri::MediaHub::Track
Definition: track.h:44
lomiri::MediaHub::TrackList::removeTrack
void removeTrack(int index)
Definition: track_list.cpp:304
MPRIS_TRACKLIST_INTERFACE
#define MPRIS_TRACKLIST_INTERFACE
Definition: dbus_constants.h:30
lomiri::MediaHub::TrackList::TrackList
TrackList(QObject *parent=nullptr)
Definition: track_list.cpp:259
lomiri::MediaHub::TrackListPrivate::onTrackListReset
void onTrackListReset()
Definition: track_list.cpp:243
lomiri::MediaHub::TrackList::~TrackList
~TrackList()
lomiri::MediaHub::TrackListPrivate::initialize
void initialize(const QVariantMap &properties)
Definition: track_list.cpp:125
DBusTrackList::DBusTrackList
DBusTrackList(const QDBusConnection &conn, const QString &path, TrackListPrivate *d)
Definition: track_list.cpp:54
MEDIAHUB_SERVICE_NAME
#define MEDIAHUB_SERVICE_NAME
Definition: dbus_constants.h:22
lomiri::MediaHub::DBusUtils::waitForFinished
static bool waitForFinished(const QDBusPendingCall &call)
Definition: dbus_utils.cpp:34
lomiri::MediaHub::TrackListPrivate::goTo
void goTo(int index)
Definition: track_list.cpp:181
lomiri::MediaHub::TrackListPrivate::onTracksAdded
void onTracksAdded(const QStringList &ids)
Definition: track_list.cpp:203
lomiri::MediaHub::TrackListPrivate::createProxy
void createProxy(const QDBusConnection &conn, const QString &objectPath)
Definition: track_list.cpp:103
lomiri::MediaHub::TrackList::currentTrack
int currentTrack
Definition: track_list.h:48
lomiri::MediaHub::TrackListPrivate::remotePos
QString remotePos(int index) const
Definition: track_list.cpp:118
lomiri::MediaHub::TrackList
Definition: track_list.h:35
track_list_p.h
lomiri::MediaHub::TrackListPrivate::removeTrack
void removeTrack(int index)
Definition: track_list.cpp:172
dbus_utils.h
lomiri::MediaHub::TrackListPrivate::moveTrack
void moveTrack(int index, int to)
Definition: track_list.cpp:162
lomiri::MediaHub::TrackListPrivate::onTrackRemoved
void onTrackRemoved(const QString &id)
Definition: track_list.cpp:234
lomiri::MediaHub::TrackListPrivate::addTracksWithUriAt
void addTracksWithUriAt(const QVector< QUrl > &uris, int position)
Definition: track_list.cpp:145
lomiri::MediaHub::TrackListPrivate::onTrackChanged
void onTrackChanged(const QString &id)
Definition: track_list.cpp:252
lomiri::MediaHub::TrackList::reset
void reset()
Definition: track_list.cpp:316
lomiri::MediaHub::TrackListPrivate::addTrackWithUriAt
void addTrackWithUriAt(const QUrl &uri, int position, bool makeCurrent)
Definition: track_list.cpp:132