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 | 39 | public: | 39 | public: |
6 | 40 | typedef std::string UriType; | 40 | typedef std::string UriType; |
7 | 41 | typedef std::string Id; | 41 | typedef std::string Id; |
8 | 42 | typedef std::map<std::string, std::string> MetaDataType; | ||
9 | 42 | 43 | ||
10 | 43 | class MetaData | 44 | class MetaData |
11 | 44 | { | 45 | { |
12 | 45 | public: | 46 | public: |
13 | 47 | static constexpr const char* TrackArtlUrlKey = "mpris:artUrl"; | ||
14 | 48 | static constexpr const char* TrackLengthKey = "mpris:length"; | ||
15 | 49 | static constexpr const char* TrackIdKey = "mpris:trackid"; | ||
16 | 50 | |||
17 | 46 | bool operator==(const MetaData& rhs) const | 51 | bool operator==(const MetaData& rhs) const |
18 | 47 | { | 52 | { |
19 | 48 | return map == rhs.map; | 53 | return map == rhs.map; |
20 | @@ -90,13 +95,36 @@ | |||
21 | 90 | return map.at(key); | 95 | return map.at(key); |
22 | 91 | } | 96 | } |
23 | 92 | 97 | ||
24 | 98 | bool is_set(const std::string& key) const | ||
25 | 99 | { | ||
26 | 100 | try { | ||
27 | 101 | return map.at(key).empty(); | ||
28 | 102 | } catch (const std::out_of_range& e) { | ||
29 | 103 | return false; | ||
30 | 104 | } | ||
31 | 105 | } | ||
32 | 106 | |||
33 | 93 | const std::map<std::string, std::string>& operator*() const | 107 | const std::map<std::string, std::string>& operator*() const |
34 | 94 | { | 108 | { |
35 | 95 | return map; | 109 | return map; |
36 | 96 | } | 110 | } |
37 | 97 | 111 | ||
38 | 112 | const std::string& album() const; | ||
39 | 113 | const std::string& artist() const; | ||
40 | 114 | const std::string& title() const; | ||
41 | 115 | const std::string& track_id() const; | ||
42 | 116 | const std::string& art_url() const; | ||
43 | 117 | const std::string& last_used() const; | ||
44 | 118 | |||
45 | 119 | void set_album(const std::string& album); | ||
46 | 120 | void set_artist(const std::string& artist); | ||
47 | 121 | void set_title(const std::string& title); | ||
48 | 122 | void set_track_id(const std::string& id); | ||
49 | 123 | void set_art_url(const std::string& url); | ||
50 | 124 | void set_last_used(const std::string& datetime); | ||
51 | 125 | |||
52 | 98 | private: | 126 | private: |
54 | 99 | std::map<std::string, std::string> map; | 127 | MetaDataType map; |
55 | 100 | }; | 128 | }; |
56 | 101 | 129 | ||
57 | 102 | Track(const Id& id); | 130 | Track(const Id& id); |
58 | @@ -108,51 +136,7 @@ | |||
59 | 108 | 136 | ||
60 | 109 | virtual const Id& id() const; | 137 | virtual const Id& id() const; |
61 | 110 | virtual const UriType& uri() const; | 138 | virtual const UriType& uri() const; |
107 | 111 | /* | 139 | |
63 | 112 | class MetaData | ||
64 | 113 | { | ||
65 | 114 | public: | ||
66 | 115 | MetaData() = default; | ||
67 | 116 | MetaData(const MetaData&) = default; | ||
68 | 117 | ~MetaData() = default; | ||
69 | 118 | |||
70 | 119 | MetaData& operator=(const MetaData&) = default; | ||
71 | 120 | |||
72 | 121 | bool operator==(const MetaData&) const | ||
73 | 122 | { | ||
74 | 123 | return true; | ||
75 | 124 | } | ||
76 | 125 | |||
77 | 126 | bool operator!=(const MetaData&) const | ||
78 | 127 | { | ||
79 | 128 | return false; | ||
80 | 129 | } | ||
81 | 130 | |||
82 | 131 | struct NotImplementedFields | ||
83 | 132 | { | ||
84 | 133 | NotImplementedFields() = default; | ||
85 | 134 | |||
86 | 135 | virtual const UriType& uri() const = 0; | ||
87 | 136 | virtual const std::chrono::microseconds length() const = 0; | ||
88 | 137 | virtual const UriType& art_uri() const = 0; | ||
89 | 138 | virtual const std::string& album() const = 0; | ||
90 | 139 | virtual const std::vector<std::string>& album_artist() const = 0; | ||
91 | 140 | virtual const std::vector<std::string>& artist() const = 0; | ||
92 | 141 | virtual const std::string& as_text() const = 0; | ||
93 | 142 | virtual unsigned int audio_bpm() const = 0; | ||
94 | 143 | virtual float auto_rating() const = 0; | ||
95 | 144 | virtual const std::vector<std::string>& comment() const = 0; | ||
96 | 145 | virtual const std::vector<std::string>& composer() const = 0; | ||
97 | 146 | virtual unsigned int disc_number() const = 0; | ||
98 | 147 | virtual const std::vector<std::string>& genre() const = 0; | ||
99 | 148 | virtual const std::vector<std::string>& lyricist() const = 0; | ||
100 | 149 | virtual const std::string title() const = 0; | ||
101 | 150 | virtual unsigned int track_number() const = 0; | ||
102 | 151 | virtual unsigned int use_count() const = 0; | ||
103 | 152 | virtual float user_rating() const = 0; | ||
104 | 153 | }; | ||
105 | 154 | }; | ||
106 | 155 | */ | ||
108 | 156 | private: | 140 | private: |
109 | 157 | struct Private; | 141 | struct Private; |
110 | 158 | std::unique_ptr<Private> d; | 142 | std::unique_ptr<Private> d; |
111 | 159 | 143 | ||
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 | 49 | service.cpp | 49 | service.cpp |
117 | 50 | track.cpp | 50 | track.cpp |
118 | 51 | track_list.cpp | 51 | track_list.cpp |
119 | 52 | metadata.cpp | ||
120 | 52 | 53 | ||
121 | 53 | player_stub.cpp | 54 | player_stub.cpp |
122 | 54 | service_stub.cpp | 55 | service_stub.cpp |
123 | @@ -99,6 +100,7 @@ | |||
124 | 99 | hybris_client_death_observer.cpp | 100 | hybris_client_death_observer.cpp |
125 | 100 | cover_art_resolver.cpp | 101 | cover_art_resolver.cpp |
126 | 101 | engine.cpp | 102 | engine.cpp |
127 | 103 | metadata.cpp | ||
128 | 102 | 104 | ||
129 | 103 | apparmor/context.cpp | 105 | apparmor/context.cpp |
130 | 104 | apparmor/ubuntu.cpp | 106 | apparmor/ubuntu.cpp |
131 | 105 | 107 | ||
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 | 23 | #include <core/media/player.h> | 23 | #include <core/media/player.h> |
137 | 24 | #include <core/media/track.h> | 24 | #include <core/media/track.h> |
138 | 25 | 25 | ||
139 | 26 | #include <core/dbus/types/stl/map.h> | ||
140 | 26 | #include <core/dbus/types/stl/string.h> | 27 | #include <core/dbus/types/stl/string.h> |
141 | 28 | #include <core/dbus/types/variant.h> | ||
142 | 27 | #include <core/dbus/types/stl/vector.h> | 29 | #include <core/dbus/types/stl/vector.h> |
143 | 28 | #include <core/dbus/codec.h> | 30 | #include <core/dbus/codec.h> |
144 | 29 | 31 | ||
145 | 32 | #include <boost/lexical_cast.hpp> | ||
146 | 33 | |||
147 | 34 | #include "core/media/xesam.h" | ||
148 | 35 | |||
149 | 36 | #include "core/media/logger/logger.h" | ||
150 | 37 | |||
151 | 30 | namespace core | 38 | namespace core |
152 | 31 | { | 39 | { |
153 | 32 | namespace dbus | 40 | namespace dbus |
154 | @@ -38,7 +46,7 @@ | |||
155 | 38 | { | 46 | { |
156 | 39 | constexpr static ArgumentType type_value() | 47 | constexpr static ArgumentType type_value() |
157 | 40 | { | 48 | { |
159 | 41 | return ArgumentType::floating_point; | 49 | return ArgumentType::array; |
160 | 42 | } | 50 | } |
161 | 43 | constexpr static bool is_basic_type() | 51 | constexpr static bool is_basic_type() |
162 | 44 | { | 52 | { |
163 | @@ -51,7 +59,7 @@ | |||
164 | 51 | 59 | ||
165 | 52 | static std::string signature() | 60 | static std::string signature() |
166 | 53 | { | 61 | { |
168 | 54 | static const std::string s = TypeMapper<double>::signature(); | 62 | static const std::string s = TypeMapper<std::map<std::string, dbus::types::Variant>>::signature(); |
169 | 55 | return s; | 63 | return s; |
170 | 56 | } | 64 | } |
171 | 57 | }; | 65 | }; |
172 | @@ -60,16 +68,88 @@ | |||
173 | 60 | template<> | 68 | template<> |
174 | 61 | struct Codec<core::ubuntu::media::Track::MetaData> | 69 | struct Codec<core::ubuntu::media::Track::MetaData> |
175 | 62 | { | 70 | { |
177 | 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) |
178 | 64 | { | 72 | { |
181 | 65 | (void) out; | 73 | typedef std::pair<std::string, dbus::types::Variant> Pair; |
182 | 66 | (void) in; | 74 | auto dict = writer.open_array(dbus::types::Signature |
183 | 75 | {dbus::helper::TypeMapper<Pair>::signature()}); | ||
184 | 76 | |||
185 | 77 | for (const auto& pair : *md) | ||
186 | 78 | { | ||
187 | 79 | // The following tags are not part of the MPRIS spec and should not be encoded | ||
188 | 80 | if (pair.first == tags::Image::name or | ||
189 | 81 | pair.first == tags::PreviewImage::name) | ||
190 | 82 | continue; | ||
191 | 83 | |||
192 | 84 | auto de = dict.open_dict_entry(); | ||
193 | 85 | { | ||
194 | 86 | if (pair.first == core::ubuntu::media::Track::MetaData::TrackLengthKey | ||
195 | 87 | and not pair.second.empty()) | ||
196 | 88 | { | ||
197 | 89 | Codec<Pair>::encode_argument(de, std::make_pair(pair.first, | ||
198 | 90 | dbus::types::Variant::encode( | ||
199 | 91 | boost::lexical_cast<std::int64_t>(pair.second)))); | ||
200 | 92 | } | ||
201 | 93 | else if (pair.first == xesam::Album::name and not pair.second.empty()) | ||
202 | 94 | { | ||
203 | 95 | Codec<Pair>::encode_argument(de, std::make_pair(pair.first, | ||
204 | 96 | dbus::types::Variant::encode(pair.second))); | ||
205 | 97 | } | ||
206 | 98 | else if (pair.first == xesam::AlbumArtist::name and not pair.second.empty()) | ||
207 | 99 | { | ||
208 | 100 | #if 0 | ||
209 | 101 | // TODO: This code doesn't work but will be needed for full MPRIS compliance. | ||
210 | 102 | // Technically there can be more than one album artist stuffed into an array. | ||
211 | 103 | // How to satisfy stuffing this data into dbus-cpp is what needs fixing. | ||
212 | 104 | auto array = de.open_array(dbus::types::Signature | ||
213 | 105 | {dbus::helper::TypeMapper<Pair>::signature()}); | ||
214 | 106 | { | ||
215 | 107 | // TODO: this should really iterate over all artists, but seems | ||
216 | 108 | // we only extract one artist from playbin | ||
217 | 109 | Codec<Pair>::encode_argument(array, std::make_pair(pair.first, | ||
218 | 110 | dbus::types::Variant::encode(pair.second))); | ||
219 | 111 | } | ||
220 | 112 | de.close_array(std::move(array)); | ||
221 | 113 | #else | ||
222 | 114 | Codec<Pair>::encode_argument(de, std::make_pair(pair.first, | ||
223 | 115 | dbus::types::Variant::encode(pair.second))); | ||
224 | 116 | #endif | ||
225 | 117 | } | ||
226 | 118 | else | ||
227 | 119 | { | ||
228 | 120 | Codec<Pair>::encode_argument(de, std::make_pair(pair.first, | ||
229 | 121 | dbus::types::Variant::encode(pair.second))); | ||
230 | 122 | } | ||
231 | 123 | } | ||
232 | 124 | dict.close_dict_entry(std::move(de)); | ||
233 | 125 | } | ||
234 | 126 | writer.close_array(std::move(dict)); | ||
235 | 67 | } | 127 | } |
236 | 68 | 128 | ||
238 | 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) |
239 | 70 | { | 130 | { |
242 | 71 | (void) out; | 131 | auto array = reader.pop_array(); |
243 | 72 | (void) in; | 132 | |
244 | 133 | while (array.type() != dbus::ArgumentType::invalid) | ||
245 | 134 | { | ||
246 | 135 | auto entry = array.pop_dict_entry(); | ||
247 | 136 | { | ||
248 | 137 | std::string key {entry.pop_string()}; | ||
249 | 138 | auto variant = entry.pop_variant(); | ||
250 | 139 | { | ||
251 | 140 | if (key == xesam::Album::name) | ||
252 | 141 | { | ||
253 | 142 | const std::string album = variant.pop_string(); | ||
254 | 143 | MH_DEBUG("Getting key \"%s\" and value \"%s\"", key, album); | ||
255 | 144 | md.set_album(album); | ||
256 | 145 | } | ||
257 | 146 | else | ||
258 | 147 | { | ||
259 | 148 | MH_WARNING("Unknown metadata key \"%s\" while decoding dbus message", key); | ||
260 | 149 | } | ||
261 | 150 | } | ||
262 | 151 | } | ||
263 | 152 | } | ||
264 | 73 | } | 153 | } |
265 | 74 | }; | 154 | }; |
266 | 75 | 155 | ||
267 | 76 | 156 | ||
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 | 22 | { | 22 | { |
273 | 23 | return [](const std::string&, const std::string&, const std::string&) | 23 | return [](const std::string&, const std::string&, const std::string&) |
274 | 24 | { | 24 | { |
276 | 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"; |
277 | 26 | }; | 26 | }; |
278 | 27 | } | 27 | } |
279 | 28 | 28 | ||
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 | 24 | 24 | ||
285 | 25 | #include "bus.h" | 25 | #include "bus.h" |
286 | 26 | 26 | ||
287 | 27 | #include "core/media/logger/logger.h" | ||
288 | 28 | |||
289 | 27 | #include <gst/gst.h> | 29 | #include <gst/gst.h> |
290 | 28 | 30 | ||
291 | 29 | #include <exception> | 31 | #include <exception> |
292 | @@ -49,7 +51,11 @@ | |||
293 | 49 | {GST_TAG_GENRE, std::string{xesam::Genre::name}}, | 51 | {GST_TAG_GENRE, std::string{xesam::Genre::name}}, |
294 | 50 | {GST_TAG_TITLE, std::string{xesam::Title::name}}, | 52 | {GST_TAG_TITLE, std::string{xesam::Title::name}}, |
295 | 51 | {GST_TAG_TRACK_NUMBER, std::string{xesam::TrackNumber::name}}, | 53 | {GST_TAG_TRACK_NUMBER, std::string{xesam::TrackNumber::name}}, |
297 | 52 | {GST_TAG_USER_RATING, std::string{xesam::UserRating::name}} | 54 | {GST_TAG_USER_RATING, std::string{xesam::UserRating::name}}, |
298 | 55 | // Below this line are custom entries related but not directly from | ||
299 | 56 | // the MPRIS spec: | ||
300 | 57 | {GST_TAG_IMAGE, std::string{tags::Image::name}}, | ||
301 | 58 | {GST_TAG_PREVIEW_IMAGE, std::string{tags::PreviewImage::name}} | ||
302 | 53 | }; | 59 | }; |
303 | 54 | 60 | ||
304 | 55 | return lut; | 61 | return lut; |
305 | @@ -72,7 +78,7 @@ | |||
306 | 72 | auto md = static_cast<media::Track::MetaData*>(user_data); | 78 | auto md = static_cast<media::Track::MetaData*>(user_data); |
307 | 73 | std::stringstream ss; | 79 | std::stringstream ss; |
308 | 74 | 80 | ||
310 | 75 | switch(gst_tag_get_type(tag)) | 81 | switch (gst_tag_get_type(tag)) |
311 | 76 | { | 82 | { |
312 | 77 | case G_TYPE_BOOLEAN: | 83 | case G_TYPE_BOOLEAN: |
313 | 78 | { | 84 | { |
314 | @@ -137,9 +143,22 @@ | |||
315 | 137 | break; | 143 | break; |
316 | 138 | } | 144 | } |
317 | 139 | 145 | ||
321 | 140 | (*md).set( | 146 | const bool has_tag_from_lut = (gstreamer_to_mpris_tag_lut().count(tag) > 0); |
322 | 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) ? |
323 | 142 | ss.str()); | 148 | gstreamer_to_mpris_tag_lut().at(tag) : tag}; |
324 | 149 | |||
325 | 150 | |||
326 | 151 | // Specific handling for the following tag types: | ||
327 | 152 | if (tag_name == tags::PreviewImage::name) | ||
328 | 153 | ss << "true"; | ||
329 | 154 | if (tag_name == tags::Image::name) | ||
330 | 155 | ss << "true"; | ||
331 | 156 | |||
332 | 157 | (*md).set((has_tag_from_lut ? | ||
333 | 158 | gstreamer_to_mpris_tag_lut().at(tag) : tag), ss.str()); | ||
334 | 159 | |||
335 | 160 | MH_DEBUG("Tag name: \"%s\", value: \"%s\"", tag_name, ss.str()); | ||
336 | 161 | |||
337 | 143 | }, | 162 | }, |
338 | 144 | &md); | 163 | &md); |
339 | 145 | } | 164 | } |
340 | 146 | 165 | ||
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 | 30 | 30 | ||
346 | 31 | #include <utility> | 31 | #include <utility> |
347 | 32 | 32 | ||
348 | 33 | //#define VERBOSE_DEBUG | ||
349 | 34 | |||
350 | 35 | namespace | 33 | namespace |
351 | 36 | { | 34 | { |
352 | 37 | void setup_video_sink_for_buffer_streaming(GstElement* pipeline) | 35 | void setup_video_sink_for_buffer_streaming(GstElement* pipeline) |
353 | @@ -256,7 +254,7 @@ | |||
354 | 256 | 254 | ||
355 | 257 | void gstreamer::Playbin::on_new_message_async(const Bus::Message& message) | 255 | void gstreamer::Playbin::on_new_message_async(const Bus::Message& message) |
356 | 258 | { | 256 | { |
358 | 259 | switch(message.type) | 257 | switch (message.type) |
359 | 260 | { | 258 | { |
360 | 261 | case GST_MESSAGE_ERROR: | 259 | case GST_MESSAGE_ERROR: |
361 | 262 | signals.on_error(message.detail.error_warning_info); | 260 | signals.on_error(message.detail.error_warning_info); |
362 | 263 | 261 | ||
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 | 1 | /* | ||
368 | 2 | * Copyright © 2016 Canonical Ltd. | ||
369 | 3 | * | ||
370 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
371 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
372 | 6 | * as published by the Free Software Foundation. | ||
373 | 7 | * | ||
374 | 8 | * This program is distributed in the hope that it will be useful, | ||
375 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
376 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
377 | 11 | * GNU Lesser General Public License for more details. | ||
378 | 12 | * | ||
379 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
380 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
381 | 15 | * | ||
382 | 16 | * Authored by: Jim Hodapp <jim.hodapp@canonical.com> | ||
383 | 17 | */ | ||
384 | 18 | |||
385 | 19 | #include "xesam.h" | ||
386 | 20 | |||
387 | 21 | #include <core/media/track.h> | ||
388 | 22 | |||
389 | 23 | namespace media = core::ubuntu::media; | ||
390 | 24 | |||
391 | 25 | const std::string& media::Track::MetaData::album() const | ||
392 | 26 | { | ||
393 | 27 | return map.at(xesam::Album::name); | ||
394 | 28 | } | ||
395 | 29 | |||
396 | 30 | const std::string& media::Track::MetaData::artist() const | ||
397 | 31 | { | ||
398 | 32 | return map.at(xesam::Artist::name); | ||
399 | 33 | } | ||
400 | 34 | |||
401 | 35 | const std::string& media::Track::MetaData::title() const | ||
402 | 36 | { | ||
403 | 37 | return map.at(xesam::Title::name); | ||
404 | 38 | } | ||
405 | 39 | |||
406 | 40 | const std::string& media::Track::MetaData::track_id() const | ||
407 | 41 | { | ||
408 | 42 | return map.at(media::Track::MetaData::TrackIdKey); | ||
409 | 43 | } | ||
410 | 44 | |||
411 | 45 | const std::string& media::Track::MetaData::art_url() const | ||
412 | 46 | { | ||
413 | 47 | return map.at(media::Track::MetaData::TrackArtlUrlKey); | ||
414 | 48 | } | ||
415 | 49 | |||
416 | 50 | const std::string& media::Track::MetaData::last_used() const | ||
417 | 51 | { | ||
418 | 52 | return map.at(xesam::LastUsed::name); | ||
419 | 53 | } | ||
420 | 54 | |||
421 | 55 | void media::Track::MetaData::set_album(const std::string& album) | ||
422 | 56 | { | ||
423 | 57 | map[xesam::Album::name] = album; | ||
424 | 58 | } | ||
425 | 59 | |||
426 | 60 | void media::Track::MetaData::set_artist(const std::string& artist) | ||
427 | 61 | { | ||
428 | 62 | map[xesam::Artist::name] = artist; | ||
429 | 63 | } | ||
430 | 64 | |||
431 | 65 | void media::Track::MetaData::set_title(const std::string& title) | ||
432 | 66 | { | ||
433 | 67 | map[xesam::Title::name] = title; | ||
434 | 68 | } | ||
435 | 69 | |||
436 | 70 | void media::Track::MetaData::set_track_id(const std::string& id) | ||
437 | 71 | { | ||
438 | 72 | map[media::Track::MetaData::TrackIdKey] = id; | ||
439 | 73 | } | ||
440 | 74 | |||
441 | 75 | void media::Track::MetaData::set_art_url(const std::string& url) | ||
442 | 76 | { | ||
443 | 77 | map[media::Track::MetaData::TrackArtlUrlKey] = url; | ||
444 | 78 | } | ||
445 | 79 | |||
446 | 80 | void media::Track::MetaData::set_last_used(const std::string& datetime) | ||
447 | 81 | { | ||
448 | 82 | map[xesam::LastUsed::name] = datetime; | ||
449 | 83 | } | ||
450 | 0 | 84 | ||
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 | 44 | 44 | ||
456 | 45 | #include <cstdint> | 45 | #include <cstdint> |
457 | 46 | 46 | ||
458 | 47 | #include "core/media/logger/logger.h" | ||
459 | 48 | |||
460 | 47 | namespace dbus = core::dbus; | 49 | namespace dbus = core::dbus; |
461 | 48 | 50 | ||
462 | 49 | namespace mpris | 51 | namespace mpris |
463 | @@ -172,8 +174,7 @@ | |||
464 | 172 | DBUS_CPP_WRITABLE_PROPERTY_DEF(PlaybackRate, Player, double) | 174 | DBUS_CPP_WRITABLE_PROPERTY_DEF(PlaybackRate, Player, double) |
465 | 173 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Rate, Player, double) | 175 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Rate, Player, double) |
466 | 174 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Shuffle, Player, bool) | 176 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Shuffle, Player, bool) |
469 | 175 | DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, Dictionary) | 177 | DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, core::ubuntu::media::Track::MetaData) |
468 | 176 | DBUS_CPP_READABLE_PROPERTY_DEF(TypedMetaData, Player, core::ubuntu::media::Track::MetaData) | ||
470 | 177 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Player, double) | 178 | DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Player, double) |
471 | 178 | DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::int64_t) | 179 | DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::int64_t) |
472 | 179 | DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::int64_t) | 180 | DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::int64_t) |
473 | @@ -222,7 +223,7 @@ | |||
474 | 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}; |
475 | 223 | Properties::PlaybackRate::ValueType playback_rate{1.f}; | 224 | Properties::PlaybackRate::ValueType playback_rate{1.f}; |
476 | 224 | Properties::Shuffle::ValueType shuffle{false}; | 225 | Properties::Shuffle::ValueType shuffle{false}; |
478 | 225 | Properties::TypedMetaData::ValueType typed_meta_data{}; | 226 | Properties::Metadata::ValueType meta_data{}; |
479 | 226 | Properties::Volume::ValueType volume{0.f}; | 227 | Properties::Volume::ValueType volume{0.f}; |
480 | 227 | Properties::Position::ValueType position{0}; | 228 | Properties::Position::ValueType position{0}; |
481 | 228 | Properties::Duration::ValueType duration{0}; | 229 | Properties::Duration::ValueType duration{0}; |
482 | @@ -253,7 +254,7 @@ | |||
483 | 253 | configuration.object->template get_property<Properties::Lifetime>(), | 254 | configuration.object->template get_property<Properties::Lifetime>(), |
484 | 254 | configuration.object->template get_property<Properties::PlaybackRate>(), | 255 | configuration.object->template get_property<Properties::PlaybackRate>(), |
485 | 255 | configuration.object->template get_property<Properties::Shuffle>(), | 256 | configuration.object->template get_property<Properties::Shuffle>(), |
487 | 256 | configuration.object->template get_property<Properties::TypedMetaData>(), | 257 | configuration.object->template get_property<Properties::Metadata>(), |
488 | 257 | configuration.object->template get_property<Properties::Volume>(), | 258 | configuration.object->template get_property<Properties::Volume>(), |
489 | 258 | configuration.object->template get_property<Properties::Position>(), | 259 | configuration.object->template get_property<Properties::Position>(), |
490 | 259 | configuration.object->template get_property<Properties::Duration>(), | 260 | configuration.object->template get_property<Properties::Duration>(), |
491 | @@ -288,6 +289,7 @@ | |||
492 | 288 | properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal); | 289 | properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal); |
493 | 289 | properties.playback_rate->set(configuration.defaults.playback_rate); | 290 | properties.playback_rate->set(configuration.defaults.playback_rate); |
494 | 290 | properties.shuffle->set(configuration.defaults.shuffle); | 291 | properties.shuffle->set(configuration.defaults.shuffle); |
495 | 292 | properties.meta_data_for_current_track->set(configuration.defaults.meta_data); | ||
496 | 291 | properties.position->set(configuration.defaults.position); | 293 | properties.position->set(configuration.defaults.position); |
497 | 292 | properties.duration->set(configuration.defaults.duration); | 294 | properties.duration->set(configuration.defaults.duration); |
498 | 293 | properties.minimum_playback_rate->set(configuration.defaults.minimum_rate); | 295 | properties.minimum_playback_rate->set(configuration.defaults.minimum_rate); |
499 | @@ -351,9 +353,9 @@ | |||
500 | 351 | Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value); | 353 | Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value); |
501 | 352 | 354 | ||
502 | 353 | signals.properties_changed->emit(std::make_tuple( | 355 | signals.properties_changed->emit(std::make_tuple( |
506 | 354 | dbus::traits::Service<Player>::interface_name(), | 356 | dbus::traits::Service<Player>::interface_name(), |
507 | 355 | dict, | 357 | dict, |
508 | 356 | the_empty_list_of_invalidated_properties())); | 358 | the_empty_list_of_invalidated_properties())); |
509 | 357 | } | 359 | } |
510 | 358 | 360 | ||
511 | 359 | Dictionary get_all_properties() | 361 | Dictionary get_all_properties() |
512 | @@ -374,6 +376,7 @@ | |||
513 | 374 | dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->get()); | 376 | dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->get()); |
514 | 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()); |
515 | 376 | dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.shuffle->get()); | 378 | dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.shuffle->get()); |
516 | 379 | dict[Properties::Metadata::name()] = dbus::types::Variant::encode(properties.meta_data_for_current_track->get()); | ||
517 | 377 | dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get()); | 380 | dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get()); |
518 | 378 | dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get()); | 381 | dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get()); |
519 | 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()); |
520 | @@ -405,7 +408,7 @@ | |||
521 | 405 | std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime; | 408 | std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime; |
522 | 406 | std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate; | 409 | std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate; |
523 | 407 | std::shared_ptr<core::dbus::Property<Properties::Shuffle>> shuffle; | 410 | std::shared_ptr<core::dbus::Property<Properties::Shuffle>> shuffle; |
525 | 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; |
526 | 409 | std::shared_ptr<core::dbus::Property<Properties::Volume>> volume; | 412 | std::shared_ptr<core::dbus::Property<Properties::Volume>> volume; |
527 | 410 | std::shared_ptr<core::dbus::Property<Properties::Position>> position; | 413 | std::shared_ptr<core::dbus::Property<Properties::Position>> position; |
528 | 411 | std::shared_ptr<core::dbus::Property<Properties::Duration>> duration; | 414 | std::shared_ptr<core::dbus::Property<Properties::Duration>> duration; |
529 | 412 | 415 | ||
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 | 24 | #include "util/timeout.h" | 24 | #include "util/timeout.h" |
535 | 25 | 25 | ||
536 | 26 | #include <unistd.h> | 26 | #include <unistd.h> |
537 | 27 | #include <ctime> | ||
538 | 27 | 28 | ||
539 | 28 | #include "client_death_observer.h" | 29 | #include "client_death_observer.h" |
540 | 29 | #include "engine.h" | 30 | #include "engine.h" |
541 | 30 | #include "track_list_implementation.h" | 31 | #include "track_list_implementation.h" |
542 | 32 | #include "xesam.h" | ||
543 | 31 | 33 | ||
544 | 32 | #include "gstreamer/engine.h" | 34 | #include "gstreamer/engine.h" |
545 | 33 | 35 | ||
546 | @@ -138,9 +140,19 @@ | |||
547 | 138 | } | 140 | } |
548 | 139 | case Engine::State::playing: | 141 | case Engine::State::playing: |
549 | 140 | { | 142 | { |
551 | 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. |
552 | 142 | // Some MPRIS clients expect this order of events. | 144 | // Some MPRIS clients expect this order of events. |
554 | 143 | parent->meta_data_for_current_track().set(std::get<1>(engine->track_meta_data().get())); | 145 | time_t now; |
555 | 146 | time(&now); | ||
556 | 147 | char buf[sizeof("2011-10-08T07:07:09Z")]; | ||
557 | 148 | strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&now)); | ||
558 | 149 | media::Track::MetaData metadata{std::get<1>(engine->track_meta_data().get())}; | ||
559 | 150 | // Setting this with second resolution makes sure that the track_meta_data property changes | ||
560 | 151 | // and thus the track_meta_data().changed() signal gets sent out over dbus. Otherwise the | ||
561 | 152 | // Property caching mechanism would prevent this. | ||
562 | 153 | metadata.set_last_used(std::string{buf}); | ||
563 | 154 | update_mpris_metadata(std::get<0>(engine->track_meta_data().get()), metadata); | ||
564 | 155 | |||
565 | 144 | // And update our playback status. | 156 | // And update our playback status. |
566 | 145 | parent->playback_status().set(media::Player::playing); | 157 | parent->playback_status().set(media::Player::playing); |
567 | 146 | MH_INFO("Requesting power state"); | 158 | MH_INFO("Requesting power state"); |
568 | @@ -325,6 +337,78 @@ | |||
569 | 325 | parent->can_go_next().set(has_next); | 337 | parent->can_go_next().set(has_next); |
570 | 326 | } | 338 | } |
571 | 327 | 339 | ||
572 | 340 | std::string get_uri_for_album_artwork(const media::Track::UriType& uri, | ||
573 | 341 | const media::Track::MetaData& metadata) | ||
574 | 342 | { | ||
575 | 343 | std::string art_uri; | ||
576 | 344 | static const std::string file_uri_prefix{"file://"}; | ||
577 | 345 | size_t pos = uri.find_first_of(file_uri_prefix); | ||
578 | 346 | MH_DEBUG("find_first_of: %d", pos != std::string::npos); | ||
579 | 347 | MH_DEBUG("uri.at(0): %c", uri.at(0)); | ||
580 | 348 | const bool is_local_file = (pos != std::string::npos or uri.at(0) == '/'); | ||
581 | 349 | MH_DEBUG("is_local_file ? %d", is_local_file); | ||
582 | 350 | MH_DEBUG("is_video_source: %d", parent->is_video_source().get()); | ||
583 | 351 | // If the track has a full image or preview image or is a video and it is a local file, | ||
584 | 352 | // then use the thumbnailer cache | ||
585 | 353 | if ( (( metadata.count(tags::PreviewImage::name) > 0 | ||
586 | 354 | and metadata.get(tags::PreviewImage::name) == "true") | ||
587 | 355 | or ( metadata.count(tags::Image::name) > 0 | ||
588 | 356 | and metadata.get(tags::Image::name) == "true") | ||
589 | 357 | or parent->is_video_source().get()) | ||
590 | 358 | and is_local_file) | ||
591 | 359 | { | ||
592 | 360 | MH_DEBUG("Detected the presence of a local preview image or full image"); | ||
593 | 361 | art_uri = "image://thumbnailer/" + uri; | ||
594 | 362 | MH_DEBUG("art_uri: %s", art_uri); | ||
595 | 363 | } | ||
596 | 364 | // Otherwise we'll try and see if we can look up the album art online through | ||
597 | 365 | // the dash's artwork proxy | ||
598 | 366 | else if (metadata.is_set(xesam::Album::name) or metadata.is_set(xesam::Artist::name)) | ||
599 | 367 | { | ||
600 | 368 | if (metadata.is_set(xesam::Album::name) and metadata.is_set(xesam::Artist::name)) | ||
601 | 369 | { | ||
602 | 370 | art_uri = "image://albumart/artist=" + metadata.artist() | ||
603 | 371 | + "&album=" + metadata.album(); | ||
604 | 372 | } | ||
605 | 373 | else | ||
606 | 374 | { | ||
607 | 375 | art_uri = "image://albumart/"; | ||
608 | 376 | if (metadata.is_set(xesam::Artist::name)) | ||
609 | 377 | art_uri += "artist=" + metadata.artist(); | ||
610 | 378 | else if (metadata.is_set(xesam::Album::name)) | ||
611 | 379 | art_uri += "album=" + metadata.album(); | ||
612 | 380 | } | ||
613 | 381 | } | ||
614 | 382 | // If all else fails, display a placeholder icon | ||
615 | 383 | else | ||
616 | 384 | { | ||
617 | 385 | art_uri = "file:///usr/lib/arm-linux-gnueabihf/unity-scopes/mediascanner-music/album_missing.svg"; | ||
618 | 386 | } | ||
619 | 387 | |||
620 | 388 | return art_uri; | ||
621 | 389 | } | ||
622 | 390 | |||
623 | 391 | // Makes sure all relevant metadata fields are set to current data and | ||
624 | 392 | // will trigger the track_meta_data().changed() signal to go out over dbus | ||
625 | 393 | void update_mpris_metadata(const media::Track::UriType& uri, const media::Track::MetaData& md) | ||
626 | 394 | { | ||
627 | 395 | media::Track::MetaData metadata{md}; | ||
628 | 396 | if (not metadata.is_set(media::Track::MetaData::TrackIdKey)) | ||
629 | 397 | { | ||
630 | 398 | const std::size_t last_slash = track_list->current().find_last_of("/"); | ||
631 | 399 | const std::string track_id = track_list->current().substr(last_slash+1); | ||
632 | 400 | if (not track_id.empty()) | ||
633 | 401 | metadata.set_track_id("/org/mpris/MediaPlayer2/Track/" + track_id); | ||
634 | 402 | else | ||
635 | 403 | MH_WARNING("Failed to set MPRIS track id since the id value is NULL"); | ||
636 | 404 | } | ||
637 | 405 | |||
638 | 406 | if (not metadata.is_set(media::Track::MetaData::TrackArtlUrlKey)) | ||
639 | 407 | metadata.set_art_url(get_uri_for_album_artwork(uri, metadata)); | ||
640 | 408 | |||
641 | 409 | parent->meta_data_for_current_track().set(metadata); | ||
642 | 410 | } | ||
643 | 411 | |||
644 | 328 | bool pause_other_players(media::Player::PlayerKey key) | 412 | bool pause_other_players(media::Player::PlayerKey key) |
645 | 329 | { | 413 | { |
646 | 330 | if (not config.parent.player_service) | 414 | if (not config.parent.player_service) |
647 | @@ -501,6 +585,12 @@ | |||
648 | 501 | d->engine->lifetime().set(lifetime); | 585 | d->engine->lifetime().set(lifetime); |
649 | 502 | }); | 586 | }); |
650 | 503 | 587 | ||
651 | 588 | d->engine->track_meta_data().changed().connect([this, config]( | ||
652 | 589 | const std::tuple<media::Track::UriType, media::Track::MetaData>& md) | ||
653 | 590 | { | ||
654 | 591 | d->update_mpris_metadata(std::get<0>(md), std::get<1>(md)); | ||
655 | 592 | }); | ||
656 | 593 | |||
657 | 504 | d->engine->about_to_finish_signal().connect([this]() | 594 | d->engine->about_to_finish_signal().connect([this]() |
658 | 505 | { | 595 | { |
659 | 506 | if (d->doing_abandon) | 596 | if (d->doing_abandon) |
660 | 507 | 597 | ||
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 | 478 | 478 | ||
666 | 479 | const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const | 479 | const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const |
667 | 480 | { | 480 | { |
669 | 481 | return *d->skeleton.properties.typed_meta_data_for_current_track; | 481 | return *d->skeleton.properties.meta_data_for_current_track; |
670 | 482 | } | 482 | } |
671 | 483 | 483 | ||
672 | 484 | const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const | 484 | const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const |
673 | @@ -608,7 +608,7 @@ | |||
674 | 608 | 608 | ||
675 | 609 | core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() | 609 | core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() |
676 | 610 | { | 610 | { |
678 | 611 | return *d->skeleton.properties.typed_meta_data_for_current_track; | 611 | return *d->skeleton.properties.meta_data_for_current_track; |
679 | 612 | } | 612 | } |
680 | 613 | 613 | ||
681 | 614 | core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() | 614 | core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() |
682 | 615 | 615 | ||
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 | 70 | object->get_property<mpris::Player::Properties::TypedLoopStatus>(), | 70 | object->get_property<mpris::Player::Properties::TypedLoopStatus>(), |
688 | 71 | object->get_property<mpris::Player::Properties::PlaybackRate>(), | 71 | object->get_property<mpris::Player::Properties::PlaybackRate>(), |
689 | 72 | object->get_property<mpris::Player::Properties::Shuffle>(), | 72 | object->get_property<mpris::Player::Properties::Shuffle>(), |
691 | 73 | object->get_property<mpris::Player::Properties::TypedMetaData>(), | 73 | object->get_property<mpris::Player::Properties::Metadata>(), |
692 | 74 | object->get_property<mpris::Player::Properties::Volume>(), | 74 | object->get_property<mpris::Player::Properties::Volume>(), |
693 | 75 | object->get_property<mpris::Player::Properties::Position>(), | 75 | object->get_property<mpris::Player::Properties::Position>(), |
694 | 76 | object->get_property<mpris::Player::Properties::Duration>(), | 76 | object->get_property<mpris::Player::Properties::Duration>(), |
695 | @@ -118,7 +118,7 @@ | |||
696 | 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; |
697 | 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; |
698 | 120 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> shuffle; | 120 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> shuffle; |
700 | 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; |
701 | 122 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume; | 122 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume; |
702 | 123 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position; | 123 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position; |
703 | 124 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration; | 124 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration; |
704 | 125 | 125 | ||
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 | 732 | player.properties.can_go_next->set(can_go_next); | 732 | player.properties.can_go_next->set(can_go_next); |
710 | 733 | }); | 733 | }); |
711 | 734 | 734 | ||
712 | 735 | // NOTE: the metadata first gets updated in the PlayerImplementation constructor which connects | ||
713 | 736 | // and reacts to the Engine track_meta_data changed signal. Setting the | ||
714 | 737 | // meta_data_for_current_track here makes sure that the signal uses the MPRIS object path | ||
715 | 738 | connections.meta_data_changed = player_sp->meta_data_for_current_track().changed().connect( | ||
716 | 739 | [this](const media::Track::MetaData& metadata) | ||
717 | 740 | { | ||
718 | 741 | player.properties.meta_data_for_current_track->set(metadata); | ||
719 | 742 | mpris::Player::Dictionary dict; dict[mpris::Player::Properties::Metadata::name()] | ||
720 | 743 | = dbus::types::Variant::encode(metadata); | ||
721 | 744 | player.signals.properties_changed->emit(std::make_tuple( | ||
722 | 745 | dbus::traits::Service<mpris::Player>::interface_name(), | ||
723 | 746 | dict, | ||
724 | 747 | mpris::Player::Skeleton::the_empty_list_of_invalidated_properties())); | ||
725 | 748 | }); | ||
726 | 749 | |||
727 | 735 | // Sync property values between session and player mpris::Player instances | 750 | // Sync property values between session and player mpris::Player instances |
728 | 736 | // TODO Getters from media::Player actually return values from a | 751 | // TODO Getters from media::Player actually return values from a |
729 | 737 | // mpris::Player::Skeleton instance different from "player". Each of them use | 752 | // mpris::Player::Skeleton instance different from "player". Each of them use |
730 | @@ -748,43 +763,6 @@ | |||
731 | 748 | player.properties.can_pause->set(player_sp->can_pause().get()); | 763 | player.properties.can_pause->set(player_sp->can_pause().get()); |
732 | 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()); |
733 | 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()); |
734 | 751 | |||
735 | 752 | #if 0 | ||
736 | 753 | // TODO cover_art_resolver() is not implemented yet | ||
737 | 754 | connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect( | ||
738 | 755 | [this](const core::ubuntu::media::Track::MetaData& md) | ||
739 | 756 | { | ||
740 | 757 | mpris::Player::Dictionary dict; | ||
741 | 758 | |||
742 | 759 | bool has_title = md.count(xesam::Title::name) > 0; | ||
743 | 760 | bool has_album_name = md.count(xesam::Album::name) > 0; | ||
744 | 761 | bool has_artist_name = md.count(xesam::Artist::name) > 0; | ||
745 | 762 | |||
746 | 763 | if (has_title) | ||
747 | 764 | dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name)); | ||
748 | 765 | if (has_album_name) | ||
749 | 766 | dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name)); | ||
750 | 767 | if (has_artist_name) | ||
751 | 768 | dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name)); | ||
752 | 769 | |||
753 | 770 | dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode( | ||
754 | 771 | cover_art_resolver( | ||
755 | 772 | has_title ? md.get(xesam::Title::name) : "", | ||
756 | 773 | has_album_name ? md.get(xesam::Album::name) : "", | ||
757 | 774 | has_artist_name ? md.get(xesam::Artist::name) : "")); | ||
758 | 775 | |||
759 | 776 | mpris::Player::Dictionary wrap; | ||
760 | 777 | wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict); | ||
761 | 778 | |||
762 | 779 | player.signals.properties_changed->emit( | ||
763 | 780 | std::make_tuple( | ||
764 | 781 | dbus::traits::Service< | ||
765 | 782 | mpris::Player::Properties::Metadata::Interface> | ||
766 | 783 | ::interface_name(), | ||
767 | 784 | wrap, | ||
768 | 785 | std::vector<std::string>())); | ||
769 | 786 | }); | ||
770 | 787 | #endif | ||
771 | 788 | } | 766 | } |
772 | 789 | 767 | ||
773 | 790 | void reset_current_player() | 768 | void reset_current_player() |
774 | @@ -807,6 +785,7 @@ | |||
775 | 807 | connections.can_pause_changed = the_empty_signal.connect([](){}); | 785 | connections.can_pause_changed = the_empty_signal.connect([](){}); |
776 | 808 | connections.can_go_previous_changed = the_empty_signal.connect([](){}); | 786 | connections.can_go_previous_changed = the_empty_signal.connect([](){}); |
777 | 809 | connections.can_go_next_changed = the_empty_signal.connect([](){}); | 787 | connections.can_go_next_changed = the_empty_signal.connect([](){}); |
778 | 788 | connections.meta_data_changed = the_empty_signal.connect([](){}); | ||
779 | 810 | } | 789 | } |
780 | 811 | 790 | ||
781 | 812 | bool is_current_player(media::Player::PlayerKey key) | 791 | bool is_current_player(media::Player::PlayerKey key) |
782 | 813 | 792 | ||
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 | 53 | DATUM(UserRating, xesam:userRating, double) | 53 | DATUM(UserRating, xesam:userRating, double) |
788 | 54 | } | 54 | } |
789 | 55 | 55 | ||
790 | 56 | namespace tags | ||
791 | 57 | { | ||
792 | 58 | // Does the track contain album art? | ||
793 | 59 | DATUM(Image, tag:image, bool) | ||
794 | 60 | // Does the track contain a small album art preview image? | ||
795 | 61 | DATUM(PreviewImage, tag::previewImage, bool) | ||
796 | 62 | } | ||
797 | 63 | |||
798 | 56 | #endif // XESAM_H_ | 64 | #endif // XESAM_H_ |