Merge lp:~jamesh/mediascanner/albumart-download into lp:mediascanner
- albumart-download
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Jussi Pakkanen |
Approved revision: | 401 |
Merged at revision: | 390 |
Proposed branch: | lp:~jamesh/mediascanner/albumart-download |
Merge into: | lp:mediascanner |
Diff against target: |
867 lines (+471/-15) 22 files modified
CMakeLists.txt (+1/-0) debian/control (+1/-0) mediascanner.gschema.xml.in (+1/-1) src/mediascanner-service/main.cpp (+4/-0) src/mediascanner/CMakeLists.txt (+1/-0) src/mediascanner/declarations.h (+2/-0) src/mediascanner/filesystemscanner.cpp (+7/-0) src/mediascanner/filesystemwalker.cpp (+17/-2) src/mediascanner/filesystemwalker.h (+1/-0) src/mediascanner/glibutils.h (+6/-0) src/mediascanner/mediaartcache.cpp (+25/-1) src/mediascanner/mediaartcache.h (+2/-1) src/mediascanner/mediaartdownloader.cpp (+297/-0) src/mediascanner/mediaartdownloader.h (+50/-0) src/mediascanner/mediaindex.cpp (+27/-5) src/mediascanner/mediaindex.h (+3/-3) src/mediascanner/metadataresolver.cpp (+3/-0) src/mediascanner/propertyschema.cpp (+16/-0) src/mediascanner/propertyschema.h (+3/-0) tests/auto/filesystemscannertest.cpp (+1/-0) tests/auto/loggingtest.cpp (+1/-1) tests/auto/propertytest.cpp (+2/-1) |
To merge this branch: | bzr merge lp:~jamesh/mediascanner/albumart-download |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paweł Stołowski (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+189631@code.launchpad.net |
Commit message
Download album art to the media art cache during scanning rather than storing remote URLs in the index.
Description of the change
Instead of just adding album art URLs to the index from the metadata resolver, actively download the art to the media art cache.
When reading metadata from the index later, add any art found in the cache to the results.
PS Jenkins bot (ps-jenkins) wrote : | # |
Paweł Stołowski (stolowski) wrote : | # |
Please add libsoup to debian/control
Paweł Stołowski (stolowski) wrote : | # |
Also it fails for me on tests when building the deb.
Paweł Stołowski (stolowski) wrote : | # |
I got quite a few warnings about getting image/png instead of image/jpeg. Looking at the code we ignore such album art? Can we handle it?
- 399. By James Henstridge
-
Separate out loading of the Grilo plugin from getting the source in
MediaArtDownloader, and fix the FileSystemScanner tests to set up the
lastfm plugin. - 400. By James Henstridge
-
Add libsoup as a Build-Depend.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:400
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:401
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : | # |
Looks good to me, functional tests confirm it works. I'm slightly concerned about the image/png case mentioned in the comment, but don't want to block on that; to my understanding it should work as long as mediascanner returns "" to the shell; if it's a problem, then it will be a minor fix for later.
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2013-08-29 08:00:35 +0000 |
3 | +++ CMakeLists.txt 2013-10-09 07:38:10 +0000 |
4 | @@ -105,6 +105,7 @@ |
5 | gstreamer-pbutils-1.0>=0.11.93 |
6 | gudev-1.0>=175 |
7 | liblucene++>=3.0.3.4 |
8 | + libsoup-2.4 |
9 | ) |
10 | |
11 | # http://public.kitware.com/Bug/view.php?id=14381 |
12 | |
13 | === modified file 'debian/control' |
14 | --- debian/control 2013-08-28 13:33:08 +0000 |
15 | +++ debian/control 2013-10-09 07:38:10 +0000 |
16 | @@ -20,6 +20,7 @@ |
17 | libgtest-dev, |
18 | libgudev-1.0-dev, |
19 | liblucene++-dev (>= 3.0.4), |
20 | + libsoup2.4-dev, |
21 | python, |
22 | Standards-Version: 3.9.4 |
23 | Homepage: https://launchpad.net/mediascanner |
24 | |
25 | === modified file 'mediascanner.gschema.xml.in' |
26 | --- mediascanner.gschema.xml.in 2013-08-23 02:58:42 +0000 |
27 | +++ mediascanner.gschema.xml.in 2013-10-09 07:38:10 +0000 |
28 | @@ -76,7 +76,7 @@ |
29 | dictionary of strings describing configuration parameters like API keys. |
30 | </description> |
31 | |
32 | - <default>[("grl-tmdb", "grl-tmdb", {"api-key": "@TMDB_APIKEY@"}), ("grl-lastfm-albumart", "grl-lastfm-albumart", {})]</default> |
33 | + <default>[("grl-tmdb", "grl-tmdb", {"api-key": "@TMDB_APIKEY@"})]</default> |
34 | </key> |
35 | </schema> |
36 | </schemalist> |
37 | |
38 | === modified file 'src/mediascanner-service/main.cpp' |
39 | --- src/mediascanner-service/main.cpp 2013-09-11 09:49:12 +0000 |
40 | +++ src/mediascanner-service/main.cpp 2013-10-09 07:38:10 +0000 |
41 | @@ -43,6 +43,7 @@ |
42 | #include "mediascanner/glibutils.h" |
43 | #include "mediascanner/locale.h" |
44 | #include "mediascanner/logging.h" |
45 | +#include "mediascanner/mediaartdownloader.h" |
46 | #include "mediascanner/mediaroot.h" |
47 | #include "mediascanner/metadataresolver.h" |
48 | #include "mediascanner/property.h" |
49 | @@ -881,6 +882,9 @@ |
50 | if (not LoadMetadataSources()) |
51 | return false; |
52 | |
53 | + // Set up the media art downloader |
54 | + MediaArtDownloader::LoadGriloPlugin(); |
55 | + |
56 | // Setup the scanner itself. |
57 | notifier_.reset(new CommitNotifier(this)); |
58 | task_manager_.reset(new TaskManager("media scanner service")); |
59 | |
60 | === modified file 'src/mediascanner/CMakeLists.txt' |
61 | --- src/mediascanner/CMakeLists.txt 2013-08-27 14:41:14 +0000 |
62 | +++ src/mediascanner/CMakeLists.txt 2013-10-09 07:38:10 +0000 |
63 | @@ -38,6 +38,7 @@ |
64 | logging.cpp |
65 | logging.h |
66 | mediaartcache.cpp |
67 | + mediaartdownloader.cpp |
68 | mediaindex.cpp |
69 | mediaroot.cpp |
70 | mediautils.cpp |
71 | |
72 | === modified file 'src/mediascanner/declarations.h' |
73 | --- src/mediascanner/declarations.h 2013-08-23 09:45:11 +0000 |
74 | +++ src/mediascanner/declarations.h 2013-10-09 07:38:10 +0000 |
75 | @@ -112,6 +112,7 @@ |
76 | |
77 | class CommitPolicy; |
78 | class Filter; |
79 | +class MediaArtDownloader; |
80 | class MediaIndex; |
81 | class MediaInfo; |
82 | class MediaRoot; |
83 | @@ -127,6 +128,7 @@ |
84 | class Wrapper; |
85 | |
86 | typedef std::shared_ptr<CommitPolicy> CommitPolicyPtr; |
87 | +typedef std::shared_ptr<MediaArtDownloader> MediaArtDownloaderPtr; |
88 | typedef std::shared_ptr<MediaRootManager> MediaRootManagerPtr; |
89 | typedef std::shared_ptr<MetadataResolver> MetadataResolverPtr; |
90 | typedef std::shared_ptr<TaskManager> TaskManagerPtr; |
91 | |
92 | === modified file 'src/mediascanner/filesystemscanner.cpp' |
93 | --- src/mediascanner/filesystemscanner.cpp 2013-09-09 11:49:53 +0000 |
94 | +++ src/mediascanner/filesystemscanner.cpp 2013-10-09 07:38:10 +0000 |
95 | @@ -32,6 +32,7 @@ |
96 | #include "mediascanner/logging.h" |
97 | #include "mediascanner/mediaroot.h" |
98 | #include "mediascanner/metadataresolver.h" |
99 | +#include "mediascanner/mediaartdownloader.h" |
100 | #include "mediascanner/taskfacades.h" |
101 | #include "mediascanner/taskmanager.h" |
102 | #include "mediascanner/writablemediaindex.h" |
103 | @@ -58,6 +59,7 @@ |
104 | TaskManagerPtr index_task_manager, |
105 | TaskFacadePtr index_task_facade) |
106 | : metadata_resolver_(resolver) |
107 | + , art_downloader_(new MediaArtDownloader()) |
108 | , file_task_manager_(new TaskManager("file system scanner")) |
109 | , index_task_manager_(index_task_manager) |
110 | , index_task_facade_(index_task_facade) |
111 | @@ -116,6 +118,7 @@ |
112 | } |
113 | |
114 | MetadataResolverPtr metadata_resolver_; |
115 | + MediaArtDownloaderPtr art_downloader_; |
116 | TaskManagerPtr file_task_manager_; |
117 | TaskManagerPtr index_task_manager_; |
118 | TaskFacadePtr index_task_facade_; |
119 | @@ -153,6 +156,7 @@ |
120 | } |
121 | |
122 | FileSystemWalkerPtr walker(new FileSystemWalker(root, metadata_resolver_, |
123 | + art_downloader_, |
124 | file_task_manager_, |
125 | index_task_manager_, |
126 | index_task_facade_)); |
127 | @@ -348,6 +352,9 @@ |
128 | if (metadata_resolver_) |
129 | metadata_resolver_->WaitForFinished(); |
130 | |
131 | + if (art_downloader_) |
132 | + art_downloader_->WaitForFinished(); |
133 | + |
134 | BOOST_ASSERT(is_idle()); |
135 | |
136 | Idle::AddOnce(std::bind(&Private::NotifyScanningFinished, |
137 | |
138 | === modified file 'src/mediascanner/filesystemwalker.cpp' |
139 | --- src/mediascanner/filesystemwalker.cpp 2013-09-17 10:48:31 +0000 |
140 | +++ src/mediascanner/filesystemwalker.cpp 2013-10-09 07:38:10 +0000 |
141 | @@ -45,6 +45,7 @@ |
142 | #include "mediascanner/glibutils.h" |
143 | #include "mediascanner/locale.h" |
144 | #include "mediascanner/logging.h" |
145 | +#include "mediascanner/mediaartdownloader.h" |
146 | #include "mediascanner/mediaroot.h" |
147 | #include "mediascanner/mediautils.h" |
148 | #include "mediascanner/metadataresolver.h" |
149 | @@ -97,6 +98,7 @@ |
150 | Private(size_t group_id, |
151 | const MediaRoot &media_root, |
152 | MetadataResolverPtr resolver, |
153 | + MediaArtDownloaderPtr art_downloader, |
154 | TaskManagerPtr file_task_manager, |
155 | TaskManagerPtr index_task_manager, |
156 | TaskFacadePtr index_task_facade, |
157 | @@ -189,6 +191,7 @@ |
158 | const ErrorFunction report_fatal_error_; |
159 | const Wrapper<GCancellable> cancellable_; |
160 | const MetadataResolverPtr metadata_resolver_; |
161 | + const MediaArtDownloaderPtr art_downloader_; |
162 | const TaskManagerPtr file_task_manager_; |
163 | const TaskManagerPtr index_task_manager_; |
164 | const TaskFacadePtr index_task_facade_; |
165 | @@ -220,6 +223,7 @@ |
166 | FileSystemWalker::Private::Private(size_t group_id, |
167 | const MediaRoot &media_root, |
168 | MetadataResolverPtr resolver, |
169 | + MediaArtDownloaderPtr art_downloader, |
170 | TaskManagerPtr file_task_manager, |
171 | TaskManagerPtr index_task_manager, |
172 | TaskFacadePtr index_task_facade, |
173 | @@ -227,6 +231,7 @@ |
174 | : report_fatal_error_(report_fatal_error) |
175 | , cancellable_(take(g_cancellable_new())) |
176 | , metadata_resolver_(resolver) |
177 | + , art_downloader_(art_downloader) |
178 | , file_task_manager_(file_task_manager) |
179 | , index_task_manager_(index_task_manager) |
180 | , index_task_facade_(index_task_facade) |
181 | @@ -248,12 +253,13 @@ |
182 | |
183 | FileSystemWalker::FileSystemWalker(const MediaRoot &media_root, |
184 | MetadataResolverPtr resolver, |
185 | + MediaArtDownloaderPtr art_downloader, |
186 | TaskManagerPtr file_task_manager, |
187 | TaskManagerPtr index_task_manager, |
188 | TaskFacadePtr index_task_facade) |
189 | : d(new Private(reinterpret_cast<size_t>(this), |
190 | - media_root, resolver, file_task_manager, |
191 | - index_task_manager, index_task_facade, |
192 | + media_root, resolver, art_downloader, |
193 | + file_task_manager, index_task_manager, index_task_facade, |
194 | d->bind_report_error_literal())) { |
195 | } |
196 | |
197 | @@ -926,6 +932,15 @@ |
198 | metadata.add_single(schema::kTitle.bind_value(w_title)); |
199 | } |
200 | |
201 | + // If the media has artist and album, then resolve artwork |
202 | + if (not metadata.first(schema::kArtist).empty() && |
203 | + not metadata.first(schema::kAlbum).empty()) { |
204 | + std::string artist = FromUnicode(metadata.first(schema::kArtist)); |
205 | + std::string album = FromUnicode(metadata.first(schema::kAlbum)); |
206 | + |
207 | + art_downloader_->resolve(artist, album); |
208 | + } |
209 | + |
210 | StoreMediaInfo(w_url, metadata); |
211 | |
212 | if ((details & ScanMetadata) && metadata_resolver_) |
213 | |
214 | === modified file 'src/mediascanner/filesystemwalker.h' |
215 | --- src/mediascanner/filesystemwalker.h 2013-08-23 09:45:11 +0000 |
216 | +++ src/mediascanner/filesystemwalker.h 2013-10-09 07:38:10 +0000 |
217 | @@ -39,6 +39,7 @@ |
218 | |
219 | FileSystemWalker(const MediaRoot &media_root, |
220 | MetadataResolverPtr resolver, |
221 | + MediaArtDownloaderPtr art_downloader, |
222 | TaskManagerPtr file_task_manager, |
223 | TaskManagerPtr index_task_manager, |
224 | TaskFacadePtr index_task_facade); |
225 | |
226 | === modified file 'src/mediascanner/glibutils.h' |
227 | --- src/mediascanner/glibutils.h 2013-09-10 14:02:38 +0000 |
228 | +++ src/mediascanner/glibutils.h 2013-10-09 07:38:10 +0000 |
229 | @@ -26,6 +26,7 @@ |
230 | #include <grilo.h> |
231 | #include <gst/pbutils/gstdiscoverer.h> |
232 | #include <gudev/gudev.h> |
233 | +#include <libsoup/soup.h> |
234 | |
235 | // Boost C++ |
236 | #include <boost/date_time/posix_time/posix_time_duration.hpp> |
237 | @@ -350,6 +351,11 @@ |
238 | template<> struct CopyHelper<GUdevDevice> : |
239 | public ObjectCopyHelper<GUdevDevice> { }; |
240 | |
241 | +template<> struct CopyHelper<SoupMessage> : |
242 | + public ObjectCopyHelper<SoupMessage> { }; |
243 | +template<> struct CopyHelper<SoupSession> : |
244 | + public ObjectCopyHelper<SoupSession> { }; |
245 | + |
246 | template<> struct CopyHelper<char> { |
247 | static char *Copy(char *p) { return g_strdup(p); } |
248 | static void Free(char *p) { g_free(p); } |
249 | |
250 | === modified file 'src/mediascanner/mediaartcache.cpp' |
251 | --- src/mediascanner/mediaartcache.cpp 2013-08-29 11:28:09 +0000 |
252 | +++ src/mediascanner/mediaartcache.cpp 2013-10-09 07:38:10 +0000 |
253 | @@ -85,7 +85,7 @@ |
254 | } |
255 | |
256 | void MediaArtCache::add_art(const std::string &artist, const std::string &album, |
257 | - char *data, unsigned int datalen) { |
258 | + const char *data, unsigned int datalen) { |
259 | string abs_fname = get_full_filename(artist, album); |
260 | GError *err = nullptr; |
261 | if(!g_file_set_contents(abs_fname.c_str(), data, datalen, &err)) { |
262 | @@ -108,6 +108,30 @@ |
263 | return ""; |
264 | } |
265 | |
266 | +string MediaArtCache::get_art_uri(const string &artist, const string &album) const |
267 | +{ |
268 | + std::string filename = get_art_file(artist, album); |
269 | + |
270 | + if (filename == "") { |
271 | + return ""; |
272 | + } |
273 | + |
274 | + GError *err = nullptr; |
275 | + char *c_uri = g_filename_to_uri(filename.c_str(), "", &err); |
276 | + if (!c_uri) { |
277 | + string e("Could not convert file name "); |
278 | + e += filename; |
279 | + e += " to URI: "; |
280 | + e += err->message; |
281 | + g_error_free(err); |
282 | + throw runtime_error(e); |
283 | + } |
284 | + |
285 | + std::string uri = c_uri; |
286 | + g_free(c_uri); |
287 | + return uri; |
288 | +} |
289 | + |
290 | std::string MediaArtCache::get_full_filename(const std::string &artist, const std::string & album) const { |
291 | return root_dir + "/" + compute_base_name(artist, album); |
292 | } |
293 | |
294 | === modified file 'src/mediascanner/mediaartcache.h' |
295 | --- src/mediascanner/mediaartcache.h 2013-08-29 11:28:09 +0000 |
296 | +++ src/mediascanner/mediaartcache.h 2013-10-09 07:38:10 +0000 |
297 | @@ -47,8 +47,9 @@ |
298 | MediaArtCache(); |
299 | bool has_art(const std::string &artist, const std::string &album) const; |
300 | void add_art(const std::string &artist, const std::string &album, |
301 | - char *data, unsigned int datalen); |
302 | + const char *data, unsigned int datalen); |
303 | std::string get_art_file(const std::string &artist, const std::string &album) const; |
304 | + std::string get_art_uri(const std::string &artist, const std::string &album) const; |
305 | void clear() const; |
306 | void prune(); |
307 | std::string get_cache_dir() const { return root_dir; } |
308 | |
309 | === added file 'src/mediascanner/mediaartdownloader.cpp' |
310 | --- src/mediascanner/mediaartdownloader.cpp 1970-01-01 00:00:00 +0000 |
311 | +++ src/mediascanner/mediaartdownloader.cpp 2013-10-09 07:38:10 +0000 |
312 | @@ -0,0 +1,297 @@ |
313 | +/* |
314 | + * This file is part of the Ubuntu TV Media Scanner |
315 | + * Copyright (C) 2012-2013 Canonical Ltd. |
316 | + * |
317 | + * This program is free software: you can redistribute it and/or modify |
318 | + * it under the terms of the GNU Lesser General Public License version 3 as |
319 | + * published by the Free Software Foundation. |
320 | + * |
321 | + * This program is distributed in the hope that it will be useful, |
322 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
323 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
324 | + * GNU Lesser General Public License for more details. |
325 | + * |
326 | + * You should have received a copy of the GNU Lesser General Public License |
327 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
328 | + * |
329 | + * Contact: Jim Hodapp <jim.hodapp@canonical.com> |
330 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
331 | + */ |
332 | + |
333 | +#include "mediascanner/logging.h" |
334 | +#include "mediascanner/glibutils.h" |
335 | +#include "mediascanner/mediaartcache.h" |
336 | +#include "mediascanner/mediaartdownloader.h" |
337 | + |
338 | +#define ALBUM_ART_PLUGIN "grl-lastfm-albumart" |
339 | +#define ALBUM_ART_SOURCE "grl-lastfm-albumart" |
340 | + |
341 | +namespace mediascanner { |
342 | + |
343 | +static const logging::Domain kInfo("info/mediaartdownloader", logging::info()); |
344 | +static const logging::Domain kWarning("warning/mediaartdownloader", logging::warning()); |
345 | + |
346 | +class MediaArtDownloader::Private { |
347 | +public: |
348 | + struct ResolveData { |
349 | + ResolveData(Private *d, |
350 | + const std::string &artist, |
351 | + const std::string &album) |
352 | + : d(d) |
353 | + , artist(artist) |
354 | + , album(album) { |
355 | + } |
356 | + |
357 | + Private *const d; |
358 | + const std::string artist; |
359 | + const std::string album; |
360 | + |
361 | + guint operation_id; // Grilo operation |
362 | + Wrapper<SoupMessage> message; |
363 | + }; |
364 | + typedef std::shared_ptr<ResolveData> ResolveDataPtr; |
365 | + |
366 | + Private() { |
367 | + g_mutex_init(&mutex); |
368 | + g_cond_init(&cond); |
369 | + setup_resolver(); |
370 | + setup_soup_session(); |
371 | + } |
372 | + |
373 | + ~Private() { |
374 | + cancel(); |
375 | + } |
376 | + |
377 | + bool is_idle() const { |
378 | + return jobs.empty(); |
379 | + } |
380 | + |
381 | + GMutex mutex; |
382 | + GCond cond; |
383 | + MediaArtCache cache; |
384 | + Wrapper<GrlSource> art_resolver; |
385 | + Wrapper<SoupSession> session; |
386 | + |
387 | + std::vector<ResolveDataPtr> jobs; |
388 | + |
389 | + void cancel(); |
390 | + void setup_resolver(); |
391 | + void setup_soup_session(); |
392 | + |
393 | + void add_job(ResolveDataPtr data); |
394 | + void complete(ResolveData *data); |
395 | + |
396 | + static void OnResolveThumbnail(GrlSource *source, unsigned operation_id, |
397 | + GrlMedia *media, void *user_data, |
398 | + const GError *error); |
399 | + static void OnResponseReceived(SoupSession *session, SoupMessage *message, |
400 | + void *user_data); |
401 | +}; |
402 | + |
403 | +MediaArtDownloader::MediaArtDownloader() |
404 | + : d(new Private) { |
405 | +} |
406 | + |
407 | +MediaArtDownloader::~MediaArtDownloader() { |
408 | + delete d; |
409 | +} |
410 | + |
411 | +void MediaArtDownloader::LoadGriloPlugin() { |
412 | + const Wrapper<GrlRegistry> registry = wrap(grl_registry_get_default()); |
413 | + Wrapper<GError> error; |
414 | + |
415 | + if (not grl_registry_load_plugin_by_id( |
416 | + registry.get(), ALBUM_ART_PLUGIN, error.out_param())) { |
417 | + const std::string error_message = to_string(error); |
418 | + kWarning("Could not load album art plugin: {1}") % error_message; |
419 | + return; |
420 | + } |
421 | +} |
422 | + |
423 | +void MediaArtDownloader::Private::setup_resolver() { |
424 | + const Wrapper<GrlRegistry> registry = wrap(grl_registry_get_default()); |
425 | + |
426 | + art_resolver = wrap(grl_registry_lookup_source( |
427 | + registry.get(), ALBUM_ART_SOURCE)); |
428 | + if (not art_resolver) { |
429 | + kWarning("Could not load album art source"); |
430 | + return; |
431 | + } |
432 | +} |
433 | + |
434 | +void MediaArtDownloader::Private::setup_soup_session() { |
435 | + session = wrap(soup_session_async_new_with_options( |
436 | + SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, |
437 | + SOUP_SESSION_SSL_STRICT, TRUE, |
438 | + SOUP_SESSION_USER_AGENT, "Ubuntu-Media-Scanner/1.0", |
439 | + NULL)); |
440 | + soup_session_add_feature_by_type( |
441 | + session.get(), SOUP_TYPE_PROXY_RESOLVER_DEFAULT); |
442 | +} |
443 | + |
444 | +void MediaArtDownloader::Private::add_job(ResolveDataPtr data) |
445 | +{ |
446 | + jobs.push_back(data); |
447 | +} |
448 | + |
449 | +void MediaArtDownloader::Private::complete(ResolveData *data) |
450 | +{ |
451 | + for (auto it = jobs.begin(); it != jobs.end(); it++) { |
452 | + if (it->get() == data) { |
453 | + jobs.erase(it); |
454 | + break; |
455 | + } |
456 | + } |
457 | + if (is_idle()) |
458 | + g_cond_signal(&cond); |
459 | +} |
460 | + |
461 | +void MediaArtDownloader::resolve(const std::string &artist, |
462 | + const std::string &album) { |
463 | + // If we couldn't configure either of these objects, then we won't |
464 | + // have any luck resolving the album art. |
465 | + if (not d->art_resolver || not d->session) |
466 | + return; |
467 | + |
468 | + kInfo("Resolving art for {1} - {2}") % artist % album; |
469 | + |
470 | + if (d->cache.has_art(artist, album)) { |
471 | + return; |
472 | + } |
473 | + |
474 | + g_mutex_lock(&d->mutex); |
475 | + |
476 | + /* First check whether there is any queued requests for this art */ |
477 | + for (auto it = d->jobs.begin(); it != d->jobs.end(); it++) { |
478 | + if ((*it)->artist == artist && (*it)->album == album) { |
479 | + kInfo("Art for {1} - {2} already being resolved") % artist % album; |
480 | + g_mutex_unlock(&d->mutex); |
481 | + return; |
482 | + } |
483 | + } |
484 | + |
485 | + |
486 | + const Private::ResolveDataPtr data( |
487 | + new Private::ResolveData(d, artist, album)); |
488 | + d->add_job(data); |
489 | + |
490 | + Wrapper<GrlMedia> media = take(grl_media_audio_new()); |
491 | + grl_media_audio_set_artist(GRL_MEDIA_AUDIO(media.get()), artist.c_str()); |
492 | + grl_media_audio_set_album(GRL_MEDIA_AUDIO(media.get()), album.c_str()); |
493 | + Wrapper<GList> resolve_keys = take(grl_metadata_key_list_new( |
494 | + GRL_METADATA_KEY_THUMBNAIL, |
495 | + GRL_METADATA_KEY_INVALID)); |
496 | + Wrapper<GrlOperationOptions> options = |
497 | + take(grl_operation_options_new(nullptr)); |
498 | + |
499 | + data->operation_id = grl_source_resolve( |
500 | + d->art_resolver.get(), media.get(), resolve_keys, |
501 | + options.get(), &Private::OnResolveThumbnail, data.get()); |
502 | + g_mutex_unlock(&d->mutex); |
503 | +} |
504 | + |
505 | +void MediaArtDownloader::Private::OnResolveThumbnail( |
506 | + GrlSource *source, unsigned operation_id, GrlMedia *media, |
507 | + void *user_data, const GError *error) { |
508 | + ResolveData *data = static_cast<ResolveData*>(user_data); |
509 | + Private *const d = data->d; |
510 | + |
511 | + g_mutex_lock(&d->mutex); |
512 | + data->operation_id = 0; |
513 | + if (error != NULL) { |
514 | + kWarning("Error determining album art for {1} - {2}: {3}") |
515 | + % data->artist % data->album % error->message; |
516 | + d->complete(data); |
517 | + g_mutex_unlock(&d->mutex); |
518 | + return; |
519 | + } |
520 | + |
521 | + const char *thumbnail = grl_media_get_thumbnail(media); |
522 | + if (thumbnail == NULL) { |
523 | + kInfo("No thumbnail found for {1} - {2}") % data->artist % data->album; |
524 | + d->complete(data); |
525 | + g_mutex_unlock(&d->mutex); |
526 | + return; |
527 | + } |
528 | + |
529 | + kInfo("Thumbnail for {1} - {2}: {3}") % data->artist % data->album % thumbnail; |
530 | + |
531 | + data->message = take(soup_message_new("GET", thumbnail)); |
532 | + // queue_message() takes ownership of the message passed to it. |
533 | + soup_session_queue_message(d->session.get(), data->message.dup(), |
534 | + &OnResponseReceived, data); |
535 | + g_mutex_unlock(&d->mutex); |
536 | +} |
537 | + |
538 | +void MediaArtDownloader::Private::OnResponseReceived( |
539 | + SoupSession *session, SoupMessage *message, void *user_data) { |
540 | + // If we have been cancelled, then our data pointer has been |
541 | + // cleaned up. |
542 | + if (message->status_code == SOUP_STATUS_CANCELLED) |
543 | + return; |
544 | + |
545 | + ResolveData *data = static_cast<ResolveData*>(user_data); |
546 | + Private *const d = data->d; |
547 | + |
548 | + g_mutex_lock(&d->mutex); |
549 | + data->message = NULL; |
550 | + |
551 | + if (message->status_code != SOUP_STATUS_OK) { |
552 | + kWarning("Failed to retrieve album art for {1} - {2}") |
553 | + % data->artist % data->album; |
554 | + d->complete(data); |
555 | + g_mutex_unlock(&d->mutex); |
556 | + return; |
557 | + } |
558 | + |
559 | + std::string content_type = soup_message_headers_get_content_type( |
560 | + message->response_headers, NULL); |
561 | + if (content_type != "image/jpeg") { |
562 | + kWarning("Expected album art for {1} - {2} to be image/jpeg, but got {3}") |
563 | + % data->artist % data->album % content_type; |
564 | + d->complete(data); |
565 | + g_mutex_unlock(&d->mutex); |
566 | + return; |
567 | + } |
568 | + |
569 | + kInfo("Storing album art for {1} - {2}") % data->artist % data->album; |
570 | + d->cache.add_art( |
571 | + data->artist, data->album, |
572 | + message->response_body->data, message->response_body->length); |
573 | + d->complete(data); |
574 | + g_mutex_unlock(&d->mutex); |
575 | +} |
576 | + |
577 | +bool MediaArtDownloader::is_idle() const { |
578 | + return d->is_idle(); |
579 | +} |
580 | + |
581 | +void MediaArtDownloader::WaitForFinished() { |
582 | + g_mutex_lock(&d->mutex); |
583 | + kInfo("Waiting for pending album art downloads."); |
584 | + while (not is_idle()) { |
585 | + g_cond_wait(&d->cond, &d->mutex); |
586 | + } |
587 | + kInfo("Album art downloads complete."); |
588 | + g_mutex_unlock(&d->mutex); |
589 | +} |
590 | + |
591 | +void MediaArtDownloader::Private::cancel() { |
592 | + g_mutex_lock(&mutex); |
593 | + |
594 | + for (const auto &data: jobs) { |
595 | + if (data->operation_id != 0) { |
596 | + grl_operation_cancel(data->operation_id); |
597 | + } |
598 | + data->operation_id = 0; |
599 | + if (data->message) { |
600 | + soup_session_cancel_message( |
601 | + session.get(), data->message.get(), SOUP_STATUS_CANCELLED); |
602 | + } |
603 | + data->message = NULL; |
604 | + } |
605 | + |
606 | + g_mutex_unlock(&mutex); |
607 | +} |
608 | + |
609 | +} |
610 | |
611 | === added file 'src/mediascanner/mediaartdownloader.h' |
612 | --- src/mediascanner/mediaartdownloader.h 1970-01-01 00:00:00 +0000 |
613 | +++ src/mediascanner/mediaartdownloader.h 2013-10-09 07:38:10 +0000 |
614 | @@ -0,0 +1,50 @@ |
615 | +/* |
616 | + * This file is part of the Ubuntu TV Media Scanner |
617 | + * Copyright (C) 2012-2013 Canonical Ltd. |
618 | + * |
619 | + * This program is free software: you can redistribute it and/or modify |
620 | + * it under the terms of the GNU Lesser General Public License version 3 as |
621 | + * published by the Free Software Foundation. |
622 | + * |
623 | + * This program is distributed in the hope that it will be useful, |
624 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
625 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
626 | + * GNU Lesser General Public License for more details. |
627 | + * |
628 | + * You should have received a copy of the GNU Lesser General Public License |
629 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
630 | + * |
631 | + * Contact: Jim Hodapp <jim.hodapp@canonical.com> |
632 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
633 | + */ |
634 | + |
635 | +#ifndef MEDIAARTDOWNLOADER_H |
636 | +#define MEDIAARTDOWNLOADER_H |
637 | + |
638 | +#include <string> |
639 | + |
640 | +namespace mediascanner { |
641 | + |
642 | +class MediaArtDownloader { |
643 | + class Private; |
644 | + friend class Private; |
645 | +private: |
646 | + |
647 | +public: |
648 | + MediaArtDownloader(); |
649 | + ~MediaArtDownloader(); |
650 | + |
651 | + static void LoadGriloPlugin(); |
652 | + |
653 | + void resolve(const std::string &artist, const std::string &album); |
654 | + |
655 | + bool is_idle() const; |
656 | + void WaitForFinished(); |
657 | + |
658 | +private: |
659 | + Private *const d; |
660 | +}; |
661 | + |
662 | +} |
663 | + |
664 | +#endif |
665 | |
666 | === modified file 'src/mediascanner/mediaindex.cpp' |
667 | --- src/mediascanner/mediaindex.cpp 2013-10-02 12:27:35 +0000 |
668 | +++ src/mediascanner/mediaindex.cpp 2013-10-09 07:38:10 +0000 |
669 | @@ -60,6 +60,7 @@ |
670 | #include "mediascanner/filter.h" |
671 | #include "mediascanner/locale.h" |
672 | #include "mediascanner/logging.h" |
673 | +#include "mediascanner/mediaartcache.h" |
674 | #include "mediascanner/mediaroot.h" |
675 | #include "mediascanner/mediautils.h" |
676 | #include "mediascanner/glibutils.h" |
677 | @@ -182,6 +183,8 @@ |
678 | typedef std::map<MediaRoot, Lucene::IndexReaderPtr> MediaReaderMap; |
679 | MediaReaderMap media_readers_; |
680 | |
681 | + MediaArtCache art_cache_; |
682 | + |
683 | private: |
684 | Lucene::IndexReaderPtr index_reader_; |
685 | Lucene::IndexSearcherPtr searcher_; |
686 | @@ -644,6 +647,21 @@ |
687 | } |
688 | } |
689 | |
690 | + // If we have an artist and album name, check to see if there is |
691 | + // art in the cache. |
692 | + if (not metadata.first(schema::kArtist).empty() && |
693 | + not metadata.first(schema::kAlbum).empty()) { |
694 | + std::string artist = FromUnicode(metadata.first(schema::kArtist)); |
695 | + std::string album = FromUnicode(metadata.first(schema::kAlbum)); |
696 | + std::string cover = d->art_cache_.get_art_uri(artist, album); |
697 | + |
698 | + g_warning("Found cover art: %s", cover.c_str()); |
699 | + if (not cover.empty()) { |
700 | + metadata.add_single( |
701 | + std::make_pair(schema::kCover, ToUnicode(cover))); |
702 | + } |
703 | + } |
704 | + |
705 | return metadata; |
706 | } |
707 | |
708 | @@ -702,9 +720,11 @@ |
709 | |
710 | class MediaIndex::Collector : public Lucene::Collector { |
711 | public: |
712 | - Collector(const ItemVistor &visit_item, Lucene::SearcherPtr searcher, |
713 | - Lucene::QueryPtr query, int32_t offset, int32_t limit) |
714 | - : visit_item_(visit_item) |
715 | + Collector(MediaIndex *index, const ItemVistor &visit_item, |
716 | + Lucene::SearcherPtr searcher, Lucene::QueryPtr query, |
717 | + int32_t offset, int32_t limit) |
718 | + : index_(index) |
719 | + , visit_item_(visit_item) |
720 | , searcher_(searcher) |
721 | , query_(query) |
722 | , offset_(offset) |
723 | @@ -729,7 +749,7 @@ |
724 | Lucene::DocumentPtr document = reader_->document(doc); |
725 | |
726 | if (document) |
727 | - visit_item_(ExtractProperties(document), limit_); |
728 | + visit_item_(index_->ExtractProperties(document), limit_); |
729 | |
730 | if (limit_ > 0) |
731 | --limit_; |
732 | @@ -744,6 +764,7 @@ |
733 | } |
734 | |
735 | private: |
736 | + MediaIndex *const index_; |
737 | ItemVistor visit_item_; |
738 | Lucene::IndexReaderPtr reader_; |
739 | Lucene::SearcherPtr searcher_; |
740 | @@ -837,7 +858,8 @@ |
741 | if (limit < 0) { |
742 | /* If no limit is specified, return results unsorted */ |
743 | const Lucene::CollectorPtr media_collector = |
744 | - newLucene<Collector>(visit_item, searcher, query, offset, limit); |
745 | + newLucene<Collector>(this, visit_item, searcher, query, |
746 | + offset, limit); |
747 | searcher->search(query, media_collector); |
748 | } else { |
749 | const auto collector = Lucene::TopScoreDocCollector::create( |
750 | |
751 | === modified file 'src/mediascanner/mediaindex.h' |
752 | --- src/mediascanner/mediaindex.h 2013-09-10 14:02:38 +0000 |
753 | +++ src/mediascanner/mediaindex.h 2013-10-09 07:38:10 +0000 |
754 | @@ -208,9 +208,9 @@ |
755 | |
756 | static Property::Set GetFields(Lucene::DocumentPtr document); |
757 | |
758 | - static MediaInfo ExtractProperties(Lucene::DocumentPtr document); |
759 | - static MediaInfo ExtractProperties(Lucene::DocumentPtr document, |
760 | - const Property::Set &fields); |
761 | + MediaInfo ExtractProperties(Lucene::DocumentPtr document); |
762 | + MediaInfo ExtractProperties(Lucene::DocumentPtr document, |
763 | + const Property::Set &fields); |
764 | |
765 | bool Lookup(const std::wstring &url, const Property &key, |
766 | Property::Value *value); |
767 | |
768 | === modified file 'src/mediascanner/metadataresolver.cpp' |
769 | --- src/mediascanner/metadataresolver.cpp 2013-09-17 10:48:31 +0000 |
770 | +++ src/mediascanner/metadataresolver.cpp 2013-10-09 07:38:10 +0000 |
771 | @@ -277,6 +277,9 @@ |
772 | const size_t removals = pending_pushes_.erase(push_id); |
773 | BOOST_ASSERT(removals == 1); |
774 | |
775 | + if (is_idle()) |
776 | + g_cond_signal(&cond_); |
777 | + |
778 | g_mutex_unlock(&mutex_); |
779 | } |
780 | |
781 | |
782 | === modified file 'src/mediascanner/propertyschema.cpp' |
783 | --- src/mediascanner/propertyschema.cpp 2013-09-09 12:17:56 +0000 |
784 | +++ src/mediascanner/propertyschema.cpp 2013-10-09 07:38:10 +0000 |
785 | @@ -220,6 +220,21 @@ |
786 | merge_nothing) { |
787 | } |
788 | |
789 | +static bool get_network_available(const GstDiscovererInfo *media) { |
790 | + GNetworkMonitor *monitor = g_network_monitor_get_default(); |
791 | + return g_network_monitor_get_network_available(monitor); |
792 | +} |
793 | + |
794 | +NetworkAvailable::NetworkAvailable() |
795 | + : NumericProperty(L"network-available", |
796 | + define("lucene-network-available", |
797 | + N_("Network available (Lucene)"), |
798 | + N_("...")), |
799 | + Property::Category::Generic, |
800 | + Property::MergeReplace, |
801 | + bind_attr(get_network_available)) { |
802 | +} |
803 | + |
804 | // File Properties ///////////////////////////////////////////////////////////// |
805 | |
806 | Url::Url() |
807 | @@ -1320,6 +1335,7 @@ |
808 | const MaximumVideoBitRate kMaximumVideoBitRate; |
809 | const MeteringMode kMeteringMode; |
810 | const MimeType kMimeType; |
811 | +const NetworkAvailable kNetworkAvailable; |
812 | const Organization kOrganization; |
813 | const OriginalTitle kOriginalTitle; |
814 | const Performer kPerformer; |
815 | |
816 | === modified file 'src/mediascanner/propertyschema.h' |
817 | --- src/mediascanner/propertyschema.h 2013-08-06 06:36:26 +0000 |
818 | +++ src/mediascanner/propertyschema.h 2013-10-09 07:38:10 +0000 |
819 | @@ -108,6 +108,9 @@ |
820 | struct Favourite : public NumericProperty<bool> { Favourite(); } |
821 | extern const kFavourite; |
822 | |
823 | +struct NetworkAvailable : public NumericProperty<bool> { NetworkAvailable(); } |
824 | +extern const kNetworkAvailable; |
825 | + |
826 | // File Properties ///////////////////////////////////////////////////////////// |
827 | |
828 | // Local URL of the media, taken from File System |
829 | |
830 | === modified file 'tests/auto/filesystemscannertest.cpp' |
831 | --- tests/auto/filesystemscannertest.cpp 2013-09-11 09:49:12 +0000 |
832 | +++ tests/auto/filesystemscannertest.cpp 2013-10-09 07:38:10 +0000 |
833 | @@ -341,6 +341,7 @@ |
834 | |
835 | AddGlobalTestEnvironment(new LuceneEnvironment(kMediaIndexPath)); |
836 | AddGlobalTestEnvironment(new TheMovieDbEnvironment(REQUEST_FILE)); |
837 | + AddGlobalTestEnvironment(new LastFmEnvironment(REQUEST_FILE)); |
838 | } |
839 | |
840 | } // namespace filesystemscannertest |
841 | |
842 | === modified file 'tests/auto/loggingtest.cpp' |
843 | --- tests/auto/loggingtest.cpp 2013-09-11 09:49:12 +0000 |
844 | +++ tests/auto/loggingtest.cpp 2013-10-09 07:38:10 +0000 |
845 | @@ -205,7 +205,7 @@ |
846 | } // namespace mediascanner |
847 | |
848 | int main(int argc, char *argv[]) { |
849 | - ::setenv("MEDIASCANNER_DEBUG", 0, true); |
850 | + ::setenv("MEDIASCANNER_DEBUG", "0", true); |
851 | mediascanner::InitTests(&argc, argv); |
852 | return RUN_ALL_TESTS(); |
853 | } |
854 | |
855 | === modified file 'tests/auto/propertytest.cpp' |
856 | --- tests/auto/propertytest.cpp 2013-08-23 07:49:06 +0000 |
857 | +++ tests/auto/propertytest.cpp 2013-10-09 07:38:10 +0000 |
858 | @@ -71,7 +71,8 @@ |
859 | &schema::kCertification, |
860 | &schema::kComment, |
861 | &schema::kKeyword, |
862 | - &schema::kLanguage)); |
863 | + &schema::kLanguage, |
864 | + &schema::kNetworkAvailable)); |
865 | |
866 | INSTANTIATE_TEST_CASE_P(FileProperties, PropertyTest, |
867 | testing::Values(&schema::kMimeType, |
FAILED: Continuous integration, rev:398 jenkins. qa.ubuntu. com/job/ mediascanner- ci/70/ jenkins. qa.ubuntu. com/job/ mediascanner- saucy-amd64- ci/70/console jenkins. qa.ubuntu. com/job/ mediascanner- saucy-armhf- ci/70/console jenkins. qa.ubuntu. com/job/ mediascanner- saucy-i386- ci/70/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: 10.97.0. 26:8080/ job/mediascanne r-ci/70/ rebuild
http://