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/track_list.h"
31 
32 #include <core/dbus/object.h>
33 #include <core/dbus/property.h>
34 #include <core/dbus/types/object_path.h>
35 #include <core/dbus/types/variant.h>
36 #include <core/dbus/types/stl/map.h>
37 #include <core/dbus/types/stl/vector.h>
38 
39 #include <iostream>
40 #include <limits>
41 
42 namespace dbus = core::dbus;
43 namespace media = core::ubuntu::media;
44 
46 {
47  Private(media::TrackListSkeleton* impl, const dbus::Bus::Ptr& bus, const dbus::Object::Ptr& object,
48  const apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
49  const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
50  : impl(impl),
51  bus(bus),
52  object(object),
53  request_context_resolver(request_context_resolver),
54  request_authenticator(request_authenticator),
56  current_track(skeleton.properties.tracks->get().begin()),
57  empty_iterator(skeleton.properties.tracks->get().begin()),
58  loop_status(media::Player::LoopStatus::none),
59  signals
60  {
64  }
65  {
66  }
67 
68  void handle_get_tracks_metadata(const core::dbus::Message::Ptr& msg)
69  {
70  media::Track::Id track;
71  msg->reader() >> track;
72 
73  auto meta_data = impl->query_meta_data_for_track(track);
74 
75  auto reply = dbus::Message::make_method_return(msg);
76  reply->writer() << *meta_data;
77  bus->send(reply);
78  }
79 
80  void handle_add_track_with_uri_at(const core::dbus::Message::Ptr& msg)
81  {
82  request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context)
83  {
84  Track::UriType uri; media::Track::Id after; bool make_current;
85  msg->reader() >> uri >> after >> make_current;
86 
87  // Make sure the client has adequate apparmor permissions to open the URI
88  const auto result = request_authenticator->authenticate_open_uri_request(context, uri);
89 
90  auto reply = dbus::Message::make_method_return(msg);
91  // Only add the track to the TrackList if it passes the apparmor permissions check
92  if (std::get<0>(result))
93  impl->add_track_with_uri_at(uri, after, make_current);
94  else
95  std::cerr << "Warning: Not adding track " << uri <<
96  " to TrackList because of inadequate client apparmor permissions." << std::endl;
97 
98  bus->send(reply);
99  });
100  }
101 
102  void handle_remove_track(const core::dbus::Message::Ptr& msg)
103  {
104  media::Track::Id track;
105  msg->reader() >> track;
106 
107  impl->remove_track(track);
108 
109  auto reply = dbus::Message::make_method_return(msg);
110  bus->send(reply);
111  }
112 
113  void handle_go_to(const core::dbus::Message::Ptr& msg)
114  {
115  media::Track::Id track;
116  msg->reader() >> track;
117 
118  current_track = std::find(skeleton.properties.tracks->get().begin(), skeleton.properties.tracks->get().end(), track);
119  const bool toggle_player_state = true;
120  impl->go_to(track, toggle_player_state);
121 
122  auto reply = dbus::Message::make_method_return(msg);
123  bus->send(reply);
124  }
125 
127  dbus::Bus::Ptr bus;
128  dbus::Object::Ptr object;
129  media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
130  media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
131 
136 
137  struct Signals
138  {
139  typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal;
140  typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal;
141  typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal;
142 
143  Signals(const std::shared_ptr<DBusTrackAddedSignal>& remote_track_added,
144  const std::shared_ptr<DBusTrackRemovedSignal>& remote_track_removed,
145  const std::shared_ptr<DBusTrackListReplacedSignal>& remote_track_list_replaced)
146  {
147  // Connect all of the MPRIS interface signals to be emitted over dbus
148  on_track_added.connect([remote_track_added](const media::Track::Id &id)
149  {
150  remote_track_added->emit(id);
151  });
152 
153  on_track_removed.connect([remote_track_removed](const media::Track::Id &id)
154  {
155  remote_track_removed->emit(id);
156  });
157 
158  on_track_list_replaced.connect([remote_track_list_replaced](const media::TrackList::ContainerTrackIdTuple &tltuple)
159  {
160  remote_track_list_replaced->emit(tltuple);
161  });
162  }
163 
164  core::Signal<Track::Id> on_track_added;
165  core::Signal<Track::Id> on_track_removed;
166  core::Signal<TrackList::ContainerTrackIdTuple> on_track_list_replaced;
167  core::Signal<Track::Id> on_track_changed;
168  core::Signal<std::pair<Track::Id, bool>> on_go_to_track;
169  } signals;
170 };
171 
172 media::TrackListSkeleton::TrackListSkeleton(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object,
173  const media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
174  const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
175  : d(new Private(this, bus, object, request_context_resolver, request_authenticator))
176 {
177  d->object->install_method_handler<mpris::TrackList::GetTracksMetadata>(
179  std::ref(d),
180  std::placeholders::_1));
181 
182  d->object->install_method_handler<mpris::TrackList::AddTrack>(
184  std::ref(d),
185  std::placeholders::_1));
186 
187  d->object->install_method_handler<mpris::TrackList::RemoveTrack>(
188  std::bind(&Private::handle_remove_track,
189  std::ref(d),
190  std::placeholders::_1));
191 
192  d->object->install_method_handler<mpris::TrackList::GoTo>(
193  std::bind(&Private::handle_go_to,
194  std::ref(d),
195  std::placeholders::_1));
196 }
197 
199 {
200 }
201 
203 {
204  const auto next_track = std::next(d->current_track);
205  std::cout << "has_next track? " << (next_track != tracks().get().end() ? "yes" : "no") << std::endl;
206  return next_track != tracks().get().end();
207 }
208 
210 {
211  // If we are looping over the entire list, then there is always a previous track
212  if (d->loop_status == media::Player::LoopStatus::playlist)
213  return true;
214 
215  std::cout << "has_previous track? " << (d->current_track != tracks().get().begin() ? "yes" : "no") << std::endl;
216  return d->current_track != tracks().get().begin();
217 }
218 
220 {
221  std::cout << __PRETTY_FUNCTION__ << std::endl;
222  if (tracks().get().empty())
223  return *(d->current_track);
224 
225  // Loop on the current track forever
226  if (d->loop_status == media::Player::LoopStatus::track)
227  {
228  std::cout << "Looping on the current track..." << std::endl;
229  return *(d->current_track);
230  }
231  // Loop over the whole playlist and repeat
232  else if (d->loop_status == media::Player::LoopStatus::playlist && !has_next())
233  {
234  std::cout << "Looping on the entire TrackList..." << std::endl;
235  d->current_track = tracks().get().begin();
236  return *(d->current_track);
237  }
238  else if (has_next())
239  {
240  // Keep returning the next track until the last track is reached
241  d->current_track = std::next(d->current_track);
242  std::cout << *this << std::endl;
243  }
244 
245  return *(d->current_track);
246 }
247 
249 {
250  // TODO: Add logic to calculate the previous track
251  return *(d->current_track);
252 }
253 
255 {
256  // Prevent the TrackList from sitting at the end which will cause
257  // a segfault when calling current()
258  if (tracks().get().size() && (d->current_track == d->empty_iterator))
259  d->current_track = d->skeleton.properties.tracks->get().begin();
260  else if (tracks().get().empty())
261  std::cerr << "TrackList is empty therefore there is no valid current track" << std::endl;
262 
263  return *(d->current_track);
264 }
265 
266 const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const
267 {
268  return *d->skeleton.properties.can_edit_tracks;
269 }
270 
271 core::Property<bool>& media::TrackListSkeleton::can_edit_tracks()
272 {
273  return *d->skeleton.properties.can_edit_tracks;
274 }
275 
276 core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks()
277 {
278  return *d->skeleton.properties.tracks;
279 }
280 
282 {
283  d->loop_status = loop_status;
284 }
285 
287 {
288  return d->loop_status;
289 }
290 
292 {
293  if (shuffle)
294  shuffle_tracks();
295  else
297 }
298 
299 const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const
300 {
301  return *d->skeleton.properties.tracks;
302 }
303 
304 const core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced() const
305 {
306  // Print the TrackList instance
307  std::cout << *this << std::endl;
308  return d->signals.on_track_list_replaced;
309 }
310 
311 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added() const
312 {
313  return d->signals.on_track_added;
314 }
315 
316 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const
317 {
318  return d->signals.on_track_removed;
319 }
320 
321 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() const
322 {
323  return d->signals.on_track_changed;
324 }
325 
326 const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track() const
327 {
328  return d->signals.on_go_to_track;
329 }
330 
331 core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced()
332 {
333  return d->signals.on_track_list_replaced;
334 }
335 
336 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added()
337 {
338  return d->signals.on_track_added;
339 }
340 
341 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed()
342 {
343  return d->signals.on_track_removed;
344 }
345 
346 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed()
347 {
348  return d->signals.on_track_changed;
349 }
350 
351 core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track()
352 {
353  return d->signals.on_go_to_track;
354 }
355 
356 // operator<< pretty prints the given TrackList to the given output stream.
357 inline std::ostream& media::operator<<(std::ostream& out, const media::TrackList& tracklist)
358 {
359  auto non_const_tl = const_cast<media::TrackList*>(&tracklist);
360  out << "TrackList\n---------------" << std::endl;
361  for (const media::Track::Id &id : tracklist.tracks().get())
362  {
363  // '*' denotes the current track
364  out << "\t" << ((dynamic_cast<media::TrackListSkeleton*>(non_const_tl)->current() == id) ? "*" : "");
365  out << "Track Id: " << id << std::endl;
366  out << "\t\turi: " << dynamic_cast<media::TrackListImplementation*>(non_const_tl)->query_uri_for_track(id) << std::endl;
367  }
368 
369  out << "---------------\nEnd TrackList" << std::endl;
370  return out;
371 }
372 
core::dbus::Signal< mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType > DBusTrackAddedSignal
const core::Property< Container > & tracks() const
virtual const core::Property< Container > & tracks() const =0
struct mpris::TrackList::Skeleton::@20 signals
std::shared_ptr< core::dbus::Property< Properties::Tracks > > tracks
Definition: track_list.h:163
const core::Signal< std::pair< Track::Id, bool > > & on_go_to_track() const
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
void handle_remove_track(const core::dbus::Message::Ptr &msg)
std::ostream & operator<<(std::ostream &out, Player::PlaybackStatus status)
Definition: player.h:188
core::dbus::Signal< Signals::TrackListReplaced, Signals::TrackListReplaced::ArgumentType >::Ptr tracklist_replaced
Definition: track_list.h:169
void handle_get_tracks_metadata(const core::dbus::Message::Ptr &msg)
std::tuple< std::vector< Track::Id >, Track::Id > ContainerTrackIdTuple
Definition: track_list.h:43
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)
media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator
virtual void unshuffle_tracks()=0
const core::Signal< Track::Id > & on_track_added() const
TrackList::ConstIterator current_track
core::dbus::Signal< Signals::TrackAdded, Signals::TrackAdded::ArgumentType >::Ptr track_added
Definition: track_list.h:170
void on_loop_status_changed(const core::ubuntu::media::Player::LoopStatus &loop_status)
struct media::TrackListSkeleton::Private::Signals signals
mpris::TrackList::Skeleton skeleton
core::dbus::Signal< mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType > DBusTrackListReplacedSignal
core::Signal< std::pair< Track::Id, bool > > on_go_to_track
std::string UriType
Definition: track.h:40
virtual void shuffle_tracks()=0
media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver
const core::Property< bool > & can_edit_tracks() const
Container::const_iterator ConstIterator
Definition: track_list.h:45
core::dbus::Signal< Signals::TrackRemoved, Signals::TrackRemoved::ArgumentType >::Ptr track_removed
Definition: track_list.h:171
struct mpris::TrackList::Skeleton::@19 properties
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)
Signals(const std::shared_ptr< DBusTrackAddedSignal > &remote_track_added, const std::shared_ptr< DBusTrackRemovedSignal > &remote_track_removed, const std::shared_ptr< DBusTrackListReplacedSignal > &remote_track_list_replaced)
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)