Merge lp:~phablet-team/media-hub/support-albumart into lp:media-hub
- support-albumart
- Merge into trunk
Proposed by
Jim Hodapp
Status: | Superseded |
---|---|
Proposed branch: | lp:~phablet-team/media-hub/support-albumart |
Merge into: | lp:media-hub |
Diff against target: |
798 lines (+360/-114) 13 files modified
include/core/media/track.h (+30/-46) src/core/media/CMakeLists.txt (+2/-0) src/core/media/codec.h (+88/-8) src/core/media/cover_art_resolver.cpp (+1/-1) src/core/media/gstreamer/meta_data_extractor.h (+24/-5) 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 (+92/-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) src/core/media/xesam.h (+8/-0) |
To merge this branch: | bzr merge lp:~phablet-team/media-hub/support-albumart |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phablet Team | Pending | ||
Review via email: mp+297388@code.launchpad.net |
Commit message
Support albumart for music and video tracks.
Description of the change
Support albumart for music and video tracks.
To post a comment you must log in.
- 192. By Jim Hodapp
-
Add some more robustness and make sure artist and album names are percent encoded when being passed as an image://albumart/ URI
- 193. By Jim Hodapp
-
Clean up unnecessary debug statements
- 194. By Jim Hodapp
-
Got rid of one last unnecessary debug line
- 195. By Jim Hodapp
-
Merged with upstream
Unmerged revisions
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-14 19:43:39 +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-14 19:43:39 +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-14 19:43:39 +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,88 @@ |
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 | + // The following tags are not part of the MPRIS spec and should not be encoded |
188 | + if (pair.first == tags::Image::name or |
189 | + pair.first == tags::PreviewImage::name) |
190 | + continue; |
191 | + |
192 | + auto de = dict.open_dict_entry(); |
193 | + { |
194 | + if (pair.first == core::ubuntu::media::Track::MetaData::TrackLengthKey |
195 | + and not pair.second.empty()) |
196 | + { |
197 | + Codec<Pair>::encode_argument(de, std::make_pair(pair.first, |
198 | + dbus::types::Variant::encode( |
199 | + boost::lexical_cast<std::int64_t>(pair.second)))); |
200 | + } |
201 | + else if (pair.first == xesam::Album::name and not pair.second.empty()) |
202 | + { |
203 | + Codec<Pair>::encode_argument(de, std::make_pair(pair.first, |
204 | + dbus::types::Variant::encode(pair.second))); |
205 | + } |
206 | + else if (pair.first == xesam::AlbumArtist::name and not pair.second.empty()) |
207 | + { |
208 | +#if 0 |
209 | + // TODO: This code doesn't work but will be needed for full MPRIS compliance. |
210 | + // Technically there can be more than one album artist stuffed into an array. |
211 | + // How to satisfy stuffing this data into dbus-cpp is what needs fixing. |
212 | + auto array = de.open_array(dbus::types::Signature |
213 | + {dbus::helper::TypeMapper<Pair>::signature()}); |
214 | + { |
215 | + // TODO: this should really iterate over all artists, but seems |
216 | + // we only extract one artist from playbin |
217 | + Codec<Pair>::encode_argument(array, std::make_pair(pair.first, |
218 | + dbus::types::Variant::encode(pair.second))); |
219 | + } |
220 | + de.close_array(std::move(array)); |
221 | +#else |
222 | + Codec<Pair>::encode_argument(de, std::make_pair(pair.first, |
223 | + dbus::types::Variant::encode(pair.second))); |
224 | +#endif |
225 | + } |
226 | + else |
227 | + { |
228 | + Codec<Pair>::encode_argument(de, std::make_pair(pair.first, |
229 | + dbus::types::Variant::encode(pair.second))); |
230 | + } |
231 | + } |
232 | + dict.close_dict_entry(std::move(de)); |
233 | + } |
234 | + writer.close_array(std::move(dict)); |
235 | } |
236 | |
237 | - static void decode_argument(core::dbus::Message::Reader& out, core::ubuntu::media::Track::MetaData& in) |
238 | + static void decode_argument(core::dbus::Message::Reader& reader, core::ubuntu::media::Track::MetaData& md) |
239 | { |
240 | - (void) out; |
241 | - (void) in; |
242 | + auto array = reader.pop_array(); |
243 | + |
244 | + while (array.type() != dbus::ArgumentType::invalid) |
245 | + { |
246 | + auto entry = array.pop_dict_entry(); |
247 | + { |
248 | + std::string key {entry.pop_string()}; |
249 | + auto variant = entry.pop_variant(); |
250 | + { |
251 | + if (key == xesam::Album::name) |
252 | + { |
253 | + const std::string album = variant.pop_string(); |
254 | + MH_DEBUG("Getting key \"%s\" and value \"%s\"", key, album); |
255 | + md.set_album(album); |
256 | + } |
257 | + else |
258 | + { |
259 | + MH_WARNING("Unknown metadata key \"%s\" while decoding dbus message", key); |
260 | + } |
261 | + } |
262 | + } |
263 | + } |
264 | } |
265 | }; |
266 | |
267 | |
268 | === modified file 'src/core/media/cover_art_resolver.cpp' |
269 | --- src/core/media/cover_art_resolver.cpp 2014-09-09 14:02:52 +0000 |
270 | +++ src/core/media/cover_art_resolver.cpp 2016-06-14 19:43:39 +0000 |
271 | @@ -22,6 +22,6 @@ |
272 | { |
273 | return [](const std::string&, const std::string&, const std::string&) |
274 | { |
275 | - return "file:///usr/share/unity/icons/album_missing.png"; |
276 | + return "file:///usr/lib/arm-linux-gnueabihf/unity-scopes/mediascanner-music/album_missing.svg"; |
277 | }; |
278 | } |
279 | |
280 | === modified file 'src/core/media/gstreamer/meta_data_extractor.h' |
281 | --- src/core/media/gstreamer/meta_data_extractor.h 2015-09-23 11:17:26 +0000 |
282 | +++ src/core/media/gstreamer/meta_data_extractor.h 2016-06-14 19:43:39 +0000 |
283 | @@ -24,6 +24,8 @@ |
284 | |
285 | #include "bus.h" |
286 | |
287 | +#include "core/media/logger/logger.h" |
288 | + |
289 | #include <gst/gst.h> |
290 | |
291 | #include <exception> |
292 | @@ -49,7 +51,11 @@ |
293 | {GST_TAG_GENRE, std::string{xesam::Genre::name}}, |
294 | {GST_TAG_TITLE, std::string{xesam::Title::name}}, |
295 | {GST_TAG_TRACK_NUMBER, std::string{xesam::TrackNumber::name}}, |
296 | - {GST_TAG_USER_RATING, std::string{xesam::UserRating::name}} |
297 | + {GST_TAG_USER_RATING, std::string{xesam::UserRating::name}}, |
298 | + // Below this line are custom entries related but not directly from |
299 | + // the MPRIS spec: |
300 | + {GST_TAG_IMAGE, std::string{tags::Image::name}}, |
301 | + {GST_TAG_PREVIEW_IMAGE, std::string{tags::PreviewImage::name}} |
302 | }; |
303 | |
304 | return lut; |
305 | @@ -72,7 +78,7 @@ |
306 | auto md = static_cast<media::Track::MetaData*>(user_data); |
307 | std::stringstream ss; |
308 | |
309 | - switch(gst_tag_get_type(tag)) |
310 | + switch (gst_tag_get_type(tag)) |
311 | { |
312 | case G_TYPE_BOOLEAN: |
313 | { |
314 | @@ -137,9 +143,22 @@ |
315 | break; |
316 | } |
317 | |
318 | - (*md).set( |
319 | - (gstreamer_to_mpris_tag_lut().count(tag) > 0 ? gstreamer_to_mpris_tag_lut().at(tag) : tag), |
320 | - ss.str()); |
321 | + const bool has_tag_from_lut = (gstreamer_to_mpris_tag_lut().count(tag) > 0); |
322 | + const std::string tag_name{(has_tag_from_lut) ? |
323 | + gstreamer_to_mpris_tag_lut().at(tag) : tag}; |
324 | + |
325 | + |
326 | + // Specific handling for the following tag types: |
327 | + if (tag_name == tags::PreviewImage::name) |
328 | + ss << "true"; |
329 | + if (tag_name == tags::Image::name) |
330 | + ss << "true"; |
331 | + |
332 | + (*md).set((has_tag_from_lut ? |
333 | + gstreamer_to_mpris_tag_lut().at(tag) : tag), ss.str()); |
334 | + |
335 | + MH_DEBUG("Tag name: \"%s\", value: \"%s\"", tag_name, ss.str()); |
336 | + |
337 | }, |
338 | &md); |
339 | } |
340 | |
341 | === modified file 'src/core/media/gstreamer/playbin.cpp' |
342 | --- src/core/media/gstreamer/playbin.cpp 2016-04-06 15:28:29 +0000 |
343 | +++ src/core/media/gstreamer/playbin.cpp 2016-06-14 19:43:39 +0000 |
344 | @@ -30,8 +30,6 @@ |
345 | |
346 | #include <utility> |
347 | |
348 | -//#define VERBOSE_DEBUG |
349 | - |
350 | namespace |
351 | { |
352 | void setup_video_sink_for_buffer_streaming(GstElement* pipeline) |
353 | @@ -256,7 +254,7 @@ |
354 | |
355 | void gstreamer::Playbin::on_new_message_async(const Bus::Message& message) |
356 | { |
357 | - switch(message.type) |
358 | + switch (message.type) |
359 | { |
360 | case GST_MESSAGE_ERROR: |
361 | signals.on_error(message.detail.error_warning_info); |
362 | |
363 | === added file 'src/core/media/metadata.cpp' |
364 | --- src/core/media/metadata.cpp 1970-01-01 00:00:00 +0000 |
365 | +++ src/core/media/metadata.cpp 2016-06-14 19:43:39 +0000 |
366 | @@ -0,0 +1,83 @@ |
367 | +/* |
368 | + * Copyright © 2016 Canonical Ltd. |
369 | + * |
370 | + * This program is free software: you can redistribute it and/or modify it |
371 | + * under the terms of the GNU Lesser General Public License version 3, |
372 | + * as published by the Free Software Foundation. |
373 | + * |
374 | + * This program is distributed in the hope that it will be useful, |
375 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
376 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
377 | + * GNU Lesser General Public License for more details. |
378 | + * |
379 | + * You should have received a copy of the GNU Lesser General Public License |
380 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
381 | + * |
382 | + * Authored by: Jim Hodapp <jim.hodapp@canonical.com> |
383 | + */ |
384 | + |
385 | +#include "xesam.h" |
386 | + |
387 | +#include <core/media/track.h> |
388 | + |
389 | +namespace media = core::ubuntu::media; |
390 | + |
391 | +const std::string& media::Track::MetaData::album() const |
392 | +{ |
393 | + return map.at(xesam::Album::name); |
394 | +} |
395 | + |
396 | +const std::string& media::Track::MetaData::artist() const |
397 | +{ |
398 | + return map.at(xesam::Artist::name); |
399 | +} |
400 | + |
401 | +const std::string& media::Track::MetaData::title() const |
402 | +{ |
403 | + return map.at(xesam::Title::name); |
404 | +} |
405 | + |
406 | +const std::string& media::Track::MetaData::track_id() const |
407 | +{ |
408 | + return map.at(media::Track::MetaData::TrackIdKey); |
409 | +} |
410 | + |
411 | +const std::string& media::Track::MetaData::art_url() const |
412 | +{ |
413 | + return map.at(media::Track::MetaData::TrackArtlUrlKey); |
414 | +} |
415 | + |
416 | +const std::string& media::Track::MetaData::last_used() const |
417 | +{ |
418 | + return map.at(xesam::LastUsed::name); |
419 | +} |
420 | + |
421 | +void media::Track::MetaData::set_album(const std::string& album) |
422 | +{ |
423 | + map[xesam::Album::name] = album; |
424 | +} |
425 | + |
426 | +void media::Track::MetaData::set_artist(const std::string& artist) |
427 | +{ |
428 | + map[xesam::Artist::name] = artist; |
429 | +} |
430 | + |
431 | +void media::Track::MetaData::set_title(const std::string& title) |
432 | +{ |
433 | + map[xesam::Title::name] = title; |
434 | +} |
435 | + |
436 | +void media::Track::MetaData::set_track_id(const std::string& id) |
437 | +{ |
438 | + map[media::Track::MetaData::TrackIdKey] = id; |
439 | +} |
440 | + |
441 | +void media::Track::MetaData::set_art_url(const std::string& url) |
442 | +{ |
443 | + map[media::Track::MetaData::TrackArtlUrlKey] = url; |
444 | +} |
445 | + |
446 | +void media::Track::MetaData::set_last_used(const std::string& datetime) |
447 | +{ |
448 | + map[xesam::LastUsed::name] = datetime; |
449 | +} |
450 | |
451 | === modified file 'src/core/media/mpris/player.h' |
452 | --- src/core/media/mpris/player.h 2016-02-19 16:14:42 +0000 |
453 | +++ src/core/media/mpris/player.h 2016-06-14 19:43:39 +0000 |
454 | @@ -44,6 +44,8 @@ |
455 | |
456 | #include <cstdint> |
457 | |
458 | +#include "core/media/logger/logger.h" |
459 | + |
460 | namespace dbus = core::dbus; |
461 | |
462 | namespace mpris |
463 | @@ -172,8 +174,7 @@ |
464 | DBUS_CPP_WRITABLE_PROPERTY_DEF(PlaybackRate, Player, double) |
465 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Rate, Player, double) |
466 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Shuffle, Player, bool) |
467 | - DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, Dictionary) |
468 | - DBUS_CPP_READABLE_PROPERTY_DEF(TypedMetaData, Player, core::ubuntu::media::Track::MetaData) |
469 | + DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, core::ubuntu::media::Track::MetaData) |
470 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Player, double) |
471 | DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::int64_t) |
472 | DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::int64_t) |
473 | @@ -222,7 +223,7 @@ |
474 | Properties::TypedLoopStatus::ValueType typed_loop_status{core::ubuntu::media::Player::LoopStatus::none}; |
475 | Properties::PlaybackRate::ValueType playback_rate{1.f}; |
476 | Properties::Shuffle::ValueType shuffle{false}; |
477 | - Properties::TypedMetaData::ValueType typed_meta_data{}; |
478 | + Properties::Metadata::ValueType meta_data{}; |
479 | Properties::Volume::ValueType volume{0.f}; |
480 | Properties::Position::ValueType position{0}; |
481 | Properties::Duration::ValueType duration{0}; |
482 | @@ -253,7 +254,7 @@ |
483 | configuration.object->template get_property<Properties::Lifetime>(), |
484 | configuration.object->template get_property<Properties::PlaybackRate>(), |
485 | configuration.object->template get_property<Properties::Shuffle>(), |
486 | - configuration.object->template get_property<Properties::TypedMetaData>(), |
487 | + configuration.object->template get_property<Properties::Metadata>(), |
488 | configuration.object->template get_property<Properties::Volume>(), |
489 | configuration.object->template get_property<Properties::Position>(), |
490 | configuration.object->template get_property<Properties::Duration>(), |
491 | @@ -288,6 +289,7 @@ |
492 | properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal); |
493 | properties.playback_rate->set(configuration.defaults.playback_rate); |
494 | properties.shuffle->set(configuration.defaults.shuffle); |
495 | + properties.meta_data_for_current_track->set(configuration.defaults.meta_data); |
496 | properties.position->set(configuration.defaults.position); |
497 | properties.duration->set(configuration.defaults.duration); |
498 | properties.minimum_playback_rate->set(configuration.defaults.minimum_rate); |
499 | @@ -351,9 +353,9 @@ |
500 | Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value); |
501 | |
502 | signals.properties_changed->emit(std::make_tuple( |
503 | - dbus::traits::Service<Player>::interface_name(), |
504 | - dict, |
505 | - the_empty_list_of_invalidated_properties())); |
506 | + dbus::traits::Service<Player>::interface_name(), |
507 | + dict, |
508 | + the_empty_list_of_invalidated_properties())); |
509 | } |
510 | |
511 | Dictionary get_all_properties() |
512 | @@ -374,6 +376,7 @@ |
513 | dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->get()); |
514 | dict[Properties::PlaybackRate::name()] = dbus::types::Variant::encode(properties.playback_rate->get()); |
515 | dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.shuffle->get()); |
516 | + dict[Properties::Metadata::name()] = dbus::types::Variant::encode(properties.meta_data_for_current_track->get()); |
517 | dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get()); |
518 | dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get()); |
519 | dict[Properties::MinimumRate::name()] = dbus::types::Variant::encode(properties.minimum_playback_rate->get()); |
520 | @@ -405,7 +408,7 @@ |
521 | std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime; |
522 | std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate; |
523 | std::shared_ptr<core::dbus::Property<Properties::Shuffle>> shuffle; |
524 | - std::shared_ptr<core::dbus::Property<Properties::TypedMetaData>> typed_meta_data_for_current_track; |
525 | + std::shared_ptr<core::dbus::Property<Properties::Metadata>> meta_data_for_current_track; |
526 | std::shared_ptr<core::dbus::Property<Properties::Volume>> volume; |
527 | std::shared_ptr<core::dbus::Property<Properties::Position>> position; |
528 | std::shared_ptr<core::dbus::Property<Properties::Duration>> duration; |
529 | |
530 | === modified file 'src/core/media/player_implementation.cpp' |
531 | --- src/core/media/player_implementation.cpp 2016-05-03 19:08:00 +0000 |
532 | +++ src/core/media/player_implementation.cpp 2016-06-14 19:43:39 +0000 |
533 | @@ -24,10 +24,12 @@ |
534 | #include "util/timeout.h" |
535 | |
536 | #include <unistd.h> |
537 | +#include <ctime> |
538 | |
539 | #include "client_death_observer.h" |
540 | #include "engine.h" |
541 | #include "track_list_implementation.h" |
542 | +#include "xesam.h" |
543 | |
544 | #include "gstreamer/engine.h" |
545 | |
546 | @@ -138,9 +140,19 @@ |
547 | } |
548 | case Engine::State::playing: |
549 | { |
550 | - // We update the track meta data prior to updating the playback status. |
551 | + // We update the track metadata prior to updating the playback status. |
552 | // Some MPRIS clients expect this order of events. |
553 | - parent->meta_data_for_current_track().set(std::get<1>(engine->track_meta_data().get())); |
554 | + time_t now; |
555 | + time(&now); |
556 | + char buf[sizeof("2011-10-08T07:07:09Z")]; |
557 | + strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&now)); |
558 | + media::Track::MetaData metadata{std::get<1>(engine->track_meta_data().get())}; |
559 | + // Setting this with second resolution makes sure that the track_meta_data property changes |
560 | + // and thus the track_meta_data().changed() signal gets sent out over dbus. Otherwise the |
561 | + // Property caching mechanism would prevent this. |
562 | + metadata.set_last_used(std::string{buf}); |
563 | + update_mpris_metadata(std::get<0>(engine->track_meta_data().get()), metadata); |
564 | + |
565 | // And update our playback status. |
566 | parent->playback_status().set(media::Player::playing); |
567 | MH_INFO("Requesting power state"); |
568 | @@ -325,6 +337,78 @@ |
569 | parent->can_go_next().set(has_next); |
570 | } |
571 | |
572 | + std::string get_uri_for_album_artwork(const media::Track::UriType& uri, |
573 | + const media::Track::MetaData& metadata) |
574 | + { |
575 | + std::string art_uri; |
576 | + static const std::string file_uri_prefix{"file://"}; |
577 | + size_t pos = uri.find_first_of(file_uri_prefix); |
578 | + MH_DEBUG("find_first_of: %d", pos != std::string::npos); |
579 | + MH_DEBUG("uri.at(0): %c", uri.at(0)); |
580 | + const bool is_local_file = (pos != std::string::npos or uri.at(0) == '/'); |
581 | + MH_DEBUG("is_local_file ? %d", is_local_file); |
582 | + MH_DEBUG("is_video_source: %d", parent->is_video_source().get()); |
583 | + // If the track has a full image or preview image or is a video and it is a local file, |
584 | + // then use the thumbnailer cache |
585 | + if ( (( metadata.count(tags::PreviewImage::name) > 0 |
586 | + and metadata.get(tags::PreviewImage::name) == "true") |
587 | + or ( metadata.count(tags::Image::name) > 0 |
588 | + and metadata.get(tags::Image::name) == "true") |
589 | + or parent->is_video_source().get()) |
590 | + and is_local_file) |
591 | + { |
592 | + MH_DEBUG("Detected the presence of a local preview image or full image"); |
593 | + art_uri = "image://thumbnailer/" + uri; |
594 | + MH_DEBUG("art_uri: %s", art_uri); |
595 | + } |
596 | + // Otherwise we'll try and see if we can look up the album art online through |
597 | + // the dash's artwork proxy |
598 | + else if (metadata.is_set(xesam::Album::name) or metadata.is_set(xesam::Artist::name)) |
599 | + { |
600 | + if (metadata.is_set(xesam::Album::name) and metadata.is_set(xesam::Artist::name)) |
601 | + { |
602 | + art_uri = "image://albumart/artist=" + metadata.artist() |
603 | + + "&album=" + metadata.album(); |
604 | + } |
605 | + else |
606 | + { |
607 | + art_uri = "image://albumart/"; |
608 | + if (metadata.is_set(xesam::Artist::name)) |
609 | + art_uri += "artist=" + metadata.artist(); |
610 | + else if (metadata.is_set(xesam::Album::name)) |
611 | + art_uri += "album=" + metadata.album(); |
612 | + } |
613 | + } |
614 | + // If all else fails, display a placeholder icon |
615 | + else |
616 | + { |
617 | + art_uri = "file:///usr/lib/arm-linux-gnueabihf/unity-scopes/mediascanner-music/album_missing.svg"; |
618 | + } |
619 | + |
620 | + return art_uri; |
621 | + } |
622 | + |
623 | + // Makes sure all relevant metadata fields are set to current data and |
624 | + // will trigger the track_meta_data().changed() signal to go out over dbus |
625 | + void update_mpris_metadata(const media::Track::UriType& uri, const media::Track::MetaData& md) |
626 | + { |
627 | + media::Track::MetaData metadata{md}; |
628 | + if (not metadata.is_set(media::Track::MetaData::TrackIdKey)) |
629 | + { |
630 | + const std::size_t last_slash = track_list->current().find_last_of("/"); |
631 | + const std::string track_id = track_list->current().substr(last_slash+1); |
632 | + if (not track_id.empty()) |
633 | + metadata.set_track_id("/org/mpris/MediaPlayer2/Track/" + track_id); |
634 | + else |
635 | + MH_WARNING("Failed to set MPRIS track id since the id value is NULL"); |
636 | + } |
637 | + |
638 | + if (not metadata.is_set(media::Track::MetaData::TrackArtlUrlKey)) |
639 | + metadata.set_art_url(get_uri_for_album_artwork(uri, metadata)); |
640 | + |
641 | + parent->meta_data_for_current_track().set(metadata); |
642 | + } |
643 | + |
644 | bool pause_other_players(media::Player::PlayerKey key) |
645 | { |
646 | if (not config.parent.player_service) |
647 | @@ -501,6 +585,12 @@ |
648 | d->engine->lifetime().set(lifetime); |
649 | }); |
650 | |
651 | + d->engine->track_meta_data().changed().connect([this, config]( |
652 | + const std::tuple<media::Track::UriType, media::Track::MetaData>& md) |
653 | + { |
654 | + d->update_mpris_metadata(std::get<0>(md), std::get<1>(md)); |
655 | + }); |
656 | + |
657 | d->engine->about_to_finish_signal().connect([this]() |
658 | { |
659 | if (d->doing_abandon) |
660 | |
661 | === modified file 'src/core/media/player_skeleton.cpp' |
662 | --- src/core/media/player_skeleton.cpp 2016-04-06 15:28:29 +0000 |
663 | +++ src/core/media/player_skeleton.cpp 2016-06-14 19:43:39 +0000 |
664 | @@ -478,7 +478,7 @@ |
665 | |
666 | const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const |
667 | { |
668 | - return *d->skeleton.properties.typed_meta_data_for_current_track; |
669 | + return *d->skeleton.properties.meta_data_for_current_track; |
670 | } |
671 | |
672 | const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const |
673 | @@ -608,7 +608,7 @@ |
674 | |
675 | core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() |
676 | { |
677 | - return *d->skeleton.properties.typed_meta_data_for_current_track; |
678 | + return *d->skeleton.properties.meta_data_for_current_track; |
679 | } |
680 | |
681 | core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() |
682 | |
683 | === modified file 'src/core/media/player_stub.cpp' |
684 | --- src/core/media/player_stub.cpp 2016-04-06 15:28:29 +0000 |
685 | +++ src/core/media/player_stub.cpp 2016-06-14 19:43:39 +0000 |
686 | @@ -70,7 +70,7 @@ |
687 | object->get_property<mpris::Player::Properties::TypedLoopStatus>(), |
688 | object->get_property<mpris::Player::Properties::PlaybackRate>(), |
689 | object->get_property<mpris::Player::Properties::Shuffle>(), |
690 | - object->get_property<mpris::Player::Properties::TypedMetaData>(), |
691 | + object->get_property<mpris::Player::Properties::Metadata>(), |
692 | object->get_property<mpris::Player::Properties::Volume>(), |
693 | object->get_property<mpris::Player::Properties::Position>(), |
694 | object->get_property<mpris::Player::Properties::Duration>(), |
695 | @@ -118,7 +118,7 @@ |
696 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> loop_status; |
697 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate; |
698 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> shuffle; |
699 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track; |
700 | + std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Metadata>> meta_data_for_current_track; |
701 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume; |
702 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position; |
703 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration; |
704 | |
705 | === modified file 'src/core/media/service_skeleton.cpp' |
706 | --- src/core/media/service_skeleton.cpp 2016-05-17 17:51:16 +0000 |
707 | +++ src/core/media/service_skeleton.cpp 2016-06-14 19:43:39 +0000 |
708 | @@ -732,6 +732,21 @@ |
709 | player.properties.can_go_next->set(can_go_next); |
710 | }); |
711 | |
712 | + // NOTE: the metadata first gets updated in the PlayerImplementation constructor which connects |
713 | + // and reacts to the Engine track_meta_data changed signal. Setting the |
714 | + // meta_data_for_current_track here makes sure that the signal uses the MPRIS object path |
715 | + connections.meta_data_changed = player_sp->meta_data_for_current_track().changed().connect( |
716 | + [this](const media::Track::MetaData& metadata) |
717 | + { |
718 | + player.properties.meta_data_for_current_track->set(metadata); |
719 | + mpris::Player::Dictionary dict; dict[mpris::Player::Properties::Metadata::name()] |
720 | + = dbus::types::Variant::encode(metadata); |
721 | + player.signals.properties_changed->emit(std::make_tuple( |
722 | + dbus::traits::Service<mpris::Player>::interface_name(), |
723 | + dict, |
724 | + mpris::Player::Skeleton::the_empty_list_of_invalidated_properties())); |
725 | + }); |
726 | + |
727 | // Sync property values between session and player mpris::Player instances |
728 | // TODO Getters from media::Player actually return values from a |
729 | // mpris::Player::Skeleton instance different from "player". Each of them use |
730 | @@ -748,43 +763,6 @@ |
731 | player.properties.can_pause->set(player_sp->can_pause().get()); |
732 | player.properties.can_go_previous->set(player_sp->can_go_previous().get()); |
733 | player.properties.can_go_next->set(player_sp->can_go_next().get()); |
734 | - |
735 | -#if 0 |
736 | - // TODO cover_art_resolver() is not implemented yet |
737 | - connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect( |
738 | - [this](const core::ubuntu::media::Track::MetaData& md) |
739 | - { |
740 | - mpris::Player::Dictionary dict; |
741 | - |
742 | - bool has_title = md.count(xesam::Title::name) > 0; |
743 | - bool has_album_name = md.count(xesam::Album::name) > 0; |
744 | - bool has_artist_name = md.count(xesam::Artist::name) > 0; |
745 | - |
746 | - if (has_title) |
747 | - dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name)); |
748 | - if (has_album_name) |
749 | - dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name)); |
750 | - if (has_artist_name) |
751 | - dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name)); |
752 | - |
753 | - dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode( |
754 | - cover_art_resolver( |
755 | - has_title ? md.get(xesam::Title::name) : "", |
756 | - has_album_name ? md.get(xesam::Album::name) : "", |
757 | - has_artist_name ? md.get(xesam::Artist::name) : "")); |
758 | - |
759 | - mpris::Player::Dictionary wrap; |
760 | - wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict); |
761 | - |
762 | - player.signals.properties_changed->emit( |
763 | - std::make_tuple( |
764 | - dbus::traits::Service< |
765 | - mpris::Player::Properties::Metadata::Interface> |
766 | - ::interface_name(), |
767 | - wrap, |
768 | - std::vector<std::string>())); |
769 | - }); |
770 | -#endif |
771 | } |
772 | |
773 | void reset_current_player() |
774 | @@ -807,6 +785,7 @@ |
775 | connections.can_pause_changed = the_empty_signal.connect([](){}); |
776 | connections.can_go_previous_changed = the_empty_signal.connect([](){}); |
777 | connections.can_go_next_changed = the_empty_signal.connect([](){}); |
778 | + connections.meta_data_changed = the_empty_signal.connect([](){}); |
779 | } |
780 | |
781 | bool is_current_player(media::Player::PlayerKey key) |
782 | |
783 | === modified file 'src/core/media/xesam.h' |
784 | --- src/core/media/xesam.h 2014-09-10 07:05:15 +0000 |
785 | +++ src/core/media/xesam.h 2016-06-14 19:43:39 +0000 |
786 | @@ -53,4 +53,12 @@ |
787 | DATUM(UserRating, xesam:userRating, double) |
788 | } |
789 | |
790 | +namespace tags |
791 | +{ |
792 | +// Does the track contain album art? |
793 | +DATUM(Image, tag:image, bool) |
794 | +// Does the track contain a small album art preview image? |
795 | +DATUM(PreviewImage, tag::previewImage, bool) |
796 | +} |
797 | + |
798 | #endif // XESAM_H_ |