Merge lp:~jamesh/mediascanner2/folder-coverart into lp:mediascanner2
- folder-coverart
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michi Henning |
Approved revision: | 325 |
Merged at revision: | 315 |
Proposed branch: | lp:~jamesh/mediascanner2/folder-coverart |
Merge into: | lp:mediascanner2 |
Diff against target: |
730 lines (+453/-36) 16 files modified
CMakeLists.txt (+1/-1) debian/changelog (+9/-0) debian/libmediascanner-2.0-3.symbols (+2/-0) debian/libmediascanner-2.0-4.shlibs (+1/-1) src/mediascanner/Album.cc (+25/-8) src/mediascanner/Album.hh (+4/-0) src/mediascanner/CMakeLists.txt (+1/-0) src/mediascanner/FolderArtCache.cc (+169/-0) src/mediascanner/MediaFile.cc (+10/-4) src/mediascanner/MediaStore.cc (+1/-1) src/mediascanner/internal/FolderArtCache.hh (+55/-0) src/ms-dbus/dbus-codec.cc (+4/-2) src/ms-dbus/dbus-codec.hh (+1/-1) test/test_dbus.cc (+3/-2) test/test_mfbuilder.cc (+164/-16) test/test_util.cc (+3/-0) |
To merge this branch: | bzr merge lp:~jamesh/mediascanner2/folder-coverart |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michi Henning (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+278099@code.launchpad.net |
Commit message
If a folder contains an image file named {cover,
Description of the change
Add folder based cover art detection to the MediaFile:
PS Jenkins bot (ps-jenkins) wrote : | # |
- 321. By James Henstridge
-
Remove unneeded #define.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:321
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 322. By James Henstridge
-
Add new constructor for album, explicitly passing the has_thumbnail boolean.
- 323. By James Henstridge
-
Add Album::
getHasThumbnail () for use by dbus code, and make MediaStore
pass in has_thumbnail to Album. - 324. By James Henstridge
-
Add folder artwork support to Album class.
- 325. By James Henstridge
-
Bump version number to account for API additions.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:325
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michi Henning (michihenning) wrote : | # |
Looks good! I tested on the phone and, after dropping a folder.jpg file into a dir with songs without artwork, the image was used by the music app.
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-11-07 09:13:53 +0000 |
3 | +++ CMakeLists.txt 2015-11-23 03:14:55 +0000 |
4 | @@ -1,7 +1,7 @@ |
5 | project(mediascanner2 CXX C) |
6 | cmake_minimum_required(VERSION 2.8.9) |
7 | |
8 | -set(MEDIASCANNER_VERSION "0.108") |
9 | +set(MEDIASCANNER_VERSION "0.109") |
10 | |
11 | execute_process( |
12 | COMMAND /bin/sh ${CMAKE_CURRENT_SOURCE_DIR}/get-soversion.sh |
13 | |
14 | === modified file 'debian/changelog' |
15 | --- debian/changelog 2015-11-09 01:56:39 +0000 |
16 | +++ debian/changelog 2015-11-23 03:14:55 +0000 |
17 | @@ -1,3 +1,12 @@ |
18 | +mediascanner2 (0.109-0ubuntu1) UNRELEASED; urgency=medium |
19 | + |
20 | + * If a folder contains an image file named |
21 | + {cover,album,albumart,.folder,folder}.{jpeg,jpg,png} use it as album |
22 | + art for songs in preference to online art if the songs do not have |
23 | + embedded art. (LP: #1372000) |
24 | + |
25 | + -- James Henstridge <james.henstridge@canonical.com> Mon, 23 Nov 2015 11:12:59 +0800 |
26 | + |
27 | mediascanner2 (0.108+16.04.20151109-0ubuntu1) xenial; urgency=medium |
28 | |
29 | * Move the metadata extractor to a separate process to isolate bugs in |
30 | |
31 | === modified file 'debian/libmediascanner-2.0-3.symbols' |
32 | --- debian/libmediascanner-2.0-3.symbols 2015-09-21 10:56:50 +0000 |
33 | +++ debian/libmediascanner-2.0-3.symbols 2015-11-23 03:14:55 +0000 |
34 | @@ -5,11 +5,13 @@ |
35 | (c++)"mediascanner::Album::Album(mediascanner::Album const&)@Base" 0.105+14.10.20140903 |
36 | (c++)"mediascanner::Album::Album(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.105+14.10.20140903 |
37 | (c++)"mediascanner::Album::Album(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.105+14.10.20140903 |
38 | + (c++)"mediascanner::Album::Album(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)@Base" 0replaceme |
39 | (c++)"mediascanner::Album::getArtFile() const@Base" 0.105+14.10.20140903 |
40 | (c++)"mediascanner::Album::getArtist() const@Base" 0.105+14.10.20140903 |
41 | (c++)"mediascanner::Album::getArtUri() const@Base" 0.105+14.10.20140903 |
42 | (c++)"mediascanner::Album::getDate() const@Base" 0.105+14.10.20140903 |
43 | (c++)"mediascanner::Album::getGenre() const@Base" 0.105+14.10.20140903 |
44 | + (c++)"mediascanner::Album::getHasThumbnail() const@Base" 0replaceme |
45 | (c++)"mediascanner::Album::getTitle() const@Base" 0.101+14.10.20140613 |
46 | (c++)"mediascanner::Album::operator=(mediascanner::Album&&)@Base" 0.105+14.10.20140903 |
47 | (c++)"mediascanner::Album::operator=(mediascanner::Album const&)@Base" 0.105+14.10.20140903 |
48 | |
49 | === modified file 'debian/libmediascanner-2.0-4.shlibs' |
50 | --- debian/libmediascanner-2.0-4.shlibs 2015-09-21 10:20:38 +0000 |
51 | +++ debian/libmediascanner-2.0-4.shlibs 2015-11-23 03:14:55 +0000 |
52 | @@ -1,1 +1,1 @@ |
53 | -libmediascanner-2.0 4 libmediascanner-2.0-4 (>= 0.107) |
54 | +libmediascanner-2.0 4 libmediascanner-2.0-4 (>= 0.109) |
55 | |
56 | === modified file 'src/mediascanner/Album.cc' |
57 | --- src/mediascanner/Album.cc 2014-09-03 07:00:02 +0000 |
58 | +++ src/mediascanner/Album.cc 2015-11-23 03:14:55 +0000 |
59 | @@ -18,6 +18,7 @@ |
60 | */ |
61 | |
62 | #include "Album.hh" |
63 | +#include "internal/FolderArtCache.hh" |
64 | #include "internal/utils.hh" |
65 | |
66 | using namespace std; |
67 | @@ -30,13 +31,14 @@ |
68 | string date; |
69 | string genre; |
70 | string filename; |
71 | + bool has_thumbnail; |
72 | |
73 | Private() {} |
74 | Private(const string &title, const string &artist, |
75 | const string &date, const string &genre, |
76 | - const string &filename) |
77 | + const string &filename, bool has_thumbnail) |
78 | : title(title), artist(artist), date(date), genre(genre), |
79 | - filename(filename) {} |
80 | + filename(filename), has_thumbnail(has_thumbnail) {} |
81 | Private(const Private &other) { |
82 | *this = other; |
83 | } |
84 | @@ -46,13 +48,19 @@ |
85 | } |
86 | |
87 | Album::Album(const std::string &title, const std::string &artist) |
88 | - : Album(title, artist, "", "", "") { |
89 | + : Album(title, artist, "", "", "", false) { |
90 | } |
91 | |
92 | Album::Album(const std::string &title, const std::string &artist, |
93 | const std::string &date, const std::string &genre, |
94 | const std::string &filename) |
95 | - : p(new Private(title, artist, date, genre, filename)) { |
96 | + : Album(title, artist, date, genre, filename, !filename.empty()) { |
97 | +} |
98 | + |
99 | +Album::Album(const std::string &title, const std::string &artist, |
100 | + const std::string &date, const std::string &genre, |
101 | + const std::string &filename, bool has_thumbnail) |
102 | + : p(new Private(title, artist, date, genre, filename, has_thumbnail)) { |
103 | } |
104 | |
105 | Album::Album(const Album &other) : p(new Private(*other.p)) { |
106 | @@ -100,11 +108,19 @@ |
107 | return p->filename; |
108 | } |
109 | |
110 | +bool Album::getHasThumbnail() const noexcept { |
111 | + return p->has_thumbnail; |
112 | +} |
113 | + |
114 | std::string Album::getArtUri() const { |
115 | - if (p->filename.empty()) { |
116 | + if (p->has_thumbnail) { |
117 | + return make_thumbnail_uri(getUri(p->filename)); |
118 | + } else { |
119 | + auto standalone = FolderArtCache::get().get_art_for_file(p->filename); |
120 | + if (!standalone.empty()) { |
121 | + return make_thumbnail_uri(getUri(standalone)); |
122 | + } |
123 | return make_album_art_uri(p->artist, p->title); |
124 | - } else { |
125 | - return make_thumbnail_uri(getUri(p->filename)); |
126 | } |
127 | } |
128 | |
129 | @@ -113,7 +129,8 @@ |
130 | p->artist == other.p->artist && |
131 | p->date == other.p->date && |
132 | p->genre == other.p->genre && |
133 | - p->filename == other.p->filename; |
134 | + p->filename == other.p->filename && |
135 | + p->has_thumbnail == other.p->has_thumbnail; |
136 | } |
137 | |
138 | bool Album::operator!=(const Album &other) const { |
139 | |
140 | === modified file 'src/mediascanner/Album.hh' |
141 | --- src/mediascanner/Album.hh 2014-09-03 07:00:02 +0000 |
142 | +++ src/mediascanner/Album.hh 2015-11-23 03:14:55 +0000 |
143 | @@ -32,6 +32,9 @@ |
144 | Album(const std::string &title, const std::string &artist, |
145 | const std::string &date, const std::string &genre, |
146 | const std::string &filename); |
147 | + Album(const std::string &title, const std::string &artist, |
148 | + const std::string &date, const std::string &genre, |
149 | + const std::string &filename, bool has_thumbnail); |
150 | Album(const Album &other); |
151 | Album(Album &&other); |
152 | ~Album(); |
153 | @@ -44,6 +47,7 @@ |
154 | const std::string& getDate() const noexcept; |
155 | const std::string& getGenre() const noexcept; |
156 | const std::string& getArtFile() const noexcept; |
157 | + bool getHasThumbnail() const noexcept; |
158 | std::string getArtUri() const; |
159 | bool operator==(const Album &other) const; |
160 | bool operator!=(const Album &other) const; |
161 | |
162 | === modified file 'src/mediascanner/CMakeLists.txt' |
163 | --- src/mediascanner/CMakeLists.txt 2015-07-14 08:59:01 +0000 |
164 | +++ src/mediascanner/CMakeLists.txt 2015-11-23 03:14:55 +0000 |
165 | @@ -6,6 +6,7 @@ |
166 | Album.cc |
167 | MediaStore.cc |
168 | MediaStoreBase.cc |
169 | + FolderArtCache.cc |
170 | utils.cc |
171 | mozilla/fts3_porter.c |
172 | mozilla/Normalize.c |
173 | |
174 | === added file 'src/mediascanner/FolderArtCache.cc' |
175 | --- src/mediascanner/FolderArtCache.cc 1970-01-01 00:00:00 +0000 |
176 | +++ src/mediascanner/FolderArtCache.cc 2015-11-23 03:14:55 +0000 |
177 | @@ -0,0 +1,169 @@ |
178 | +/* |
179 | + * Copyright (C) 2015 Canonical, Ltd. |
180 | + * |
181 | + * Authors: |
182 | + * James Henstridge <james.henstridge@canonical.com> |
183 | + * |
184 | + * This program is free software: you can redistribute it and/or modify |
185 | + * it under the terms of the GNU Lesser General Public License version 3 as |
186 | + * published by the Free Software Foundation. |
187 | + * |
188 | + * This program is distributed in the hope that it will be useful, |
189 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
190 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
191 | + * GNU Lesser General Public License for more details. |
192 | + * |
193 | + * You should have received a copy of the GNU Lesser General Public License |
194 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
195 | + */ |
196 | + |
197 | +#include "internal/FolderArtCache.hh" |
198 | + |
199 | +#include <algorithm> |
200 | +#include <array> |
201 | +#include <cctype> |
202 | +#include <cerrno> |
203 | +#include <cstdlib> |
204 | +#include <cstring> |
205 | +#include <dirent.h> |
206 | +#include <memory> |
207 | +#include <stdexcept> |
208 | +#include <sys/types.h> |
209 | +#include <sys/stat.h> |
210 | +#include <unistd.h> |
211 | + |
212 | +using namespace std; |
213 | + |
214 | +namespace { |
215 | + |
216 | +const int CACHE_SIZE = 50; |
217 | + |
218 | +string detect_albumart(string directory) { |
219 | + static const array<const char*, 5> art_basenames = { |
220 | + "cover", |
221 | + "album", |
222 | + "albumart", |
223 | + ".folder", |
224 | + "folder", |
225 | + }; |
226 | + static const array<const char*, 3> art_extensions = { |
227 | + "jpeg", |
228 | + "jpg", |
229 | + "png", |
230 | + }; |
231 | + if (!directory.empty() && directory[directory.size()-1] != '/') { |
232 | + directory += "/"; |
233 | + } |
234 | + unique_ptr<DIR, decltype(&closedir)> dir( |
235 | + opendir(directory.c_str()), &closedir); |
236 | + if (!dir) { |
237 | + return ""; |
238 | + } |
239 | + |
240 | + const int dirent_size = sizeof(dirent) + fpathconf(dirfd(dir.get()), _PC_NAME_MAX) + 1; |
241 | + unique_ptr<struct dirent, decltype(&free)> entry( |
242 | + reinterpret_cast<struct dirent*>(malloc(dirent_size)), &free); |
243 | + |
244 | + string detected; |
245 | + int best_score = 0; |
246 | + struct dirent *de = nullptr; |
247 | + while (readdir_r(dir.get(), entry.get(), &de) == 0 && de) { |
248 | + const string filename(de->d_name); |
249 | + auto dot = filename.rfind('.'); |
250 | + // Ignore files with no extension |
251 | + if (dot == string::npos) { |
252 | + continue; |
253 | + } |
254 | + auto basename = filename.substr(0, dot); |
255 | + auto extension = filename.substr(dot+1); |
256 | + |
257 | + // Does the file name match one of the required names when |
258 | + // converted to lower case? |
259 | + transform(basename.begin(), basename.end(), |
260 | + basename.begin(), ::tolower); |
261 | + transform(extension.begin(), extension.end(), |
262 | + extension.begin(), ::tolower); |
263 | + auto base_pos = find(art_basenames.begin(), art_basenames.end(), basename); |
264 | + if (base_pos == art_basenames.end()) { |
265 | + continue; |
266 | + } |
267 | + auto ext_pos = find(art_extensions.begin(), art_extensions.end(), extension); |
268 | + if (ext_pos == art_extensions.end()) { |
269 | + continue; |
270 | + } |
271 | + |
272 | + int score = (base_pos - art_basenames.begin()) * art_basenames.size() + |
273 | + (ext_pos - art_extensions.begin()); |
274 | + if (detected.empty() || score < best_score) { |
275 | + detected = filename; |
276 | + best_score = score; |
277 | + } |
278 | + } |
279 | + if (detected.empty()) { |
280 | + return detected; |
281 | + } |
282 | + return directory + detected; |
283 | +} |
284 | + |
285 | +} |
286 | + |
287 | +namespace mediascanner { |
288 | + |
289 | +FolderArtCache::FolderArtCache() = default; |
290 | +FolderArtCache::~FolderArtCache() = default; |
291 | + |
292 | +// Get a singleton instance of the cache |
293 | +FolderArtCache& FolderArtCache::get() { |
294 | + static FolderArtCache cache; |
295 | + return cache; |
296 | +} |
297 | + |
298 | +string FolderArtCache::get_art_for_directory(const string &directory) { |
299 | + struct stat s; |
300 | + if (lstat(directory.c_str(), &s) < 0) { |
301 | + return ""; |
302 | + } |
303 | + if (!S_ISDIR(s.st_mode)) { |
304 | + return ""; |
305 | + } |
306 | + FolderArtInfo info; |
307 | + bool update = false; |
308 | + try { |
309 | + info = cache_.at(directory); |
310 | + } catch (const out_of_range &) { |
311 | + // Fall back to checking the previous iteration of the cache |
312 | + try { |
313 | + info = old_cache_.at(directory); |
314 | + update = true; |
315 | + } catch (const out_of_range &) { |
316 | + } |
317 | + } |
318 | + |
319 | + if (info.dir_mtime.tv_sec != s.st_mtim.tv_sec || |
320 | + info.dir_mtime.tv_nsec != s.st_mtim.tv_nsec) { |
321 | + info.art = detect_albumart(directory); |
322 | + info.dir_mtime = s.st_mtim; |
323 | + update = true; |
324 | + } |
325 | + |
326 | + if (update) { |
327 | + cache_[directory] = info; |
328 | + // Start new cache generation if we've exceeded the size. |
329 | + if (cache_.size() > CACHE_SIZE) { |
330 | + old_cache_ = move(cache_); |
331 | + cache_.clear(); |
332 | + } |
333 | + } |
334 | + return info.art; |
335 | +} |
336 | + |
337 | +string FolderArtCache::get_art_for_file(const string &filename) { |
338 | + auto slash = filename.rfind('/'); |
339 | + if (slash == string::npos) { |
340 | + return ""; |
341 | + } |
342 | + auto directory = filename.substr(0, slash + 1); |
343 | + return get_art_for_directory(directory); |
344 | +} |
345 | + |
346 | +} |
347 | |
348 | === modified file 'src/mediascanner/MediaFile.cc' |
349 | --- src/mediascanner/MediaFile.cc 2015-07-07 04:36:02 +0000 |
350 | +++ src/mediascanner/MediaFile.cc 2015-11-23 03:14:55 +0000 |
351 | @@ -20,6 +20,7 @@ |
352 | #include "MediaFile.hh" |
353 | #include "MediaFileBuilder.hh" |
354 | #include "internal/MediaFilePrivate.hh" |
355 | +#include "internal/FolderArtCache.hh" |
356 | #include "internal/utils.hh" |
357 | #include <stdexcept> |
358 | |
359 | @@ -155,12 +156,17 @@ |
360 | |
361 | std::string MediaFile::getArtUri() const { |
362 | switch (p->type) { |
363 | - case AudioMedia: |
364 | + case AudioMedia: { |
365 | if (p->has_thumbnail) { |
366 | return make_thumbnail_uri(getUri()); |
367 | - } else { |
368 | - return make_album_art_uri(getAuthor(), getAlbum()); |
369 | - } |
370 | + } |
371 | + auto standalone = FolderArtCache::get().get_art_for_file(p->filename); |
372 | + if(!standalone.empty()) { |
373 | + return make_thumbnail_uri(mediascanner::getUri(standalone)); |
374 | + } |
375 | + return make_album_art_uri(getAuthor(), getAlbum()); |
376 | + } |
377 | + |
378 | default: |
379 | return make_thumbnail_uri(getUri()); |
380 | } |
381 | |
382 | === modified file 'src/mediascanner/MediaStore.cc' |
383 | --- src/mediascanner/MediaStore.cc 2015-10-07 06:49:12 +0000 |
384 | +++ src/mediascanner/MediaStore.cc 2015-11-23 03:14:55 +0000 |
385 | @@ -604,7 +604,7 @@ |
386 | const string genre = query.getText(3); |
387 | const string filename = query.getText(4); |
388 | const bool has_thumbnail = query.getInt(5); |
389 | - return Album(album, album_artist, date, genre, has_thumbnail ? filename : ""); |
390 | + return Album(album, album_artist, date, genre, filename, has_thumbnail); |
391 | } |
392 | |
393 | static vector<Album> collect_albums(Statement &query) { |
394 | |
395 | === added file 'src/mediascanner/internal/FolderArtCache.hh' |
396 | --- src/mediascanner/internal/FolderArtCache.hh 1970-01-01 00:00:00 +0000 |
397 | +++ src/mediascanner/internal/FolderArtCache.hh 2015-11-23 03:14:55 +0000 |
398 | @@ -0,0 +1,55 @@ |
399 | +/* |
400 | + * Copyright (C) 2015 Canonical, Ltd. |
401 | + * |
402 | + * Authors: |
403 | + * James Henstridge <james.henstridge@canonical.com> |
404 | + * |
405 | + * This program is free software: you can redistribute it and/or modify |
406 | + * it under the terms of the GNU Lesser General Public License version 3 as |
407 | + * published by the Free Software Foundation. |
408 | + * |
409 | + * This program is distributed in the hope that it will be useful, |
410 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
411 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
412 | + * GNU Lesser General Public License for more details. |
413 | + * |
414 | + * You should have received a copy of the GNU Lesser General Public License |
415 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
416 | + */ |
417 | + |
418 | +#ifndef FOLDERARTCACHE_HH |
419 | +#define FOLDERARTCACHE_HH |
420 | + |
421 | +#include <cstdint> |
422 | +#include <ctime> |
423 | +#include <map> |
424 | +#include <string> |
425 | + |
426 | +namespace mediascanner { |
427 | + |
428 | +struct FolderArtInfo { |
429 | + std::string art; |
430 | + struct timespec dir_mtime {0, 0}; |
431 | +}; |
432 | + |
433 | +class FolderArtCache final { |
434 | +public: |
435 | + FolderArtCache(); |
436 | + ~FolderArtCache(); |
437 | + |
438 | + FolderArtCache(const FolderArtCache &other) = delete; |
439 | + FolderArtCache& operator=(const FolderArtCache &other) = delete; |
440 | + |
441 | + // Get a singleton instance of the cache |
442 | + static FolderArtCache& get(); |
443 | + |
444 | + std::string get_art_for_directory(const std::string &directory); |
445 | + std::string get_art_for_file(const std::string &filename); |
446 | +private: |
447 | + std::map<std::string, FolderArtInfo> cache_; |
448 | + std::map<std::string, FolderArtInfo> old_cache_; |
449 | +}; |
450 | + |
451 | +} |
452 | + |
453 | +#endif |
454 | |
455 | === modified file 'src/ms-dbus/dbus-codec.cc' |
456 | --- src/ms-dbus/dbus-codec.cc 2015-07-07 05:00:16 +0000 |
457 | +++ src/ms-dbus/dbus-codec.cc 2015-11-23 03:14:55 +0000 |
458 | @@ -104,14 +104,16 @@ |
459 | core::dbus::encode_argument(w, album.getDate()); |
460 | core::dbus::encode_argument(w, album.getGenre()); |
461 | core::dbus::encode_argument(w, album.getArtFile()); |
462 | + core::dbus::encode_argument(w, album.getHasThumbnail()); |
463 | out.close_structure(std::move(w)); |
464 | } |
465 | |
466 | void Codec<Album>::decode_argument(Message::Reader &in, Album &album) { |
467 | auto r = in.pop_structure(); |
468 | string title, artist, date, genre, art_file; |
469 | - r >> title >> artist >> date >> genre >> art_file; |
470 | - album = Album(title, artist, date, genre, art_file); |
471 | + bool has_thumbnail; |
472 | + r >> title >> artist >> date >> genre >> art_file >> has_thumbnail; |
473 | + album = Album(title, artist, date, genre, art_file, has_thumbnail); |
474 | } |
475 | |
476 | void Codec<Filter>::encode_argument(Message::Writer &out, const Filter &filter) { |
477 | |
478 | === modified file 'src/ms-dbus/dbus-codec.hh' |
479 | --- src/ms-dbus/dbus-codec.hh 2015-07-07 05:00:16 +0000 |
480 | +++ src/ms-dbus/dbus-codec.hh 2015-11-23 03:14:55 +0000 |
481 | @@ -82,7 +82,7 @@ |
482 | return true; |
483 | } |
484 | static const std::string &signature() { |
485 | - static const std::string s = "(sssss)"; |
486 | + static const std::string s = "(sssssb)"; |
487 | return s; |
488 | } |
489 | }; |
490 | |
491 | === modified file 'test/test_dbus.cc' |
492 | --- test/test_dbus.cc 2015-07-07 05:00:16 +0000 |
493 | +++ test/test_dbus.cc 2015-11-23 03:14:55 +0000 |
494 | @@ -53,10 +53,10 @@ |
495 | } |
496 | |
497 | TEST_F(MediaStoreDBusTests, album_codec) { |
498 | - mediascanner::Album album("title", "artist", "date", "genre", "art_file"); |
499 | + mediascanner::Album album("title", "artist", "date", "genre", "art_file", true); |
500 | message->writer() << album; |
501 | |
502 | - EXPECT_EQ("(sssss)", message->signature()); |
503 | + EXPECT_EQ("(sssssb)", message->signature()); |
504 | EXPECT_EQ(core::dbus::helper::TypeMapper<mediascanner::Album>::signature(), message->signature()); |
505 | |
506 | mediascanner::Album album2; |
507 | @@ -66,6 +66,7 @@ |
508 | EXPECT_EQ("date", album2.getDate()); |
509 | EXPECT_EQ("genre", album2.getGenre()); |
510 | EXPECT_EQ("art_file", album2.getArtFile()); |
511 | + EXPECT_EQ(true, album2.getHasThumbnail()); |
512 | EXPECT_EQ(album, album2); |
513 | } |
514 | |
515 | |
516 | === modified file 'test/test_mfbuilder.cc' |
517 | --- test/test_mfbuilder.cc 2015-07-07 08:21:29 +0000 |
518 | +++ test/test_mfbuilder.cc 2015-11-23 03:14:55 +0000 |
519 | @@ -17,26 +17,60 @@ |
520 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
521 | */ |
522 | |
523 | -#include<gtest/gtest.h> |
524 | -#include"mediascanner/MediaFile.hh" |
525 | -#include"mediascanner/MediaFileBuilder.hh" |
526 | -#include<stdexcept> |
527 | +#include "test_config.h" |
528 | +#include "mediascanner/Album.hh" |
529 | +#include "mediascanner/MediaFile.hh" |
530 | +#include "mediascanner/MediaFileBuilder.hh" |
531 | + |
532 | +#include <gtest/gtest.h> |
533 | + |
534 | +#include <chrono> |
535 | +#include <cstdlib> |
536 | +#include <fcntl.h> |
537 | +#include <sys/stat.h> |
538 | +#include <sys/types.h> |
539 | +#include <stdexcept> |
540 | +#include <thread> |
541 | +#include <vector> |
542 | |
543 | using namespace mediascanner; |
544 | |
545 | class MFBTest : public ::testing::Test { |
546 | - protected: |
547 | - MFBTest() { |
548 | - } |
549 | - |
550 | - virtual ~MFBTest() { |
551 | - } |
552 | - |
553 | - virtual void SetUp() { |
554 | - } |
555 | - |
556 | - virtual void TearDown() { |
557 | - } |
558 | +protected: |
559 | + MFBTest() = default; |
560 | + virtual ~MFBTest() = default; |
561 | + |
562 | + virtual void SetUp() override { |
563 | + tmpdir = TEST_DIR "/mfbuilder-test.XXXXXX"; |
564 | + ASSERT_NE(nullptr, mkdtemp(&tmpdir[0])); |
565 | + } |
566 | + |
567 | + virtual void TearDown() override { |
568 | + if (!tmpdir.empty()) { |
569 | + std::string cmd = "rm -rf " + tmpdir; |
570 | + ASSERT_EQ(0, system(cmd.c_str())); |
571 | + } |
572 | + } |
573 | + |
574 | + void touch(const std::string &fname, bool sleep=false) { |
575 | + if (sleep) { |
576 | + // Ensure time stamps change |
577 | + std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
578 | + } |
579 | + int fd = open(fname.c_str(), O_CREAT, 0600); |
580 | + ASSERT_GT(fd, 0); |
581 | + ASSERT_EQ(0, close(fd)); |
582 | + } |
583 | + |
584 | + void remove(const std::string &fname, bool sleep=false) { |
585 | + if (sleep) { |
586 | + // Ensure time stamps change |
587 | + std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
588 | + } |
589 | + ASSERT_EQ(0, unlink(fname.c_str())); |
590 | + } |
591 | + |
592 | + std::string tmpdir; |
593 | }; |
594 | |
595 | TEST_F(MFBTest, basic) { |
596 | @@ -209,6 +243,120 @@ |
597 | EXPECT_EQ("image://thumbnailer/file:///foo/bar/baz.mp4", mf.getArtUri()); |
598 | } |
599 | |
600 | +TEST_F(MFBTest, folder_art) { |
601 | + std::string fname = tmpdir + "/dummy.mp3"; |
602 | + MediaFile media = MediaFileBuilder(fname) |
603 | + .setType(AudioMedia) |
604 | + .setTitle("Title") |
605 | + .setAuthor("Artist") |
606 | + .setAlbum("Album"); |
607 | + |
608 | + EXPECT_EQ("image://albumart/artist=Artist&album=Album", media.getArtUri()); |
609 | + touch(tmpdir + "/folder.jpg", true); |
610 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri(); |
611 | + remove(tmpdir + "/folder.jpg", true); |
612 | + EXPECT_EQ("image://albumart/artist=Artist&album=Album", media.getArtUri()); |
613 | +} |
614 | + |
615 | +TEST_F(MFBTest, folder_art_case_insensitive) { |
616 | + std::string fname = tmpdir + "/dummy.mp3"; |
617 | + MediaFile media = MediaFileBuilder(fname) |
618 | + .setType(AudioMedia) |
619 | + .setTitle("Title") |
620 | + .setAuthor("Artist") |
621 | + .setAlbum("Album"); |
622 | + |
623 | + touch(tmpdir + "/FOLDER.JPG"); |
624 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/FOLDER.JPG")) << media.getArtUri(); |
625 | +} |
626 | + |
627 | +TEST_F(MFBTest, folder_art_precedence) { |
628 | + std::string fname = tmpdir + "/dummy.mp3"; |
629 | + MediaFile media = MediaFileBuilder(fname) |
630 | + .setType(AudioMedia) |
631 | + .setTitle("Title") |
632 | + .setAuthor("Artist") |
633 | + .setAlbum("Album"); |
634 | + |
635 | + touch(tmpdir + "/cover.jpg"); |
636 | + touch(tmpdir + "/album.jpg"); |
637 | + touch(tmpdir + "/albumart.jpg"); |
638 | + touch(tmpdir + "/.folder.jpg"); |
639 | + touch(tmpdir + "/folder.jpeg"); |
640 | + touch(tmpdir + "/folder.jpg"); |
641 | + touch(tmpdir + "/folder.png"); |
642 | + |
643 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/cover.jpg")) << media.getArtUri(); |
644 | + remove(tmpdir + "/cover.jpg", true); |
645 | + |
646 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/album.jpg")) << media.getArtUri(); |
647 | + remove(tmpdir + "/album.jpg", true); |
648 | + |
649 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/albumart.jpg")) << media.getArtUri(); |
650 | + remove(tmpdir + "/albumart.jpg", true); |
651 | + |
652 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/.folder.jpg")) << media.getArtUri(); |
653 | + remove(tmpdir + "/.folder.jpg", true); |
654 | + |
655 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpeg")) << media.getArtUri(); |
656 | + remove(tmpdir + "/folder.jpeg", true); |
657 | + |
658 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri(); |
659 | + remove(tmpdir + "/folder.jpg", true); |
660 | + |
661 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.png")) << media.getArtUri(); |
662 | +} |
663 | + |
664 | +TEST_F(MFBTest, folder_art_cache_coverage) { |
665 | + std::vector<MediaFile> files; |
666 | + for (int i = 0; i < 100; i++) { |
667 | + std::string directory = tmpdir + "/" + std::to_string(i); |
668 | + ASSERT_EQ(0, mkdir(directory.c_str(), 0700)); |
669 | + touch(directory + "/folder.jpg"); |
670 | + |
671 | + std::string fname = directory + "/dummy.mp3"; |
672 | + files.emplace_back(MediaFileBuilder(fname) |
673 | + .setType(AudioMedia) |
674 | + .setTitle("Title") |
675 | + .setAuthor("Artist") |
676 | + .setAlbum("Album")); |
677 | + } |
678 | + |
679 | + // Check art for a number of files smaller than the cache size twice |
680 | + for (int i = 0; i < 10; i++) { |
681 | + const auto &media = files[i]; |
682 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri(); |
683 | + } |
684 | + for (int i = 0; i < 10; i++) { |
685 | + const auto &media = files[i]; |
686 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri(); |
687 | + } |
688 | + |
689 | + // Now check a larger number of files twice |
690 | + for (const auto &media : files) { |
691 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri(); |
692 | + } |
693 | + for (const auto &media : files) { |
694 | + EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri(); |
695 | + } |
696 | +} |
697 | + |
698 | +TEST_F(MFBTest, album_art) { |
699 | + std::string fname = tmpdir + "/dummy.mp3"; |
700 | + |
701 | + // File with embedded art |
702 | + Album album("Album", "Artist", "2015-11-23", "Rock", fname, true); |
703 | + EXPECT_NE(std::string::npos, album.getArtUri().find("/dummy.mp3")) << album.getArtUri(); |
704 | + |
705 | + // No embedded art |
706 | + album = Album("Album", "Artist", "2015-11-23", "Rock", fname, false); |
707 | + EXPECT_EQ("image://albumart/artist=Artist&album=Album", album.getArtUri()); |
708 | + |
709 | + // No embedded art, but folder art available |
710 | + touch(tmpdir + "/folder.jpg", true); |
711 | + EXPECT_NE(std::string::npos, album.getArtUri().find("/folder.jpg")) << album.getArtUri(); |
712 | +} |
713 | + |
714 | int main(int argc, char **argv) { |
715 | ::testing::InitGoogleTest(&argc, argv); |
716 | return RUN_ALL_TESTS(); |
717 | |
718 | === modified file 'test/test_util.cc' |
719 | --- test/test_util.cc 2014-05-09 14:04:48 +0000 |
720 | +++ test/test_util.cc 2015-11-23 03:14:55 +0000 |
721 | @@ -19,6 +19,9 @@ |
722 | |
723 | #include <gtest/gtest.h> |
724 | #include"../src/mediascanner/internal/utils.hh" |
725 | +#include"../src/mediascanner/MediaFile.hh" |
726 | +#include"../src/mediascanner/MediaFileBuilder.hh" |
727 | + |
728 | #include "test_config.h" |
729 | |
730 | using namespace mediascanner; |
FAILED: Continuous integration, rev:320 /code.launchpad .net/~jamesh/ mediascanner2/ folder- coverart/ +merge/ 278099/ +edit-commit- message
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http:// jenkins. qa.ubuntu. com/job/ mediascanner2- ci/199/ jenkins. qa.ubuntu. com/job/ mediascanner2- vivid-amd64- ci/36 jenkins. qa.ubuntu. com/job/ mediascanner2- vivid-armhf- ci/36 jenkins. qa.ubuntu. com/job/ mediascanner2- vivid-armhf- ci/36/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mediascanner2- vivid-i386- ci/36 jenkins. qa.ubuntu. com/job/ mediascanner2- wily-amd64- ci/11 jenkins. qa.ubuntu. com/job/ mediascanner2- wily-armhf- ci/11 jenkins. qa.ubuntu. com/job/ mediascanner2- wily-armhf- ci/11/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mediascanner2- wily-i386- ci/11
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mediascanne r2-ci/199/ rebuild
http://