Music Hub  ..
A session-wide music playback service
track_list_implementation.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2013-2015 Canonical Ltd.
3  * Copyright © 2022 UBports Foundation.
4  *
5  * Contact: Alberto Mardegan <mardy@users.sourceforge.net>
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License version 3,
9  * as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authored by: Thomas Voß <thomas.voss@canonical.com>
20  */
21 
23 
24 #include "engine.h"
25 #include "logging.h"
26 
27 #include <QMap>
28 #include <QPair>
29 #include <QRandomGenerator>
30 #include <QSharedPointer>
31 #include <QUrl>
32 
33 #include <algorithm>
34 #include <sstream>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <tuple>
38 #include <unistd.h>
39 
40 namespace media = lomiri::MediaHubService;
41 
42 using namespace media;
43 
44 namespace lomiri {
45 namespace MediaHubService {
46 
48 {
49  Q_DECLARE_PUBLIC(TrackListImplementation)
50 
51 public:
52  typedef QMap<Track::Id, QPair<QUrl, Track::MetaData>> MetaDataCache;
53 
55  const QSharedPointer<media::Engine::MetaDataExtractor> &extractor,
57 
58  TrackList::ConstIterator empty_iterator() const { return m_tracks.begin(); }
59  bool is_first_track(const TrackList::ConstIterator &it) const {
60  return it == m_tracks.begin();
61  }
62  bool is_last_track(const TrackList::ConstIterator &it) const {
63  return it == m_tracks.end();
64  }
65 
66  TrackList::ConstIterator current_iterator() const;
67  void set_current_track(const Track::Id &id);
68  Track::Id get_current_track() const;
69  TrackList::ConstIterator get_current_shuffled() const;
70 
71  void add_track_with_uri_at(const QUrl &uri,
72  const Track::Id &position,
73  bool make_current);
74  void add_tracks_with_uri_at(const QVector<QUrl> &uris,
75  const Track::Id &position);
76  bool move_track(const Track::Id &id, const Track::Id &to);
77  void remove_track(const Track::Id &id);
78  void do_remove_track(const Track::Id &id);
79  void go_to(const Track::Id &track);
80  void updateCachedTrackMetadata(const Track::Id &id, const QUrl &uri)
81  {
82  auto i = meta_data_cache.find(id);
83  if (i == meta_data_cache.end()) {
84  meta_data_cache[id] = qMakePair(uri, Track::MetaData{});
85  } else {
86  i.value().first = uri;
87  }
88  }
89 
90  media::TrackList::Container::iterator get_shuffled_insert_it()
91  {
92  auto random_it = shuffled_tracks.begin();
93  if (random_it == shuffled_tracks.end())
94  return random_it;
95 
96  uint32_t random = QRandomGenerator::global()->generate();
97  // This is slightly biased, but not much, as RAND_MAX >= 32767, which is
98  // much more than the average number of tracks.
99  // Note that for N tracks we have N + 1 possible insertion positions.
100  std::advance(random_it, random % (shuffled_tracks.count() + 1));
101  return random_it;
102  }
103 
106  QSharedPointer<Engine::MetaDataExtractor> extractor;
107  // Used for caching the original tracklist order to be used to restore the order
108  // to the live TrackList after shuffle is turned off
111  bool shuffle;
116 };
117 
118 }} // namespace
119 
121  const QSharedPointer<media::Engine::MetaDataExtractor> &extractor,
123  track_counter(0),
124  extractor(extractor),
125  shuffle(false),
126  loop_status(media::Player::LoopStatus::none),
127  current_position(0),
128  q_ptr(q)
129 {
130 }
131 
134 {
135  // Prevent the TrackList from sitting at the end which will cause
136  // a segfault when calling current()
137  if (!m_tracks.isEmpty() && current_track.isEmpty())
138  {
139  MH_DEBUG("Wrapping d->current_track back to begin()");
140  current_track = m_tracks.first();
141  }
142  else if (m_tracks.isEmpty())
143  {
144  MH_ERROR("TrackList is empty therefore there is no valid current track");
145  }
146 
147  return std::find(m_tracks.begin(), m_tracks.end(), current_track);
148 }
149 
151 {
152  if (m_tracks.contains(id))
153  current_track = id;
154 }
155 
157 {
158  if (m_tracks.isEmpty())
159  return Track::Id{};
160 
161  return current_track;
162 }
163 
166 {
167  auto current_id = get_current_track();
168  return std::find(shuffled_tracks.begin(), shuffled_tracks.end(), current_id);
169 }
170 
172  const QUrl &uri,
173  const media::Track::Id& position,
174  bool make_current)
175 {
177  MH_TRACE("");
178 
179  Track::Id id = q->objectName() + '/' + QString::number(track_counter++);
180 
181  MH_DEBUG("Adding Track::Id: %s", qUtf8Printable(id));
182  MH_DEBUG("\tURI: %s", qUtf8Printable(uri.toString()));
183 
184  const auto current = get_current_track();
185 
186  auto it = std::find(m_tracks.begin(), m_tracks.end(), position);
187  m_tracks.insert(it, id);
188 
189  updateCachedTrackMetadata(id, uri);
190 
191  if (shuffle)
193 
194  if (make_current) {
195  set_current_track(id);
196  go_to(id);
197  } else {
198  set_current_track(current);
199  }
200 
201  MH_DEBUG("Signaling that we just added track id: %s", qUtf8Printable(id));
202  // Signal to the client that a track was added to the TrackList
203  Q_EMIT q->trackAdded(id);
204 
205  // Signal to the client that the current track has changed for the first
206  // track added to the TrackList
207  if (m_tracks.count() == 1) {
208  Q_EMIT q->trackChanged(id);
209  }
210 }
211 
213  const QVector<QUrl> &uris,
214  const Track::Id &position)
215 {
217 
218  const auto current = get_current_track();
219 
220  Track::Id current_id;
221  QVector<QUrl> tmp;
222  for (const auto uri : uris)
223  {
224  // TODO: Refactor this code to use a smaller common function shared with add_track_with_uri_at()
225  Track::Id id = q->objectName() + '/' +
226  QString::number(track_counter++);
227  MH_DEBUG("Adding Track::Id: %s", qUtf8Printable(id));
228  MH_DEBUG("\tURI: %s", qUtf8Printable(uri.toString()));
229 
230  tmp.push_back(id);
231 
232  Track::Id insert_position = position;
233 
234  auto it = std::find(m_tracks.begin(), m_tracks.end(), insert_position);
235  m_tracks.insert(it, id);
236  // Make sure the next insert position is after the current insert position
237  // Update the Track::Id after which to insert the next one from uris
238  insert_position = id;
239 
240  updateCachedTrackMetadata(id, uri);
241 
242  if (shuffle)
244 
245  if (m_tracks.count() == 1)
246  current_id = id;
247  }
248 
249  set_current_track(current);
250 
251  MH_DEBUG("Signaling that we just added %d tracks to the TrackList", tmp.size());
252  Q_EMIT q->tracksAdded(tmp);
253 
254  if (!current_id.isEmpty()) {
255  Q_EMIT q->trackChanged(current_id);
256  }
257 }
258 
260  const media::Track::Id &to)
261 {
263  MH_DEBUG("-----------------------------------------------------");
264  if (id.isEmpty() or to.isEmpty())
265  {
266  MH_ERROR("Can't move track since 'id' or 'to' are empty");
267  return false;
268  }
269 
270  if (id == to)
271  {
272  MH_ERROR("Can't move track to it's same position");
273  return false;
274  }
275 
276  if (m_tracks.count() == 1)
277  {
278  MH_ERROR("Can't move track since TrackList contains only one track");
279  return false;
280  }
281 
282  MH_DEBUG("current_track id: %s", qUtf8Printable(current_track));
283  // Get an iterator that points to the track that is the insertion point
284  auto insert_point_it = std::find(m_tracks.begin(), m_tracks.end(), to);
285  if (insert_point_it == m_tracks.end()) {
286  throw media::TrackList::Errors::FailedToFindMoveTrackSource
287  ("Failed to find source track " + id);
288  }
289 
290  // Get an iterator that points to the track to move within the TrackList
291  auto to_move_it = std::find(m_tracks.begin(), m_tracks.end(), id);
292  if (to_move_it != m_tracks.end())
293  {
294  m_tracks.erase(to_move_it);
295  }
296  else
297  {
298  throw media::TrackList::Errors::FailedToFindMoveTrackDest
299  ("Failed to find destination track " + to);
300  }
301 
302  // Insert id at the location just before insert_point_it
303  m_tracks.insert(insert_point_it, id);
304 
305  const auto new_current_track_it = std::find(m_tracks.begin(), m_tracks.end(), current_track);
306  if (!current_track.isEmpty() && new_current_track_it == m_tracks.end()) {
307  MH_ERROR("Can't update current_iterator - failed to find track after move");
308  throw media::TrackList::Errors::FailedToMoveTrack();
309  }
310 
311  MH_DEBUG("TrackList after move");
312  for(const auto track : m_tracks)
313  {
314  MH_DEBUG("%s", qUtf8Printable(track));
315  }
316  // Signal to the client that track 'id' was moved within the TrackList
317  Q_EMIT q->trackMoved(id, to);
318 
319  MH_DEBUG("-----------------------------------------------------");
320 
321  return true;
322 }
323 
325 {
327 
328  media::Track::Id track = id;
329 
330  auto id_it = std::find(m_tracks.begin(), m_tracks.end(), track);
331  if (id_it == m_tracks.end()) {
332  QString err_str = QString("Track ") + track + " not found in track list";
333  MH_WARNING() << err_str;
334  throw media::TrackList::Errors::TrackNotFound(err_str);
335  }
336 
337  media::Track::Id next;
338  bool deleting_current = false;
339 
340  if (id == current_track)
341  {
342  MH_DEBUG("Removing current track");
343  deleting_current = true;
344 
345  auto next_it = std::next(id_it);
346 
347  if (next_it == m_tracks.end() &&
348  loop_status == media::Player::LoopStatus::playlist)
349  {
350  // Removed the last track, current is the first track and make sure that
351  // the player starts playing it
352  next_it = m_tracks.begin();
353  }
354 
355  if (next_it == m_tracks.end())
356  {
357  current_track.clear();
358  // Nothing else to play, stop playback
359  Q_EMIT q->endOfTrackList();
360  }
361  else
362  {
363  current_track = *next_it;
364  }
365  }
366 
367  do_remove_track(track);
368 
369  if (!current_track.isEmpty() and deleting_current)
371 }
372 
374 {
376  int removed = m_tracks.removeAll(id);
377 
378  if (removed > 0)
379  {
380  meta_data_cache.remove(id);
381 
382  if (shuffle)
383  shuffled_tracks.removeAll(id);
384 
385  Q_EMIT q->trackRemoved(id);
386 
387  // Make sure playback stops if all tracks were removed
388  if (m_tracks.isEmpty())
389  Q_EMIT q->endOfTrackList();
390  }
391 }
392 
394 {
396  set_current_track(track);
397  // Signal the Player instance to go to a specific track for playback
398  Q_EMIT q->onGoToTrack(track);
399  Q_EMIT q->trackChanged(track);
400 }
401 
403  const QSharedPointer<media::Engine::MetaDataExtractor> &extractor,
404  QObject *parent):
405  QObject(parent),
406  d_ptr(new TrackListImplementationPrivate(extractor, this))
407 {
408 }
409 
411 {
412 }
413 
415 {
417  d->shuffle = shuffle;
418 
419  if (shuffle) {
420  d->shuffled_tracks = d->m_tracks;
421  std::random_shuffle(d->shuffled_tracks.begin(),
422  d->shuffled_tracks.end());
423  }
424 }
425 
427 {
428  Q_D(const TrackListImplementation);
429  return d->shuffle;
430 }
431 
433 {
435  const auto it = d->meta_data_cache.find(id);
436 
437  if (it == d->meta_data_cache.end())
438  return QUrl();
439 
440  return it.value().first;
441 }
442 
444 {
446  const auto it = d->meta_data_cache.find(id);
447 
448  if (it == d->meta_data_cache.end())
449  return Track::MetaData{};
450 
451  return it.value().second;
452 }
453 
455  const QUrl &uri,
456  const media::Track::Id& position,
457  bool make_current)
458 {
460  MH_TRACE("");
461  d->add_track_with_uri_at(uri, position, make_current);
462 }
463 
464 void TrackListImplementation::add_tracks_with_uri_at(const QVector<QUrl> &uris,
465  const Track::Id &position)
466 {
468  MH_TRACE("");
469  d->add_tracks_with_uri_at(uris, position);
470 }
471 
473  const Track::Id &to)
474 {
476  MH_TRACE("");
477  return d->move_track(id, to);
478 }
479 
481 {
483  d->remove_track(id);
484 }
485 
487 {
489  MH_TRACE("");
490  d->go_to(track);
491 }
492 
494 {
495  Q_D(const TrackListImplementation);
496  return d->shuffled_tracks;
497 }
498 
500 {
502  MH_TRACE("");
503 
504  d->current_track.clear();
505 
506  // Make sure playback stops
507  Q_EMIT endOfTrackList();
508  // And make sure there is no "current" track
509  d->m_tracks.clear();
510  d->track_counter = 0;
511  d->shuffled_tracks.clear();
512 
513  Q_EMIT trackListReset();
514 }
515 
517 {
518  Q_D(const TrackListImplementation);
519  return d->m_tracks;
520 }
521 
522 /*
523  * NOTE We do not consider the loop status in this function due to the use of it
524  * we do in TrackListImplementation::next() (the function is used to know whether we
525  * need to wrap when looping is active).
526  */
528 {
529  Q_D(const TrackListImplementation);
530  const auto n_tracks = d->m_tracks.count();
531 
532  if (n_tracks == 0)
533  return false;
534 
535  // TODO Using current_iterator() makes media-hub crash later. Logic for
536  // handling the iterators must be reviewed. As a minimum updates to the
537  // track list should update current_track instead of the list being sneakly
538  // changed in player_implementation.cpp.
539  // To avoid the crash we consider that current_track will be eventually
540  // initialized to the first track when current_iterator() gets called.
541  if (d->current_track == d->empty_iterator())
542  {
543  if (n_tracks < 2)
544  return false;
545  else
546  return true;
547  }
548 
549  if (shuffle())
550  {
551  auto it = d->get_current_shuffled();
552  return ++it != d->shuffled_tracks.end();
553  }
554  else
555  {
556  const auto next_track = std::next(d->current_iterator());
557  return !d->is_last_track(next_track);
558  }
559 }
560 
561 /*
562  * NOTE We do not consider the loop status in this function due to the use of it
563  * we do in TrackListImplementation::previous() (the function is used to know whether we
564  * need to wrap when looping is active).
565  */
567 {
568  Q_D(const TrackListImplementation);
569  if (d->m_tracks.isEmpty() || d->current_track == d->empty_iterator())
570  return false;
571 
572  MH_DEBUG() << "shuffle is" << shuffle();
573  if (shuffle())
574  return d->get_current_shuffled() != d->shuffled_tracks.begin();
575  else
576  return d->current_track != d->m_tracks.begin();
577 }
578 
580 {
582  MH_TRACE("");
583  if (d->m_tracks.isEmpty()) {
584  // TODO Change ServiceSkeleton to return with error from DBus call
585  MH_ERROR("No tracks, cannot go to next");
586  return media::Track::Id{};
587  }
588 
589  bool go_to_track = false;
590 
591  // End of the track reached so loop around to the beginning of the track
592  if (d->loop_status == media::Player::LoopStatus::track)
593  {
594  MH_INFO("Looping on the current track since LoopStatus is set to track");
595  go_to_track = true;
596  }
597  // End of the tracklist reached so loop around to the beginning of the tracklist
598  else if (d->loop_status == media::Player::LoopStatus::playlist && not hasNext())
599  {
600  MH_INFO("Looping on the tracklist since LoopStatus is set to playlist");
601 
602  if (shuffle())
603  {
604  /* Re-shuffle the tracks, but make sure that the new first track
605  * is not the same as the current one */
606  const auto currentId = d->get_current_track();
607  std::random_shuffle(d->shuffled_tracks.begin(),
608  d->shuffled_tracks.end());
609  auto id = *d->shuffled_tracks.begin();
610  if (id == currentId) {
611  std::iter_swap(d->shuffled_tracks.begin(),
612  d->shuffled_tracks.end() - 1);
613  id = *d->shuffled_tracks.begin();
614  }
615  d->set_current_track(id);
616  }
617  else
618  {
619  d->current_track = d->m_tracks.first();
620  }
621  go_to_track = true;
622  }
623  else
624  {
625  if (shuffle())
626  {
627  auto it = d->get_current_shuffled();
628  if (it == d->shuffled_tracks.end()) {
629  d->set_current_track(d->shuffled_tracks.first());
630  go_to_track = true;
631  } else if (++it != d->shuffled_tracks.end()) {
632  MH_INFO("Advancing to next track: %s", qUtf8Printable(*it));
633  d->set_current_track(*it);
634  go_to_track = true;
635  }
636  }
637  else
638  {
639  const auto it = std::next(d->current_iterator());
640  if (not d->is_last_track(it))
641  {
642  MH_INFO("Advancing to next track: %s", qUtf8Printable(*it));
643  d->current_track = *it;
644  go_to_track = true;
645  }
646  }
647 
648  }
649 
650  const media::Track::Id id = *(d->current_iterator());
651  if (go_to_track)
652  {
653  MH_DEBUG("next track id is %s", qUtf8Printable(id));
654  Q_EMIT trackChanged(id);
655  // Signal the PlayerImplementation to play the next track
656  Q_EMIT onGoToTrack(id);
657  }
658  else
659  {
660  // At the end of the tracklist and not set to loop
661  MH_INFO("End of tracklist reached");
662  Q_EMIT endOfTrackList();
663  }
664 
665  return id;
666 }
667 
669 {
671  MH_TRACE("");
672  if (d->m_tracks.isEmpty()) {
673  // TODO Change ServiceSkeleton to return with error from DBus call
674  MH_ERROR("No tracks, cannot go to previous");
675  return media::Track::Id{};
676  }
677 
678  bool go_to_track = false;
679  // Position is measured in nanoseconds
680  const uint64_t max_position = 5 * UINT64_C(1000000000);
681 
682  // If we're playing the current track for > max_position time then
683  // repeat it from the beginning
684  if (d->current_position > max_position)
685  {
686  MH_INFO("Repeating current track...");
687  go_to_track = true;
688  }
689  // Loop on the current track forever
690  else if (d->loop_status == media::Player::LoopStatus::track)
691  {
692  MH_INFO("Looping on the current track...");
693  go_to_track = true;
694  }
695  // Loop over the whole playlist and repeat
696  else if (d->loop_status == media::Player::LoopStatus::playlist && not hasPrevious())
697  {
698  MH_INFO("Looping on the entire TrackList...");
699 
700  if (shuffle())
701  {
702  const auto id = *std::prev(d->shuffled_tracks.end());
703  d->set_current_track(id);
704  }
705  else
706  {
707  d->current_track = d->m_tracks.last();
708  }
709 
710  go_to_track = true;
711  }
712  else
713  {
714  if (shuffle())
715  {
716  auto it = d->get_current_shuffled();
717  if (it != d->shuffled_tracks.begin()) {
718  d->set_current_track(*(--it));
719  go_to_track = true;
720  }
721  }
722  else if (not d->is_first_track(d->current_iterator()))
723  {
724  // Keep returning the previous track until the first track is reached
725  d->current_track = *std::prev(d->current_iterator());
726  go_to_track = true;
727  }
728  }
729 
730  const media::Track::Id id = *(d->current_iterator());
731  if (go_to_track)
732  {
733  Q_EMIT trackChanged(id);
734  Q_EMIT onGoToTrack(id);
735  }
736  else
737  {
738  // At the beginning of the tracklist and not set to loop
739  MH_INFO("Beginning of tracklist reached");
740  Q_EMIT endOfTrackList();
741  }
742 
743  return id;
744 }
745 
747 {
748  Q_D(const TrackListImplementation);
749  return *(d->current_iterator());
750 }
751 
753 {
755  d->loop_status = loop_status;
756 }
757 
759 {
760  Q_D(const TrackListImplementation);
761  return d->loop_status;
762 }
763 
765 {
767  d->current_position = position;
768 }
769 
771 {
772  return true;
773 }
774 
776 {
777  static const media::Track::Id id{"/org/mpris/MediaPlayer2/TrackList/NoTrack"};
778  return id;
779 }
lomiri::MediaHubService::TrackListImplementation::query_meta_data_for_track
Track::MetaData query_meta_data_for_track(const Track::Id &id)
Definition: track_list_implementation.cpp:443
lomiri::MediaHubService::TrackListImplementationPrivate::get_current_track
Track::Id get_current_track() const
Definition: track_list_implementation.cpp:156
QObject
lomiri::MediaHubService::Track::MetaData
Definition: track.h:37
lomiri::MediaHubService::TrackListImplementationPrivate::is_last_track
bool is_last_track(const TrackList::ConstIterator &it) const
Definition: track_list_implementation.cpp:62
lomiri::MediaHubService::TrackListImplementationPrivate
Definition: track_list_implementation.cpp:47
lomiri::MediaHubService::Player::LoopStatus
LoopStatus
Definition: player.h:101
lomiri::MediaHubService::TrackListImplementation::shuffle
bool shuffle() const
Definition: track_list_implementation.cpp:426
lomiri::MediaHubService::TrackList::Container
QVector< Track::Id > Container
Definition: track_list_implementation.h:69
lomiri::MediaHubService::TrackListImplementationPrivate::TrackListImplementationPrivate
TrackListImplementationPrivate(const QSharedPointer< media::Engine::MetaDataExtractor > &extractor, TrackListImplementation *q)
Definition: track_list_implementation.cpp:120
lomiri::MediaHubService::TrackListImplementationPrivate::m_tracks
TrackList::Container m_tracks
Definition: track_list_implementation.cpp:110
lomiri::MediaHubService::TrackListImplementation::next
Track::Id next()
Definition: track_list_implementation.cpp:579
lomiri::MediaHubService::TrackListImplementation::reset
void reset()
Definition: track_list_implementation.cpp:499
lomiri::MediaHubService::TrackListImplementationPrivate::is_first_track
bool is_first_track(const TrackList::ConstIterator &it) const
Definition: track_list_implementation.cpp:59
lomiri::MediaHubService::TrackListImplementationPrivate::do_remove_track
void do_remove_track(const Track::Id &id)
Definition: track_list_implementation.cpp:373
lomiri::MediaHubService::TrackListImplementation::add_tracks_with_uri_at
void add_tracks_with_uri_at(const QVector< QUrl > &uris, const Track::Id &position)
Definition: track_list_implementation.cpp:464
lomiri::MediaHubService::Track::Id
QString Id
Definition: track.h:34
lomiri::MediaHubService::TrackListImplementation::current
const Track::Id & current() const
Definition: track_list_implementation.cpp:746
lomiri::MediaHubService::TrackListImplementation::shuffled_tracks
const TrackList::Container & shuffled_tracks() const
Definition: track_list_implementation.cpp:493
lomiri::MediaHubService::TrackListImplementation::tracks
const TrackList::Container & tracks() const
Definition: track_list_implementation.cpp:516
lomiri::MediaHubService::TrackListImplementation::query_uri_for_track
QUrl query_uri_for_track(const Track::Id &id)
Definition: track_list_implementation.cpp:432
lomiri::MediaHubService::TrackListImplementation::move_track
bool move_track(const Track::Id &id, const Track::Id &to)
Definition: track_list_implementation.cpp:472
lomiri::MediaHubService::TrackListImplementationPrivate::shuffled_tracks
TrackList::Container shuffled_tracks
Definition: track_list_implementation.cpp:109
lomiri::MediaHubService::TrackListImplementation::setShuffle
void setShuffle(bool shuffle)
Definition: track_list_implementation.cpp:414
lomiri::MediaHubService::TrackListImplementation::loopStatus
Player::LoopStatus loopStatus() const
Definition: track_list_implementation.cpp:758
lomiri::MediaHubService::TrackListImplementationPrivate::move_track
bool move_track(const Track::Id &id, const Track::Id &to)
Definition: track_list_implementation.cpp:259
lomiri::MediaHubService::TrackListImplementation::~TrackListImplementation
~TrackListImplementation()
Definition: track_list_implementation.cpp:410
lomiri::MediaHubService::TrackListImplementation::hasNext
bool hasNext() const
Definition: track_list_implementation.cpp:527
lomiri::MediaHubService::TrackListImplementationPrivate::current_iterator
TrackList::ConstIterator current_iterator() const
Definition: track_list_implementation.cpp:133
MH_ERROR
#define MH_ERROR(...)
Definition: logging.h:41
lomiri::MediaHubService::TrackListImplementationPrivate::updateCachedTrackMetadata
void updateCachedTrackMetadata(const Track::Id &id, const QUrl &uri)
Definition: track_list_implementation.cpp:80
lomiri::MediaHubService::TrackListImplementationPrivate::get_current_shuffled
TrackList::ConstIterator get_current_shuffled() const
Definition: track_list_implementation.cpp:165
lomiri::MediaHubService::TrackListImplementationPrivate::add_track_with_uri_at
void add_track_with_uri_at(const QUrl &uri, const Track::Id &position, bool make_current)
Definition: track_list_implementation.cpp:171
lomiri::MediaHubService::TrackListImplementation::previous
Track::Id previous()
Definition: track_list_implementation.cpp:668
lomiri::MediaHubService::TrackListImplementation::canEditTracks
bool canEditTracks() const
Definition: track_list_implementation.cpp:770
lomiri::MediaHubService::TrackListImplementation::hasPrevious
bool hasPrevious() const
Definition: track_list_implementation.cpp:566
lomiri::MediaHubService::TrackListImplementation::afterEmptyTrack
static const Track::Id & afterEmptyTrack()
Definition: track_list_implementation.cpp:775
MH_TRACE
#define MH_TRACE(...)
Definition: logging.h:37
lomiri::MediaHubService::TrackListImplementationPrivate::get_shuffled_insert_it
media::TrackList::Container::iterator get_shuffled_insert_it()
Definition: track_list_implementation.cpp:90
lomiri::MediaHubService::TrackListImplementationPrivate::current_track
media::Track::Id current_track
Definition: track_list_implementation.cpp:112
lomiri::MediaHubService::TrackListImplementationPrivate::extractor
QSharedPointer< Engine::MetaDataExtractor > extractor
Definition: track_list_implementation.cpp:106
lomiri::MediaHubService
Definition: context.h:28
lomiri::MediaHubService::TrackListImplementationPrivate::current_position
uint64_t current_position
Definition: track_list_implementation.cpp:114
lomiri::MediaHubService::TrackListImplementation::TrackListImplementation
TrackListImplementation(const QSharedPointer< Engine::MetaDataExtractor > &extractor, QObject *parent=nullptr)
Definition: track_list_implementation.cpp:402
lomiri::MediaHubService::TrackListImplementationPrivate::shuffle
bool shuffle
Definition: track_list_implementation.cpp:111
lomiri::MediaHubService::TrackListImplementationPrivate::track_counter
size_t track_counter
Definition: track_list_implementation.cpp:104
lomiri::MediaHubService::TrackListImplementationPrivate::set_current_track
void set_current_track(const Track::Id &id)
Definition: track_list_implementation.cpp:150
lomiri::MediaHubService::TrackListImplementationPrivate::remove_track
void remove_track(const Track::Id &id)
Definition: track_list_implementation.cpp:324
lomiri::MediaHubService::TrackListImplementationPrivate::go_to
void go_to(const Track::Id &track)
Definition: track_list_implementation.cpp:393
lomiri::MediaHubService::TrackListImplementationPrivate::empty_iterator
TrackList::ConstIterator empty_iterator() const
Definition: track_list_implementation.cpp:58
lomiri::MediaHubService::TrackListImplementationPrivate::meta_data_cache
MetaDataCache meta_data_cache
Definition: track_list_implementation.cpp:105
lomiri::MediaHubService::TrackListImplementationPrivate::loop_status
media::Player::LoopStatus loop_status
Definition: track_list_implementation.cpp:113
lomiri::MediaHubService::TrackListImplementation::add_track_with_uri_at
void add_track_with_uri_at(const QUrl &uri, const Track::Id &position, bool make_current)
Definition: track_list_implementation.cpp:454
lomiri::MediaHubService::TrackListImplementation::go_to
void go_to(const Track::Id &track)
Definition: track_list_implementation.cpp:486
MH_DEBUG
#define MH_DEBUG(...)
Definition: logging.h:38
lomiri::MediaHubService::TrackListImplementation::setCurrentPosition
void setCurrentPosition(uint64_t position)
Definition: track_list_implementation.cpp:764
MH_INFO
#define MH_INFO(...)
Definition: logging.h:39
track_list_implementation.h
engine.h
lomiri::MediaHubService::TrackListImplementationPrivate::q_ptr
TrackListImplementation * q_ptr
Definition: track_list_implementation.cpp:115
lomiri::MediaHubService::TrackList::ConstIterator
Container::ConstIterator ConstIterator
Definition: track_list_implementation.h:70
lomiri::MediaHubService::TrackListImplementation::remove_track
void remove_track(const Track::Id &id)
Definition: track_list_implementation.cpp:480
lomiri
Definition: dbus_utils.h:24
lomiri::MediaHubService::Player
Definition: player.h:50
lomiri::MediaHubService::TrackListImplementation::setLoopStatus
void setLoopStatus(Player::LoopStatus status)
Definition: track_list_implementation.cpp:752
lomiri::MediaHubService::TrackListImplementationPrivate::MetaDataCache
QMap< Track::Id, QPair< QUrl, Track::MetaData > > MetaDataCache
Definition: track_list_implementation.cpp:52
MH_WARNING
#define MH_WARNING(...)
Definition: logging.h:40
lomiri::MediaHubService::TrackListImplementationPrivate::add_tracks_with_uri_at
void add_tracks_with_uri_at(const QVector< QUrl > &uris, const Track::Id &position)
Definition: track_list_implementation.cpp:212
logging.h
lomiri::MediaHubService::TrackListImplementation
Definition: track_list_implementation.h:74