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>
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
47namespace dbus = core::dbus;
48namespace media = core::ubuntu::media;
49
50using 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),
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),
70 {
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,
131 mpris::Player::Error::UriNotFound::name,
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,
183 mpris::Player::Error::UriNotFound::name,
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 {
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
429media::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>(
456 std::ref(d),
457 std::placeholders::_1));
458
459 d->object->install_method_handler<mpris::TrackList::RemoveTrack>(
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();
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);
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());
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
751const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const
752{
753 return *d->skeleton.properties.can_edit_tracks;
754}
755
756core::Property<bool>& media::TrackListSkeleton::can_edit_tracks()
757{
758 return *d->skeleton.properties.can_edit_tracks;
759}
760
761core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks()
762{
763 return *d->skeleton.properties.tracks;
764}
765
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("");
785}
786
787const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const
788{
789 return *d->skeleton.properties.tracks;
790}
791
792const 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
799const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added() const
800{
801 return d->signals.on_track_added;
802}
803
804const core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added() const
805{
806 return d->signals.on_tracks_added;
807}
808
809const core::Signal<media::TrackList::TrackIdTuple>& media::TrackListSkeleton::on_track_moved() const
810{
811 return d->signals.on_track_moved;
812}
813
814const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const
815{
816 return d->signals.on_track_removed;
817}
818
819const core::Signal<void>& media::TrackListSkeleton::on_track_list_reset() const
820{
821 return d->signals.on_track_list_reset;
822}
823
824const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() const
825{
826 return d->signals.on_track_changed;
827}
828
829const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_go_to_track() const
830{
831 return d->signals.on_go_to_track;
832}
833
834const core::Signal<void>& media::TrackListSkeleton::on_end_of_tracklist() const
835{
836 return d->signals.on_end_of_tracklist;
837}
838
839core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced()
840{
841 return d->signals.on_track_list_replaced;
842}
843
844core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added()
845{
846 return d->signals.on_track_added;
847}
848
849core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added()
850{
851 return d->signals.on_tracks_added;
852}
853
854core::Signal<media::TrackList::TrackIdTuple>& media::TrackListSkeleton::on_track_moved()
855{
856 return d->signals.on_track_moved;
857}
858
859core::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
869core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed()
870{
871 return d->signals.on_track_changed;
872}
873
874core::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.
890inline 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
const core::Signal< TrackIdTuple > & on_track_moved() const
core::ubuntu::media::Player::LoopStatus loop_status() const
const core::Signal< Track::Id > & on_track_added() const
const core::Signal< Track::Id > & on_go_to_track() const
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)
TrackList::ConstIterator get_current_shuffled()
const core::Signal< Track::Id > & on_track_changed() const
const core::Signal< void > & on_track_list_reset() const
media::Track::Id get_current_track(void)
const core::Property< bool > & can_edit_tracks() const
const core::Signal< void > & on_end_of_tracklist() const
const core::Signal< ContainerTrackIdTuple > & on_track_list_replaced() const
bool is_last_track(const ConstIterator &it)
void on_loop_status_changed(const core::ubuntu::media::Player::LoopStatus &loop_status)
const TrackList::ConstIterator & current_iterator()
bool update_current_iterator(const TrackList::ConstIterator &it)
const core::Signal< ContainerURI > & on_tracks_added() const
void on_position_changed(uint64_t position)
virtual const media::TrackList::Container & shuffled_tracks()=0
const core::Property< Container > & tracks() const
bool is_first_track(const ConstIterator &it)
const core::Signal< Track::Id > & on_track_removed() const
void set_current_track(const media::Track::Id &id)
virtual void set_shuffle(bool shuffle)=0
std::tuple< Track::Id, Track::Id > TrackIdTuple
Definition: track_list.h:46
virtual const core::Property< Container > & tracks() const =0
std::tuple< std::vector< Track::Id >, Track::Id > ContainerTrackIdTuple
Definition: track_list.h:45
virtual Track::UriType query_uri_for_track(const Track::Id &id)=0
Container::const_iterator ConstIterator
Definition: track_list.h:48
std::vector< Track::UriType > ContainerURI
Definition: track_list.h:44
std::shared_ptr< UriCheck > Ptr
Definition: uri_check.h:37
#define MH_TRACE(...)
Definition: logger.h:121
#define MH_ERROR(...)
Definition: logger.h:128
#define MH_INFO(...)
Definition: logger.h:125
#define MH_WARNING(...)
Definition: logger.h:127
#define MH_DEBUG(...)
Definition: logger.h:123
std::ostream & operator<<(std::ostream &out, Player::PlaybackStatus status)
Definition: player.h:217
core::dbus::Signal< mpris::TrackList::Signals::TrackListReset, mpris::TrackList::Signals::TrackListReset::ArgumentType > DBusTrackListResetSignal
core::Signal< TrackList::ContainerTrackIdTuple > on_track_list_replaced
core::dbus::Signal< mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType > DBusTrackListReplacedSignal
core::Signal< TrackList::TrackIdTuple > on_track_moved
core::dbus::Signal< mpris::TrackList::Signals::TrackMoved, mpris::TrackList::Signals::TrackMoved::ArgumentType > DBusTrackMovedSignal
core::Signal< TrackList::ContainerURI > on_tracks_added
core::dbus::Signal< mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType > DBusTrackRemovedSignal
core::dbus::Signal< mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType > DBusTrackAddedSignal
core::dbus::Signal< mpris::TrackList::Signals::TracksAdded, mpris::TrackList::Signals::TracksAdded::ArgumentType > DBusTracksAddedSignal
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)
core::dbus::Signal< mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType > DBusTrackChangedSignal
struct media::TrackListSkeleton::Private::Signals signals
media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator
TrackList::ConstIterator current_track
media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver
void handle_move_track(const core::dbus::Message::Ptr &msg)
void handle_get_tracks_metadata(const core::dbus::Message::Ptr &msg)
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)
mpris::TrackList::Skeleton skeleton
void handle_add_tracks_with_uri_at(const core::dbus::Message::Ptr &msg)
void handle_add_track_with_uri_at(const core::dbus::Message::Ptr &msg)
void handle_go_to(const core::dbus::Message::Ptr &msg)
void handle_reset(const core::dbus::Message::Ptr &msg)
media::Player::LoopStatus loop_status
void handle_remove_track(const core::dbus::Message::Ptr &msg)
void handle_get_tracks_uri(const core::dbus::Message::Ptr &msg)
TrackList::ConstIterator empty_iterator
static constexpr const char * name
Definition: track_list.h:64
static constexpr const char * name
Definition: track_list.h:88
core::dbus::Signal< Signals::TrackListReplaced, Signals::TrackListReplaced::ArgumentType >::Ptr tracklist_replaced
Definition: track_list.h:248
core::dbus::Signal< Signals::TrackListReset, Signals::TrackListReset::ArgumentType >::Ptr track_list_reset
Definition: track_list.h:254
core::dbus::Signal< Signals::TrackAdded, Signals::TrackAdded::ArgumentType >::Ptr track_added
Definition: track_list.h:249
struct mpris::TrackList::Skeleton::@20 signals
core::dbus::Signal< Signals::TrackMoved, Signals::TrackMoved::ArgumentType >::Ptr track_moved
Definition: track_list.h:251
core::dbus::Signal< Signals::TrackChanged, Signals::TrackChanged::ArgumentType >::Ptr track_changed
Definition: track_list.h:253
core::dbus::Signal< Signals::TrackRemoved, Signals::TrackRemoved::ArgumentType >::Ptr track_removed
Definition: track_list.h:252
core::dbus::Signal< Signals::TracksAdded, Signals::TracksAdded::ArgumentType >::Ptr tracks_added
Definition: track_list.h:250