Merge lp:~phablet-team/media-hub/fix-1498962 into lp:media-hub
- fix-1498962
- Merge into trunk
Proposed by
Jim Hodapp
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Alfonso Sanchez-Beato | ||||
Approved revision: | 190 | ||||
Merged at revision: | 185 | ||||
Proposed branch: | lp:~phablet-team/media-hub/fix-1498962 | ||||
Merge into: | lp:media-hub | ||||
Diff against target: |
691 lines (+275/-112) 12 files modified
include/core/media/track.h (+30/-46) src/core/media/CMakeLists.txt (+2/-0) src/core/media/codec.h (+83/-8) src/core/media/cover_art_resolver.cpp (+1/-1) src/core/media/gstreamer/meta_data_extractor.h (+4/-3) src/core/media/gstreamer/playbin.cpp (+1/-3) src/core/media/metadata.cpp (+83/-0) src/core/media/mpris/player.h (+11/-8) src/core/media/player_implementation.cpp (+40/-2) src/core/media/player_skeleton.cpp (+2/-2) src/core/media/player_stub.cpp (+2/-2) src/core/media/service_skeleton.cpp (+16/-37) |
||||
To merge this branch: | bzr merge lp:~phablet-team/media-hub/fix-1498962 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alfonso Sanchez-Beato | Approve | ||
Konrad Zapałowicz (community) | code | Approve | |
PS Jenkins bot | continuous-integration | Pending | |
Review via email: mp+296604@code.launchpad.net |
Commit message
Enable exporting of metadata via MPRIS over dbus. This does not export everything yet and focused solely on track title, track album name and artist name.
Description of the change
Enable exporting of metadata via MPRIS over dbus. This does not export everything yet and focused solely on track title, track album name and artist name.
To post a comment you must log in.
- 186. By Jim Hodapp
-
Add metadata.cpp
- 187. By Jim Hodapp
-
Make sure we don't lose the album_missing.svg image in indicator-sound when toggling play/pause
- 188. By Jim Hodapp
-
Undo last set of extra changes that snuck in to a commit
- 189. By Jim Hodapp
-
Make sure that the MPRIS client sees a the same data after switching between MPRIS clients
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote : | # |
review:
Needs Fixing
Revision history for this message
Konrad Zapałowicz (kzapalowicz) wrote : | # |
LGTM except on the sizeof thing.
review:
Needs Fixing
(code)
Revision history for this message
Jim Hodapp (jhodapp) : | # |
- 190. By Jim Hodapp
-
Address review comments
Revision history for this message
Konrad Zapałowicz (kzapalowicz) wrote : | # |
LGTM
review:
Approve
(code)
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote : | # |
LGTM
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'include/core/media/track.h' |
2 | --- include/core/media/track.h 2015-07-02 12:30:09 +0000 |
3 | +++ include/core/media/track.h 2016-06-09 16:54:40 +0000 |
4 | @@ -39,10 +39,15 @@ |
5 | public: |
6 | typedef std::string UriType; |
7 | typedef std::string Id; |
8 | + typedef std::map<std::string, std::string> MetaDataType; |
9 | |
10 | class MetaData |
11 | { |
12 | public: |
13 | + static constexpr const char* TrackArtlUrlKey = "mpris:artUrl"; |
14 | + static constexpr const char* TrackLengthKey = "mpris:length"; |
15 | + static constexpr const char* TrackIdKey = "mpris:trackid"; |
16 | + |
17 | bool operator==(const MetaData& rhs) const |
18 | { |
19 | return map == rhs.map; |
20 | @@ -90,13 +95,36 @@ |
21 | return map.at(key); |
22 | } |
23 | |
24 | + bool is_set(const std::string& key) const |
25 | + { |
26 | + try { |
27 | + return map.at(key).empty(); |
28 | + } catch (const std::out_of_range& e) { |
29 | + return false; |
30 | + } |
31 | + } |
32 | + |
33 | const std::map<std::string, std::string>& operator*() const |
34 | { |
35 | return map; |
36 | } |
37 | |
38 | + const std::string& album() const; |
39 | + const std::string& artist() const; |
40 | + const std::string& title() const; |
41 | + const std::string& track_id() const; |
42 | + const std::string& art_url() const; |
43 | + const std::string& last_used() const; |
44 | + |
45 | + void set_album(const std::string& album); |
46 | + void set_artist(const std::string& artist); |
47 | + void set_title(const std::string& title); |
48 | + void set_track_id(const std::string& id); |
49 | + void set_art_url(const std::string& url); |
50 | + void set_last_used(const std::string& datetime); |
51 | + |
52 | private: |
53 | - std::map<std::string, std::string> map; |
54 | + MetaDataType map; |
55 | }; |
56 | |
57 | Track(const Id& id); |
58 | @@ -108,51 +136,7 @@ |
59 | |
60 | virtual const Id& id() const; |
61 | virtual const UriType& uri() const; |
62 | - /* |
63 | - class MetaData |
64 | - { |
65 | - public: |
66 | - MetaData() = default; |
67 | - MetaData(const MetaData&) = default; |
68 | - ~MetaData() = default; |
69 | - |
70 | - MetaData& operator=(const MetaData&) = default; |
71 | - |
72 | - bool operator==(const MetaData&) const |
73 | - { |
74 | - return true; |
75 | - } |
76 | - |
77 | - bool operator!=(const MetaData&) const |
78 | - { |
79 | - return false; |
80 | - } |
81 | - |
82 | - struct NotImplementedFields |
83 | - { |
84 | - NotImplementedFields() = default; |
85 | - |
86 | - virtual const UriType& uri() const = 0; |
87 | - virtual const std::chrono::microseconds length() const = 0; |
88 | - virtual const UriType& art_uri() const = 0; |
89 | - virtual const std::string& album() const = 0; |
90 | - virtual const std::vector<std::string>& album_artist() const = 0; |
91 | - virtual const std::vector<std::string>& artist() const = 0; |
92 | - virtual const std::string& as_text() const = 0; |
93 | - virtual unsigned int audio_bpm() const = 0; |
94 | - virtual float auto_rating() const = 0; |
95 | - virtual const std::vector<std::string>& comment() const = 0; |
96 | - virtual const std::vector<std::string>& composer() const = 0; |
97 | - virtual unsigned int disc_number() const = 0; |
98 | - virtual const std::vector<std::string>& genre() const = 0; |
99 | - virtual const std::vector<std::string>& lyricist() const = 0; |
100 | - virtual const std::string title() const = 0; |
101 | - virtual unsigned int track_number() const = 0; |
102 | - virtual unsigned int use_count() const = 0; |
103 | - virtual float user_rating() const = 0; |
104 | - }; |
105 | - }; |
106 | -*/ |
107 | + |
108 | private: |
109 | struct Private; |
110 | std::unique_ptr<Private> d; |
111 | |
112 | === modified file 'src/core/media/CMakeLists.txt' |
113 | --- src/core/media/CMakeLists.txt 2016-04-05 15:40:11 +0000 |
114 | +++ src/core/media/CMakeLists.txt 2016-06-09 16:54:40 +0000 |
115 | @@ -49,6 +49,7 @@ |
116 | service.cpp |
117 | track.cpp |
118 | track_list.cpp |
119 | + metadata.cpp |
120 | |
121 | player_stub.cpp |
122 | service_stub.cpp |
123 | @@ -99,6 +100,7 @@ |
124 | hybris_client_death_observer.cpp |
125 | cover_art_resolver.cpp |
126 | engine.cpp |
127 | + metadata.cpp |
128 | |
129 | apparmor/context.cpp |
130 | apparmor/ubuntu.cpp |
131 | |
132 | === modified file 'src/core/media/codec.h' |
133 | --- src/core/media/codec.h 2015-04-14 19:11:15 +0000 |
134 | +++ src/core/media/codec.h 2016-06-09 16:54:40 +0000 |
135 | @@ -23,10 +23,18 @@ |
136 | #include <core/media/player.h> |
137 | #include <core/media/track.h> |
138 | |
139 | +#include <core/dbus/types/stl/map.h> |
140 | #include <core/dbus/types/stl/string.h> |
141 | +#include <core/dbus/types/variant.h> |
142 | #include <core/dbus/types/stl/vector.h> |
143 | #include <core/dbus/codec.h> |
144 | |
145 | +#include <boost/lexical_cast.hpp> |
146 | + |
147 | +#include "core/media/xesam.h" |
148 | + |
149 | +#include "core/media/logger/logger.h" |
150 | + |
151 | namespace core |
152 | { |
153 | namespace dbus |
154 | @@ -38,7 +46,7 @@ |
155 | { |
156 | constexpr static ArgumentType type_value() |
157 | { |
158 | - return ArgumentType::floating_point; |
159 | + return ArgumentType::array; |
160 | } |
161 | constexpr static bool is_basic_type() |
162 | { |
163 | @@ -51,7 +59,7 @@ |
164 | |
165 | static std::string signature() |
166 | { |
167 | - static const std::string s = TypeMapper<double>::signature(); |
168 | + static const std::string s = TypeMapper<std::map<std::string, dbus::types::Variant>>::signature(); |
169 | return s; |
170 | } |
171 | }; |
172 | @@ -60,16 +68,83 @@ |
173 | template<> |
174 | struct Codec<core::ubuntu::media::Track::MetaData> |
175 | { |
176 | - static void encode_argument(core::dbus::Message::Writer& out, const core::ubuntu::media::Track::MetaData& in) |
177 | + static void encode_argument(core::dbus::Message::Writer& writer, const core::ubuntu::media::Track::MetaData& md) |
178 | { |
179 | - (void) out; |
180 | - (void) in; |
181 | + typedef std::pair<std::string, dbus::types::Variant> Pair; |
182 | + auto dict = writer.open_array(dbus::types::Signature |
183 | + {dbus::helper::TypeMapper<Pair>::signature()}); |
184 | + |
185 | + for (const auto& pair : *md) |
186 | + { |
187 | + auto de = dict.open_dict_entry(); |
188 | + { |
189 | + if (pair.first == core::ubuntu::media::Track::MetaData::TrackLengthKey |
190 | + and not pair.second.empty()) |
191 | + { |
192 | + Codec<Pair>::encode_argument(de, std::make_pair(pair.first, |
193 | + dbus::types::Variant::encode( |
194 | + boost::lexical_cast<std::int64_t>(pair.second)))); |
195 | + } |
196 | + else if (pair.first == xesam::Album::name and not pair.second.empty()) |
197 | + { |
198 | + Codec<Pair>::encode_argument(de, std::make_pair(pair.first, |
199 | + dbus::types::Variant::encode(pair.second))); |
200 | + } |
201 | + else if (pair.first == xesam::AlbumArtist::name and not pair.second.empty()) |
202 | + { |
203 | +#if 0 |
204 | + // TODO: This code doesn't work but will be needed for full MPRIS compliance. |
205 | + // Technically there can be more than one album artist stuffed into an array. |
206 | + // How to satisfy stuffing this data into dbus-cpp is what needs fixing. |
207 | + auto array = de.open_array(dbus::types::Signature |
208 | + {dbus::helper::TypeMapper<Pair>::signature()}); |
209 | + { |
210 | + // TODO: this should really iterate over all artists, but seems |
211 | + // we only extract one artist from playbin |
212 | + Codec<Pair>::encode_argument(array, std::make_pair(pair.first, |
213 | + dbus::types::Variant::encode(pair.second))); |
214 | + } |
215 | + de.close_array(std::move(array)); |
216 | +#else |
217 | + Codec<Pair>::encode_argument(de, std::make_pair(pair.first, |
218 | + dbus::types::Variant::encode(pair.second))); |
219 | +#endif |
220 | + } |
221 | + else |
222 | + { |
223 | + Codec<Pair>::encode_argument(de, std::make_pair(pair.first, |
224 | + dbus::types::Variant::encode(pair.second))); |
225 | + } |
226 | + } |
227 | + dict.close_dict_entry(std::move(de)); |
228 | + } |
229 | + writer.close_array(std::move(dict)); |
230 | } |
231 | |
232 | - static void decode_argument(core::dbus::Message::Reader& out, core::ubuntu::media::Track::MetaData& in) |
233 | + static void decode_argument(core::dbus::Message::Reader& reader, core::ubuntu::media::Track::MetaData& md) |
234 | { |
235 | - (void) out; |
236 | - (void) in; |
237 | + auto array = reader.pop_array(); |
238 | + |
239 | + while (array.type() != dbus::ArgumentType::invalid) |
240 | + { |
241 | + auto entry = array.pop_dict_entry(); |
242 | + { |
243 | + std::string key {entry.pop_string()}; |
244 | + auto variant = entry.pop_variant(); |
245 | + { |
246 | + if (key == xesam::Album::name) |
247 | + { |
248 | + const std::string album = variant.pop_string(); |
249 | + MH_DEBUG("Getting key \"%s\" and value \"%s\"", key, album); |
250 | + md.set_album(album); |
251 | + } |
252 | + else |
253 | + { |
254 | + MH_WARNING("Unknown metadata key \"%s\" while decoding dbus message", key); |
255 | + } |
256 | + } |
257 | + } |
258 | + } |
259 | } |
260 | }; |
261 | |
262 | |
263 | === modified file 'src/core/media/cover_art_resolver.cpp' |
264 | --- src/core/media/cover_art_resolver.cpp 2014-09-09 14:02:52 +0000 |
265 | +++ src/core/media/cover_art_resolver.cpp 2016-06-09 16:54:40 +0000 |
266 | @@ -22,6 +22,6 @@ |
267 | { |
268 | return [](const std::string&, const std::string&, const std::string&) |
269 | { |
270 | - return "file:///usr/share/unity/icons/album_missing.png"; |
271 | + return "file:///usr/lib/arm-linux-gnueabihf/unity-scopes/mediascanner-music/album_missing.svg"; |
272 | }; |
273 | } |
274 | |
275 | === modified file 'src/core/media/gstreamer/meta_data_extractor.h' |
276 | --- src/core/media/gstreamer/meta_data_extractor.h 2015-09-23 11:17:26 +0000 |
277 | +++ src/core/media/gstreamer/meta_data_extractor.h 2016-06-09 16:54:40 +0000 |
278 | @@ -24,6 +24,8 @@ |
279 | |
280 | #include "bus.h" |
281 | |
282 | +#include "core/media/logger/logger.h" |
283 | + |
284 | #include <gst/gst.h> |
285 | |
286 | #include <exception> |
287 | @@ -72,7 +74,7 @@ |
288 | auto md = static_cast<media::Track::MetaData*>(user_data); |
289 | std::stringstream ss; |
290 | |
291 | - switch(gst_tag_get_type(tag)) |
292 | + switch (gst_tag_get_type(tag)) |
293 | { |
294 | case G_TYPE_BOOLEAN: |
295 | { |
296 | @@ -137,8 +139,7 @@ |
297 | break; |
298 | } |
299 | |
300 | - (*md).set( |
301 | - (gstreamer_to_mpris_tag_lut().count(tag) > 0 ? gstreamer_to_mpris_tag_lut().at(tag) : tag), |
302 | + (*md).set( (gstreamer_to_mpris_tag_lut().count(tag) > 0 ? gstreamer_to_mpris_tag_lut().at(tag) : tag), |
303 | ss.str()); |
304 | }, |
305 | &md); |
306 | |
307 | === modified file 'src/core/media/gstreamer/playbin.cpp' |
308 | --- src/core/media/gstreamer/playbin.cpp 2016-04-06 15:28:29 +0000 |
309 | +++ src/core/media/gstreamer/playbin.cpp 2016-06-09 16:54:40 +0000 |
310 | @@ -30,8 +30,6 @@ |
311 | |
312 | #include <utility> |
313 | |
314 | -//#define VERBOSE_DEBUG |
315 | - |
316 | namespace |
317 | { |
318 | void setup_video_sink_for_buffer_streaming(GstElement* pipeline) |
319 | @@ -256,7 +254,7 @@ |
320 | |
321 | void gstreamer::Playbin::on_new_message_async(const Bus::Message& message) |
322 | { |
323 | - switch(message.type) |
324 | + switch (message.type) |
325 | { |
326 | case GST_MESSAGE_ERROR: |
327 | signals.on_error(message.detail.error_warning_info); |
328 | |
329 | === added file 'src/core/media/metadata.cpp' |
330 | --- src/core/media/metadata.cpp 1970-01-01 00:00:00 +0000 |
331 | +++ src/core/media/metadata.cpp 2016-06-09 16:54:40 +0000 |
332 | @@ -0,0 +1,83 @@ |
333 | +/* |
334 | + * Copyright © 2016 Canonical Ltd. |
335 | + * |
336 | + * This program is free software: you can redistribute it and/or modify it |
337 | + * under the terms of the GNU Lesser General Public License version 3, |
338 | + * as published by the Free Software Foundation. |
339 | + * |
340 | + * This program is distributed in the hope that it will be useful, |
341 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
342 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
343 | + * GNU Lesser General Public License for more details. |
344 | + * |
345 | + * You should have received a copy of the GNU Lesser General Public License |
346 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
347 | + * |
348 | + * Authored by: Jim Hodapp <jim.hodapp@canonical.com> |
349 | + */ |
350 | + |
351 | +#include "xesam.h" |
352 | + |
353 | +#include <core/media/track.h> |
354 | + |
355 | +namespace media = core::ubuntu::media; |
356 | + |
357 | +const std::string& media::Track::MetaData::album() const |
358 | +{ |
359 | + return map.at(xesam::Album::name); |
360 | +} |
361 | + |
362 | +const std::string& media::Track::MetaData::artist() const |
363 | +{ |
364 | + return map.at(xesam::Artist::name); |
365 | +} |
366 | + |
367 | +const std::string& media::Track::MetaData::title() const |
368 | +{ |
369 | + return map.at(xesam::Title::name); |
370 | +} |
371 | + |
372 | +const std::string& media::Track::MetaData::track_id() const |
373 | +{ |
374 | + return map.at(media::Track::MetaData::TrackIdKey); |
375 | +} |
376 | + |
377 | +const std::string& media::Track::MetaData::art_url() const |
378 | +{ |
379 | + return map.at(media::Track::MetaData::TrackArtlUrlKey); |
380 | +} |
381 | + |
382 | +const std::string& media::Track::MetaData::last_used() const |
383 | +{ |
384 | + return map.at(xesam::LastUsed::name); |
385 | +} |
386 | + |
387 | +void media::Track::MetaData::set_album(const std::string& album) |
388 | +{ |
389 | + map[xesam::Album::name] = album; |
390 | +} |
391 | + |
392 | +void media::Track::MetaData::set_artist(const std::string& artist) |
393 | +{ |
394 | + map[xesam::Artist::name] = artist; |
395 | +} |
396 | + |
397 | +void media::Track::MetaData::set_title(const std::string& title) |
398 | +{ |
399 | + map[xesam::Title::name] = title; |
400 | +} |
401 | + |
402 | +void media::Track::MetaData::set_track_id(const std::string& id) |
403 | +{ |
404 | + map[media::Track::MetaData::TrackIdKey] = id; |
405 | +} |
406 | + |
407 | +void media::Track::MetaData::set_art_url(const std::string& url) |
408 | +{ |
409 | + map[media::Track::MetaData::TrackArtlUrlKey] = url; |
410 | +} |
411 | + |
412 | +void media::Track::MetaData::set_last_used(const std::string& datetime) |
413 | +{ |
414 | + map[xesam::LastUsed::name] = datetime; |
415 | +} |
416 | |
417 | === modified file 'src/core/media/mpris/player.h' |
418 | --- src/core/media/mpris/player.h 2016-02-19 16:14:42 +0000 |
419 | +++ src/core/media/mpris/player.h 2016-06-09 16:54:40 +0000 |
420 | @@ -44,6 +44,8 @@ |
421 | |
422 | #include <cstdint> |
423 | |
424 | +#include "core/media/logger/logger.h" |
425 | + |
426 | namespace dbus = core::dbus; |
427 | |
428 | namespace mpris |
429 | @@ -172,8 +174,7 @@ |
430 | DBUS_CPP_WRITABLE_PROPERTY_DEF(PlaybackRate, Player, double) |
431 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Rate, Player, double) |
432 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Shuffle, Player, bool) |
433 | - DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, Dictionary) |
434 | - DBUS_CPP_READABLE_PROPERTY_DEF(TypedMetaData, Player, core::ubuntu::media::Track::MetaData) |
435 | + DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, core::ubuntu::media::Track::MetaData) |
436 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Player, double) |
437 | DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::int64_t) |
438 | DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::int64_t) |
439 | @@ -222,7 +223,7 @@ |
440 | Properties::TypedLoopStatus::ValueType typed_loop_status{core::ubuntu::media::Player::LoopStatus::none}; |
441 | Properties::PlaybackRate::ValueType playback_rate{1.f}; |
442 | Properties::Shuffle::ValueType shuffle{false}; |
443 | - Properties::TypedMetaData::ValueType typed_meta_data{}; |
444 | + Properties::Metadata::ValueType meta_data{}; |
445 | Properties::Volume::ValueType volume{0.f}; |
446 | Properties::Position::ValueType position{0}; |
447 | Properties::Duration::ValueType duration{0}; |
448 | @@ -253,7 +254,7 @@ |
449 | configuration.object->template get_property<Properties::Lifetime>(), |
450 | configuration.object->template get_property<Properties::PlaybackRate>(), |
451 | configuration.object->template get_property<Properties::Shuffle>(), |
452 | - configuration.object->template get_property<Properties::TypedMetaData>(), |
453 | + configuration.object->template get_property<Properties::Metadata>(), |
454 | configuration.object->template get_property<Properties::Volume>(), |
455 | configuration.object->template get_property<Properties::Position>(), |
456 | configuration.object->template get_property<Properties::Duration>(), |
457 | @@ -288,6 +289,7 @@ |
458 | properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal); |
459 | properties.playback_rate->set(configuration.defaults.playback_rate); |
460 | properties.shuffle->set(configuration.defaults.shuffle); |
461 | + properties.meta_data_for_current_track->set(configuration.defaults.meta_data); |
462 | properties.position->set(configuration.defaults.position); |
463 | properties.duration->set(configuration.defaults.duration); |
464 | properties.minimum_playback_rate->set(configuration.defaults.minimum_rate); |
465 | @@ -351,9 +353,9 @@ |
466 | Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value); |
467 | |
468 | signals.properties_changed->emit(std::make_tuple( |
469 | - dbus::traits::Service<Player>::interface_name(), |
470 | - dict, |
471 | - the_empty_list_of_invalidated_properties())); |
472 | + dbus::traits::Service<Player>::interface_name(), |
473 | + dict, |
474 | + the_empty_list_of_invalidated_properties())); |
475 | } |
476 | |
477 | Dictionary get_all_properties() |
478 | @@ -374,6 +376,7 @@ |
479 | dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->get()); |
480 | dict[Properties::PlaybackRate::name()] = dbus::types::Variant::encode(properties.playback_rate->get()); |
481 | dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.shuffle->get()); |
482 | + dict[Properties::Metadata::name()] = dbus::types::Variant::encode(properties.meta_data_for_current_track->get()); |
483 | dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get()); |
484 | dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get()); |
485 | dict[Properties::MinimumRate::name()] = dbus::types::Variant::encode(properties.minimum_playback_rate->get()); |
486 | @@ -405,7 +408,7 @@ |
487 | std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime; |
488 | std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate; |
489 | std::shared_ptr<core::dbus::Property<Properties::Shuffle>> shuffle; |
490 | - std::shared_ptr<core::dbus::Property<Properties::TypedMetaData>> typed_meta_data_for_current_track; |
491 | + std::shared_ptr<core::dbus::Property<Properties::Metadata>> meta_data_for_current_track; |
492 | std::shared_ptr<core::dbus::Property<Properties::Volume>> volume; |
493 | std::shared_ptr<core::dbus::Property<Properties::Position>> position; |
494 | std::shared_ptr<core::dbus::Property<Properties::Duration>> duration; |
495 | |
496 | === modified file 'src/core/media/player_implementation.cpp' |
497 | --- src/core/media/player_implementation.cpp 2016-05-03 19:08:00 +0000 |
498 | +++ src/core/media/player_implementation.cpp 2016-06-09 16:54:40 +0000 |
499 | @@ -24,6 +24,7 @@ |
500 | #include "util/timeout.h" |
501 | |
502 | #include <unistd.h> |
503 | +#include <ctime> |
504 | |
505 | #include "client_death_observer.h" |
506 | #include "engine.h" |
507 | @@ -138,9 +139,19 @@ |
508 | } |
509 | case Engine::State::playing: |
510 | { |
511 | - // We update the track meta data prior to updating the playback status. |
512 | + // We update the track metadata prior to updating the playback status. |
513 | // Some MPRIS clients expect this order of events. |
514 | - parent->meta_data_for_current_track().set(std::get<1>(engine->track_meta_data().get())); |
515 | + time_t now; |
516 | + time(&now); |
517 | + char buf[sizeof("2011-10-08T07:07:09Z")]; |
518 | + strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&now)); |
519 | + media::Track::MetaData metadata{std::get<1>(engine->track_meta_data().get())}; |
520 | + // Setting this with second resolution makes sure that the track_meta_data property changes |
521 | + // and thus the track_meta_data().changed() signal gets sent out over dbus. Otherwise the |
522 | + // Property caching mechanism would prevent this. |
523 | + metadata.set_last_used(std::string{buf}); |
524 | + update_mpris_metadata(metadata); |
525 | + |
526 | // And update our playback status. |
527 | parent->playback_status().set(media::Player::playing); |
528 | MH_INFO("Requesting power state"); |
529 | @@ -325,6 +336,27 @@ |
530 | parent->can_go_next().set(has_next); |
531 | } |
532 | |
533 | + // Makes sure all relevant metadata fields are set to current data and |
534 | + // will trigger the track_meta_data().changed() signal to go out over dbus |
535 | + void update_mpris_metadata(const media::Track::MetaData& md) |
536 | + { |
537 | + media::Track::MetaData metadata{md}; |
538 | + if (not metadata.is_set(media::Track::MetaData::TrackIdKey)) |
539 | + { |
540 | + const std::size_t last_slash = track_list->current().find_last_of("/"); |
541 | + const std::string track_id = track_list->current().substr(last_slash+1); |
542 | + if (not track_id.empty()) |
543 | + metadata.set_track_id("/org/mpris/MediaPlayer2/Track/" + track_id); |
544 | + else |
545 | + MH_WARNING("Failed to set MPRIS track id since the id value is NULL"); |
546 | + } |
547 | + // TODO: This needs to be extracted from GStreamer and dynamically set |
548 | + if (not metadata.is_set(media::Track::MetaData::TrackArtlUrlKey)) |
549 | + metadata.set_art_url("file:///usr/lib/arm-linux-gnueabihf/unity-scopes/mediascanner-music/album_missing.svg"); |
550 | + |
551 | + parent->meta_data_for_current_track().set(metadata); |
552 | + } |
553 | + |
554 | bool pause_other_players(media::Player::PlayerKey key) |
555 | { |
556 | if (not config.parent.player_service) |
557 | @@ -501,6 +533,12 @@ |
558 | d->engine->lifetime().set(lifetime); |
559 | }); |
560 | |
561 | + d->engine->track_meta_data().changed().connect([this, config]( |
562 | + const std::tuple<media::Track::UriType, media::Track::MetaData>& md) |
563 | + { |
564 | + d->update_mpris_metadata(std::get<1>(md)); |
565 | + }); |
566 | + |
567 | d->engine->about_to_finish_signal().connect([this]() |
568 | { |
569 | if (d->doing_abandon) |
570 | |
571 | === modified file 'src/core/media/player_skeleton.cpp' |
572 | --- src/core/media/player_skeleton.cpp 2016-04-06 15:28:29 +0000 |
573 | +++ src/core/media/player_skeleton.cpp 2016-06-09 16:54:40 +0000 |
574 | @@ -478,7 +478,7 @@ |
575 | |
576 | const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const |
577 | { |
578 | - return *d->skeleton.properties.typed_meta_data_for_current_track; |
579 | + return *d->skeleton.properties.meta_data_for_current_track; |
580 | } |
581 | |
582 | const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const |
583 | @@ -608,7 +608,7 @@ |
584 | |
585 | core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() |
586 | { |
587 | - return *d->skeleton.properties.typed_meta_data_for_current_track; |
588 | + return *d->skeleton.properties.meta_data_for_current_track; |
589 | } |
590 | |
591 | core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() |
592 | |
593 | === modified file 'src/core/media/player_stub.cpp' |
594 | --- src/core/media/player_stub.cpp 2016-04-06 15:28:29 +0000 |
595 | +++ src/core/media/player_stub.cpp 2016-06-09 16:54:40 +0000 |
596 | @@ -70,7 +70,7 @@ |
597 | object->get_property<mpris::Player::Properties::TypedLoopStatus>(), |
598 | object->get_property<mpris::Player::Properties::PlaybackRate>(), |
599 | object->get_property<mpris::Player::Properties::Shuffle>(), |
600 | - object->get_property<mpris::Player::Properties::TypedMetaData>(), |
601 | + object->get_property<mpris::Player::Properties::Metadata>(), |
602 | object->get_property<mpris::Player::Properties::Volume>(), |
603 | object->get_property<mpris::Player::Properties::Position>(), |
604 | object->get_property<mpris::Player::Properties::Duration>(), |
605 | @@ -118,7 +118,7 @@ |
606 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> loop_status; |
607 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate; |
608 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> shuffle; |
609 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track; |
610 | + std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Metadata>> meta_data_for_current_track; |
611 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume; |
612 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position; |
613 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration; |
614 | |
615 | === modified file 'src/core/media/service_skeleton.cpp' |
616 | --- src/core/media/service_skeleton.cpp 2016-05-17 17:51:16 +0000 |
617 | +++ src/core/media/service_skeleton.cpp 2016-06-09 16:54:40 +0000 |
618 | @@ -732,6 +732,21 @@ |
619 | player.properties.can_go_next->set(can_go_next); |
620 | }); |
621 | |
622 | + // NOTE: the metadata first gets updated in the PlayerImplementation constructor which connects |
623 | + // and reacts to the Engine track_meta_data changed signal. Setting the |
624 | + // meta_data_for_current_track here makes sure that the signal uses the MPRIS object path |
625 | + connections.meta_data_changed = player_sp->meta_data_for_current_track().changed().connect( |
626 | + [this](const media::Track::MetaData& metadata) |
627 | + { |
628 | + player.properties.meta_data_for_current_track->set(metadata); |
629 | + mpris::Player::Dictionary dict; dict[mpris::Player::Properties::Metadata::name()] |
630 | + = dbus::types::Variant::encode(metadata); |
631 | + player.signals.properties_changed->emit(std::make_tuple( |
632 | + dbus::traits::Service<mpris::Player>::interface_name(), |
633 | + dict, |
634 | + mpris::Player::Skeleton::the_empty_list_of_invalidated_properties())); |
635 | + }); |
636 | + |
637 | // Sync property values between session and player mpris::Player instances |
638 | // TODO Getters from media::Player actually return values from a |
639 | // mpris::Player::Skeleton instance different from "player". Each of them use |
640 | @@ -748,43 +763,6 @@ |
641 | player.properties.can_pause->set(player_sp->can_pause().get()); |
642 | player.properties.can_go_previous->set(player_sp->can_go_previous().get()); |
643 | player.properties.can_go_next->set(player_sp->can_go_next().get()); |
644 | - |
645 | -#if 0 |
646 | - // TODO cover_art_resolver() is not implemented yet |
647 | - connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect( |
648 | - [this](const core::ubuntu::media::Track::MetaData& md) |
649 | - { |
650 | - mpris::Player::Dictionary dict; |
651 | - |
652 | - bool has_title = md.count(xesam::Title::name) > 0; |
653 | - bool has_album_name = md.count(xesam::Album::name) > 0; |
654 | - bool has_artist_name = md.count(xesam::Artist::name) > 0; |
655 | - |
656 | - if (has_title) |
657 | - dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name)); |
658 | - if (has_album_name) |
659 | - dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name)); |
660 | - if (has_artist_name) |
661 | - dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name)); |
662 | - |
663 | - dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode( |
664 | - cover_art_resolver( |
665 | - has_title ? md.get(xesam::Title::name) : "", |
666 | - has_album_name ? md.get(xesam::Album::name) : "", |
667 | - has_artist_name ? md.get(xesam::Artist::name) : "")); |
668 | - |
669 | - mpris::Player::Dictionary wrap; |
670 | - wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict); |
671 | - |
672 | - player.signals.properties_changed->emit( |
673 | - std::make_tuple( |
674 | - dbus::traits::Service< |
675 | - mpris::Player::Properties::Metadata::Interface> |
676 | - ::interface_name(), |
677 | - wrap, |
678 | - std::vector<std::string>())); |
679 | - }); |
680 | -#endif |
681 | } |
682 | |
683 | void reset_current_player() |
684 | @@ -807,6 +785,7 @@ |
685 | connections.can_pause_changed = the_empty_signal.connect([](){}); |
686 | connections.can_go_previous_changed = the_empty_signal.connect([](){}); |
687 | connections.can_go_next_changed = the_empty_signal.connect([](){}); |
688 | + connections.meta_data_changed = the_empty_signal.connect([](){}); |
689 | } |
690 | |
691 | bool is_current_player(media::Player::PlayerKey key) |
Some minor nits around.