Merge lp:~phablet-team/media-hub/support-albumart into lp:media-hub

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
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_

Subscribers

People subscribed via source and target branches

to all changes: