Merge lp:~thomas-voss/media-hub/enable-dual-landings into lp:media-hub

Proposed by Thomas Voß
Status: Merged
Approved by: Łukasz Zemczak
Approved revision: 163
Merged at revision: 163
Proposed branch: lp:~thomas-voss/media-hub/enable-dual-landings
Merge into: lp:media-hub
Diff against target: 3020 lines (+1499/-633)
26 files modified
CMakeLists.txt (+26/-3)
README (+4/-0)
debian/VERSION (+1/-0)
debian/VERSION.vivid (+1/-0)
debian/changelog (+45/-0)
debian/control (+6/-0)
debian/control.in (+94/-0)
debian/gen-debian-files.sh (+102/-0)
debian/get-versions.sh (+68/-0)
debian/rules (+19/-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:~thomas-voss/media-hub/enable-dual-landings
Reviewer Review Type Date Requested Status
Łukasz Zemczak packaging Approve
Timo Jyrinki packaging Needs Information
PS Jenkins bot continuous-integration Approve
Jim Hodapp (community) code Approve
Review via email: mp+278270@code.launchpad.net

Commit message

Enable dual landings.

Description of the change

Enable dual landings.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
161. By Thomas Voß

* Set gstreamer context directly instead of using gobject to
  communicate with the video sink
[ CI Train Bot ]
* No-change rebuild.
[ Jim Hodapp ]
* Added move_track and various fixes for TrackList.
* Major bump for new TrackList API changes.
[ Alfonso Sanchez-Beato ]
* Make sure our iterator for the current track points to the right
  place when (un)shuffling (LP #1510219). Fix crash when client tries
  to set the player for a non-existing key. Do not add empty URIs to
  the list (LP: #1511029). (LP: #1511073, #1511385, #1510219,
  #1510227, #1511029)
* Force rebuild against dbus-cpp.

162. By Thomas Voß

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

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Jim Hodapp (jhodapp) wrote :

LGTM

review: Approve (code)
Revision history for this message
Łukasz Zemczak (sil2100) wrote :

Dual-landability-wise it looks ok, don't have any obvious concerns. The only concern I have is the lack of symbol files and/or anything else like a combination of shlibs+abi-compliance-checker. I also noticed that media-hub dropped its symbol files a while ago again and I was unable to find out who signed it off (maybe me?). If someone did, I wonder what was the agreement as I suppose no one sane would let this happen without some kind of a declaration like: "ok, we'll add those in x weeks".

So this is obviously my only concern. If some core-developer with more experience than me says: "ok, this is good to go without abi checks or symbols" then I guess it's fine.

review: Needs Information (packaging)
163. 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)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Timo Jyrinki (timo-jyrinki) wrote :
review: Needs Information (packaging)
Revision history for this message
Łukasz Zemczak (sil2100) wrote :

I have the same concerns as Timo, but I have agreed to let this in without ACC temporarily. This project never had valid symbols files so it's no regression here. Thomas promised to take care of all the ABI compliance bits as a priority for the next release - so approving conditionally.

review: Approve (packaging)

Preview Diff

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