Music Hub  ..
A session-wide music playback service
track_list_skeleton.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2015 Canonical Ltd.
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License version 3,
6  * as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY {} without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authored by: Thomas Voß <thomas.voss@canonical.com>
17  */
18 
19 #include "track_list_skeleton.h"
21 
22 #include <core/media/player.h>
23 #include <core/media/track_list.h>
24 
25 #include "codec.h"
26 #include "property_stub.h"
27 #include "track_list_traits.h"
28 #include "the_session_bus.h"
29 
30 #include "mpris/player.h"
31 #include "mpris/track_list.h"
32 
33 #include "util/uri_check.h"
35 
36 #include <core/dbus/object.h>
37 #include <core/dbus/property.h>
38 #include <core/dbus/types/object_path.h>
39 #include <core/dbus/types/variant.h>
40 #include <core/dbus/types/stl/map.h>
41 #include <core/dbus/types/stl/vector.h>
42 
43 #include <iostream>
44 #include <limits>
45 #include <cstdint>
46 
47 namespace dbus = core::dbus;
48 namespace media = core::ubuntu::media;
49 
50 using namespace std;
51 
53 {
54  Private(media::TrackListSkeleton* impl, const dbus::Bus::Ptr& bus, const dbus::Object::Ptr& object,
55  const apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
56  const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
57  : impl(impl),
58  bus(bus),
59  object(object),
60  request_context_resolver(request_context_resolver),
61  request_authenticator(request_authenticator),
62  uri_check(std::make_shared<UriCheck>()),
63  skeleton(mpris::TrackList::Skeleton::Configuration{object, mpris::TrackList::Skeleton::Configuration::Defaults{}}),
64  current_track(skeleton.properties.tracks->get().begin()),
65  empty_iterator(skeleton.properties.tracks->get().begin()),
66  loop_status(media::Player::LoopStatus::none),
67  current_position(0),
68  id_after_remove(),
69  signals
70  {
71  skeleton.signals.track_added,
72  skeleton.signals.tracks_added,
73  skeleton.signals.track_moved,
74  skeleton.signals.track_removed,
75  skeleton.signals.track_changed,
76  skeleton.signals.track_list_reset,
77  skeleton.signals.tracklist_replaced
78  }
79  {
80  }
81 
82  void handle_get_tracks_metadata(const core::dbus::Message::Ptr& msg)
83  {
84  media::Track::Id track;
85  msg->reader() >> track;
86 
87  const auto meta_data = impl->query_meta_data_for_track(track);
88 
89  const auto reply = dbus::Message::make_method_return(msg);
90  reply->writer() << *meta_data;
91  bus->send(reply);
92  }
93 
94  void handle_get_tracks_uri(const core::dbus::Message::Ptr& msg)
95  {
96  media::Track::Id track;
97  msg->reader() >> track;
98 
99  const auto uri = impl->query_uri_for_track(track);
100 
101  const auto reply = dbus::Message::make_method_return(msg);
102  reply->writer() << uri;
103  bus->send(reply);
104  }
105 
106  void handle_add_track_with_uri_at(const core::dbus::Message::Ptr& msg)
107  {
108  MH_TRACE("");
109  request_context_resolver->resolve_context_for_dbus_name_async
110  (msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context)
111  {
112  Track::UriType uri;
113  media::Track::Id after;
114  bool make_current;
115  msg->reader() >> uri >> after >> make_current;
116 
117  // Make sure the client has adequate apparmor permissions to open the URI
118  const auto result = request_authenticator->authenticate_open_uri_request(context, uri);
119  auto reply = dbus::Message::make_method_return(msg);
120 
121  uri_check->set(uri);
122  const bool valid_uri = !uri_check->is_local_file() or
123  (uri_check->is_local_file() and uri_check->file_exists());
124  if (!valid_uri)
125  {
126  const std::string err_str = {"Warning: Not adding track " + uri +
127  " to TrackList because it can't be found."};
128  MH_WARNING("%s", err_str.c_str());
129  reply = dbus::Message::make_error(
130  msg,
132  err_str);
133  }
134  else
135  {
136  // Only add the track to the TrackList if it passes the apparmor permissions check
137  if (std::get<0>(result))
138  {
139  impl->add_track_with_uri_at(uri, after, make_current);
140  }
141  else
142  {
143  const std::string err_str = {"Warning: Not adding track " + uri +
144  " to TrackList because of inadequate client apparmor permissions."};
145  MH_WARNING("%s", err_str.c_str());
146  reply = dbus::Message::make_error(
147  msg,
149  err_str);
150  }
151  }
152 
153  bus->send(reply);
154  });
155  }
156 
157  void handle_add_tracks_with_uri_at(const core::dbus::Message::Ptr& msg)
158  {
159  MH_TRACE("");
160  request_context_resolver->resolve_context_for_dbus_name_async
161  (msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context)
162  {
163  ContainerURI uris;
164  media::Track::Id after;
165  msg->reader() >> uris >> after;
166 
167  bool valid_uri = false;
168  media::apparmor::ubuntu::RequestAuthenticator::Result result;
169  std::string uri_err_str, err_str;
170  core::dbus::Message::Ptr reply;
171  for (const auto uri : uris)
172  {
173  uri_check->set(uri);
174  valid_uri = !uri_check->is_local_file() or
175  (uri_check->is_local_file() and uri_check->file_exists());
176  if (!valid_uri)
177  {
178  uri_err_str = {"Warning: Not adding track " + uri +
179  " to TrackList because it can't be found."};
180  MH_WARNING("%s", uri_err_str.c_str());
181  reply = dbus::Message::make_error(
182  msg,
184  err_str);
185  }
186 
187  // Make sure the client has adequate apparmor permissions to open the URI
188  result = request_authenticator->authenticate_open_uri_request(context, uri);
189  if (not std::get<0>(result))
190  {
191  err_str = {"Warning: Not adding track " + uri +
192  " to TrackList because of inadequate client apparmor permissions."};
193  break;
194  }
195  }
196 
197  // Only add the track to the TrackList if it passes the apparmor permissions check
198  if (std::get<0>(result))
199  {
200  reply = dbus::Message::make_method_return(msg);
201  impl->add_tracks_with_uri_at(uris, after);
202  }
203  else
204  {
205  MH_WARNING("%s", err_str.c_str());
206  reply = dbus::Message::make_error(
207  msg,
209  err_str);
210  }
211 
212  bus->send(reply);
213  });
214  }
215 
216  void handle_move_track(const core::dbus::Message::Ptr& msg)
217  {
218  media::Track::Id id;
219  media::Track::Id to;
220  msg->reader() >> id >> to;
221 
222  core::dbus::Message::Ptr reply;
223  try {
224  const bool ret = impl->move_track(id, to);
225  if (!ret)
226  {
227  const std::string err_str = {"Error: Not moving track " + id +
228  " to destination " + to};
229  MH_WARNING("%s", err_str.c_str());
230  reply = dbus::Message::make_error(
231  msg,
233  err_str);
234  }
235  else
236  {
237  reply = dbus::Message::make_method_return(msg);
238  }
239  } catch(media::TrackList::Errors::FailedToMoveTrack& e) {
240  reply = dbus::Message::make_error(
241  msg,
243  e.what());
244  } catch(media::TrackList::Errors::FailedToFindMoveTrackSource& e) {
245  reply = dbus::Message::make_error(
246  msg,
248  e.what());
249  } catch(media::TrackList::Errors::FailedToFindMoveTrackDest& e) {
250  reply = dbus::Message::make_error(
251  msg,
253  e.what());
254  }
255 
256  bus->send(reply);
257  }
258 
259  void handle_remove_track(const core::dbus::Message::Ptr& msg)
260  {
261  media::Track::Id track;
262  msg->reader() >> track;
263 
264  auto id_it = find(impl->tracks().get().begin(), impl->tracks().get().end(), track);
265  if (id_it == impl->tracks().get().end()) {
266  stringstream err_str;
267  err_str << "Track " << track << " not found in track list";
268  MH_WARNING("%s", err_str.str());
269  auto reply = dbus::Message::make_error(
270  msg,
272  err_str.str());
273  bus->send(reply);
274  return;
275  }
276 
277  media::Track::Id next;
278  bool deleting_current = false;
279 
280  if (id_it == impl->current_iterator())
281  {
282  MH_DEBUG("Removing current track");
283  deleting_current = true;
284 
285  if (current_track != empty_iterator)
286  {
287  ++current_track;
288 
289  if (current_track == impl->tracks().get().end()
290  && loop_status == media::Player::LoopStatus::playlist)
291  {
292  // Removed the last track, current is the first track and make sure that
293  // the player starts playing it
294  current_track = impl->tracks().get().begin();
295  }
296 
297  if (current_track == impl->tracks().get().end())
298  {
299  current_track = empty_iterator;
300  // Nothing else to play, stop playback
301  impl->emit_on_end_of_tracklist();
302  }
303  else
304  {
305  next = *current_track;
306  }
307  }
308  }
309  else if (current_track != empty_iterator)
310  {
311  next = *current_track;
312  }
313  id_after_remove = next;
314 
315  // Calls reset_current_iterator_if_needed(), which updates the iterator
316  impl->remove_track(track);
317 
318  if ((not next.empty()) and deleting_current)
319  impl->go_to(next);
320 
321  auto reply = dbus::Message::make_method_return(msg);
322  bus->send(reply);
323  }
324 
325  void handle_go_to(const core::dbus::Message::Ptr& msg)
326  {
327  media::Track::Id track;
328  msg->reader() >> track;
329 
330  current_track = std::find(skeleton.properties.tracks->get().begin(), skeleton.properties.tracks->get().end(), track);
331  impl->go_to(track);
332 
333  auto reply = dbus::Message::make_method_return(msg);
334  bus->send(reply);
335  }
336 
337  void handle_reset(const core::dbus::Message::Ptr& msg)
338  {
339  impl->reset();
340 
341  auto reply = dbus::Message::make_method_return(msg);
342  bus->send(reply);
343  }
344 
346  dbus::Bus::Ptr bus;
347  dbus::Object::Ptr object;
348  media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
349  media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
351 
353  TrackList::ConstIterator current_track;
354  TrackList::ConstIterator empty_iterator;
358 
359  struct Signals
360  {
361  typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal;
362  typedef core::dbus::Signal<mpris::TrackList::Signals::TracksAdded, mpris::TrackList::Signals::TracksAdded::ArgumentType> DBusTracksAddedSignal;
363  typedef core::dbus::Signal<mpris::TrackList::Signals::TrackMoved, mpris::TrackList::Signals::TrackMoved::ArgumentType> DBusTrackMovedSignal;
364  typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal;
365  typedef core::dbus::Signal<mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType> DBusTrackChangedSignal;
366  typedef core::dbus::Signal<
367  mpris::TrackList::Signals::TrackListReset,
368  mpris::TrackList::Signals::TrackListReset::ArgumentType>
370  typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal;
371 
372  Signals(const std::shared_ptr<DBusTrackAddedSignal>& remote_track_added,
373  const std::shared_ptr<DBusTracksAddedSignal>& remote_tracks_added,
374  const std::shared_ptr<DBusTrackMovedSignal>& remote_track_moved,
375  const std::shared_ptr<DBusTrackRemovedSignal>& remote_track_removed,
376  const std::shared_ptr<DBusTrackChangedSignal>& remote_track_changed,
377  const std::shared_ptr<DBusTrackListResetSignal>& remote_track_list_reset,
378  const std::shared_ptr<DBusTrackListReplacedSignal>& remote_track_list_replaced)
379  {
380  // Connect all of the MPRIS interface signals to be emitted over dbus
381  on_track_added.connect([remote_track_added](const media::Track::Id &id)
382  {
383  remote_track_added->emit(id);
384  });
385 
386  on_tracks_added.connect([remote_tracks_added](const media::TrackList::ContainerURI &tracks)
387  {
388  remote_tracks_added->emit(tracks);
389  });
390 
391  on_track_moved.connect([remote_track_moved](const media::TrackList::TrackIdTuple &ids)
392  {
393  remote_track_moved->emit(ids);
394  });
395 
396  on_track_removed.connect([remote_track_removed](const media::Track::Id &id)
397  {
398  remote_track_removed->emit(id);
399  });
400 
401  on_track_list_reset.connect([remote_track_list_reset]()
402  {
403  remote_track_list_reset->emit();
404  });
405 
406  on_track_changed.connect([remote_track_changed](const media::Track::Id &id)
407  {
408  remote_track_changed->emit(id);
409  });
410 
411  on_track_list_replaced.connect([remote_track_list_replaced](const media::TrackList::ContainerTrackIdTuple &tltuple)
412  {
413  remote_track_list_replaced->emit(tltuple);
414  });
415  }
416 
417  core::Signal<Track::Id> on_track_added;
418  core::Signal<TrackList::ContainerURI> on_tracks_added;
419  core::Signal<TrackList::TrackIdTuple> on_track_moved;
420  core::Signal<Track::Id> on_track_removed;
421  core::Signal<void> on_track_list_reset;
422  core::Signal<Track::Id> on_track_changed;
423  core::Signal<TrackList::ContainerTrackIdTuple> on_track_list_replaced;
424  core::Signal<Track::Id> on_go_to_track;
425  core::Signal<void> on_end_of_tracklist;
426  } signals;
427 };
428 
429 media::TrackListSkeleton::TrackListSkeleton(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object,
430  const media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
431  const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
432  : d(new Private(this, bus, object, request_context_resolver, request_authenticator))
433 {
434  d->object->install_method_handler<mpris::TrackList::GetTracksMetadata>(
436  std::ref(d),
437  std::placeholders::_1));
438 
439  d->object->install_method_handler<mpris::TrackList::GetTracksUri>(
441  std::ref(d),
442  std::placeholders::_1));
443 
444  d->object->install_method_handler<mpris::TrackList::AddTrack>(
446  std::ref(d),
447  std::placeholders::_1));
448 
449  d->object->install_method_handler<mpris::TrackList::AddTracks>(
451  std::ref(d),
452  std::placeholders::_1));
453 
454  d->object->install_method_handler<mpris::TrackList::MoveTrack>(
455  std::bind(&Private::handle_move_track,
456  std::ref(d),
457  std::placeholders::_1));
458 
459  d->object->install_method_handler<mpris::TrackList::RemoveTrack>(
460  std::bind(&Private::handle_remove_track,
461  std::ref(d),
462  std::placeholders::_1));
463 
464  d->object->install_method_handler<mpris::TrackList::GoTo>(
465  std::bind(&Private::handle_go_to,
466  std::ref(d),
467  std::placeholders::_1));
468 
469  d->object->install_method_handler<mpris::TrackList::Reset>(
470  std::bind(&Private::handle_reset,
471  std::ref(d),
472  std::placeholders::_1));
473 }
474 
476 {
477 }
478 
479 /*
480  * NOTE We do not consider the loop status in this function due to the use of it
481  * we do in TrackListSkeleton::next() (the function is used to know whether we
482  * need to wrap when looping is active).
483  */
485 {
486  const auto n_tracks = tracks().get().size();
487 
488  if (n_tracks == 0)
489  return false;
490 
491  // TODO Using current_iterator() makes media-hub crash later. Logic for
492  // handling the iterators must be reviewed. As a minimum updates to the
493  // track list should update current_track instead of the list being sneakly
494  // changed in player_implementation.cpp.
495  // To avoid the crash we consider that current_track will be eventually
496  // initialized to the first track when current_iterator() gets called.
497  if (d->current_track == d->empty_iterator)
498  {
499  if (n_tracks < 2)
500  return false;
501  else
502  return true;
503  }
504 
505  if (shuffle())
506  {
507  auto it = get_current_shuffled();
508  return ++it != shuffled_tracks().end();
509  }
510  else
511  {
512  const auto next_track = std::next(current_iterator());
513  return !is_last_track(next_track);
514  }
515 }
516 
517 /*
518  * NOTE We do not consider the loop status in this function due to the use of it
519  * we do in TrackListSkeleton::previous() (the function is used to know whether we
520  * need to wrap when looping is active).
521  */
523 {
524  if (tracks().get().empty() || d->current_track == d->empty_iterator)
525  return false;
526 
527  if (shuffle())
528  return get_current_shuffled() != shuffled_tracks().begin();
529  else
530  return d->current_track != std::begin(tracks().get());
531 }
532 
534 {
535  auto current_id = *current_iterator();
536  return find(shuffled_tracks().begin(), shuffled_tracks().end(), current_id);
537 }
538 
540 {
541  MH_TRACE("");
542  if (tracks().get().empty()) {
543  // TODO Change ServiceSkeleton to return with error from DBus call
544  MH_ERROR("No tracks, cannot go to next");
545  return media::Track::Id{};
546  }
547 
548  bool go_to_track = false;
549 
550  // End of the track reached so loop around to the beginning of the track
551  if (d->loop_status == media::Player::LoopStatus::track)
552  {
553  MH_INFO("Looping on the current track since LoopStatus is set to track");
554  go_to_track = true;
555  }
556  // End of the tracklist reached so loop around to the beginning of the tracklist
557  else if (d->loop_status == media::Player::LoopStatus::playlist && not has_next())
558  {
559  MH_INFO("Looping on the tracklist since LoopStatus is set to playlist");
560 
561  if (shuffle())
562  {
563  const auto id = *shuffled_tracks().begin();
564  set_current_track(id);
565  }
566  else
567  {
568  d->current_track = tracks().get().begin();
569  }
570  go_to_track = true;
571  }
572  else
573  {
574  if (shuffle())
575  {
576  auto it = get_current_shuffled();
577  if (++it != shuffled_tracks().end()) {
578  MH_INFO("Advancing to next track: %s", *it);
579  set_current_track(*it);
580  go_to_track = true;
581  }
582  }
583  else
584  {
585  const auto it = std::next(current_iterator());
586  if (not is_last_track(it))
587  {
588  MH_INFO("Advancing to next track: %s", *it);
589  d->current_track = it;
590  go_to_track = true;
591  }
592  }
593 
594  }
595 
596  if (go_to_track)
597  {
598  MH_DEBUG("next track id is %s", *(current_iterator()));
600  const media::Track::Id id = *(current_iterator());
601  // Signal the PlayerImplementation to play the next track
602  on_go_to_track()(id);
603  }
604  else
605  {
606  // At the end of the tracklist and not set to loop
607  MH_INFO("End of tracklist reached");
609  }
610 
611  return *(current_iterator());
612 }
613 
615 {
616  MH_TRACE("");
617  if (tracks().get().empty()) {
618  // TODO Change ServiceSkeleton to return with error from DBus call
619  MH_ERROR("No tracks, cannot go to previous");
620  return media::Track::Id{};
621  }
622 
623  bool go_to_track = false;
624  // Position is measured in nanoseconds
625  const uint64_t max_position = 5 * UINT64_C(1000000000);
626 
627  // If we're playing the current track for > max_position time then
628  // repeat it from the beginning
629  if (d->current_position > max_position)
630  {
631  MH_INFO("Repeating current track...");
632  go_to_track = true;
633  }
634  // Loop on the current track forever
635  else if (d->loop_status == media::Player::LoopStatus::track)
636  {
637  MH_INFO("Looping on the current track...");
638  go_to_track = true;
639  }
640  // Loop over the whole playlist and repeat
641  else if (d->loop_status == media::Player::LoopStatus::playlist && not has_previous())
642  {
643  MH_INFO("Looping on the entire TrackList...");
644 
645  if (shuffle())
646  {
647  const auto id = *std::prev(shuffled_tracks().end());
648  set_current_track(id);
649  }
650  else
651  {
652  d->current_track = std::prev(tracks().get().end());
653  }
654 
655  go_to_track = true;
656  }
657  else
658  {
659  if (shuffle())
660  {
661  auto it = get_current_shuffled();
662  if (it != shuffled_tracks().begin()) {
663  set_current_track(*(--it));
664  go_to_track = true;
665  }
666  }
667  else if (not is_first_track(current_iterator()))
668  {
669  // Keep returning the previous track until the first track is reached
670  d->current_track = std::prev(current_iterator());
671  go_to_track = true;
672  }
673  }
674 
675  if (go_to_track)
676  {
678  const media::Track::Id id = *(current_iterator());
679  on_go_to_track()(id);
680  }
681  else
682  {
683  // At the beginning of the tracklist and not set to loop
684  MH_INFO("Beginning of tracklist reached");
686  }
687 
688  return *(current_iterator());
689 }
690 
692 {
693  return *(current_iterator());
694 }
695 
697 {
698  // Prevent the TrackList from sitting at the end which will cause
699  // a segfault when calling current()
700  if (tracks().get().size() && (d->current_track == d->empty_iterator))
701  {
702  MH_DEBUG("Wrapping d->current_track back to begin()");
703  d->current_track = d->skeleton.properties.tracks->get().begin();
704  }
705  else if (tracks().get().empty())
706  {
707  MH_ERROR("TrackList is empty therefore there is no valid current track");
708  }
709 
710  return d->current_track;
711 }
712 
714 {
715  MH_TRACE("");
716  if (it == tracks().get().end())
717  return false;
718 
719  d->current_track = it;
720 
721  return true;
722 }
723 
725 {
726  d->current_track = find(tracks().get().begin(), tracks().get().end(), d->id_after_remove);
727  if (d->current_track == tracks().get().end())
728  d->current_track = d->empty_iterator;
729 }
730 
732 {
733  if (d->current_track == d->empty_iterator || tracks().get().empty())
734  return media::Track::Id{};
735 
736  return *(current_iterator());
737 }
738 
740 {
741  const auto id_it = find(tracks().get().begin(), tracks().get().end(), id);
742  if (id_it != tracks().get().end())
743  d->current_track = id_it;
744 }
745 
747 {
749 }
750 
751 const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const
752 {
753  return *d->skeleton.properties.can_edit_tracks;
754 }
755 
756 core::Property<bool>& media::TrackListSkeleton::can_edit_tracks()
757 {
758  return *d->skeleton.properties.can_edit_tracks;
759 }
760 
761 core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks()
762 {
763  return *d->skeleton.properties.tracks;
764 }
765 
766 void media::TrackListSkeleton::on_position_changed(uint64_t position)
767 {
768  d->current_position = position;
769 }
770 
772 {
773  d->loop_status = loop_status;
774 }
775 
777 {
778  return d->loop_status;
779 }
780 
782 {
783  MH_TRACE("");
784  set_shuffle(shuffle);
785 }
786 
787 const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const
788 {
789  return *d->skeleton.properties.tracks;
790 }
791 
792 const core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced() const
793 {
794  // Print the TrackList instance
795  MH_DEBUG("%s", *this);
796  return d->signals.on_track_list_replaced;
797 }
798 
799 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added() const
800 {
801  return d->signals.on_track_added;
802 }
803 
804 const core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added() const
805 {
806  return d->signals.on_tracks_added;
807 }
808 
809 const core::Signal<media::TrackList::TrackIdTuple>& media::TrackListSkeleton::on_track_moved() const
810 {
811  return d->signals.on_track_moved;
812 }
813 
814 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const
815 {
816  return d->signals.on_track_removed;
817 }
818 
819 const core::Signal<void>& media::TrackListSkeleton::on_track_list_reset() const
820 {
821  return d->signals.on_track_list_reset;
822 }
823 
824 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() const
825 {
826  return d->signals.on_track_changed;
827 }
828 
829 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_go_to_track() const
830 {
831  return d->signals.on_go_to_track;
832 }
833 
834 const core::Signal<void>& media::TrackListSkeleton::on_end_of_tracklist() const
835 {
836  return d->signals.on_end_of_tracklist;
837 }
838 
839 core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced()
840 {
841  return d->signals.on_track_list_replaced;
842 }
843 
844 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added()
845 {
846  return d->signals.on_track_added;
847 }
848 
849 core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added()
850 {
851  return d->signals.on_tracks_added;
852 }
853 
854 core::Signal<media::TrackList::TrackIdTuple>& media::TrackListSkeleton::on_track_moved()
855 {
856  return d->signals.on_track_moved;
857 }
858 
859 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed()
860 {
861  return d->signals.on_track_removed;
862 }
863 
865 {
866  return d->signals.on_track_list_reset;
867 }
868 
869 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed()
870 {
871  return d->signals.on_track_changed;
872 }
873 
874 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_go_to_track()
875 {
876  return d->signals.on_go_to_track;
877 }
878 
880 {
881  return d->signals.on_end_of_tracklist;
882 }
883 
885 {
886  d->current_track = d->empty_iterator;
887 }
888 
889 // operator<< pretty prints the given TrackList to the given output stream.
890 inline std::ostream& media::operator<<(std::ostream& out, const media::TrackList& tracklist)
891 {
892  auto non_const_tl = const_cast<media::TrackList*>(&tracklist);
893  out << "TrackList\n---------------" << std::endl;
894  for (const media::Track::Id &id : tracklist.tracks().get())
895  {
896  // '*' denotes the current track
897  out << "\t" << ((dynamic_cast<media::TrackListSkeleton*>(non_const_tl)->current() == id) ? "*" : "");
898  out << "Track Id: " << id << std::endl;
899  out << "\t\turi: " << dynamic_cast<media::TrackListImplementation*>(non_const_tl)->query_uri_for_track(id) << std::endl;
900  }
901 
902  out << "---------------\nEnd TrackList" << std::endl;
903  return out;
904 }
905 
core::dbus::Signal< mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType > DBusTrackAddedSignal
media::Track::Id get_current_track(void)
const core::Signal< Track::Id > & on_go_to_track() const
virtual const core::Property< Container > & tracks() const =0
virtual void set_shuffle(bool shuffle)=0
const core::Property< Container > & tracks() const
core::dbus::Signal< mpris::TrackList::Signals::TrackMoved, mpris::TrackList::Signals::TrackMoved::ArgumentType > DBusTrackMovedSignal
core::Signal< TrackList::ContainerTrackIdTuple > on_track_list_replaced
media::Player::LoopStatus loop_status
#define MH_INFO(...)
Definition: logger.h:125
const core::Signal< Track::Id > & on_track_removed() const
core::dbus::Signal< mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType > DBusTrackRemovedSignal
core::dbus::Signal< mpris::TrackList::Signals::TracksAdded, mpris::TrackList::Signals::TracksAdded::ArgumentType > DBusTracksAddedSignal
void handle_remove_track(const core::dbus::Message::Ptr &msg)
std::ostream & operator<<(std::ostream &out, Player::PlaybackStatus status)
Definition: player.h:217
STL namespace.
#define MH_ERROR(...)
Definition: logger.h:128
void set_current_track(const media::Track::Id &id)
void on_position_changed(uint64_t position)
const core::Signal< void > & on_track_list_reset() const
void handle_get_tracks_metadata(const core::dbus::Message::Ptr &msg)
std::tuple< std::vector< Track::Id >, Track::Id > ContainerTrackIdTuple
Definition: track_list.h:45
#define MH_DEBUG(...)
Definition: logger.h:123
const core::Signal< ContainerTrackIdTuple > & on_track_list_replaced() const
static constexpr const char * name
Definition: player.h:133
#define MH_WARNING(...)
Definition: logger.h:127
const core::Signal< ContainerURI > & on_tracks_added() const
std::shared_ptr< UriCheck > Ptr
Definition: uri_check.h:37
void handle_go_to(const core::dbus::Message::Ptr &msg)
void handle_add_track_with_uri_at(const core::dbus::Message::Ptr &msg)
void handle_reset(const core::dbus::Message::Ptr &msg)
const core::Signal< void > & on_end_of_tracklist() const
media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator
TrackList::ConstIterator get_current_shuffled()
bool update_current_iterator(const TrackList::ConstIterator &it)
Signals(const std::shared_ptr< DBusTrackAddedSignal > &remote_track_added, const std::shared_ptr< DBusTracksAddedSignal > &remote_tracks_added, const std::shared_ptr< DBusTrackMovedSignal > &remote_track_moved, const std::shared_ptr< DBusTrackRemovedSignal > &remote_track_removed, const std::shared_ptr< DBusTrackChangedSignal > &remote_track_changed, const std::shared_ptr< DBusTrackListResetSignal > &remote_track_list_reset, const std::shared_ptr< DBusTrackListReplacedSignal > &remote_track_list_replaced)
const core::Property< bool > & can_edit_tracks() const
TrackList::ConstIterator current_track
static constexpr const char * name
Definition: track_list.h:64
bool is_last_track(const ConstIterator &it)
void on_loop_status_changed(const core::ubuntu::media::Player::LoopStatus &loop_status)
core::dbus::Signal< mpris::TrackList::Signals::TrackListReset, mpris::TrackList::Signals::TrackListReset::ArgumentType > DBusTrackListResetSignal
virtual Track::UriType query_uri_for_track(const Track::Id &id)=0
const core::Signal< TrackIdTuple > & on_track_moved() const
#define MH_TRACE(...)
Definition: logger.h:121
const core::Signal< Track::Id > & on_track_added() const
mpris::TrackList::Skeleton skeleton
core::dbus::Signal< mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType > DBusTrackListReplacedSignal
const core::Signal< Track::Id > & on_track_changed() const
core::Signal< TrackList::TrackIdTuple > on_track_moved
std::tuple< Track::Id, Track::Id > TrackIdTuple
Definition: track_list.h:46
core::Signal< TrackList::ContainerURI > on_tracks_added
virtual const media::TrackList::Container & shuffled_tracks()=0
bool is_first_track(const ConstIterator &it)
core::dbus::Signal< mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType > DBusTrackChangedSignal
media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver
std::vector< Track::UriType > ContainerURI
Definition: track_list.h:44
Container::const_iterator ConstIterator
Definition: track_list.h:48
core::ubuntu::media::Player::LoopStatus loop_status() const
const TrackList::ConstIterator & current_iterator()
void handle_add_tracks_with_uri_at(const core::dbus::Message::Ptr &msg)
static constexpr const char * name
Definition: track_list.h:88
TrackListSkeleton(const core::dbus::Bus::Ptr &bus, const core::dbus::Object::Ptr &object, const core::ubuntu::media::apparmor::ubuntu::RequestContextResolver::Ptr &request_context_resolver, const core::ubuntu::media::apparmor::ubuntu::RequestAuthenticator::Ptr &request_authenticator)
void handle_get_tracks_uri(const core::dbus::Message::Ptr &msg)
void handle_move_track(const core::dbus::Message::Ptr &msg)
TrackList::ConstIterator empty_iterator
Private(media::TrackListSkeleton *impl, const dbus::Bus::Ptr &bus, const dbus::Object::Ptr &object, const apparmor::ubuntu::RequestContextResolver::Ptr &request_context_resolver, const media::apparmor::ubuntu::RequestAuthenticator::Ptr &request_authenticator)