Merge lp:~phablet-team/media-hub/sync-trunk into lp:media-hub

Proposed by Alfonso Sanchez-Beato
Status: Merged
Approved by: Thomas Voß
Approved revision: 161
Merged at revision: 162
Proposed branch: lp:~phablet-team/media-hub/sync-trunk
Merge into: lp:media-hub
Diff against target: 2628 lines (+1182/-630)
18 files modified
README (+4/-0)
debian/changelog (+45/-0)
include/core/media/track_list.h (+53/-12)
src/core/media/gstreamer/playbin.cpp (+9/-1)
src/core/media/mpris/track_list.h (+72/-0)
src/core/media/player_implementation.cpp (+40/-10)
src/core/media/service_skeleton.cpp (+9/-3)
src/core/media/track_list.cpp (+27/-0)
src/core/media/track_list_implementation.cpp (+246/-92)
src/core/media/track_list_implementation.h (+6/-3)
src/core/media/track_list_skeleton.cpp (+398/-91)
src/core/media/track_list_skeleton.h (+25/-5)
src/core/media/track_list_stub.cpp (+118/-21)
src/core/media/track_list_stub.h (+7/-5)
tests/CMakeLists.txt (+1/-1)
tests/acceptance-tests/service.cpp (+119/-382)
tests/test-track-list/test_track_list.cpp (+2/-3)
tests/test-track-list/test_track_list.h (+1/-1)
To merge this branch: bzr merge lp:~phablet-team/media-hub/sync-trunk
Reviewer Review Type Date Requested Status
Jim Hodapp (community) code Approve
Review via email: mp+280738@code.launchpad.net

Commit message

Sync with stable branch

Description of the change

Sync with stable branch

To post a comment you must log in.
Revision history for this message
Jim Hodapp (jhodapp) wrote :

LGTM

review: Approve (code)
161. By Thomas Voß

