Music Hub  ..
A session-wide music playback service
track_list_implementation.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2013-2015 Canonical Ltd.
3  *
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 <algorithm>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <tuple>
23 
25 
26 #include "engine.h"
27 
28 namespace dbus = core::dbus;
29 namespace media = core::ubuntu::media;
30 
32 {
33  typedef std::map<Track::Id, std::tuple<Track::UriType, Track::MetaData>> MetaDataCache;
34 
35  dbus::Object::Ptr object;
36  size_t track_counter;
37  MetaDataCache meta_data_cache;
38  std::shared_ptr<media::Engine::MetaDataExtractor> extractor;
39  // Used for caching the original tracklist order to be used to restore the order
40  // to the live TrackList after shuffle is turned off
42 };
43 
45  const dbus::Bus::Ptr& bus,
46  const dbus::Object::Ptr& object,
47  const std::shared_ptr<media::Engine::MetaDataExtractor>& extractor,
48  const media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
49  const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
50  : media::TrackListSkeleton(bus, object, request_context_resolver, request_authenticator),
52 {
53  can_edit_tracks().set(true);
54 }
55 
57 {
58 }
59 
61 {
62  const auto it = d->meta_data_cache.find(id);
63 
64  if (it == d->meta_data_cache.end())
65  return Track::UriType{};
66 
67  return std::get<0>(it->second);
68 }
69 
71 {
72  const auto it = d->meta_data_cache.find(id);
73 
74  if (it == d->meta_data_cache.end())
75  return Track::MetaData{};
76 
77  return std::get<1>(it->second);
78 }
79 
81  const media::Track::UriType& uri,
82  const media::Track::Id& position,
83  bool make_current)
84 {
85  std::cout << __PRETTY_FUNCTION__ << std::endl;
86 
87  std::stringstream ss; ss << d->object->path().as_string() << "/" << d->track_counter++;
88  Track::Id id{ss.str()};
89 
90  std::cout << "Adding Track::Id: " << id << std::endl;
91  std::cout << "\tURI: " << uri << std::endl;
92 
93  auto result = tracks().update([this, id, position, make_current](TrackList::Container& container)
94  {
95  auto it = std::find(container.begin(), container.end(), position);
96  container.insert(it, id);
97  std::cout << "container.size(): " << container.size() << std::endl;
98 
99  return true;
100  });
101 
102  if (result)
103  {
104  if (d->meta_data_cache.count(id) == 0)
105  {
106  // FIXME: This code seems to conflict badly when called multiple times in a row: causes segfaults
107 #if 0
108  try {
109  d->meta_data_cache[id] = std::make_tuple(
110  uri,
111  d->extractor->meta_data_for_track_with_uri(uri));
112  } catch (const std::runtime_error &e) {
113  std::cerr << "Failed to retrieve metadata for track '" << uri << "' (" << e.what() << ")" << std::endl;
114  }
115 #else
116  d->meta_data_cache[id] = std::make_tuple(
117  uri,
119 #endif
120  } else
121  {
122  std::get<0>(d->meta_data_cache[id]) = uri;
123  }
124 
125  if (make_current)
126  {
127  // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track()
128  // since this breaks video playback when using open_uri() (stop() and play() are unwanted in
129  // this scenario since the qtubuntu-media will handle this automatically)
130  const bool toggle_player_state = false;
131  go_to(id, toggle_player_state);
132  }
133 
134  // Signal to the client that the current track has changed for the first track added to the TrackList
135  if (tracks().get().size() == 1)
136  on_track_changed()(id);
137 
138  std::cout << "Signaling that we just added track id: " << id << std::endl;
139  // Signal to the client that a track was added to the TrackList
140  on_track_added()(id);
141  std::cout << "Signaled that we just added track id: " << id << std::endl;
142  }
143 }
144 
146 {
147  auto result = tracks().update([id](TrackList::Container& container)
148  {
149  container.erase(std::find(container.begin(), container.end(), id));
150  return true;
151  });
152 
154 
155  if (result)
156  {
157  d->meta_data_cache.erase(id);
158 
159  on_track_removed()(id);
160  }
161 
162 }
163 
164 void media::TrackListImplementation::go_to(const media::Track::Id& track, bool toggle_player_state)
165 {
166  std::cout << __PRETTY_FUNCTION__ << std::endl;
167  std::pair<const media::Track::Id, bool> p = std::make_pair(track, toggle_player_state);
168  // Signal the Player instance to go to a specific track for playback
169  on_go_to_track()(p);
170  on_track_changed()(track);
171 }
172 
174 {
175  std::cout << __PRETTY_FUNCTION__ << std::endl;
176 
177  if (tracks().get().empty())
178  return;
179 
180  auto result = tracks().update([this](TrackList::Container& container)
181  {
182  // Save off the original TrackList ordering
183  d->original_tracklist.assign(container.begin(), container.end());
184  std::random_shuffle(container.begin(), container.end());
185  return true;
186  });
187 
188  if (result)
189  {
190  media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())};
192  }
193 }
194 
196 {
197  std::cout << __PRETTY_FUNCTION__ << std::endl;
198 
199  if (tracks().get().empty() or d->original_tracklist.empty())
200  return;
201 
202  auto result = tracks().update([this](TrackList::Container& container)
203  {
204  // Restore the original TrackList ordering
205  container.assign(d->original_tracklist.begin(), d->original_tracklist.end());
206  return true;
207  });
208 
209  if (result)
210  {
211  media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())};
213  }
214 }
215 
217 {
218  std::cout << __PRETTY_FUNCTION__ << std::endl;
219 
220  // Make sure playback stops
222 
223  auto result = tracks().update([this](TrackList::Container& container)
224  {
225  container.clear();
226  container.resize(0);
227  d->track_counter = 0;
228  return true;
229  });
230 
232 
233  (void) result;
234 }
void add_track_with_uri_at(const Track::UriType &uri, const Track::Id &position, bool make_current)
const core::Property< Container > & tracks() const
const core::Signal< std::pair< Track::Id, bool > > & on_go_to_track() const
const core::Signal< Track::Id > & on_track_changed() const
const core::Signal< Track::Id > & on_track_removed() const
Track::UriType query_uri_for_track(const Track::Id &id)
const core::Signal< void > & on_end_of_tracklist() const
std::tuple< std::vector< Track::Id >, Track::Id > ContainerTrackIdTuple
Definition: track_list.h:44
TrackListImplementation(const core::dbus::Bus::Ptr &bus, const core::dbus::Object::Ptr &object, const std::shared_ptr< Engine::MetaDataExtractor > &extractor, const core::ubuntu::media::apparmor::ubuntu::RequestContextResolver::Ptr &request_context_resolver, const core::ubuntu::media::apparmor::ubuntu::RequestAuthenticator::Ptr &request_authenticator)
const core::Signal< ContainerTrackIdTuple > & on_track_list_replaced() const
const core::Signal< Track::Id > & on_track_added() const
Track::MetaData query_meta_data_for_track(const Track::Id &id)
std::shared_ptr< media::Engine::MetaDataExtractor > extractor
std::string UriType
Definition: track.h:40
std::vector< Track::Id > Container
Definition: track_list.h:43
std::map< Track::Id, std::tuple< Track::UriType, Track::MetaData > > MetaDataCache
const core::Property< bool > & can_edit_tracks() const
void go_to(const Track::Id &track, bool toggle_player_state)