Merge lp:~thomas-voss/media-hub/enable-dual-landings into lp:media-hub
- enable-dual-landings
- Merge into trunk
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 |
Related bugs: |
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 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ß
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:161
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:162
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Łukasz Zemczak (sil2100) wrote : | # |
Dual-landabilit
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.
- 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)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:163
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Timo Jyrinki (timo-jyrinki) wrote : | # |
I checked and there's no unit test for the abi compliance testing. This'd be needed to fully implement https:/
Like http://
Ł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.
Preview Diff
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", ¤t_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 |
PASSED: Continuous integration, rev:160 jenkins. qa.ubuntu. com/job/ media-hub- ci/378/ jenkins. qa.ubuntu. com/job/ media-hub- vivid-amd64- ci/218 jenkins. qa.ubuntu. com/job/ media-hub- vivid-armhf- ci/218 jenkins. qa.ubuntu. com/job/ media-hub- vivid-armhf- ci/218/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ media-hub- vivid-i386- ci/218
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/media- hub-ci/ 378/rebuild
http://