Fix bug #1479036 which prevents the out_of_range exception from
causing media-hub-server from crashing when a player key is not
found (LP: #1479036)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2015-09-09 14:08:12 +0000
3+++ README 2015-12-23 14:19:01 +0000
4@@ -6,6 +6,10 @@
5
6 https://google-styleguide.googlecode.com/svn/trunk/cppguide.html
7
8+Deviations from the Google Style Guide above:
9+
10+1. We will agree to maximum of 100 characters in a single line instead of 80.
11+
12
13 To Build:
14 ---------
15
16=== modified file 'debian/changelog'
17--- debian/changelog 2015-12-21 18:55:55 +0000
18+++ debian/changelog 2015-12-23 14:19:01 +0000
19@@ -57,12 +57,57 @@
20
21 -- CI Train Bot <ci-train-bot@canonical.com> Tue, 01 Sep 2015 08:46:00 +0000
22
23+media-hub (4.0.0+15.04.20151209-0ubuntu1) vivid; urgency=medium
24+
25+ * Set gstreamer context directly instead of using gobject to
26+ communicate with the video sink
27+
28+ -- Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com> Wed, 09 Dec 2015 06:49:28 +0000
29+
30+media-hub (4.0.0+15.04.20151202-0ubuntu1) vivid; urgency=medium
31+
32+ [ CI Train Bot ]
33+ * No-change rebuild.
34+
35+ -- Thomas Voß <ci-train-bot@canonical.com> Wed, 02 Dec 2015 07:40:41 +0000
36+
37+media-hub (4.0.0+15.04.20151118.1-0ubuntu1) vivid; urgency=medium
38+
39+ [ Jim Hodapp ]
40+ * Added move_track and various fixes for TrackList.
41+ * Major bump for new TrackList API changes.
42+
43+ [ Alfonso Sanchez-Beato ]
44+ * Make sure our iterator for the current track points to the right
45+ place when (un)shuffling (LP #1510219). Fix crash when client tries
46+ to set the player for a non-existing key. Do not add empty URIs to
47+ the list (LP: #1511029). (LP: #1511073, #1511385, #1510219,
48+ #1510227, #1511029)
49+
50+ -- Jim Hodapp <ci-train-bot@canonical.com> Wed, 18 Nov 2015 18:36:18 +0000
51+
52 media-hub (4.0.0-0ubuntu1) wily; urgency=medium
53
54 * Bump major revision to account for toolchain update. Fixes LP:#1452331
55
56 -- Thomas Voß <thomas.voss@canonical.com> Thu, 23 Jul 2015 08:47:21 +0200
57
58+media-hub (3.3.0+15.04.20151023.3-0ubuntu2) UNRELEASED; urgency=medium
59+
60+ * Force rebuild against dbus-cpp.
61+
62+ -- Thomas Voß <thomas.voss@canonical.com> Tue, 17 Nov 2015 09:37:46 +0100
63+
64+media-hub (3.3.0+15.04.20151023.3-0ubuntu1) vivid; urgency=medium
65+
66+ [ Jim Hodapp ]
67+ * Add batch adding of tracks to the TrackList.
68+
69+ [ CI Train Bot ]
70+ * New rebuild forced.
71+
72+ -- Jim Hodapp <ci-train-bot@canonical.com> Fri, 23 Oct 2015 19:07:52 +0000
73+
74 media-hub (3.1.0+15.10.20150724-0ubuntu1) wily; urgency=medium
75
76 [ Alfonso Sanchez-Beato (email Canonical) ]
77
78=== modified file 'include/core/media/track_list.h'
79--- include/core/media/track_list.h 2015-07-20 20:39:42 +0000
80+++ include/core/media/track_list.h 2015-12-23 14:19:01 +0000
81@@ -41,10 +41,42 @@
82 {
83 public:
84 typedef std::vector<Track::Id> Container;
85+ typedef std::vector<Track::UriType> ContainerURI;
86 typedef std::tuple<std::vector<Track::Id>, Track::Id> ContainerTrackIdTuple;
87+ typedef std::tuple<Track::Id, Track::Id> TrackIdTuple;
88 typedef Container::iterator Iterator;
89 typedef Container::const_iterator ConstIterator;
90
91+ struct Errors
92+ {
93+ Errors() = delete;
94+
95+ struct InsufficientPermissionsToAddTrack : public std::runtime_error
96+ {
97+ InsufficientPermissionsToAddTrack();
98+ };
99+
100+ struct FailedToMoveTrack : public std::runtime_error
101+ {
102+ FailedToMoveTrack();
103+ };
104+
105+ struct FailedToFindMoveTrackSource : public std::runtime_error
106+ {
107+ FailedToFindMoveTrackSource(const std::string& err);
108+ };
109+
110+ struct FailedToFindMoveTrackDest : public std::runtime_error
111+ {
112+ FailedToFindMoveTrackDest(const std::string& err);
113+ };
114+
115+ struct TrackNotFound : public std::runtime_error
116+ {
117+ TrackNotFound();
118+ };
119+ };
120+
121 static const Track::Id& after_empty_track();
122
123 TrackList(const TrackList&) = delete;
124@@ -65,14 +97,20 @@
125 /** Gets the URI for a given Track. */
126 virtual Track::UriType query_uri_for_track(const Track::Id& id) = 0;
127
128- /** Adds a URI in the TrackList. */
129+ /** Adds a URI into the TrackList. */
130 virtual void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current) = 0;
131
132+ /** Adds a list of URIs into the TrackList. */
133+ virtual void add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position) = 0;
134+
135+ /** Moves track 'id' from its old position in the TrackList to new position. */
136+ virtual bool move_track(const Track::Id& id, const Track::Id& to) = 0;
137+
138 /** Removes a Track from the TrackList. */
139 virtual void remove_track(const Track::Id& id) = 0;
140
141- /** Skip to the specified TrackId. Calls stop() and play() on the player if toggle_player_state is true. */
142- virtual void go_to(const Track::Id& track, bool toggle_player_state) = 0;
143+ /** Skip to the specified TrackId. */
144+ virtual void go_to(const Track::Id& track) = 0;
145
146 /** Returns true if there is a next track in the TrackList after the current one playing */
147 bool has_next() const;
148@@ -86,31 +124,34 @@
149 /** Skip to the previous Track in the TrackList if there is one. */
150 virtual Track::Id previous() = 0;
151
152-
153- /** Reorders the tracks such that they are in a random order. */
154- virtual void shuffle_tracks() = 0;
155-
156- /** Restores the original order of tracks before shuffle mode was turned on. */
157- virtual void unshuffle_tracks() = 0;
158-
159 /** Clears and resets the TrackList to the same as a newly constructed instance. */
160 virtual void reset() = 0;
161
162-
163 /** Indicates that the entire tracklist has been replaced. */
164 virtual const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const = 0;
165
166 /** Indicates that a track has been added to the track list. */
167 virtual const core::Signal<Track::Id>& on_track_added() const = 0;
168
169+ /** Indicates that one or more tracks have been added to the track list. */
170+ virtual const core::Signal<ContainerURI>& on_tracks_added() const = 0;
171+
172+ /** Indicates that a track has been moved within the track list. First template param
173+ * holds the id of the track being moved. Second param holds the id of the track of the
174+ * position to move the track to in the TrackList. */
175+ virtual const core::Signal<TrackIdTuple>& on_track_moved() const = 0;
176+
177 /** Indicates that a track has been removed from the track list. */
178 virtual const core::Signal<Track::Id>& on_track_removed() const = 0;
179
180+ /** Indicates that the track list has been reset and there are no tracks now */
181+ virtual const core::Signal<void>& on_track_list_reset() const = 0;
182+
183 /** Indicates that the track list advanced from one track to another. */
184 virtual const core::Signal<Track::Id>& on_track_changed() const = 0;
185
186 /** Used to notify the Player of when the client requested that the Player should immediately play a new track. */
187- virtual const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const = 0;
188+ virtual const core::Signal<Track::Id>& on_go_to_track() const = 0;
189
190 /** Used to notify the Player of when the end of the tracklist has been reached. */
191 virtual const core::Signal<void>& on_end_of_tracklist() const = 0;
192
193=== modified file 'src/core/media/gstreamer/playbin.cpp'
194--- src/core/media/gstreamer/playbin.cpp 2015-10-16 08:32:58 +0000
195+++ src/core/media/gstreamer/playbin.cpp 2015-12-23 14:19:01 +0000
196@@ -455,7 +455,13 @@
197 const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType(),
198 bool do_pipeline_reset)
199 {
200- if (do_pipeline_reset)
201+ gchar *current_uri = nullptr;
202+ g_object_get(pipeline, "current-uri", &current_uri, NULL);
203+
204+ // Checking for a current_uri being set and not resetting the pipeline
205+ // if there isn't a current_uri causes the first play to start playback
206+ // sooner since reset_pipeline won't be called
207+ if (current_uri and do_pipeline_reset)
208 reset_pipeline();
209
210 g_object_set(pipeline, "uri", uri.c_str(), NULL);
211@@ -465,6 +471,8 @@
212 file_type = MEDIA_FILE_TYPE_AUDIO;
213
214 request_headers = headers;
215+
216+ g_free(current_uri);
217 }
218
219 void gstreamer::Playbin::setup_source(GstElement *source)
220
221=== modified file 'src/core/media/mpris/track_list.h'
222--- src/core/media/mpris/track_list.h 2015-07-27 22:15:33 +0000
223+++ src/core/media/mpris/track_list.h 2015-12-23 14:19:01 +0000
224@@ -48,9 +48,54 @@
225 return s;
226 }
227
228+ struct Error
229+ {
230+ struct InsufficientPermissionsToAddTrack
231+ {
232+ static constexpr const char* name
233+ {
234+ "mpris.TrackList.Error.InsufficientPermissionsToAddTrack"
235+ };
236+ };
237+
238+ struct FailedToMoveTrack
239+ {
240+ static constexpr const char* name
241+ {
242+ "mpris.TrackList.Error.FailedToMoveTrack"
243+ };
244+ };
245+
246+ struct FailedToFindMoveTrackSource
247+ {
248+ static constexpr const char* name
249+ {
250+ "mpris.TrackList.Error.FailedToFindMoveTrackSource"
251+ };
252+ };
253+
254+ struct FailedToFindMoveTrackDest
255+ {
256+ static constexpr const char* name
257+ {
258+ "mpris.TrackList.Error.FailedToFindMoveTrackDest"
259+ };
260+ };
261+
262+ struct TrackNotFound
263+ {
264+ static constexpr const char* name
265+ {
266+ "mpris.TrackList.Error.TrackNotFound"
267+ };
268+ };
269+ };
270+
271 DBUS_CPP_METHOD_DEF(GetTracksMetadata, TrackList)
272 DBUS_CPP_METHOD_DEF(GetTracksUri, TrackList)
273 DBUS_CPP_METHOD_DEF(AddTrack, TrackList)
274+ DBUS_CPP_METHOD_DEF(AddTracks, TrackList)
275+ DBUS_CPP_METHOD_DEF(MoveTrack, TrackList)
276 DBUS_CPP_METHOD_DEF(RemoveTrack, TrackList)
277 DBUS_CPP_METHOD_DEF(GoTo, TrackList)
278 DBUS_CPP_METHOD_DEF(Reset, TrackList)
279@@ -75,6 +120,20 @@
280
281 DBUS_CPP_SIGNAL_DEF
282 (
283+ TracksAdded,
284+ TrackList,
285+ core::ubuntu::media::TrackList::ContainerURI
286+ )
287+
288+ DBUS_CPP_SIGNAL_DEF
289+ (
290+ TrackMoved,
291+ TrackList,
292+ BOOST_IDENTITY_TYPE((std::tuple<core::ubuntu::media::Track::Id, core::ubuntu::media::Track::Id>))
293+ )
294+
295+ DBUS_CPP_SIGNAL_DEF
296+ (
297 TrackRemoved,
298 TrackList,
299 core::ubuntu::media::Track::Id
300@@ -89,6 +148,13 @@
301
302 DBUS_CPP_SIGNAL_DEF
303 (
304+ TrackListReset,
305+ TrackList,
306+ void
307+ )
308+
309+ DBUS_CPP_SIGNAL_DEF
310+ (
311 TrackMetadataChanged,
312 TrackList,
313 BOOST_IDENTITY_TYPE((std::tuple<std::map<std::string, dbus::types::Variant>, dbus::types::ObjectPath>))
314@@ -134,8 +200,11 @@
315 {
316 configuration.object->template get_signal<Signals::TrackListReplaced>(),
317 configuration.object->template get_signal<Signals::TrackAdded>(),
318+ configuration.object->template get_signal<Signals::TracksAdded>(),
319+ configuration.object->template get_signal<Signals::TrackMoved>(),
320 configuration.object->template get_signal<Signals::TrackRemoved>(),
321 configuration.object->template get_signal<Signals::TrackChanged>(),
322+ configuration.object->template get_signal<Signals::TrackListReset>(),
323 configuration.object->template get_signal<Signals::TrackMetadataChanged>(),
324 configuration.object->template get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>()
325 }
326@@ -178,8 +247,11 @@
327 {
328 core::dbus::Signal<Signals::TrackListReplaced, Signals::TrackListReplaced::ArgumentType>::Ptr tracklist_replaced;
329 core::dbus::Signal<Signals::TrackAdded, Signals::TrackAdded::ArgumentType>::Ptr track_added;
330+ core::dbus::Signal<Signals::TracksAdded, Signals::TracksAdded::ArgumentType>::Ptr tracks_added;
331+ core::dbus::Signal<Signals::TrackMoved, Signals::TrackMoved::ArgumentType>::Ptr track_moved;
332 core::dbus::Signal<Signals::TrackRemoved, Signals::TrackRemoved::ArgumentType>::Ptr track_removed;
333 core::dbus::Signal<Signals::TrackChanged, Signals::TrackChanged::ArgumentType>::Ptr track_changed;
334+ core::dbus::Signal<Signals::TrackListReset, Signals::TrackListReset::ArgumentType>::Ptr track_list_reset;
335 core::dbus::Signal<Signals::TrackMetadataChanged, Signals::TrackMetadataChanged::ArgumentType>::Ptr track_metadata_changed;
336
337 dbus::Signal <core::dbus::interfaces::Properties::Signals::PropertiesChanged,
338
339=== modified file 'src/core/media/player_implementation.cpp'
340--- src/core/media/player_implementation.cpp 2015-09-29 11:07:54 +0000
341+++ src/core/media/player_implementation.cpp 2015-12-23 14:19:01 +0000
342@@ -294,7 +294,8 @@
343 {
344 // Using a TrackList for playback, added tracks via add_track(), but open_uri hasn't been called yet
345 // to load a media resource
346- std::cout << "Calling d->engine->open_resource_for_uri() for first track added only: " << uri << std::endl;
347+ std::cout << "Calling d->engine->open_resource_for_uri() for first track added only: "
348+ << uri << std::endl;
349 std::cout << "\twith a Track::Id: " << id << std::endl;
350 static const bool do_pipeline_reset = false;
351 engine->open_resource_for_uri(uri, do_pipeline_reset);
352@@ -375,6 +376,11 @@
353 };
354 Parent::position().install(position_getter);
355
356+ d->engine->position().changed().connect([this](uint64_t position)
357+ {
358+ d->track_list->on_position_changed(position);
359+ });
360+
361 // Make sure that the Duration property gets updated from the Engine
362 // every time the client requests duration
363 std::function<uint64_t()> duration_getter = [this]()
364@@ -507,8 +513,7 @@
365 }
366 });
367
368-
369- d->track_list->on_go_to_track().connect([this](std::pair<const media::Track::Id, bool> p)
370+ d->track_list->on_go_to_track().connect([this](const media::Track::Id& id)
371 {
372 // This lambda needs to be mutually exclusive with the about_to_finish lambda above
373 const bool locked = d->doing_go_to_track.try_lock();
374@@ -517,11 +522,8 @@
375 if (!locked)
376 return;
377
378- const media::Track::Id id = p.first;
379- const bool toggle_player_state = p.second;
380-
381- if (toggle_player_state)
382- d->engine->stop();
383+ // Store whether we should restore the current playing state after loading the new uri
384+ const bool auto_play = Parent::playback_status().get() == media::Player::playing;
385
386 const Track::UriType uri = d->track_list->query_uri_for_track(id);
387 if (!uri.empty())
388@@ -532,8 +534,11 @@
389 d->engine->open_resource_for_uri(uri, do_pipeline_reset);
390 }
391
392- if (toggle_player_state)
393+ if (auto_play)
394+ {
395+ std::cout << "Restoring playing state in on_go_to_track()" << std::endl;
396 d->engine->play();
397+ }
398
399 d->doing_go_to_track.unlock();
400 });
401@@ -547,11 +552,28 @@
402 d->update_mpris_properties();
403 });
404
405+ d->track_list->on_tracks_added().connect([this](const media::TrackList::ContainerURI& tracks)
406+ {
407+ std::cout << "** Track was added, handling in PlayerImplementation" << std::endl;
408+ // If the two sizes are the same, that means the TrackList was previously empty and we need
409+ // to open the first track in the TrackList so that is_audio_source() and is_video_source()
410+ // will function correctly.
411+ if (tracks.size() >= 1 and d->track_list->tracks()->size() == tracks.size())
412+ d->open_first_track_from_tracklist(tracks.front());
413+
414+ d->update_mpris_properties();
415+ });
416+
417 d->track_list->on_track_removed().connect([this](const media::Track::Id&)
418 {
419 d->update_mpris_properties();
420 });
421
422+ d->track_list->on_track_list_reset().connect([this](void)
423+ {
424+ d->update_mpris_properties();
425+ });
426+
427 d->track_list->on_track_changed().connect([this](const media::Track::Id&)
428 {
429 d->update_mpris_properties();
430@@ -664,7 +686,15 @@
431 bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri)
432 {
433 d->track_list->reset();
434- const bool ret = d->engine->open_resource_for_uri(uri, false);
435+
436+ // If empty uri, give the same meaning as QMediaPlayer::setMedia("")
437+ if (uri.empty()) {
438+ cout << __PRETTY_FUNCTION__ << ": resetting current media" << endl;
439+ return true;
440+ }
441+
442+ static const bool do_pipeline_reset = false;
443+ const bool ret = d->engine->open_resource_for_uri(uri, do_pipeline_reset);
444 // Don't set new track as the current track to play since we're calling open_resource_for_uri above
445 static const bool make_current = false;
446 d->track_list->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), make_current);
447
448=== modified file 'src/core/media/service_skeleton.cpp'
449--- src/core/media/service_skeleton.cpp 2015-12-18 16:24:01 +0000
450+++ src/core/media/service_skeleton.cpp 2015-12-23 14:19:01 +0000
451@@ -46,6 +46,8 @@
452 namespace dbus = core::dbus;
453 namespace media = core::ubuntu::media;
454
455+using namespace std;
456+
457 namespace
458 {
459 core::Signal<void> the_empty_signal;
460@@ -132,7 +134,9 @@
461 impl->access_service()->add_object_for_path(op)
462 };
463
464- std::cout << "Session created by request of: " << msg->sender() << ", uuid: " << uuid << ", path:" << op << std::endl;
465+ cout << "Session created by request of: " << msg->sender()
466+ << ", key: " << key << ", uuid: " << uuid
467+ << ", path:" << op << std::endl;
468
469 try
470 {
471@@ -205,9 +209,11 @@
472 std::string uuid;
473 msg->reader() >> uuid;
474
475- if (uuid_player_map.count(uuid) != 0) {
476+ if (uuid_player_map.count(uuid) != 0)
477+ {
478 auto key = uuid_player_map.at(uuid);
479- if (not configuration.player_store->has_player_for_key(key)) {
480+ if (not configuration.player_store->has_player_for_key(key))
481+ {
482 auto reply = dbus::Message::make_error(
483 msg,
484 mpris::Service::Errors::ReattachingSession::name(),
485
486=== modified file 'src/core/media/track_list.cpp'
487--- src/core/media/track_list.cpp 2015-07-20 20:39:42 +0000
488+++ src/core/media/track_list.cpp 2015-12-23 14:19:01 +0000
489@@ -20,6 +20,33 @@
490
491 namespace media = core::ubuntu::media;
492
493+media::TrackList::Errors::InsufficientPermissionsToAddTrack::InsufficientPermissionsToAddTrack()
494+ : std::runtime_error{"Insufficient client permissions for adding track to TrackList"}
495+{
496+}
497+
498+media::TrackList::Errors::FailedToMoveTrack::FailedToMoveTrack()
499+ : std::runtime_error{"Failed to move track within TrackList"}
500+{
501+}
502+
503+media::TrackList::Errors::FailedToFindMoveTrackSource::FailedToFindMoveTrackSource
504+ (const std::string &e)
505+ : std::runtime_error{e}
506+{
507+}
508+
509+media::TrackList::Errors::FailedToFindMoveTrackDest::FailedToFindMoveTrackDest
510+ (const std::string &e)
511+ : std::runtime_error{e}
512+{
513+}
514+
515+media::TrackList::Errors::TrackNotFound::TrackNotFound()
516+ : std::runtime_error{"Track not found in TrackList"}
517+{
518+}
519+
520 const media::Track::Id& media::TrackList::after_empty_track()
521 {
522 static const media::Track::Id id{"/org/mpris/MediaPlayer2/TrackList/NoTrack"};
523
524=== modified file 'src/core/media/track_list_implementation.cpp'
525--- src/core/media/track_list_implementation.cpp 2015-09-28 13:20:01 +0000
526+++ src/core/media/track_list_implementation.cpp 2015-12-23 14:19:01 +0000
527@@ -17,9 +17,13 @@
528 */
529
530 #include <algorithm>
531+#include <random>
532 #include <stdio.h>
533 #include <stdlib.h>
534 #include <tuple>
535+#include <unistd.h>
536+
537+#include <dbus/dbus.h>
538
539 #include "track_list_implementation.h"
540
541@@ -38,7 +42,45 @@
542 std::shared_ptr<media::Engine::MetaDataExtractor> extractor;
543 // Used for caching the original tracklist order to be used to restore the order
544 // to the live TrackList after shuffle is turned off
545- media::TrackList::Container original_tracklist;
546+ media::TrackList::Container shuffled_tracks;
547+ bool shuffle;
548+
549+ void updateCachedTrackMetadata(const media::Track::Id& id, const media::Track::UriType& uri)
550+ {
551+ if (meta_data_cache.count(id) == 0)
552+ {
553+ // FIXME: This code seems to conflict badly when called multiple times in a row: causes segfaults
554+#if 0
555+ try {
556+ meta_data_cache[id] = std::make_tuple(
557+ uri,
558+ extractor->meta_data_for_track_with_uri(uri));
559+ } catch (const std::runtime_error &e) {
560+ std::cerr << "Failed to retrieve metadata for track '" << uri << "' (" << e.what() << ")" << std::endl;
561+ }
562+#else
563+ meta_data_cache[id] = std::make_tuple(
564+ uri,
565+ core::ubuntu::media::Track::MetaData{});
566+#endif
567+ } else
568+ {
569+ std::get<0>(meta_data_cache[id]) = uri;
570+ }
571+ }
572+
573+ media::TrackList::Container::iterator get_shuffled_insert_it()
574+ {
575+ media::TrackList::Container::iterator random_it = shuffled_tracks.begin();
576+ if (random_it == shuffled_tracks.end())
577+ return random_it;
578+
579+ // This is slightly biased, but not much, as RAND_MAX >= 32767, which is
580+ // much more than the average number of tracks.
581+ // Note that for N tracks we have N + 1 possible insertion positions.
582+ std::advance(random_it, rand() % (shuffled_tracks.size() + 1));
583+ return random_it;
584+ }
585 };
586
587 media::TrackListImplementation::TrackListImplementation(
588@@ -48,7 +90,8 @@
589 const media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
590 const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
591 : media::TrackListSkeleton(bus, object, request_context_resolver, request_authenticator),
592- d(new Private{object, 0, Private::MetaDataCache{}, extractor, media::TrackList::Container{}})
593+ d(new Private{object, 0, Private::MetaDataCache{},
594+ extractor, media::TrackList::Container{}, false})
595 {
596 can_edit_tracks().set(true);
597 }
598@@ -84,12 +127,15 @@
599 {
600 std::cout << __PRETTY_FUNCTION__ << std::endl;
601
602- std::stringstream ss; ss << d->object->path().as_string() << "/" << d->track_counter++;
603+ std::stringstream ss;
604+ ss << d->object->path().as_string() << "/" << d->track_counter++;
605 Track::Id id{ss.str()};
606
607 std::cout << "Adding Track::Id: " << id << std::endl;
608 std::cout << "\tURI: " << uri << std::endl;
609
610+ const auto current = get_current_track();
611+
612 auto result = tracks().update([this, id, position, make_current](TrackList::Container& container)
613 {
614 auto it = std::find(container.begin(), container.end(), position);
615@@ -101,50 +147,180 @@
616
617 if (result)
618 {
619- if (d->meta_data_cache.count(id) == 0)
620- {
621- // FIXME: This code seems to conflict badly when called multiple times in a row: causes segfaults
622-#if 0
623- try {
624- d->meta_data_cache[id] = std::make_tuple(
625- uri,
626- d->extractor->meta_data_for_track_with_uri(uri));
627- } catch (const std::runtime_error &e) {
628- std::cerr << "Failed to retrieve metadata for track '" << uri << "' (" << e.what() << ")" << std::endl;
629- }
630-#else
631- d->meta_data_cache[id] = std::make_tuple(
632- uri,
633- core::ubuntu::media::Track::MetaData{});
634-#endif
635- } else
636- {
637- std::get<0>(d->meta_data_cache[id]) = uri;
638- }
639+ d->updateCachedTrackMetadata(id, uri);
640+
641+ if (d->shuffle)
642+ d->shuffled_tracks.insert(d->get_shuffled_insert_it(), id);
643
644 if (make_current)
645 {
646- // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track()
647- // since this breaks video playback when using open_uri() (stop() and play() are unwanted in
648- // this scenario since the qtubuntu-media will handle this automatically)
649- const bool toggle_player_state = false;
650- go_to(id, toggle_player_state);
651+ set_current_track(id);
652+ go_to(id);
653+ } else {
654+ set_current_track(current);
655 }
656
657- // Signal to the client that the current track has changed for the first track added to the TrackList
658- if (tracks().get().size() == 1)
659- on_track_changed()(id);
660-
661 std::cout << "Signaling that we just added track id: " << id << std::endl;
662 // Signal to the client that a track was added to the TrackList
663 on_track_added()(id);
664- std::cout << "Signaled that we just added track id: " << id << std::endl;
665- }
666+
667+ // Signal to the client that the current track has changed for the first
668+ // track added to the TrackList
669+ if (tracks().get().size() == 1)
670+ on_track_changed()(id);
671+ }
672+}
673+
674+void media::TrackListImplementation::add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position)
675+{
676+ std::cout << __PRETTY_FUNCTION__ << std::endl;
677+
678+ const auto current = get_current_track();
679+
680+ Track::Id current_id;
681+ ContainerURI tmp;
682+ for (const auto uri : uris)
683+ {
684+ // TODO: Refactor this code to use a smaller common function shared with add_track_with_uri_at()
685+ std::stringstream ss;
686+ ss << d->object->path().as_string() << "/" << d->track_counter++;
687+ Track::Id id{ss.str()};
688+ std::cout << "Adding Track::Id: " << id << std::endl;
689+ std::cout << "\tURI: " << uri << std::endl;
690+
691+ tmp.push_back(id);
692+
693+ Track::Id insert_position = position;
694+
695+ auto it = std::find(tracks().get().begin(), tracks().get().end(), insert_position);
696+ const auto result = tracks().update([this, id, position, it, &insert_position](TrackList::Container& container)
697+ {
698+ container.insert(it, id);
699+ // Make sure the next insert position is after the current insert position
700+ // Update the Track::Id after which to insert the next one from uris
701+ insert_position = id;
702+
703+ return true;
704+ });
705+
706+ if (result)
707+ {
708+ d->updateCachedTrackMetadata(id, uri);
709+
710+ if (d->shuffle)
711+ d->shuffled_tracks.insert(d->get_shuffled_insert_it(), id);
712+
713+ // Signal to the client that the current track has changed for the first track added to the TrackList
714+ if (tracks().get().size() == 1)
715+ current_id = id;
716+ }
717+ }
718+
719+ set_current_track(current);
720+
721+ std::cout << "Signaling that we just added " << tmp.size() << " tracks to the TrackList" << std::endl;
722+ on_tracks_added()(tmp);
723+
724+ if (!current_id.empty())
725+ on_track_changed()(current_id);
726+}
727+
728+bool media::TrackListImplementation::move_track(const media::Track::Id& id,
729+ const media::Track::Id& to)
730+{
731+ std::cout << __PRETTY_FUNCTION__ << std::endl;
732+
733+ std::cout << "-----------------------------------------------------" << std::endl;
734+ if (id.empty() or to.empty())
735+ {
736+ std::cerr << "Can't move track since 'id' or 'to' are empty" << std::endl;
737+ return false;
738+ }
739+
740+ if (id == to)
741+ {
742+ std::cerr << "Can't move track to it's same position" << std::endl;
743+ return false;
744+ }
745+
746+ if (tracks().get().size() == 1)
747+ {
748+ std::cerr << "Can't move track since TrackList contains only one track" << std::endl;
749+ return false;
750+ }
751+
752+ bool ret = false;
753+ const media::Track::Id current_id = *current_iterator();
754+ std::cout << "current_track id: " << current_id << std::endl;
755+ // Get an iterator that points to the track that is the insertion point
756+ auto insert_point_it = std::find(tracks().get().begin(), tracks().get().end(), to);
757+ if (insert_point_it != tracks().get().end())
758+ {
759+ const auto result = tracks().update([this, id, to, current_id, &insert_point_it]
760+ (TrackList::Container& container)
761+ {
762+ // Get an iterator that points to the track to move within the TrackList
763+ auto to_move_it = std::find(tracks().get().begin(), tracks().get().end(), id);
764+ std::cout << "Erasing old track position: " << *to_move_it << std::endl;
765+ if (to_move_it != tracks().get().end())
766+ {
767+ container.erase(to_move_it);
768+ }
769+ else
770+ {
771+ throw media::TrackList::Errors::FailedToFindMoveTrackDest
772+ ("Failed to find destination track " + to);
773+ }
774+
775+ // Insert id at the location just before insert_point_it
776+ container.insert(insert_point_it, id);
777+
778+ const auto new_current_track_it = std::find(tracks().get().begin(), tracks().get().end(), current_id);
779+ if (new_current_track_it != tracks().get().end())
780+ {
781+ const bool r = update_current_iterator(new_current_track_it);
782+ if (!r)
783+ {
784+ throw media::TrackList::Errors::FailedToMoveTrack();
785+ }
786+ std::cout << "*** Updated current_iterator, id: " << *current_iterator() << std::endl;
787+ }
788+ else
789+ {
790+ std::cerr << "Can't update current_iterator - failed to find track after move" << std::endl;
791+ throw media::TrackList::Errors::FailedToMoveTrack();
792+ }
793+
794+ return true;
795+ });
796+
797+ if (result)
798+ {
799+ std::cout << "TrackList after move" << std::endl;
800+ for(auto track : tracks().get())
801+ {
802+ std::cout << track << std::endl;
803+ }
804+ const media::TrackList::TrackIdTuple ids = std::make_tuple(id, to);
805+ // Signal to the client that track 'id' was moved within the TrackList
806+ on_track_moved()(ids);
807+ ret = true;
808+ }
809+ }
810+ else
811+ {
812+ throw media::TrackList::Errors::FailedToFindMoveTrackSource
813+ ("Failed to find source track " + id);
814+ }
815+
816+ std::cout << "-----------------------------------------------------" << std::endl;
817+
818+ return ret;
819 }
820
821 void media::TrackListImplementation::remove_track(const media::Track::Id& id)
822 {
823- auto result = tracks().update([id](TrackList::Container& container)
824+ const auto result = tracks().update([id](TrackList::Container& container)
825 {
826 container.erase(std::find(container.begin(), container.end(), id));
827 return true;
828@@ -156,85 +332,63 @@
829 {
830 d->meta_data_cache.erase(id);
831
832+ if (d->shuffle)
833+ d->shuffled_tracks.erase(find(d->shuffled_tracks.begin(),
834+ d->shuffled_tracks.end(), id));
835+
836 on_track_removed()(id);
837+
838+ // Make sure playback stops if all tracks were removed
839+ if (tracks().get().empty())
840+ on_end_of_tracklist()();
841 }
842-
843 }
844
845-void media::TrackListImplementation::go_to(const media::Track::Id& track, bool toggle_player_state)
846+void media::TrackListImplementation::go_to(const media::Track::Id& track)
847 {
848 std::cout << __PRETTY_FUNCTION__ << std::endl;
849- std::pair<const media::Track::Id, bool> p = std::make_pair(track, toggle_player_state);
850 // Signal the Player instance to go to a specific track for playback
851- on_go_to_track()(p);
852+ on_go_to_track()(track);
853 on_track_changed()(track);
854 }
855
856-void media::TrackListImplementation::shuffle_tracks()
857-{
858- std::cout << __PRETTY_FUNCTION__ << std::endl;
859-
860- if (tracks().get().empty())
861- return;
862-
863- auto result = tracks().update([this](TrackList::Container& container)
864- {
865- // Save off the original TrackList ordering
866- d->original_tracklist.assign(container.begin(), container.end());
867- std::random_shuffle(container.begin(), container.end());
868- return true;
869- });
870-
871- if (result)
872- {
873- media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())};
874- on_track_list_replaced()(t);
875- }
876-}
877-
878-void media::TrackListImplementation::unshuffle_tracks()
879-{
880- std::cout << __PRETTY_FUNCTION__ << std::endl;
881-
882- if (tracks().get().empty() or d->original_tracklist.empty())
883- return;
884-
885- auto result = tracks().update([this](TrackList::Container& container)
886- {
887- // Restore the original TrackList ordering
888- container.assign(d->original_tracklist.begin(), d->original_tracklist.end());
889- return true;
890- });
891-
892- if (result)
893- {
894- media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())};
895- on_track_list_replaced()(t);
896- }
897+void media::TrackListImplementation::set_shuffle(bool shuffle)
898+{
899+ d->shuffle = shuffle;
900+
901+ if (shuffle) {
902+ d->shuffled_tracks = tracks().get();
903+ random_shuffle(d->shuffled_tracks.begin(), d->shuffled_tracks.end());
904+ }
905+}
906+
907+bool media::TrackListImplementation::shuffle()
908+{
909+ return d->shuffle;
910+}
911+
912+const media::TrackList::Container& media::TrackListImplementation::shuffled_tracks()
913+{
914+ return d->shuffled_tracks;
915 }
916
917 void media::TrackListImplementation::reset()
918 {
919+ std::cout << __PRETTY_FUNCTION__ << std::endl;
920+
921 // Make sure playback stops
922 on_end_of_tracklist()();
923 // And make sure there is no "current" track
924 media::TrackListSkeleton::reset();
925
926- auto result = tracks().update([this](TrackList::Container& container)
927+ tracks().update([this](TrackList::Container& container)
928 {
929- TrackList::Container ids = container;
930-
931- for (auto it = container.begin(); it != container.end(); ) {
932- auto id = *it;
933- it = container.erase(it);
934- on_track_removed()(id);
935- }
936-
937- container.resize(0);
938+ container.clear();
939+ on_track_list_reset()();
940+
941 d->track_counter = 0;
942+ d->shuffled_tracks.clear();
943
944 return true;
945 });
946-
947- (void) result;
948 }
949
950=== modified file 'src/core/media/track_list_implementation.h'
951--- src/core/media/track_list_implementation.h 2015-05-22 21:19:28 +0000
952+++ src/core/media/track_list_implementation.h 2015-12-23 14:19:01 +0000
953@@ -43,11 +43,14 @@
954 Track::MetaData query_meta_data_for_track(const Track::Id& id);
955
956 void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current);
957+ void add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position);
958+ bool move_track(const Track::Id& id, const Track::Id& to);
959 void remove_track(const Track::Id& id);
960
961- void go_to(const Track::Id& track, bool toggle_player_state);
962- void shuffle_tracks();
963- void unshuffle_tracks();
964+ void go_to(const Track::Id& track);
965+ void set_shuffle(bool shuffle);
966+ bool shuffle();
967+ const media::TrackList::Container& shuffled_tracks();
968 void reset();
969
970 private:
971
972=== modified file 'src/core/media/track_list_skeleton.cpp'
973--- src/core/media/track_list_skeleton.cpp 2015-09-28 15:31:46 +0000
974+++ src/core/media/track_list_skeleton.cpp 2015-12-23 14:19:01 +0000
975@@ -38,10 +38,14 @@
976
977 #include <iostream>
978 #include <limits>
979+#include <sstream>
980+#include <cstdint>
981
982 namespace dbus = core::dbus;
983 namespace media = core::ubuntu::media;
984
985+using namespace std;
986+
987 struct media::TrackListSkeleton::Private
988 {
989 Private(media::TrackListSkeleton* impl, const dbus::Bus::Ptr& bus, const dbus::Object::Ptr& object,
990@@ -56,11 +60,16 @@
991 current_track(skeleton.properties.tracks->get().begin()),
992 empty_iterator(skeleton.properties.tracks->get().begin()),
993 loop_status(media::Player::LoopStatus::none),
994+ current_position(0),
995+ id_after_remove(),
996 signals
997 {
998 skeleton.signals.track_added,
999+ skeleton.signals.tracks_added,
1000+ skeleton.signals.track_moved,
1001 skeleton.signals.track_removed,
1002 skeleton.signals.track_changed,
1003+ skeleton.signals.track_list_reset,
1004 skeleton.signals.tracklist_replaced
1005 }
1006 {
1007@@ -93,9 +102,12 @@
1008 void handle_add_track_with_uri_at(const core::dbus::Message::Ptr& msg)
1009 {
1010 std::cout << "*** " << __PRETTY_FUNCTION__ << std::endl;
1011- request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context)
1012+ request_context_resolver->resolve_context_for_dbus_name_async
1013+ (msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context)
1014 {
1015- Track::UriType uri; media::Track::Id after; bool make_current;
1016+ Track::UriType uri;
1017+ media::Track::Id after;
1018+ bool make_current;
1019 msg->reader() >> uri >> after >> make_current;
1020
1021 // Make sure the client has adequate apparmor permissions to open the URI
1022@@ -104,13 +116,109 @@
1023 auto reply = dbus::Message::make_method_return(msg);
1024 // Only add the track to the TrackList if it passes the apparmor permissions check
1025 if (std::get<0>(result))
1026+ {
1027 impl->add_track_with_uri_at(uri, after, make_current);
1028- else
1029- std::cerr << "Warning: Not adding track " << uri <<
1030- " to TrackList because of inadequate client apparmor permissions." << std::endl;
1031-
1032- bus->send(reply);
1033- });
1034+ }
1035+ else
1036+ {
1037+ const std::string err_str = {"Warning: Not adding track " + uri +
1038+ " to TrackList because of inadequate client apparmor permissions."};
1039+ std::cerr << err_str << std::endl;
1040+ reply = dbus::Message::make_error(
1041+ msg,
1042+ mpris::TrackList::Error::InsufficientPermissionsToAddTrack::name,
1043+ err_str);
1044+ }
1045+
1046+ bus->send(reply);
1047+ });
1048+ }
1049+
1050+ void handle_add_tracks_with_uri_at(const core::dbus::Message::Ptr& msg)
1051+ {
1052+ std::cout << "*** " << __PRETTY_FUNCTION__ << std::endl;
1053+ request_context_resolver->resolve_context_for_dbus_name_async
1054+ (msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context)
1055+ {
1056+ ContainerURI uris;
1057+ media::Track::Id after;
1058+ msg->reader() >> uris >> after;
1059+
1060+ media::apparmor::ubuntu::RequestAuthenticator::Result result;
1061+ std::string err_str;
1062+ for (const auto uri : uris)
1063+ {
1064+ // Make sure the client has adequate apparmor permissions to open the URI
1065+ result = request_authenticator->authenticate_open_uri_request(context, uri);
1066+ if (not std::get<0>(result))
1067+ {
1068+ err_str = {"Warning: Not adding track " + uri +
1069+ " to TrackList because of inadequate client apparmor permissions."};
1070+ break;
1071+ }
1072+ }
1073+
1074+ core::dbus::Message::Ptr reply;
1075+ // Only add the track to the TrackList if it passes the apparmor permissions check
1076+ if (std::get<0>(result))
1077+ {
1078+ reply = dbus::Message::make_method_return(msg);
1079+ impl->add_tracks_with_uri_at(uris, after);
1080+ }
1081+ else
1082+ {
1083+ std::cerr << err_str << std::endl;
1084+ reply = dbus::Message::make_error(
1085+ msg,
1086+ mpris::TrackList::Error::InsufficientPermissionsToAddTrack::name,
1087+ err_str);
1088+ }
1089+
1090+ bus->send(reply);
1091+ });
1092+ }
1093+
1094+ void handle_move_track(const core::dbus::Message::Ptr& msg)
1095+ {
1096+ media::Track::Id id;
1097+ media::Track::Id to;
1098+ msg->reader() >> id >> to;
1099+
1100+ core::dbus::Message::Ptr reply;
1101+ try {
1102+ const bool ret = impl->move_track(id, to);
1103+ if (!ret)
1104+ {
1105+ const std::string err_str = {"Error: Not moving track " + id +
1106+ " to destination " + to};
1107+ std::cerr << err_str << std::endl;
1108+ reply = dbus::Message::make_error(
1109+ msg,
1110+ mpris::TrackList::Error::FailedToMoveTrack::name,
1111+ err_str);
1112+ }
1113+ else
1114+ {
1115+ reply = dbus::Message::make_method_return(msg);
1116+ }
1117+ } catch(media::TrackList::Errors::FailedToMoveTrack& e) {
1118+ reply = dbus::Message::make_error(
1119+ msg,
1120+ mpris::TrackList::Error::FailedToFindMoveTrackSource::name,
1121+ e.what());
1122+ } catch(media::TrackList::Errors::FailedToFindMoveTrackSource& e) {
1123+ reply = dbus::Message::make_error(
1124+ msg,
1125+ mpris::TrackList::Error::FailedToFindMoveTrackSource::name,
1126+ e.what());
1127+ } catch(media::TrackList::Errors::FailedToFindMoveTrackDest& e) {
1128+ reply = dbus::Message::make_error(
1129+ msg,
1130+ mpris::TrackList::Error::FailedToFindMoveTrackDest::name,
1131+ e.what());
1132+ }
1133+
1134+ bus->send(reply);
1135 }
1136
1137 void handle_remove_track(const core::dbus::Message::Ptr& msg)
1138@@ -118,8 +226,63 @@
1139 media::Track::Id track;
1140 msg->reader() >> track;
1141
1142+ auto id_it = find(impl->tracks().get().begin(), impl->tracks().get().end(), track);
1143+ if (id_it == impl->tracks().get().end()) {
1144+ ostringstream err_str;
1145+ err_str << "Track " << track << " not found in track list";
1146+ cout << __PRETTY_FUNCTION__ << " WARNING " << err_str.str() << endl;
1147+ auto reply = dbus::Message::make_error(
1148+ msg,
1149+ mpris::TrackList::Error::TrackNotFound::name,
1150+ err_str.str());
1151+ bus->send(reply);
1152+ return;
1153+ }
1154+
1155+ media::Track::Id next;
1156+ bool deleting_current = false;
1157+
1158+ if (id_it == impl->current_iterator())
1159+ {
1160+ cout << "Removing current track" << endl;
1161+ deleting_current = true;
1162+
1163+ if (current_track != empty_iterator)
1164+ {
1165+ ++current_track;
1166+
1167+ if (current_track == impl->tracks().get().end()
1168+ && loop_status == media::Player::LoopStatus::playlist)
1169+ {
1170+ // Removed the last track, current is the first track and make sure that
1171+ // the player starts playing it
1172+ current_track = impl->tracks().get().begin();
1173+ }
1174+
1175+ if (current_track == impl->tracks().get().end())
1176+ {
1177+ current_track = empty_iterator;
1178+ // Nothing else to play, stop playback
1179+ impl->emit_on_end_of_tracklist();
1180+ }
1181+ else
1182+ {
1183+ next = *current_track;
1184+ }
1185+ }
1186+ }
1187+ else if (current_track != empty_iterator)
1188+ {
1189+ next = *current_track;
1190+ }
1191+ id_after_remove = next;
1192+
1193+ // Calls reset_current_iterator_if_needed(), which updates the iterator
1194 impl->remove_track(track);
1195
1196+ if ((not next.empty()) and deleting_current)
1197+ impl->go_to(next);
1198+
1199 auto reply = dbus::Message::make_method_return(msg);
1200 bus->send(reply);
1201 }
1202@@ -130,8 +293,7 @@
1203 msg->reader() >> track;
1204
1205 current_track = std::find(skeleton.properties.tracks->get().begin(), skeleton.properties.tracks->get().end(), track);
1206- const bool toggle_player_state = true;
1207- impl->go_to(track, toggle_player_state);
1208+ impl->go_to(track);
1209
1210 auto reply = dbus::Message::make_method_return(msg);
1211 bus->send(reply);
1212@@ -155,17 +317,28 @@
1213 TrackList::ConstIterator current_track;
1214 TrackList::ConstIterator empty_iterator;
1215 media::Player::LoopStatus loop_status;
1216+ uint64_t current_position;
1217+ media::Track::Id id_after_remove;
1218
1219 struct Signals
1220 {
1221 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal;
1222+ typedef core::dbus::Signal<mpris::TrackList::Signals::TracksAdded, mpris::TrackList::Signals::TracksAdded::ArgumentType> DBusTracksAddedSignal;
1223+ typedef core::dbus::Signal<mpris::TrackList::Signals::TrackMoved, mpris::TrackList::Signals::TrackMoved::ArgumentType> DBusTrackMovedSignal;
1224 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal;
1225 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType> DBusTrackChangedSignal;
1226+ typedef core::dbus::Signal<
1227+ mpris::TrackList::Signals::TrackListReset,
1228+ mpris::TrackList::Signals::TrackListReset::ArgumentType>
1229+ DBusTrackListResetSignal;
1230 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal;
1231
1232 Signals(const std::shared_ptr<DBusTrackAddedSignal>& remote_track_added,
1233+ const std::shared_ptr<DBusTracksAddedSignal>& remote_tracks_added,
1234+ const std::shared_ptr<DBusTrackMovedSignal>& remote_track_moved,
1235 const std::shared_ptr<DBusTrackRemovedSignal>& remote_track_removed,
1236 const std::shared_ptr<DBusTrackChangedSignal>& remote_track_changed,
1237+ const std::shared_ptr<DBusTrackListResetSignal>& remote_track_list_reset,
1238 const std::shared_ptr<DBusTrackListReplacedSignal>& remote_track_list_replaced)
1239 {
1240 // Connect all of the MPRIS interface signals to be emitted over dbus
1241@@ -174,11 +347,26 @@
1242 remote_track_added->emit(id);
1243 });
1244
1245+ on_tracks_added.connect([remote_tracks_added](const media::TrackList::ContainerURI &tracks)
1246+ {
1247+ remote_tracks_added->emit(tracks);
1248+ });
1249+
1250+ on_track_moved.connect([remote_track_moved](const media::TrackList::TrackIdTuple &ids)
1251+ {
1252+ remote_track_moved->emit(ids);
1253+ });
1254+
1255 on_track_removed.connect([remote_track_removed](const media::Track::Id &id)
1256 {
1257 remote_track_removed->emit(id);
1258 });
1259
1260+ on_track_list_reset.connect([remote_track_list_reset]()
1261+ {
1262+ remote_track_list_reset->emit();
1263+ });
1264+
1265 on_track_changed.connect([remote_track_changed](const media::Track::Id &id)
1266 {
1267 remote_track_changed->emit(id);
1268@@ -191,10 +379,13 @@
1269 }
1270
1271 core::Signal<Track::Id> on_track_added;
1272+ core::Signal<TrackList::ContainerURI> on_tracks_added;
1273+ core::Signal<TrackList::TrackIdTuple> on_track_moved;
1274 core::Signal<Track::Id> on_track_removed;
1275+ core::Signal<void> on_track_list_reset;
1276 core::Signal<Track::Id> on_track_changed;
1277 core::Signal<TrackList::ContainerTrackIdTuple> on_track_list_replaced;
1278- core::Signal<std::pair<Track::Id, bool>> on_go_to_track;
1279+ core::Signal<Track::Id> on_go_to_track;
1280 core::Signal<void> on_end_of_tracklist;
1281 } signals;
1282 };
1283@@ -219,6 +410,16 @@
1284 std::ref(d),
1285 std::placeholders::_1));
1286
1287+ d->object->install_method_handler<mpris::TrackList::AddTracks>(
1288+ std::bind(&Private::handle_add_tracks_with_uri_at,
1289+ std::ref(d),
1290+ std::placeholders::_1));
1291+
1292+ d->object->install_method_handler<mpris::TrackList::MoveTrack>(
1293+ std::bind(&Private::handle_move_track,
1294+ std::ref(d),
1295+ std::placeholders::_1));
1296+
1297 d->object->install_method_handler<mpris::TrackList::RemoveTrack>(
1298 std::bind(&Private::handle_remove_track,
1299 std::ref(d),
1300@@ -239,6 +440,11 @@
1301 {
1302 }
1303
1304+/*
1305+ * NOTE We do not consider the loop status in this function due to the use of it
1306+ * we do in TrackListSkeleton::next() (the function is used to know whether we
1307+ * need to wrap when looping is active).
1308+ */
1309 bool media::TrackListSkeleton::has_next()
1310 {
1311 const auto n_tracks = tracks().get().size();
1312@@ -252,37 +458,46 @@
1313 // changed in player_implementation.cpp.
1314 // To avoid the crash we consider that current_track will be eventually
1315 // initialized to the first track when current_iterator() gets called.
1316- if (d->current_track == d->empty_iterator) {
1317+ if (d->current_track == d->empty_iterator)
1318+ {
1319 if (n_tracks < 2)
1320 return false;
1321 else
1322 return true;
1323 }
1324
1325- const auto next_track = std::next(current_iterator());
1326- return !is_last_track(next_track);
1327+ if (shuffle())
1328+ {
1329+ auto it = get_current_shuffled();
1330+ return ++it != shuffled_tracks().end();
1331+ }
1332+ else
1333+ {
1334+ const auto next_track = std::next(current_iterator());
1335+ return !is_last_track(next_track);
1336+ }
1337 }
1338
1339+/*
1340+ * NOTE We do not consider the loop status in this function due to the use of it
1341+ * we do in TrackListSkeleton::previous() (the function is used to know whether we
1342+ * need to wrap when looping is active).
1343+ */
1344 bool media::TrackListSkeleton::has_previous()
1345 {
1346 if (tracks().get().empty() || d->current_track == d->empty_iterator)
1347 return false;
1348
1349- // If we are looping over the entire list, then there is always a previous track
1350- if (d->loop_status == media::Player::LoopStatus::playlist)
1351- return true;
1352-
1353- return d->current_track != std::begin(tracks().get());
1354-}
1355-
1356-bool media::TrackListSkeleton::is_first_track(const ConstIterator &it)
1357-{
1358- return it == std::begin(tracks().get());
1359-}
1360-
1361-bool media::TrackListSkeleton::is_last_track(const TrackList::ConstIterator &it)
1362-{
1363- return it == std::end(tracks().get());
1364+ if (shuffle())
1365+ return get_current_shuffled() != shuffled_tracks().begin();
1366+ else
1367+ return d->current_track != std::begin(tracks().get());
1368+}
1369+
1370+media::TrackList::ConstIterator media::TrackListSkeleton::get_current_shuffled()
1371+{
1372+ auto current_id = *current_iterator();
1373+ return find(shuffled_tracks().begin(), shuffled_tracks().end(), current_id);
1374 }
1375
1376 media::Track::Id media::TrackListSkeleton::next()
1377@@ -294,50 +509,67 @@
1378 return media::Track::Id{};
1379 }
1380
1381- const auto next_track = std::next(current_iterator());
1382- bool do_go_to_next_track = false;
1383+ bool go_to_track = false;
1384
1385 // End of the track reached so loop around to the beginning of the track
1386 if (d->loop_status == media::Player::LoopStatus::track)
1387 {
1388 std::cout << "Looping on the current track since LoopStatus is set to track" << std::endl;
1389- do_go_to_next_track = true;
1390+ go_to_track = true;
1391 }
1392 // End of the tracklist reached so loop around to the beginning of the tracklist
1393 else if (d->loop_status == media::Player::LoopStatus::playlist && not has_next())
1394 {
1395 std::cout << "Looping on the tracklist since LoopStatus is set to playlist" << std::endl;
1396- d->current_track = tracks().get().begin();
1397- do_go_to_next_track = true;
1398+
1399+ if (shuffle())
1400+ {
1401+ const auto id = *shuffled_tracks().begin();
1402+ set_current_track(id);
1403+ }
1404+ else
1405+ {
1406+ d->current_track = tracks().get().begin();
1407+ }
1408+ go_to_track = true;
1409 }
1410 else
1411 {
1412- // Next track is not the last track
1413- if (not is_last_track(next_track))
1414+ if (shuffle())
1415 {
1416- std::cout << "Advancing to next track: " << *(next_track) << std::endl;
1417- d->current_track = next_track;
1418- do_go_to_next_track = true;
1419+ auto it = get_current_shuffled();
1420+ if (++it != shuffled_tracks().end()) {
1421+ cout << "Advancing to next track: " << *it << endl;
1422+ set_current_track(*it);
1423+ go_to_track = true;
1424+ }
1425 }
1426- // At the end of the tracklist and not set to loop, so we stop advancing the tracklist
1427 else
1428 {
1429- std::cout << "End of tracklist reached, not advancing to next since LoopStatus is set to none" << std::endl;
1430- on_end_of_tracklist()();
1431+ const auto it = std::next(current_iterator());
1432+ if (not is_last_track(it))
1433+ {
1434+ cout << "Advancing to next track: " << *it << endl;
1435+ d->current_track = it;
1436+ go_to_track = true;
1437+ }
1438 }
1439+
1440 }
1441
1442- if (do_go_to_next_track)
1443+ if (go_to_track)
1444 {
1445+ cout << "next track id is " << *(current_iterator()) << endl;
1446 on_track_changed()(*(current_iterator()));
1447- // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track()
1448- // since this breaks video playback when using open_uri() (stop() and play() are unwanted in
1449- // this scenario since the qtubuntu-media will handle this automatically)
1450- const bool toggle_player_state = false;
1451 const media::Track::Id id = *(current_iterator());
1452- const std::pair<const media::Track::Id, bool> p = std::make_pair(id, toggle_player_state);
1453 // Signal the PlayerImplementation to play the next track
1454- on_go_to_track()(p);
1455+ on_go_to_track()(id);
1456+ }
1457+ else
1458+ {
1459+ // At the end of the tracklist and not set to loop
1460+ cout << "End of tracklist reached" << endl;
1461+ on_end_of_tracklist()();
1462 }
1463
1464 return *(current_iterator());
1465@@ -352,48 +584,69 @@
1466 return media::Track::Id{};
1467 }
1468
1469- bool do_go_to_previous_track = false;
1470+ bool go_to_track = false;
1471+ // Position is measured in nanoseconds
1472+ const uint64_t max_position = 5 * UINT64_C(1000000000);
1473
1474+ // If we're playing the current track for > max_position time then
1475+ // repeat it from the beginning
1476+ if (d->current_position > max_position)
1477+ {
1478+ std::cout << "Repeating current track..." << std::endl;
1479+ go_to_track = true;
1480+ }
1481 // Loop on the current track forever
1482- if (d->loop_status == media::Player::LoopStatus::track)
1483+ else if (d->loop_status == media::Player::LoopStatus::track)
1484 {
1485 std::cout << "Looping on the current track..." << std::endl;
1486- do_go_to_previous_track = true;
1487+ go_to_track = true;
1488 }
1489 // Loop over the whole playlist and repeat
1490- else if (d->loop_status == media::Player::LoopStatus::playlist && is_first_track(current_iterator()))
1491+ else if (d->loop_status == media::Player::LoopStatus::playlist && not has_previous())
1492 {
1493 std::cout << "Looping on the entire TrackList..." << std::endl;
1494- d->current_track = std::prev(tracks().get().end());
1495- do_go_to_previous_track = true;
1496+
1497+ if (shuffle())
1498+ {
1499+ const auto id = *std::prev(shuffled_tracks().end());
1500+ set_current_track(id);
1501+ }
1502+ else
1503+ {
1504+ d->current_track = std::prev(tracks().get().end());
1505+ }
1506+
1507+ go_to_track = true;
1508 }
1509 else
1510 {
1511- // Current track is not the first track
1512- if (not is_first_track(current_iterator()))
1513+ if (shuffle())
1514+ {
1515+ auto it = get_current_shuffled();
1516+ if (it != shuffled_tracks().begin()) {
1517+ set_current_track(*(--it));
1518+ go_to_track = true;
1519+ }
1520+ }
1521+ else if (not is_first_track(current_iterator()))
1522 {
1523 // Keep returning the previous track until the first track is reached
1524 d->current_track = std::prev(current_iterator());
1525- do_go_to_previous_track = true;
1526- }
1527- // At the beginning of the tracklist and not set to loop, so we stop advancing the tracklist
1528- else
1529- {
1530- std::cout << "Beginning of tracklist reached, not advancing to previous since LoopStatus is set to none" << std::endl;
1531- on_end_of_tracklist()();
1532+ go_to_track = true;
1533 }
1534 }
1535
1536- if (do_go_to_previous_track)
1537+ if (go_to_track)
1538 {
1539 on_track_changed()(*(current_iterator()));
1540- // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track()
1541- // since this breaks video playback when using open_uri() (stop() and play() are unwanted in
1542- // this scenario since the qtubuntu-media will handle this automatically)
1543- const bool toggle_player_state = false;
1544 const media::Track::Id id = *(current_iterator());
1545- const std::pair<const media::Track::Id, bool> p = std::make_pair(id, toggle_player_state);
1546- on_go_to_track()(p);
1547+ on_go_to_track()(id);
1548+ }
1549+ else
1550+ {
1551+ // At the beginning of the tracklist and not set to loop
1552+ cout << "Beginning of tracklist reached" << endl;
1553+ on_end_of_tracklist()();
1554 }
1555
1556 return *(current_iterator());
1557@@ -421,14 +674,45 @@
1558 return d->current_track;
1559 }
1560
1561+bool media::TrackListSkeleton::update_current_iterator(const TrackList::ConstIterator &it)
1562+{
1563+ std::cout << __PRETTY_FUNCTION__ << std::endl;
1564+ if (it == tracks().get().end())
1565+ return false;
1566+
1567+ std::cout << "Updating current_track iterator" << std::endl;
1568+ d->current_track = it;
1569+
1570+ return true;
1571+}
1572+
1573 void media::TrackListSkeleton::reset_current_iterator_if_needed()
1574 {
1575- // If all tracks got removed then we need to keep a sane current
1576- // iterator for further use.
1577- if (tracks().get().empty())
1578+ d->current_track = find(tracks().get().begin(), tracks().get().end(), d->id_after_remove);
1579+ if (d->current_track == tracks().get().end())
1580 d->current_track = d->empty_iterator;
1581 }
1582
1583+media::Track::Id media::TrackListSkeleton::get_current_track(void)
1584+{
1585+ if (d->current_track == d->empty_iterator || tracks().get().empty())
1586+ return media::Track::Id{};
1587+
1588+ return *(current_iterator());
1589+}
1590+
1591+void media::TrackListSkeleton::set_current_track(const media::Track::Id& id)
1592+{
1593+ const auto id_it = find(tracks().get().begin(), tracks().get().end(), id);
1594+ if (id_it != tracks().get().end())
1595+ d->current_track = id_it;
1596+}
1597+
1598+void media::TrackListSkeleton::emit_on_end_of_tracklist()
1599+{
1600+ on_end_of_tracklist()();
1601+}
1602+
1603 const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const
1604 {
1605 return *d->skeleton.properties.can_edit_tracks;
1606@@ -444,6 +728,11 @@
1607 return *d->skeleton.properties.tracks;
1608 }
1609
1610+void media::TrackListSkeleton::on_position_changed(uint64_t position)
1611+{
1612+ d->current_position = position;
1613+}
1614+
1615 void media::TrackListSkeleton::on_loop_status_changed(const media::Player::LoopStatus& loop_status)
1616 {
1617 d->loop_status = loop_status;
1618@@ -456,21 +745,9 @@
1619
1620 void media::TrackListSkeleton::on_shuffle_changed(bool shuffle)
1621 {
1622- if (shuffle)
1623- shuffle_tracks();
1624- else
1625- {
1626- // Save the current Track::Id of what's currently playing to restore after unshuffle
1627- const media::Track::Id current_id = *(current_iterator());
1628-
1629- unshuffle_tracks();
1630-
1631- // Since we use assign() in unshuffle_tracks, which invalidates existing iterators, we need
1632- // to make sure that current is pointing to the right place
1633- auto it = std::find(tracks().get().begin(), tracks().get().end(), current_id);
1634- if (it != tracks().get().end())
1635- d->current_track = it;
1636- }
1637+ cout << __PRETTY_FUNCTION__ << endl;
1638+
1639+ set_shuffle(shuffle);
1640 }
1641
1642 const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const
1643@@ -490,17 +767,32 @@
1644 return d->signals.on_track_added;
1645 }
1646
1647+const core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added() const
1648+{
1649+ return d->signals.on_tracks_added;
1650+}
1651+
1652+const core::Signal<media::TrackList::TrackIdTuple>& media::TrackListSkeleton::on_track_moved() const
1653+{
1654+ return d->signals.on_track_moved;
1655+}
1656+
1657 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const
1658 {
1659 return d->signals.on_track_removed;
1660 }
1661
1662+const core::Signal<void>& media::TrackListSkeleton::on_track_list_reset() const
1663+{
1664+ return d->signals.on_track_list_reset;
1665+}
1666+
1667 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() const
1668 {
1669 return d->signals.on_track_changed;
1670 }
1671
1672-const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track() const
1673+const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_go_to_track() const
1674 {
1675 return d->signals.on_go_to_track;
1676 }
1677@@ -520,17 +812,32 @@
1678 return d->signals.on_track_added;
1679 }
1680
1681+core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added()
1682+{
1683+ return d->signals.on_tracks_added;
1684+}
1685+
1686+core::Signal<media::TrackList::TrackIdTuple>& media::TrackListSkeleton::on_track_moved()
1687+{
1688+ return d->signals.on_track_moved;
1689+}
1690+
1691 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed()
1692 {
1693 return d->signals.on_track_removed;
1694 }
1695
1696+core::Signal<void>& media::TrackListSkeleton::on_track_list_reset()
1697+{
1698+ return d->signals.on_track_list_reset;
1699+}
1700+
1701 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed()
1702 {
1703 return d->signals.on_track_changed;
1704 }
1705
1706-core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track()
1707+core::Signal<media::Track::Id>& media::TrackListSkeleton::on_go_to_track()
1708 {
1709 return d->signals.on_go_to_track;
1710 }
1711
1712=== modified file 'src/core/media/track_list_skeleton.h'
1713--- src/core/media/track_list_skeleton.h 2015-09-28 13:20:01 +0000
1714+++ src/core/media/track_list_skeleton.h 2015-12-23 14:19:01 +0000
1715@@ -54,31 +54,51 @@
1716 core::Signal<ContainerTrackIdTuple>& on_track_list_replaced();
1717 const core::Signal<Track::Id>& on_track_added() const;
1718 core::Signal<Track::Id>& on_track_added();
1719+ const core::Signal<ContainerURI>& on_tracks_added() const;
1720+ core::Signal<ContainerURI>& on_tracks_added();
1721+ const core::Signal<TrackIdTuple>& on_track_moved() const;
1722+ core::Signal<TrackIdTuple>& on_track_moved();
1723 const core::Signal<Track::Id>& on_track_removed() const;
1724+ const core::Signal<void>& on_track_list_reset() const;
1725 const core::Signal<Track::Id>& on_track_changed() const;
1726 core::Signal<Track::Id>& on_track_changed();
1727- const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const;
1728- core::Signal<std::pair<Track::Id, bool>>& on_go_to_track();
1729+ const core::Signal<Track::Id>& on_go_to_track() const;
1730+ core::Signal<Track::Id>& on_go_to_track();
1731 const core::Signal<void>& on_end_of_tracklist() const;
1732 core::Signal<void>& on_end_of_tracklist();
1733 core::Signal<Track::Id>& on_track_removed();
1734+ core::Signal<void>& on_track_list_reset();
1735
1736 core::Property<Container>& tracks();
1737 void on_loop_status_changed(const core::ubuntu::media::Player::LoopStatus& loop_status);
1738 core::ubuntu::media::Player::LoopStatus loop_status() const;
1739
1740+ void on_position_changed(uint64_t position);
1741+
1742 /** Gets called when the shuffle property on the Player interface is changed
1743 * by the client */
1744 void on_shuffle_changed(bool shuffle);
1745
1746+ virtual void set_shuffle(bool shuffle) = 0;
1747+ virtual bool shuffle() = 0;
1748+ virtual const media::TrackList::Container& shuffled_tracks() = 0;
1749+
1750 protected:
1751- inline bool is_first_track(const ConstIterator &it);
1752- inline bool is_last_track(const ConstIterator &it);
1753- inline const TrackList::ConstIterator& current_iterator();
1754+ inline bool is_first_track(const ConstIterator &it)
1755+ { return it == std::begin(tracks().get()); }
1756+ inline bool is_last_track(const ConstIterator &it)
1757+ { return it == std::end(tracks().get()); }
1758+ const TrackList::ConstIterator& current_iterator();
1759+ bool update_current_iterator(const TrackList::ConstIterator &it);
1760 void reset_current_iterator_if_needed();
1761+ media::Track::Id get_current_track(void);
1762+ void set_current_track(const media::Track::Id& id);
1763+ TrackList::ConstIterator get_current_shuffled();
1764
1765 core::Property<bool>& can_edit_tracks();
1766
1767+ void emit_on_end_of_tracklist();
1768+
1769 void reset();
1770
1771 private:
1772
1773=== modified file 'src/core/media/track_list_stub.cpp'
1774--- src/core/media/track_list_stub.cpp 2015-07-27 22:15:33 +0000
1775+++ src/core/media/track_list_stub.cpp 2015-12-23 14:19:01 +0000
1776@@ -52,7 +52,10 @@
1777 signals
1778 {
1779 object->get_signal<mpris::TrackList::Signals::TrackAdded>(),
1780+ object->get_signal<mpris::TrackList::Signals::TracksAdded>(),
1781+ object->get_signal<mpris::TrackList::Signals::TrackMoved>(),
1782 object->get_signal<mpris::TrackList::Signals::TrackRemoved>(),
1783+ object->get_signal<mpris::TrackList::Signals::TrackListReset>(),
1784 object->get_signal<mpris::TrackList::Signals::TrackListReplaced>(),
1785 object->get_signal<mpris::TrackList::Signals::TrackChanged>()
1786 }
1787@@ -70,22 +73,37 @@
1788 struct Signals
1789 {
1790 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal;
1791+ typedef core::dbus::Signal<mpris::TrackList::Signals::TracksAdded, mpris::TrackList::Signals::TracksAdded::ArgumentType> DBusTracksAddedSignal;
1792+ typedef core::dbus::Signal<mpris::TrackList::Signals::TrackMoved, mpris::TrackList::Signals::TrackMoved::ArgumentType> DBusTrackMovedSignal;
1793 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal;
1794+ typedef core::dbus::Signal<
1795+ mpris::TrackList::Signals::TrackListReset,
1796+ mpris::TrackList::Signals::TrackListReset::ArgumentType>
1797+ DBusTrackListResetSignal;
1798 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal;
1799 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType> DBusTrackChangedSignal;
1800
1801 Signals(const std::shared_ptr<DBusTrackAddedSignal>& track_added,
1802+ const std::shared_ptr<DBusTracksAddedSignal>& tracks_added,
1803+ const std::shared_ptr<DBusTrackMovedSignal>& track_moved,
1804 const std::shared_ptr<DBusTrackRemovedSignal>& track_removed,
1805+ const std::shared_ptr<DBusTrackListResetSignal>& track_list_reset,
1806 const std::shared_ptr<DBusTrackListReplacedSignal>& track_list_replaced,
1807 const std::shared_ptr<DBusTrackChangedSignal>& track_changed)
1808 : on_track_added(),
1809+ on_tracks_added(),
1810+ on_track_moved(),
1811 on_track_removed(),
1812+ on_track_list_reset(),
1813 on_track_list_replaced(),
1814 on_track_changed(),
1815 dbus
1816 {
1817 track_added,
1818+ tracks_added,
1819+ track_moved,
1820 track_removed,
1821+ track_list_reset,
1822 track_list_replaced,
1823 track_changed,
1824 }
1825@@ -96,15 +114,33 @@
1826 on_track_added(id);
1827 });
1828
1829+ dbus.on_tracks_added->connect([this](const media::TrackList::ContainerURI& tracks)
1830+ {
1831+ std::cout << "OnTracksAdded signal arrived via the bus." << std::endl;
1832+ on_tracks_added(tracks);
1833+ });
1834+
1835+ dbus.on_track_moved->connect([this](const media::TrackList::TrackIdTuple& ids)
1836+ {
1837+ std::cout << "OnTrackMoved signal arrived via the bus." << std::endl;
1838+ on_track_moved(ids);
1839+ });
1840+
1841 dbus.on_track_removed->connect([this](const Track::Id& id)
1842 {
1843 std::cout << "OnTrackRemoved signal arrived via the bus." << std::endl;
1844 on_track_removed(id);
1845 });
1846
1847+ dbus.on_track_list_reset->connect([this](void)
1848+ {
1849+ std::cout << "OnTrackListReset signal arrived via the bus." << std::endl;
1850+ on_track_list_reset();
1851+ });
1852+
1853 dbus.on_track_list_replaced->connect([this](const media::TrackList::ContainerTrackIdTuple& list)
1854 {
1855- std::cout << "OnTrackListRemoved signal arrived via the bus." << std::endl;
1856+ std::cout << "OnTrackListReplaced signal arrived via the bus." << std::endl;
1857 on_track_list_replaced(list);
1858 });
1859
1860@@ -116,16 +152,22 @@
1861 }
1862
1863 core::Signal<Track::Id> on_track_added;
1864+ core::Signal<media::TrackList::ContainerURI> on_tracks_added;
1865+ core::Signal<media::TrackList::TrackIdTuple> on_track_moved;
1866 core::Signal<Track::Id> on_track_removed;
1867+ core::Signal<void> on_track_list_reset;
1868 core::Signal<media::TrackList::ContainerTrackIdTuple> on_track_list_replaced;
1869 core::Signal<Track::Id> on_track_changed;
1870- core::Signal<std::pair<Track::Id, bool>> on_go_to_track;
1871+ core::Signal<Track::Id> on_go_to_track;
1872 core::Signal<void> on_end_of_tracklist;
1873
1874 struct DBus
1875 {
1876 std::shared_ptr<DBusTrackAddedSignal> on_track_added;
1877+ std::shared_ptr<DBusTracksAddedSignal> on_tracks_added;
1878+ std::shared_ptr<DBusTrackMovedSignal> on_track_moved;
1879 std::shared_ptr<DBusTrackRemovedSignal> on_track_removed;
1880+ std::shared_ptr<DBusTrackListResetSignal> on_track_list_reset;
1881 std::shared_ptr<DBusTrackListReplacedSignal> on_track_list_replaced;
1882 std::shared_ptr<DBusTrackChangedSignal> on_track_changed;
1883 } dbus;
1884@@ -184,16 +226,62 @@
1885
1886 void media::TrackListStub::add_track_with_uri_at(
1887 const media::Track::UriType& uri,
1888- const media::Track::Id& id,
1889+ const media::Track::Id& position,
1890 bool make_current)
1891 {
1892 auto op = d->object->invoke_method_synchronously<mpris::TrackList::AddTrack, void>(
1893 uri,
1894- id,
1895+ position,
1896 make_current);
1897
1898 if (op.is_error())
1899- throw std::runtime_error("Problem adding track: " + op.error());
1900+ {
1901+ if (op.error().name() ==
1902+ mpris::TrackList::Error::InsufficientPermissionsToAddTrack::name)
1903+ throw media::TrackList::Errors::InsufficientPermissionsToAddTrack{};
1904+ else
1905+ throw std::runtime_error{op.error().print()};
1906+ }
1907+}
1908+
1909+void media::TrackListStub::add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position)
1910+{
1911+ auto op = d->object->invoke_method_synchronously<mpris::TrackList::AddTracks, void>(
1912+ uris,
1913+ position);
1914+
1915+ if (op.is_error())
1916+ {
1917+ if (op.error().name() ==
1918+ mpris::TrackList::Error::InsufficientPermissionsToAddTrack::name)
1919+ throw media::TrackList::Errors::InsufficientPermissionsToAddTrack{};
1920+ else
1921+ throw std::runtime_error{op.error().print()};
1922+ }
1923+}
1924+
1925+bool media::TrackListStub::move_track(const media::Track::Id& id, const media::Track::Id& to)
1926+{
1927+ auto op = d->object->invoke_method_synchronously<mpris::TrackList::MoveTrack, void>(id, to);
1928+
1929+ if (op.is_error())
1930+ {
1931+ if (op.error().name() ==
1932+ mpris::TrackList::Error::FailedToMoveTrack::name)
1933+ throw media::TrackList::Errors::FailedToMoveTrack{};
1934+ else if (op.error().name() ==
1935+ mpris::TrackList::Error::FailedToFindMoveTrackSource::name)
1936+ throw media::TrackList::Errors::FailedToFindMoveTrackSource{op.error().print()};
1937+ else if (op.error().name() ==
1938+ mpris::TrackList::Error::FailedToFindMoveTrackDest::name)
1939+ throw media::TrackList::Errors::FailedToFindMoveTrackDest{op.error().print()};
1940+ else
1941+ throw std::runtime_error{op.error().print()};
1942+
1943+ return false;
1944+ }
1945+
1946+ return true;
1947 }
1948
1949 void media::TrackListStub::remove_track(const media::Track::Id& track)
1950@@ -202,14 +290,18 @@
1951 track);
1952
1953 if (op.is_error())
1954- throw std::runtime_error("Problem removing track: " + op.error());
1955+ {
1956+ if (op.error().name() ==
1957+ mpris::TrackList::Error::TrackNotFound::name)
1958+ throw media::TrackList::Errors::TrackNotFound{};
1959+ else
1960+ throw std::runtime_error{"Problem removing track: " + op.error().print()};
1961+ }
1962 }
1963
1964-void media::TrackListStub::go_to(const media::Track::Id& track, bool toggle_player_state)
1965+void media::TrackListStub::go_to(const media::Track::Id& track)
1966 {
1967- (void) toggle_player_state;
1968- auto op = d->object->invoke_method_synchronously<mpris::TrackList::GoTo, void>(
1969- track);
1970+ auto op = d->object->invoke_method_synchronously<mpris::TrackList::GoTo, void>(track);
1971
1972 if (op.is_error())
1973 throw std::runtime_error("Problem adding track: " + op.error());
1974@@ -227,16 +319,6 @@
1975 return media::Track::Id{"/empty/track/id"};
1976 }
1977
1978-void media::TrackListStub::shuffle_tracks()
1979-{
1980- std::cerr << "shuffle_tracks() does nothing from the client side" << std::endl;
1981-}
1982-
1983-void media::TrackListStub::unshuffle_tracks()
1984-{
1985- std::cerr << "unshuffle_tracks() does nothing from the client side" << std::endl;
1986-}
1987-
1988 void media::TrackListStub::reset()
1989 {
1990 auto op = d->object->invoke_method_synchronously<mpris::TrackList::Reset, void>();
1991@@ -255,17 +337,32 @@
1992 return d->signals.on_track_added;
1993 }
1994
1995+const core::Signal<media::TrackList::ContainerURI>& media::TrackListStub::on_tracks_added() const
1996+{
1997+ return d->signals.on_tracks_added;
1998+}
1999+
2000+const core::Signal<media::TrackList::TrackIdTuple>& media::TrackListStub::on_track_moved() const
2001+{
2002+ return d->signals.on_track_moved;
2003+}
2004+
2005 const core::Signal<media::Track::Id>& media::TrackListStub::on_track_removed() const
2006 {
2007 return d->signals.on_track_removed;
2008 }
2009
2010+const core::Signal<void>& media::TrackListStub::on_track_list_reset() const
2011+{
2012+ return d->signals.on_track_list_reset;
2013+}
2014+
2015 const core::Signal<media::Track::Id>& media::TrackListStub::on_track_changed() const
2016 {
2017 return d->signals.on_track_changed;
2018 }
2019
2020-const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListStub::on_go_to_track() const
2021+const core::Signal<media::Track::Id>& media::TrackListStub::on_go_to_track() const
2022 {
2023 return d->signals.on_go_to_track;
2024 }
2025
2026=== modified file 'src/core/media/track_list_stub.h'
2027--- src/core/media/track_list_stub.h 2015-07-20 20:39:42 +0000
2028+++ src/core/media/track_list_stub.h 2015-12-23 14:19:01 +0000
2029@@ -48,23 +48,25 @@
2030 Track::UriType query_uri_for_track(const Track::Id& id);
2031
2032 void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current);
2033+ void add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position);
2034+ bool move_track(const Track::Id& id, const Track::Id& to);
2035 void remove_track(const Track::Id& id);
2036
2037- void go_to(const Track::Id& track, bool toggle_player_state);
2038+ void go_to(const Track::Id& track);
2039
2040 Track::Id next();
2041 Track::Id previous();
2042
2043- void shuffle_tracks();
2044- void unshuffle_tracks();
2045-
2046 void reset();
2047
2048 const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const;
2049 const core::Signal<Track::Id>& on_track_added() const;
2050+ const core::Signal<ContainerURI>& on_tracks_added() const;
2051+ const core::Signal<TrackIdTuple>& on_track_moved() const;
2052 const core::Signal<Track::Id>& on_track_removed() const;
2053+ const core::Signal<void>& on_track_list_reset() const;
2054 const core::Signal<Track::Id>& on_track_changed() const;
2055- const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const;
2056+ const core::Signal<Track::Id>& on_go_to_track() const;
2057 const core::Signal<void>& on_end_of_tracklist() const;
2058
2059 private:
2060
2061=== modified file 'tests/CMakeLists.txt'
2062--- tests/CMakeLists.txt 2015-03-25 14:30:47 +0000
2063+++ tests/CMakeLists.txt 2015-12-23 14:19:01 +0000
2064@@ -50,6 +50,6 @@
2065 "COM_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME=fakesink"
2066 )
2067
2068-# add_subdirectory(acceptance-tests)
2069+#add_subdirectory(acceptance-tests)
2070 add_subdirectory(test-track-list)
2071 add_subdirectory(unit-tests)
2072
2073=== modified file 'tests/acceptance-tests/service.cpp'
2074--- tests/acceptance-tests/service.cpp 2015-03-03 22:41:22 +0000
2075+++ tests/acceptance-tests/service.cpp 2015-12-23 14:19:01 +0000
2076@@ -1,5 +1,5 @@
2077 /*
2078- * Copyright © 2013-2014 Canonical Ltd.
2079+ * Copyright © 2013-2015 Canonical Ltd.
2080 *
2081 * This program is free software: you can redistribute it and/or modify it
2082 * under the terms of the GNU Lesser General Public License version 3,
2083@@ -26,15 +26,13 @@
2084
2085 #include "test_data.h"
2086
2087-#include <core/testing/cross_process_sync.h>
2088-#include <core/testing/fork_and_run.h>
2089-
2090 #include <gtest/gtest.h>
2091
2092 #include <cstdio>
2093
2094 #include <condition_variable>
2095 #include <functional>
2096+#include <iostream>
2097 #include <thread>
2098
2099 namespace media = core::ubuntu::media;
2100@@ -70,382 +68,121 @@
2101 }
2102 }
2103
2104-TEST(MusicService, DISABLED_accessing_and_creating_a_session_works)
2105-{
2106- core::testing::CrossProcessSync sync_service_start;
2107-
2108- auto service = [this, &sync_service_start]()
2109- {
2110- SigTermCatcher sc;
2111-
2112- auto service = std::make_shared<media::ServiceImplementation>();
2113- std::thread t([&service](){service->run();});
2114-
2115- sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2116-
2117- sc.wait_for_signal(); service->stop();
2118-
2119- if (t.joinable())
2120- t.join();
2121-
2122- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2123- };
2124-
2125- auto client = [this, &sync_service_start]()
2126- {
2127- sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500});
2128-
2129- auto service = media::Service::Client::instance();
2130- auto session = service->create_session(media::Player::Client::default_configuration());
2131-
2132- EXPECT_TRUE(session != nullptr);
2133-
2134- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2135- };
2136-
2137- EXPECT_EQ(core::testing::ForkAndRunResult::empty,
2138- core::testing::fork_and_run(service, client));
2139-}
2140-
2141-TEST(MusicService, DISABLED_accessing_and_creating_a_fixed_session_works)
2142-{
2143- core::testing::CrossProcessSync sync_service_start;
2144-
2145- auto service = [this, &sync_service_start]()
2146- {
2147- SigTermCatcher sc;
2148-
2149- auto service = std::make_shared<media::ServiceImplementation>();
2150- std::thread t([&service](){service->run();});
2151-
2152- sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2153-
2154- sc.wait_for_signal(); service->stop();
2155-
2156- if (t.joinable())
2157- t.join();
2158-
2159- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2160- };
2161-
2162- auto client = [this, &sync_service_start]()
2163- {
2164- sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500});
2165-
2166- auto service = media::Service::Client::instance();
2167- auto session = service->create_fixed_session("com.ubuntu.test-session", media::Player::Client::default_configuration());
2168-
2169- EXPECT_TRUE(session != nullptr);
2170-
2171- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2172- };
2173-
2174- EXPECT_EQ(core::testing::ForkAndRunResult::empty,
2175- core::testing::fork_and_run(service, client));
2176-}
2177-
2178-TEST(MusicService, DISABLED_resuming_a_session_works)
2179-{
2180- core::testing::CrossProcessSync sync_service_start;
2181-
2182- auto service = [this, &sync_service_start]()
2183- {
2184- SigTermCatcher sc;
2185-
2186- auto service = std::make_shared<media::ServiceImplementation>();
2187- std::thread t([&service](){service->run();});
2188-
2189- sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2190-
2191- sc.wait_for_signal(); service->stop();
2192-
2193- if (t.joinable())
2194- t.join();
2195-
2196- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2197- };
2198-
2199- auto client = [this, &sync_service_start]()
2200- {
2201- sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500});
2202-
2203- auto service = media::Service::Client::instance();
2204- auto session = service->create_session(media::Player::Client::default_configuration());
2205-
2206- EXPECT_TRUE(session != nullptr);
2207-
2208- auto resumed_session = service->resume_session(session->key());
2209-
2210- EXPECT_TRUE(resumed_session != nullptr);
2211-
2212- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2213- };
2214-
2215- EXPECT_EQ(core::testing::ForkAndRunResult::empty,
2216- core::testing::fork_and_run(service, client));
2217-}
2218-
2219-TEST(MusicService, DISABLED_remotely_querying_track_meta_data_works)
2220-{
2221- const std::string test_file{"/tmp/test-audio.ogg"};
2222- const std::string test_file_uri{"file:///tmp/test-audio.ogg"};
2223- std::remove(test_file.c_str());
2224- ASSERT_TRUE(test::copy_test_media_file_to("test-audio.ogg", test_file));
2225-
2226- core::testing::CrossProcessSync sync_service_start;
2227-
2228- auto service = [this, &sync_service_start]()
2229- {
2230- SigTermCatcher sc;
2231-
2232- auto service = std::make_shared<media::ServiceImplementation>();
2233- std::thread t([&service](){service->run();});
2234-
2235- sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2236-
2237- sc.wait_for_signal(); service->stop();
2238-
2239- if (t.joinable())
2240- t.join();
2241-
2242- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2243- };
2244-
2245- auto client = [this, &sync_service_start]()
2246- {
2247- sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500});
2248-
2249- static const media::Track::UriType uri{"file:///tmp/test-audio.ogg"};
2250-
2251- auto service = media::Service::Client::instance();
2252- auto session = service->create_session(media::Player::Client::default_configuration());
2253- auto track_list = session->track_list();
2254-
2255- track_list->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), false);
2256-
2257- EXPECT_EQ(std::uint32_t{1}, track_list->tracks()->size());
2258-
2259- auto md = track_list->query_meta_data_for_track(track_list->tracks()->front());
2260-
2261- for (auto pair : *md)
2262- {
2263- std::cout << pair.first << " -> " << pair.second << std::endl;
2264- }
2265-
2266- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2267- };
2268-
2269- EXPECT_EQ(core::testing::ForkAndRunResult::empty,
2270- core::testing::fork_and_run(service, client));
2271-}
2272-
2273-TEST(MusicService, DISABLED_play_pause_seek_after_open_uri_works)
2274-{
2275- const std::string test_file{"/tmp/test-audio-1.ogg"};
2276- std::remove(test_file.c_str());
2277- ASSERT_TRUE(test::copy_test_media_file_to("test-audio-1.ogg", test_file));
2278-
2279- core::testing::CrossProcessSync sync_service_start;
2280-
2281- auto service = [this, &sync_service_start]()
2282- {
2283- SigTermCatcher sc;
2284-
2285- auto service = std::make_shared<media::ServiceImplementation>();
2286- std::thread t([&service](){service->run();});
2287-
2288- sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2289-
2290- sc.wait_for_signal(); service->stop();
2291-
2292- if (t.joinable())
2293- t.join();
2294-
2295- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2296- };
2297-
2298- auto client = [this, &sync_service_start]()
2299- {
2300- sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500});
2301-
2302- static const media::Track::UriType uri{"file:///tmp/test-audio-1.ogg"};
2303-
2304- auto service = media::Service::Client::instance();
2305- auto session = service->create_session(media::Player::Client::default_configuration());
2306-
2307- EXPECT_EQ(true, session->can_play().get());
2308- EXPECT_EQ(true, session->can_pause().get());
2309- EXPECT_EQ(true, session->can_seek().get());
2310- EXPECT_EQ(true, session->can_go_previous().get());
2311- EXPECT_EQ(true, session->can_go_next().get());
2312- EXPECT_EQ(media::Player::PlaybackStatus::null, session->playback_status());
2313- EXPECT_EQ(media::Player::LoopStatus::none, session->loop_status());
2314- EXPECT_NEAR(1.f, session->playback_rate().get(), 1E-5);
2315- EXPECT_EQ(true, session->is_shuffle());
2316- EXPECT_EQ(true, session->track_list()->can_edit_tracks().get());
2317-
2318- EXPECT_TRUE(session->open_uri(uri));
2319-
2320- core::testing::WaitableStateTransition<media::Player::PlaybackStatus>
2321- state_transition(
2322- media::Player::stopped);
2323-
2324- session->playback_status().changed().connect(
2325- std::bind(&core::testing::WaitableStateTransition<media::Player::PlaybackStatus>::trigger,
2326- std::ref(state_transition),
2327- std::placeholders::_1));
2328-
2329- session->play();
2330- sleep_for(std::chrono::seconds{1});
2331- EXPECT_EQ(media::Player::PlaybackStatus::playing, session->playback_status());
2332- session->stop();
2333- sleep_for(std::chrono::seconds{1});
2334- EXPECT_EQ(media::Player::PlaybackStatus::stopped, session->playback_status());
2335- session->play();
2336- sleep_for(std::chrono::seconds{1});
2337- EXPECT_EQ(media::Player::PlaybackStatus::playing, session->playback_status());
2338- session->pause();
2339- sleep_for(std::chrono::seconds{1});
2340- EXPECT_EQ(media::Player::PlaybackStatus::paused, session->playback_status());
2341- session->stop();
2342- sleep_for(std::chrono::seconds{1});
2343- EXPECT_EQ(media::Player::PlaybackStatus::stopped, session->playback_status());
2344-
2345- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2346- };
2347-
2348- EXPECT_EQ(core::testing::ForkAndRunResult::empty,
2349- core::testing::fork_and_run(service, client));
2350-}
2351-
2352-TEST(MusicService, DISABLED_get_position_duration_work)
2353-{
2354- const std::string test_file{"/tmp/test-audio-1.ogg"};
2355- std::remove(test_file.c_str());
2356- ASSERT_TRUE(test::copy_test_media_file_to("test-audio-1.ogg", test_file));
2357-
2358- core::testing::CrossProcessSync sync_service_start;
2359-
2360- auto service = [this, &sync_service_start]()
2361- {
2362- SigTermCatcher sc;
2363-
2364- auto service = std::make_shared<media::ServiceImplementation>();
2365- std::thread t([&service](){service->run();});
2366-
2367- sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2368-
2369- sc.wait_for_signal(); service->stop();
2370-
2371- if (t.joinable())
2372- t.join();
2373-
2374- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2375- };
2376-
2377- auto client = [this, &sync_service_start]()
2378- {
2379- sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500});
2380-
2381- static const media::Track::UriType uri{"file:///tmp/test-audio-1.ogg"};
2382-
2383- auto service = media::Service::Client::instance();
2384- auto session = service->create_session(media::Player::Client::default_configuration());
2385-
2386- EXPECT_TRUE(session->open_uri(uri));
2387-
2388- core::testing::WaitableStateTransition<media::Player::PlaybackStatus>
2389- state_transition(
2390- media::Player::stopped);
2391-
2392- session->playback_status().changed().connect(
2393- std::bind(&core::testing::WaitableStateTransition<media::Player::PlaybackStatus>::trigger,
2394- std::ref(state_transition),
2395- std::placeholders::_1));
2396-
2397- session->play();
2398- sleep_for(std::chrono::seconds{1});
2399- EXPECT_EQ(media::Player::PlaybackStatus::playing, session->playback_status());
2400- sleep_for(std::chrono::seconds{1});
2401- EXPECT_TRUE(session->position() > 1e9);
2402- EXPECT_TRUE(session->duration() > 1e9);
2403-
2404- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2405- };
2406-
2407- EXPECT_EQ(core::testing::ForkAndRunResult::empty,
2408- core::testing::fork_and_run(service, client));
2409-}
2410-
2411-TEST(MusicService, DISABLED_starting_playback_on_non_empty_playqueue_works)
2412-{
2413- const std::string test_file{"/tmp/test-audio-1.ogg"};
2414- std::remove(test_file.c_str());
2415- ASSERT_TRUE(test::copy_test_media_file_to("test-audio-1.ogg", test_file));
2416-
2417- core::testing::CrossProcessSync sync_service_start;
2418-
2419- auto service = [this, &sync_service_start]()
2420- {
2421- SigTermCatcher sc;
2422-
2423- auto service = std::make_shared<media::ServiceImplementation>();
2424- std::thread t([&service](){service->run();});
2425-
2426- sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2427-
2428- sc.wait_for_signal(); service->stop();
2429-
2430- if (t.joinable())
2431- t.join();
2432-
2433- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2434- };
2435-
2436- auto client = [this, &sync_service_start]()
2437- {
2438- sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500});
2439-
2440- static const media::Track::UriType uri{"file:///tmp/test-audio-1.ogg"};
2441- static const bool dont_make_current{false};
2442-
2443- auto service = media::Service::Client::instance();
2444- auto session = service->create_session(media::Player::Client::default_configuration());
2445-
2446- EXPECT_EQ(true, session->can_play().get());
2447- EXPECT_EQ(true, session->can_play().get());
2448- EXPECT_EQ(true, session->can_pause().get());
2449- EXPECT_EQ(true, session->can_seek().get());
2450- EXPECT_EQ(true, session->can_go_previous().get());
2451- EXPECT_EQ(true, session->can_go_next().get());
2452- EXPECT_EQ(media::Player::PlaybackStatus::null, session->playback_status());
2453- EXPECT_EQ(media::Player::LoopStatus::none, session->loop_status());
2454- EXPECT_NEAR(1.f, session->playback_rate().get(), 1E-5);
2455- EXPECT_EQ(true, session->is_shuffle());
2456- EXPECT_EQ(true, session->track_list()->can_edit_tracks().get());
2457-
2458- session->track_list()->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), dont_make_current);
2459-
2460- EXPECT_EQ(std::uint32_t{1}, session->track_list()->tracks()->size());
2461-
2462- core::testing::WaitableStateTransition<media::Player::PlaybackStatus>
2463- state_transition(
2464- media::Player::stopped);
2465-
2466- session->playback_status().changed().connect(
2467- std::bind(&core::testing::WaitableStateTransition<media::Player::PlaybackStatus>::trigger,
2468- std::ref(state_transition),
2469- std::placeholders::_1));
2470-
2471- session->play();
2472-
2473- EXPECT_TRUE(state_transition.wait_for_state_for(
2474- media::Player::playing,
2475- std::chrono::milliseconds{2000}));
2476-
2477- return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
2478- };
2479-
2480- EXPECT_EQ(core::testing::ForkAndRunResult::empty,
2481- core::testing::fork_and_run(service, client));
2482+TEST(MediaService, move_track_in_tracklist_works)
2483+{
2484+ auto service = media::Service::Client::instance();
2485+ auto session = service->create_session(media::Player::Client::default_configuration());
2486+ EXPECT_TRUE(session != nullptr);
2487+
2488+ std::vector<media::Track::Id> ids;
2489+
2490+ auto tracklist = session->track_list();
2491+
2492+ // Catch the new track Ids that are assigned each track that's added
2493+ core::Connection c = tracklist->on_track_added().connect([&ids](const media::Track::Id &new_id)
2494+ {
2495+ std::cout << "track added: " << new_id << std::endl;
2496+ ids.push_back(new_id);
2497+ });
2498+
2499+ const media::Track::UriType uri1{"file:///tmp/test-audio.ogg"};
2500+ const media::Track::UriType uri2{"file:///tmp/test-audio1.ogg"};
2501+ const media::Track::UriType uri3{"file:///tmp/test.mp3"};
2502+ const media::Track::UriType uri4{"file:///tmp/test-video.ogg"};
2503+
2504+ tracklist->add_track_with_uri_at(uri1, media::TrackList::after_empty_track(), false);
2505+ tracklist->add_track_with_uri_at(uri2, media::TrackList::after_empty_track(), false);
2506+ tracklist->add_track_with_uri_at(uri3, media::TrackList::after_empty_track(), false);
2507+ tracklist->add_track_with_uri_at(uri4, media::TrackList::after_empty_track(), false);
2508+ EXPECT_EQ(std::uint32_t{4}, tracklist->tracks()->size());
2509+
2510+ std::cout << "ids.at(0): " << ids.at(1) << std::endl;
2511+ std::cout << "ids.at(2): " << ids.at(2) << std::endl;
2512+ // Move track 3 into the second position in the TrackList
2513+ tracklist->move_track(ids.at(2), ids.at(1));
2514+
2515+ // Original list order and current order should be different
2516+ EXPECT_NE(ids, tracklist->tracks().get());
2517+ EXPECT_EQ(ids[1], tracklist->tracks()->at(2));
2518+ EXPECT_EQ(ids[2], tracklist->tracks()->at(1));
2519+}
2520+
2521+TEST(MediaService, move_track_to_first_position_in_tracklist_works)
2522+{
2523+ auto service = media::Service::Client::instance();
2524+ auto session = service->create_session(media::Player::Client::default_configuration());
2525+ EXPECT_TRUE(session != nullptr);
2526+
2527+ std::vector<media::Track::Id> ids;
2528+
2529+ auto tracklist = session->track_list();
2530+
2531+ // Catch the new track Ids that are assigned each track that's added
2532+ core::Connection c = tracklist->on_track_added().connect([&ids](const media::Track::Id &new_id)
2533+ {
2534+ std::cout << "track added: " << new_id << std::endl;
2535+ ids.push_back(new_id);
2536+ });
2537+
2538+ const media::Track::UriType uri1{"file:///tmp/test-audio.ogg"};
2539+ const media::Track::UriType uri2{"file:///tmp/test-audio1.ogg"};
2540+ const media::Track::UriType uri3{"file:///tmp/test.mp3"};
2541+ const media::Track::UriType uri4{"file:///tmp/test-video.ogg"};
2542+
2543+ tracklist->add_track_with_uri_at(uri1, media::TrackList::after_empty_track(), false);
2544+ tracklist->add_track_with_uri_at(uri2, media::TrackList::after_empty_track(), false);
2545+ tracklist->add_track_with_uri_at(uri3, media::TrackList::after_empty_track(), false);
2546+ tracklist->add_track_with_uri_at(uri4, media::TrackList::after_empty_track(), false);
2547+ EXPECT_EQ(std::uint32_t{4}, tracklist->tracks()->size());
2548+
2549+ std::cout << "ids.at(0): " << ids.at(0) << std::endl;
2550+ std::cout << "ids.at(2): " << ids.at(2) << std::endl;
2551+ // Move track 3 into the first position in the TrackList
2552+ tracklist->move_track(ids.at(2), ids.at(0));
2553+
2554+ // Original list order and current order should be different
2555+ EXPECT_NE(ids, tracklist->tracks().get());
2556+ EXPECT_EQ(ids[2], tracklist->tracks()->at(0));
2557+ EXPECT_EQ(ids[0], tracklist->tracks()->at(1));
2558+}
2559+
2560+TEST(MediaService, move_track_to_last_position_in_tracklist_works)
2561+{
2562+ auto service = media::Service::Client::instance();
2563+ auto session = service->create_session(media::Player::Client::default_configuration());
2564+ EXPECT_TRUE(session != nullptr);
2565+
2566+ std::vector<media::Track::Id> ids;
2567+
2568+ auto tracklist = session->track_list();
2569+
2570+ // Catch the new track Ids that are assigned each track that's added
2571+ core::Connection c = tracklist->on_track_added().connect([&ids](const media::Track::Id &new_id)
2572+ {
2573+ std::cout << "track added: " << new_id << std::endl;
2574+ ids.push_back(new_id);
2575+ });
2576+
2577+ const media::Track::UriType uri1{"file:///tmp/test-audio.ogg"};
2578+ const media::Track::UriType uri2{"file:///tmp/test-audio1.ogg"};
2579+ const media::Track::UriType uri3{"file:///tmp/test.mp3"};
2580+ const media::Track::UriType uri4{"file:///tmp/test-video.ogg"};
2581+
2582+ tracklist->add_track_with_uri_at(uri1, media::TrackList::after_empty_track(), false);
2583+ tracklist->add_track_with_uri_at(uri2, media::TrackList::after_empty_track(), false);
2584+ tracklist->add_track_with_uri_at(uri3, media::TrackList::after_empty_track(), false);
2585+ tracklist->add_track_with_uri_at(uri4, media::TrackList::after_empty_track(), false);
2586+ EXPECT_EQ(std::uint32_t{4}, tracklist->tracks()->size());
2587+
2588+ sleep(1);
2589+
2590+ std::cout << "ids.at(0): " << ids.at(0) << std::endl;
2591+ std::cout << "ids.at(3): " << ids.at(3) << std::endl;
2592+ // Move track 3 into the first position in the TrackList
2593+ tracklist->move_track(ids.at(0), ids.at(3));
2594+
2595+ // Original list order and current order should be different
2596+ EXPECT_NE(ids, tracklist->tracks().get());
2597+ EXPECT_EQ(ids[1], tracklist->tracks()->at(0));
2598+ EXPECT_EQ(ids[0], tracklist->tracks()->at(3));
2599 }
2600
2601=== modified file 'tests/test-track-list/test_track_list.cpp'
2602--- tests/test-track-list/test_track_list.cpp 2015-04-20 15:56:26 +0000
2603+++ tests/test-track-list/test_track_list.cpp 2015-12-23 14:19:01 +0000
2604@@ -257,9 +257,8 @@
2605
2606 const media::Track::Id id{m_hubTrackList->tracks().get()[3]};
2607 cout << "Going straight to the Track with Id " << id << std::endl;
2608- const bool toggle_player_state = true;
2609- m_hubTrackList->go_to(id, toggle_player_state);
2610-
2611+ m_hubTrackList->go_to(id);
2612+
2613 cout << "Waiting for third track to finish playing..." << endl;
2614 wait_for_about_to_finish();
2615 }
2616
2617=== modified file 'tests/test-track-list/test_track_list.h'
2618--- tests/test-track-list/test_track_list.h 2015-04-15 18:29:39 +0000
2619+++ tests/test-track-list/test_track_list.h 2015-12-23 14:19:01 +0000
2620@@ -57,7 +57,7 @@
2621
2622 // Takes two uris and confirms that they remain after a detach/reattach
2623 void test_tracklist_resume(const std::string &uri1, const std::string &uri2, const std::string &uuid);
2624-
2625+
2626 void test_ensure_tracklist_is_not_empty(const std::string &uri1, const std::string &uri2 = std::string{});
2627
2628 // Takes in one or two files for playback, adds it/them to the TrackList, plays and makes sure

Subscribers

People subscribed via source and target branches

to all changes: