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
=== modified file 'include/core/media/track.h'
--- include/core/media/track.h 2015-07-02 12:30:09 +0000
+++ include/core/media/track.h 2016-06-14 19:43:39 +0000
@@ -39,10 +39,15 @@
39public:39public:
40 typedef std::string UriType;40 typedef std::string UriType;
41 typedef std::string Id;41 typedef std::string Id;
42 typedef std::map<std::string, std::string> MetaDataType;
4243
43 class MetaData44 class MetaData
44 {45 {
45 public:46 public:
47 static constexpr const char* TrackArtlUrlKey = "mpris:artUrl";
48 static constexpr const char* TrackLengthKey = "mpris:length";
49 static constexpr const char* TrackIdKey = "mpris:trackid";
50
46 bool operator==(const MetaData& rhs) const51 bool operator==(const MetaData& rhs) const
47 {52 {
48 return map == rhs.map;53 return map == rhs.map;
@@ -90,13 +95,36 @@
90 return map.at(key);95 return map.at(key);
91 }96 }
9297
98 bool is_set(const std::string& key) const
99 {
100 try {
101 return map.at(key).empty();
102 } catch (const std::out_of_range& e) {
103 return false;
104 }
105 }
106
93 const std::map<std::string, std::string>& operator*() const107 const std::map<std::string, std::string>& operator*() const
94 {108 {
95 return map;109 return map;
96 }110 }
97111
112 const std::string& album() const;
113 const std::string& artist() const;
114 const std::string& title() const;
115 const std::string& track_id() const;
116 const std::string& art_url() const;
117 const std::string& last_used() const;
118
119 void set_album(const std::string& album);
120 void set_artist(const std::string& artist);
121 void set_title(const std::string& title);
122 void set_track_id(const std::string& id);
123 void set_art_url(const std::string& url);
124 void set_last_used(const std::string& datetime);
125
98 private:126 private:
99 std::map<std::string, std::string> map;127 MetaDataType map;
100 };128 };
101129
102 Track(const Id& id);130 Track(const Id& id);
@@ -108,51 +136,7 @@
108136
109 virtual const Id& id() const;137 virtual const Id& id() const;
110 virtual const UriType& uri() const;138 virtual const UriType& uri() const;
111 /*139
112 class MetaData
113 {
114 public:
115 MetaData() = default;
116 MetaData(const MetaData&) = default;
117 ~MetaData() = default;
118
119 MetaData& operator=(const MetaData&) = default;
120
121 bool operator==(const MetaData&) const
122 {
123 return true;
124 }
125
126 bool operator!=(const MetaData&) const
127 {
128 return false;
129 }
130
131 struct NotImplementedFields
132 {
133 NotImplementedFields() = default;
134
135 virtual const UriType& uri() const = 0;
136 virtual const std::chrono::microseconds length() const = 0;
137 virtual const UriType& art_uri() const = 0;
138 virtual const std::string& album() const = 0;
139 virtual const std::vector<std::string>& album_artist() const = 0;
140 virtual const std::vector<std::string>& artist() const = 0;
141 virtual const std::string& as_text() const = 0;
142 virtual unsigned int audio_bpm() const = 0;
143 virtual float auto_rating() const = 0;
144 virtual const std::vector<std::string>& comment() const = 0;
145 virtual const std::vector<std::string>& composer() const = 0;
146 virtual unsigned int disc_number() const = 0;
147 virtual const std::vector<std::string>& genre() const = 0;
148 virtual const std::vector<std::string>& lyricist() const = 0;
149 virtual const std::string title() const = 0;
150 virtual unsigned int track_number() const = 0;
151 virtual unsigned int use_count() const = 0;
152 virtual float user_rating() const = 0;
153 };
154 };
155*/
156private:140private:
157 struct Private;141 struct Private;
158 std::unique_ptr<Private> d;142 std::unique_ptr<Private> d;
159143
=== modified file 'src/core/media/CMakeLists.txt'
--- src/core/media/CMakeLists.txt 2016-04-05 15:40:11 +0000
+++ src/core/media/CMakeLists.txt 2016-06-14 19:43:39 +0000
@@ -49,6 +49,7 @@
49 service.cpp49 service.cpp
50 track.cpp50 track.cpp
51 track_list.cpp51 track_list.cpp
52 metadata.cpp
5253
53 player_stub.cpp54 player_stub.cpp
54 service_stub.cpp55 service_stub.cpp
@@ -99,6 +100,7 @@
99 hybris_client_death_observer.cpp100 hybris_client_death_observer.cpp
100 cover_art_resolver.cpp101 cover_art_resolver.cpp
101 engine.cpp102 engine.cpp
103 metadata.cpp
102104
103 apparmor/context.cpp105 apparmor/context.cpp
104 apparmor/ubuntu.cpp106 apparmor/ubuntu.cpp
105107
=== modified file 'src/core/media/codec.h'
--- src/core/media/codec.h 2015-04-14 19:11:15 +0000
+++ src/core/media/codec.h 2016-06-14 19:43:39 +0000
@@ -23,10 +23,18 @@
23#include <core/media/player.h>23#include <core/media/player.h>
24#include <core/media/track.h>24#include <core/media/track.h>
2525
26#include <core/dbus/types/stl/map.h>
26#include <core/dbus/types/stl/string.h>27#include <core/dbus/types/stl/string.h>
28#include <core/dbus/types/variant.h>
27#include <core/dbus/types/stl/vector.h>29#include <core/dbus/types/stl/vector.h>
28#include <core/dbus/codec.h>30#include <core/dbus/codec.h>
2931
32#include <boost/lexical_cast.hpp>
33
34#include "core/media/xesam.h"
35
36#include "core/media/logger/logger.h"
37
30namespace core38namespace core
31{39{
32namespace dbus40namespace dbus
@@ -38,7 +46,7 @@
38{46{
39 constexpr static ArgumentType type_value()47 constexpr static ArgumentType type_value()
40 {48 {
41 return ArgumentType::floating_point;49 return ArgumentType::array;
42 }50 }
43 constexpr static bool is_basic_type()51 constexpr static bool is_basic_type()
44 {52 {
@@ -51,7 +59,7 @@
5159
52 static std::string signature()60 static std::string signature()
53 {61 {
54 static const std::string s = TypeMapper<double>::signature();62 static const std::string s = TypeMapper<std::map<std::string, dbus::types::Variant>>::signature();
55 return s;63 return s;
56 }64 }
57};65};
@@ -60,16 +68,88 @@
60template<>68template<>
61struct Codec<core::ubuntu::media::Track::MetaData>69struct Codec<core::ubuntu::media::Track::MetaData>
62{70{
63 static void encode_argument(core::dbus::Message::Writer& out, const core::ubuntu::media::Track::MetaData& in)71 static void encode_argument(core::dbus::Message::Writer& writer, const core::ubuntu::media::Track::MetaData& md)
64 {72 {
65 (void) out;73 typedef std::pair<std::string, dbus::types::Variant> Pair;
66 (void) in;74 auto dict = writer.open_array(dbus::types::Signature
75 {dbus::helper::TypeMapper<Pair>::signature()});
76
77 for (const auto& pair : *md)
78 {
79 // The following tags are not part of the MPRIS spec and should not be encoded
80 if (pair.first == tags::Image::name or
81 pair.first == tags::PreviewImage::name)
82 continue;
83
84 auto de = dict.open_dict_entry();
85 {
86 if (pair.first == core::ubuntu::media::Track::MetaData::TrackLengthKey
87 and not pair.second.empty())
88 {
89 Codec<Pair>::encode_argument(de, std::make_pair(pair.first,
90 dbus::types::Variant::encode(
91 boost::lexical_cast<std::int64_t>(pair.second))));
92 }
93 else if (pair.first == xesam::Album::name and not pair.second.empty())
94 {
95 Codec<Pair>::encode_argument(de, std::make_pair(pair.first,
96 dbus::types::Variant::encode(pair.second)));
97 }
98 else if (pair.first == xesam::AlbumArtist::name and not pair.second.empty())
99 {
100#if 0
101 // TODO: This code doesn't work but will be needed for full MPRIS compliance.
102 // Technically there can be more than one album artist stuffed into an array.
103 // How to satisfy stuffing this data into dbus-cpp is what needs fixing.
104 auto array = de.open_array(dbus::types::Signature
105 {dbus::helper::TypeMapper<Pair>::signature()});
106 {
107 // TODO: this should really iterate over all artists, but seems
108 // we only extract one artist from playbin
109 Codec<Pair>::encode_argument(array, std::make_pair(pair.first,
110 dbus::types::Variant::encode(pair.second)));
111 }
112 de.close_array(std::move(array));
113#else
114 Codec<Pair>::encode_argument(de, std::make_pair(pair.first,
115 dbus::types::Variant::encode(pair.second)));
116#endif
117 }
118 else
119 {
120 Codec<Pair>::encode_argument(de, std::make_pair(pair.first,
121 dbus::types::Variant::encode(pair.second)));
122 }
123 }
124 dict.close_dict_entry(std::move(de));
125 }
126 writer.close_array(std::move(dict));
67 }127 }
68128
69 static void decode_argument(core::dbus::Message::Reader& out, core::ubuntu::media::Track::MetaData& in)129 static void decode_argument(core::dbus::Message::Reader& reader, core::ubuntu::media::Track::MetaData& md)
70 {130 {
71 (void) out;131 auto array = reader.pop_array();
72 (void) in;132
133 while (array.type() != dbus::ArgumentType::invalid)
134 {
135 auto entry = array.pop_dict_entry();
136 {
137 std::string key {entry.pop_string()};
138 auto variant = entry.pop_variant();
139 {
140 if (key == xesam::Album::name)
141 {
142 const std::string album = variant.pop_string();
143 MH_DEBUG("Getting key \"%s\" and value \"%s\"", key, album);
144 md.set_album(album);
145 }
146 else
147 {
148 MH_WARNING("Unknown metadata key \"%s\" while decoding dbus message", key);
149 }
150 }
151 }
152 }
73 }153 }
74};154};
75155
76156
=== modified file 'src/core/media/cover_art_resolver.cpp'
--- src/core/media/cover_art_resolver.cpp 2014-09-09 14:02:52 +0000
+++ src/core/media/cover_art_resolver.cpp 2016-06-14 19:43:39 +0000
@@ -22,6 +22,6 @@
22{22{
23 return [](const std::string&, const std::string&, const std::string&)23 return [](const std::string&, const std::string&, const std::string&)
24 {24 {
25 return "file:///usr/share/unity/icons/album_missing.png";25 return "file:///usr/lib/arm-linux-gnueabihf/unity-scopes/mediascanner-music/album_missing.svg";
26 };26 };
27}27}
2828
=== modified file 'src/core/media/gstreamer/meta_data_extractor.h'
--- src/core/media/gstreamer/meta_data_extractor.h 2015-09-23 11:17:26 +0000
+++ src/core/media/gstreamer/meta_data_extractor.h 2016-06-14 19:43:39 +0000
@@ -24,6 +24,8 @@
2424
25#include "bus.h"25#include "bus.h"
2626
27#include "core/media/logger/logger.h"
28
27#include <gst/gst.h>29#include <gst/gst.h>
2830
29#include <exception>31#include <exception>
@@ -49,7 +51,11 @@
49 {GST_TAG_GENRE, std::string{xesam::Genre::name}},51 {GST_TAG_GENRE, std::string{xesam::Genre::name}},
50 {GST_TAG_TITLE, std::string{xesam::Title::name}},52 {GST_TAG_TITLE, std::string{xesam::Title::name}},
51 {GST_TAG_TRACK_NUMBER, std::string{xesam::TrackNumber::name}},53 {GST_TAG_TRACK_NUMBER, std::string{xesam::TrackNumber::name}},
52 {GST_TAG_USER_RATING, std::string{xesam::UserRating::name}}54 {GST_TAG_USER_RATING, std::string{xesam::UserRating::name}},
55 // Below this line are custom entries related but not directly from
56 // the MPRIS spec:
57 {GST_TAG_IMAGE, std::string{tags::Image::name}},
58 {GST_TAG_PREVIEW_IMAGE, std::string{tags::PreviewImage::name}}
53 };59 };
5460
55 return lut;61 return lut;
@@ -72,7 +78,7 @@
72 auto md = static_cast<media::Track::MetaData*>(user_data);78 auto md = static_cast<media::Track::MetaData*>(user_data);
73 std::stringstream ss;79 std::stringstream ss;
7480
75 switch(gst_tag_get_type(tag))81 switch (gst_tag_get_type(tag))
76 {82 {
77 case G_TYPE_BOOLEAN:83 case G_TYPE_BOOLEAN:
78 {84 {
@@ -137,9 +143,22 @@
137 break;143 break;
138 }144 }
139145
140 (*md).set(146 const bool has_tag_from_lut = (gstreamer_to_mpris_tag_lut().count(tag) > 0);
141 (gstreamer_to_mpris_tag_lut().count(tag) > 0 ? gstreamer_to_mpris_tag_lut().at(tag) : tag),147 const std::string tag_name{(has_tag_from_lut) ?
142 ss.str());148 gstreamer_to_mpris_tag_lut().at(tag) : tag};
149
150
151 // Specific handling for the following tag types:
152 if (tag_name == tags::PreviewImage::name)
153 ss << "true";
154 if (tag_name == tags::Image::name)
155 ss << "true";
156
157 (*md).set((has_tag_from_lut ?
158 gstreamer_to_mpris_tag_lut().at(tag) : tag), ss.str());
159
160 MH_DEBUG("Tag name: \"%s\", value: \"%s\"", tag_name, ss.str());
161
143 },162 },
144 &md);163 &md);
145 }164 }
146165
=== modified file 'src/core/media/gstreamer/playbin.cpp'
--- src/core/media/gstreamer/playbin.cpp 2016-04-06 15:28:29 +0000
+++ src/core/media/gstreamer/playbin.cpp 2016-06-14 19:43:39 +0000
@@ -30,8 +30,6 @@
3030
31#include <utility>31#include <utility>
3232
33//#define VERBOSE_DEBUG
34
35namespace33namespace
36{34{
37void setup_video_sink_for_buffer_streaming(GstElement* pipeline)35void setup_video_sink_for_buffer_streaming(GstElement* pipeline)
@@ -256,7 +254,7 @@
256254
257void gstreamer::Playbin::on_new_message_async(const Bus::Message& message)255void gstreamer::Playbin::on_new_message_async(const Bus::Message& message)
258{256{
259 switch(message.type)257 switch (message.type)
260 {258 {
261 case GST_MESSAGE_ERROR:259 case GST_MESSAGE_ERROR:
262 signals.on_error(message.detail.error_warning_info);260 signals.on_error(message.detail.error_warning_info);
263261
=== added file 'src/core/media/metadata.cpp'
--- src/core/media/metadata.cpp 1970-01-01 00:00:00 +0000
+++ src/core/media/metadata.cpp 2016-06-14 19:43:39 +0000
@@ -0,0 +1,83 @@
1/*
2 * Copyright © 2016 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Jim Hodapp <jim.hodapp@canonical.com>
17 */
18
19#include "xesam.h"
20
21#include <core/media/track.h>
22
23namespace media = core::ubuntu::media;
24
25const std::string& media::Track::MetaData::album() const
26{
27 return map.at(xesam::Album::name);
28}
29
30const std::string& media::Track::MetaData::artist() const
31{
32 return map.at(xesam::Artist::name);
33}
34
35const std::string& media::Track::MetaData::title() const
36{
37 return map.at(xesam::Title::name);
38}
39
40const std::string& media::Track::MetaData::track_id() const
41{
42 return map.at(media::Track::MetaData::TrackIdKey);
43}
44
45const std::string& media::Track::MetaData::art_url() const
46{
47 return map.at(media::Track::MetaData::TrackArtlUrlKey);
48}
49
50const std::string& media::Track::MetaData::last_used() const
51{
52 return map.at(xesam::LastUsed::name);
53}
54
55void media::Track::MetaData::set_album(const std::string& album)
56{
57 map[xesam::Album::name] = album;
58}
59
60void media::Track::MetaData::set_artist(const std::string& artist)
61{
62 map[xesam::Artist::name] = artist;
63}
64
65void media::Track::MetaData::set_title(const std::string& title)
66{
67 map[xesam::Title::name] = title;
68}
69
70void media::Track::MetaData::set_track_id(const std::string& id)
71{
72 map[media::Track::MetaData::TrackIdKey] = id;
73}
74
75void media::Track::MetaData::set_art_url(const std::string& url)
76{
77 map[media::Track::MetaData::TrackArtlUrlKey] = url;
78}
79
80void media::Track::MetaData::set_last_used(const std::string& datetime)
81{
82 map[xesam::LastUsed::name] = datetime;
83}
084
=== modified file 'src/core/media/mpris/player.h'
--- src/core/media/mpris/player.h 2016-02-19 16:14:42 +0000
+++ src/core/media/mpris/player.h 2016-06-14 19:43:39 +0000
@@ -44,6 +44,8 @@
4444
45#include <cstdint>45#include <cstdint>
4646
47#include "core/media/logger/logger.h"
48
47namespace dbus = core::dbus;49namespace dbus = core::dbus;
4850
49namespace mpris51namespace mpris
@@ -172,8 +174,7 @@
172 DBUS_CPP_WRITABLE_PROPERTY_DEF(PlaybackRate, Player, double)174 DBUS_CPP_WRITABLE_PROPERTY_DEF(PlaybackRate, Player, double)
173 DBUS_CPP_WRITABLE_PROPERTY_DEF(Rate, Player, double)175 DBUS_CPP_WRITABLE_PROPERTY_DEF(Rate, Player, double)
174 DBUS_CPP_WRITABLE_PROPERTY_DEF(Shuffle, Player, bool)176 DBUS_CPP_WRITABLE_PROPERTY_DEF(Shuffle, Player, bool)
175 DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, Dictionary)177 DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, core::ubuntu::media::Track::MetaData)
176 DBUS_CPP_READABLE_PROPERTY_DEF(TypedMetaData, Player, core::ubuntu::media::Track::MetaData)
177 DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Player, double)178 DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Player, double)
178 DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::int64_t)179 DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::int64_t)
179 DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::int64_t)180 DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::int64_t)
@@ -222,7 +223,7 @@
222 Properties::TypedLoopStatus::ValueType typed_loop_status{core::ubuntu::media::Player::LoopStatus::none};223 Properties::TypedLoopStatus::ValueType typed_loop_status{core::ubuntu::media::Player::LoopStatus::none};
223 Properties::PlaybackRate::ValueType playback_rate{1.f};224 Properties::PlaybackRate::ValueType playback_rate{1.f};
224 Properties::Shuffle::ValueType shuffle{false};225 Properties::Shuffle::ValueType shuffle{false};
225 Properties::TypedMetaData::ValueType typed_meta_data{};226 Properties::Metadata::ValueType meta_data{};
226 Properties::Volume::ValueType volume{0.f};227 Properties::Volume::ValueType volume{0.f};
227 Properties::Position::ValueType position{0};228 Properties::Position::ValueType position{0};
228 Properties::Duration::ValueType duration{0};229 Properties::Duration::ValueType duration{0};
@@ -253,7 +254,7 @@
253 configuration.object->template get_property<Properties::Lifetime>(),254 configuration.object->template get_property<Properties::Lifetime>(),
254 configuration.object->template get_property<Properties::PlaybackRate>(),255 configuration.object->template get_property<Properties::PlaybackRate>(),
255 configuration.object->template get_property<Properties::Shuffle>(),256 configuration.object->template get_property<Properties::Shuffle>(),
256 configuration.object->template get_property<Properties::TypedMetaData>(),257 configuration.object->template get_property<Properties::Metadata>(),
257 configuration.object->template get_property<Properties::Volume>(),258 configuration.object->template get_property<Properties::Volume>(),
258 configuration.object->template get_property<Properties::Position>(),259 configuration.object->template get_property<Properties::Position>(),
259 configuration.object->template get_property<Properties::Duration>(),260 configuration.object->template get_property<Properties::Duration>(),
@@ -288,6 +289,7 @@
288 properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal);289 properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal);
289 properties.playback_rate->set(configuration.defaults.playback_rate);290 properties.playback_rate->set(configuration.defaults.playback_rate);
290 properties.shuffle->set(configuration.defaults.shuffle);291 properties.shuffle->set(configuration.defaults.shuffle);
292 properties.meta_data_for_current_track->set(configuration.defaults.meta_data);
291 properties.position->set(configuration.defaults.position);293 properties.position->set(configuration.defaults.position);
292 properties.duration->set(configuration.defaults.duration);294 properties.duration->set(configuration.defaults.duration);
293 properties.minimum_playback_rate->set(configuration.defaults.minimum_rate);295 properties.minimum_playback_rate->set(configuration.defaults.minimum_rate);
@@ -351,9 +353,9 @@
351 Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value);353 Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value);
352354
353 signals.properties_changed->emit(std::make_tuple(355 signals.properties_changed->emit(std::make_tuple(
354 dbus::traits::Service<Player>::interface_name(),356 dbus::traits::Service<Player>::interface_name(),
355 dict,357 dict,
356 the_empty_list_of_invalidated_properties()));358 the_empty_list_of_invalidated_properties()));
357 }359 }
358360
359 Dictionary get_all_properties()361 Dictionary get_all_properties()
@@ -374,6 +376,7 @@
374 dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->get());376 dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->get());
375 dict[Properties::PlaybackRate::name()] = dbus::types::Variant::encode(properties.playback_rate->get());377 dict[Properties::PlaybackRate::name()] = dbus::types::Variant::encode(properties.playback_rate->get());
376 dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.shuffle->get());378 dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.shuffle->get());
379 dict[Properties::Metadata::name()] = dbus::types::Variant::encode(properties.meta_data_for_current_track->get());
377 dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get());380 dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get());
378 dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get());381 dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get());
379 dict[Properties::MinimumRate::name()] = dbus::types::Variant::encode(properties.minimum_playback_rate->get());382 dict[Properties::MinimumRate::name()] = dbus::types::Variant::encode(properties.minimum_playback_rate->get());
@@ -405,7 +408,7 @@
405 std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime;408 std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime;
406 std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate;409 std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate;
407 std::shared_ptr<core::dbus::Property<Properties::Shuffle>> shuffle;410 std::shared_ptr<core::dbus::Property<Properties::Shuffle>> shuffle;
408 std::shared_ptr<core::dbus::Property<Properties::TypedMetaData>> typed_meta_data_for_current_track;411 std::shared_ptr<core::dbus::Property<Properties::Metadata>> meta_data_for_current_track;
409 std::shared_ptr<core::dbus::Property<Properties::Volume>> volume;412 std::shared_ptr<core::dbus::Property<Properties::Volume>> volume;
410 std::shared_ptr<core::dbus::Property<Properties::Position>> position;413 std::shared_ptr<core::dbus::Property<Properties::Position>> position;
411 std::shared_ptr<core::dbus::Property<Properties::Duration>> duration;414 std::shared_ptr<core::dbus::Property<Properties::Duration>> duration;
412415
=== modified file 'src/core/media/player_implementation.cpp'
--- src/core/media/player_implementation.cpp 2016-05-03 19:08:00 +0000
+++ src/core/media/player_implementation.cpp 2016-06-14 19:43:39 +0000
@@ -24,10 +24,12 @@
24#include "util/timeout.h"24#include "util/timeout.h"
2525
26#include <unistd.h>26#include <unistd.h>
27#include <ctime>
2728
28#include "client_death_observer.h"29#include "client_death_observer.h"
29#include "engine.h"30#include "engine.h"
30#include "track_list_implementation.h"31#include "track_list_implementation.h"
32#include "xesam.h"
3133
32#include "gstreamer/engine.h"34#include "gstreamer/engine.h"
3335
@@ -138,9 +140,19 @@
138 }140 }
139 case Engine::State::playing:141 case Engine::State::playing:
140 {142 {
141 // We update the track meta data prior to updating the playback status.143 // We update the track metadata prior to updating the playback status.
142 // Some MPRIS clients expect this order of events.144 // Some MPRIS clients expect this order of events.
143 parent->meta_data_for_current_track().set(std::get<1>(engine->track_meta_data().get()));145 time_t now;
146 time(&now);
147 char buf[sizeof("2011-10-08T07:07:09Z")];
148 strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&now));
149 media::Track::MetaData metadata{std::get<1>(engine->track_meta_data().get())};
150 // Setting this with second resolution makes sure that the track_meta_data property changes
151 // and thus the track_meta_data().changed() signal gets sent out over dbus. Otherwise the
152 // Property caching mechanism would prevent this.
153 metadata.set_last_used(std::string{buf});
154 update_mpris_metadata(std::get<0>(engine->track_meta_data().get()), metadata);
155
144 // And update our playback status.156 // And update our playback status.
145 parent->playback_status().set(media::Player::playing);157 parent->playback_status().set(media::Player::playing);
146 MH_INFO("Requesting power state");158 MH_INFO("Requesting power state");
@@ -325,6 +337,78 @@
325 parent->can_go_next().set(has_next);337 parent->can_go_next().set(has_next);
326 }338 }
327339
340 std::string get_uri_for_album_artwork(const media::Track::UriType& uri,
341 const media::Track::MetaData& metadata)
342 {
343 std::string art_uri;
344 static const std::string file_uri_prefix{"file://"};
345 size_t pos = uri.find_first_of(file_uri_prefix);
346 MH_DEBUG("find_first_of: %d", pos != std::string::npos);
347 MH_DEBUG("uri.at(0): %c", uri.at(0));
348 const bool is_local_file = (pos != std::string::npos or uri.at(0) == '/');
349 MH_DEBUG("is_local_file ? %d", is_local_file);
350 MH_DEBUG("is_video_source: %d", parent->is_video_source().get());
351 // If the track has a full image or preview image or is a video and it is a local file,
352 // then use the thumbnailer cache
353 if ( (( metadata.count(tags::PreviewImage::name) > 0
354 and metadata.get(tags::PreviewImage::name) == "true")
355 or ( metadata.count(tags::Image::name) > 0
356 and metadata.get(tags::Image::name) == "true")
357 or parent->is_video_source().get())
358 and is_local_file)
359 {
360 MH_DEBUG("Detected the presence of a local preview image or full image");
361 art_uri = "image://thumbnailer/" + uri;
362 MH_DEBUG("art_uri: %s", art_uri);
363 }
364 // Otherwise we'll try and see if we can look up the album art online through
365 // the dash's artwork proxy
366 else if (metadata.is_set(xesam::Album::name) or metadata.is_set(xesam::Artist::name))
367 {
368 if (metadata.is_set(xesam::Album::name) and metadata.is_set(xesam::Artist::name))
369 {
370 art_uri = "image://albumart/artist=" + metadata.artist()
371 + "&album=" + metadata.album();
372 }
373 else
374 {
375 art_uri = "image://albumart/";
376 if (metadata.is_set(xesam::Artist::name))
377 art_uri += "artist=" + metadata.artist();
378 else if (metadata.is_set(xesam::Album::name))
379 art_uri += "album=" + metadata.album();
380 }
381 }
382 // If all else fails, display a placeholder icon
383 else
384 {
385 art_uri = "file:///usr/lib/arm-linux-gnueabihf/unity-scopes/mediascanner-music/album_missing.svg";
386 }
387
388 return art_uri;
389 }
390
391 // Makes sure all relevant metadata fields are set to current data and
392 // will trigger the track_meta_data().changed() signal to go out over dbus
393 void update_mpris_metadata(const media::Track::UriType& uri, const media::Track::MetaData& md)
394 {
395 media::Track::MetaData metadata{md};
396 if (not metadata.is_set(media::Track::MetaData::TrackIdKey))
397 {
398 const std::size_t last_slash = track_list->current().find_last_of("/");
399 const std::string track_id = track_list->current().substr(last_slash+1);
400 if (not track_id.empty())
401 metadata.set_track_id("/org/mpris/MediaPlayer2/Track/" + track_id);
402 else
403 MH_WARNING("Failed to set MPRIS track id since the id value is NULL");
404 }
405
406 if (not metadata.is_set(media::Track::MetaData::TrackArtlUrlKey))
407 metadata.set_art_url(get_uri_for_album_artwork(uri, metadata));
408
409 parent->meta_data_for_current_track().set(metadata);
410 }
411
328 bool pause_other_players(media::Player::PlayerKey key)412 bool pause_other_players(media::Player::PlayerKey key)
329 {413 {
330 if (not config.parent.player_service)414 if (not config.parent.player_service)
@@ -501,6 +585,12 @@
501 d->engine->lifetime().set(lifetime);585 d->engine->lifetime().set(lifetime);
502 });586 });
503587
588 d->engine->track_meta_data().changed().connect([this, config](
589 const std::tuple<media::Track::UriType, media::Track::MetaData>& md)
590 {
591 d->update_mpris_metadata(std::get<0>(md), std::get<1>(md));
592 });
593
504 d->engine->about_to_finish_signal().connect([this]()594 d->engine->about_to_finish_signal().connect([this]()
505 {595 {
506 if (d->doing_abandon)596 if (d->doing_abandon)
507597
=== modified file 'src/core/media/player_skeleton.cpp'
--- src/core/media/player_skeleton.cpp 2016-04-06 15:28:29 +0000
+++ src/core/media/player_skeleton.cpp 2016-06-14 19:43:39 +0000
@@ -478,7 +478,7 @@
478478
479const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const479const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const
480{480{
481 return *d->skeleton.properties.typed_meta_data_for_current_track;481 return *d->skeleton.properties.meta_data_for_current_track;
482}482}
483483
484const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const484const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const
@@ -608,7 +608,7 @@
608608
609core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track()609core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track()
610{610{
611 return *d->skeleton.properties.typed_meta_data_for_current_track;611 return *d->skeleton.properties.meta_data_for_current_track;
612}612}
613613
614core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate()614core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate()
615615
=== modified file 'src/core/media/player_stub.cpp'
--- src/core/media/player_stub.cpp 2016-04-06 15:28:29 +0000
+++ src/core/media/player_stub.cpp 2016-06-14 19:43:39 +0000
@@ -70,7 +70,7 @@
70 object->get_property<mpris::Player::Properties::TypedLoopStatus>(),70 object->get_property<mpris::Player::Properties::TypedLoopStatus>(),
71 object->get_property<mpris::Player::Properties::PlaybackRate>(),71 object->get_property<mpris::Player::Properties::PlaybackRate>(),
72 object->get_property<mpris::Player::Properties::Shuffle>(),72 object->get_property<mpris::Player::Properties::Shuffle>(),
73 object->get_property<mpris::Player::Properties::TypedMetaData>(),73 object->get_property<mpris::Player::Properties::Metadata>(),
74 object->get_property<mpris::Player::Properties::Volume>(),74 object->get_property<mpris::Player::Properties::Volume>(),
75 object->get_property<mpris::Player::Properties::Position>(),75 object->get_property<mpris::Player::Properties::Position>(),
76 object->get_property<mpris::Player::Properties::Duration>(),76 object->get_property<mpris::Player::Properties::Duration>(),
@@ -118,7 +118,7 @@
118 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> loop_status;118 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> loop_status;
119 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate;119 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate;
120 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> shuffle;120 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> shuffle;
121 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track;121 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Metadata>> meta_data_for_current_track;
122 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume;122 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume;
123 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position;123 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position;
124 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration;124 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration;
125125
=== modified file 'src/core/media/service_skeleton.cpp'
--- src/core/media/service_skeleton.cpp 2016-05-17 17:51:16 +0000
+++ src/core/media/service_skeleton.cpp 2016-06-14 19:43:39 +0000
@@ -732,6 +732,21 @@
732 player.properties.can_go_next->set(can_go_next);732 player.properties.can_go_next->set(can_go_next);
733 });733 });
734734
735 // NOTE: the metadata first gets updated in the PlayerImplementation constructor which connects
736 // and reacts to the Engine track_meta_data changed signal. Setting the
737 // meta_data_for_current_track here makes sure that the signal uses the MPRIS object path
738 connections.meta_data_changed = player_sp->meta_data_for_current_track().changed().connect(
739 [this](const media::Track::MetaData& metadata)
740 {
741 player.properties.meta_data_for_current_track->set(metadata);
742 mpris::Player::Dictionary dict; dict[mpris::Player::Properties::Metadata::name()]
743 = dbus::types::Variant::encode(metadata);
744 player.signals.properties_changed->emit(std::make_tuple(
745 dbus::traits::Service<mpris::Player>::interface_name(),
746 dict,
747 mpris::Player::Skeleton::the_empty_list_of_invalidated_properties()));
748 });
749
735 // Sync property values between session and player mpris::Player instances750 // Sync property values between session and player mpris::Player instances
736 // TODO Getters from media::Player actually return values from a751 // TODO Getters from media::Player actually return values from a
737 // mpris::Player::Skeleton instance different from "player". Each of them use752 // mpris::Player::Skeleton instance different from "player". Each of them use
@@ -748,43 +763,6 @@
748 player.properties.can_pause->set(player_sp->can_pause().get());763 player.properties.can_pause->set(player_sp->can_pause().get());
749 player.properties.can_go_previous->set(player_sp->can_go_previous().get());764 player.properties.can_go_previous->set(player_sp->can_go_previous().get());
750 player.properties.can_go_next->set(player_sp->can_go_next().get());765 player.properties.can_go_next->set(player_sp->can_go_next().get());
751
752#if 0
753 // TODO cover_art_resolver() is not implemented yet
754 connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect(
755 [this](const core::ubuntu::media::Track::MetaData& md)
756 {
757 mpris::Player::Dictionary dict;
758
759 bool has_title = md.count(xesam::Title::name) > 0;
760 bool has_album_name = md.count(xesam::Album::name) > 0;
761 bool has_artist_name = md.count(xesam::Artist::name) > 0;
762
763 if (has_title)
764 dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name));
765 if (has_album_name)
766 dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name));
767 if (has_artist_name)
768 dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name));
769
770 dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode(
771 cover_art_resolver(
772 has_title ? md.get(xesam::Title::name) : "",
773 has_album_name ? md.get(xesam::Album::name) : "",
774 has_artist_name ? md.get(xesam::Artist::name) : ""));
775
776 mpris::Player::Dictionary wrap;
777 wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict);
778
779 player.signals.properties_changed->emit(
780 std::make_tuple(
781 dbus::traits::Service<
782 mpris::Player::Properties::Metadata::Interface>
783 ::interface_name(),
784 wrap,
785 std::vector<std::string>()));
786 });
787#endif
788 }766 }
789767
790 void reset_current_player()768 void reset_current_player()
@@ -807,6 +785,7 @@
807 connections.can_pause_changed = the_empty_signal.connect([](){});785 connections.can_pause_changed = the_empty_signal.connect([](){});
808 connections.can_go_previous_changed = the_empty_signal.connect([](){});786 connections.can_go_previous_changed = the_empty_signal.connect([](){});
809 connections.can_go_next_changed = the_empty_signal.connect([](){});787 connections.can_go_next_changed = the_empty_signal.connect([](){});
788 connections.meta_data_changed = the_empty_signal.connect([](){});
810 }789 }
811790
812 bool is_current_player(media::Player::PlayerKey key)791 bool is_current_player(media::Player::PlayerKey key)
813792
=== modified file 'src/core/media/xesam.h'
--- src/core/media/xesam.h 2014-09-10 07:05:15 +0000
+++ src/core/media/xesam.h 2016-06-14 19:43:39 +0000
@@ -53,4 +53,12 @@
53DATUM(UserRating, xesam:userRating, double)53DATUM(UserRating, xesam:userRating, double)
54}54}
5555
56namespace tags
57{
58// Does the track contain album art?
59DATUM(Image, tag:image, bool)
60// Does the track contain a small album art preview image?
61DATUM(PreviewImage, tag::previewImage, bool)
62}
63
56#endif // XESAM_H_64#endif // XESAM_H_

Subscribers

People subscribed via source and target branches

to all changes: