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