Merge lp:~jamesh/mediascanner2/folder-coverart into lp:mediascanner2

Proposed by James Henstridge
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
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,album,albumart,.folder,folder}.{jpeg,jpg,png} use it as album art for songs in preference to online art if the songs do not have embedded art.

Description of the change

Add folder based cover art detection to the MediaFile::getArtUri() method, using a small cache to reduce work when requesting art for multiple files from an album.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
321. By James Henstridge

Remove unneeded #define.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
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.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-11-07 09:13:53 +0000
+++ CMakeLists.txt 2015-11-23 03:14:55 +0000
@@ -1,7 +1,7 @@
1project(mediascanner2 CXX C)1project(mediascanner2 CXX C)
2cmake_minimum_required(VERSION 2.8.9)2cmake_minimum_required(VERSION 2.8.9)
33
4set(MEDIASCANNER_VERSION "0.108")4set(MEDIASCANNER_VERSION "0.109")
55
6execute_process(6execute_process(
7 COMMAND /bin/sh ${CMAKE_CURRENT_SOURCE_DIR}/get-soversion.sh7 COMMAND /bin/sh ${CMAKE_CURRENT_SOURCE_DIR}/get-soversion.sh
88
=== modified file 'debian/changelog'
--- debian/changelog 2015-11-09 01:56:39 +0000
+++ debian/changelog 2015-11-23 03:14:55 +0000
@@ -1,3 +1,12 @@
1mediascanner2 (0.109-0ubuntu1) UNRELEASED; urgency=medium
2
3 * If a folder contains an image file named
4 {cover,album,albumart,.folder,folder}.{jpeg,jpg,png} use it as album
5 art for songs in preference to online art if the songs do not have
6 embedded art. (LP: #1372000)
7
8 -- James Henstridge <james.henstridge@canonical.com> Mon, 23 Nov 2015 11:12:59 +0800
9
1mediascanner2 (0.108+16.04.20151109-0ubuntu1) xenial; urgency=medium10mediascanner2 (0.108+16.04.20151109-0ubuntu1) xenial; urgency=medium
211
3 * Move the metadata extractor to a separate process to isolate bugs in12 * Move the metadata extractor to a separate process to isolate bugs in
413
=== modified file 'debian/libmediascanner-2.0-3.symbols'
--- debian/libmediascanner-2.0-3.symbols 2015-09-21 10:56:50 +0000
+++ debian/libmediascanner-2.0-3.symbols 2015-11-23 03:14:55 +0000
@@ -5,11 +5,13 @@
5 (c++)"mediascanner::Album::Album(mediascanner::Album const&)@Base" 0.105+14.10.201409035 (c++)"mediascanner::Album::Album(mediascanner::Album const&)@Base" 0.105+14.10.20140903
6 (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.201409036 (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
7 (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.201409037 (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
8 (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
8 (c++)"mediascanner::Album::getArtFile() const@Base" 0.105+14.10.201409039 (c++)"mediascanner::Album::getArtFile() const@Base" 0.105+14.10.20140903
9 (c++)"mediascanner::Album::getArtist() const@Base" 0.105+14.10.2014090310 (c++)"mediascanner::Album::getArtist() const@Base" 0.105+14.10.20140903
10 (c++)"mediascanner::Album::getArtUri() const@Base" 0.105+14.10.2014090311 (c++)"mediascanner::Album::getArtUri() const@Base" 0.105+14.10.20140903
11 (c++)"mediascanner::Album::getDate() const@Base" 0.105+14.10.2014090312 (c++)"mediascanner::Album::getDate() const@Base" 0.105+14.10.20140903
12 (c++)"mediascanner::Album::getGenre() const@Base" 0.105+14.10.2014090313 (c++)"mediascanner::Album::getGenre() const@Base" 0.105+14.10.20140903
14 (c++)"mediascanner::Album::getHasThumbnail() const@Base" 0replaceme
13 (c++)"mediascanner::Album::getTitle() const@Base" 0.101+14.10.2014061315 (c++)"mediascanner::Album::getTitle() const@Base" 0.101+14.10.20140613
14 (c++)"mediascanner::Album::operator=(mediascanner::Album&&)@Base" 0.105+14.10.2014090316 (c++)"mediascanner::Album::operator=(mediascanner::Album&&)@Base" 0.105+14.10.20140903
15 (c++)"mediascanner::Album::operator=(mediascanner::Album const&)@Base" 0.105+14.10.2014090317 (c++)"mediascanner::Album::operator=(mediascanner::Album const&)@Base" 0.105+14.10.20140903
1618
=== modified file 'debian/libmediascanner-2.0-4.shlibs'
--- debian/libmediascanner-2.0-4.shlibs 2015-09-21 10:20:38 +0000
+++ debian/libmediascanner-2.0-4.shlibs 2015-11-23 03:14:55 +0000
@@ -1,1 +1,1 @@
1libmediascanner-2.0 4 libmediascanner-2.0-4 (>= 0.107)1libmediascanner-2.0 4 libmediascanner-2.0-4 (>= 0.109)
22
=== modified file 'src/mediascanner/Album.cc'
--- src/mediascanner/Album.cc 2014-09-03 07:00:02 +0000
+++ src/mediascanner/Album.cc 2015-11-23 03:14:55 +0000
@@ -18,6 +18,7 @@
18 */18 */
1919
20#include "Album.hh"20#include "Album.hh"
21#include "internal/FolderArtCache.hh"
21#include "internal/utils.hh"22#include "internal/utils.hh"
2223
23using namespace std;24using namespace std;
@@ -30,13 +31,14 @@
30 string date;31 string date;
31 string genre;32 string genre;
32 string filename;33 string filename;
34 bool has_thumbnail;
3335
34 Private() {}36 Private() {}
35 Private(const string &title, const string &artist,37 Private(const string &title, const string &artist,
36 const string &date, const string &genre,38 const string &date, const string &genre,
37 const string &filename)39 const string &filename, bool has_thumbnail)
38 : title(title), artist(artist), date(date), genre(genre),40 : title(title), artist(artist), date(date), genre(genre),
39 filename(filename) {}41 filename(filename), has_thumbnail(has_thumbnail) {}
40 Private(const Private &other) {42 Private(const Private &other) {
41 *this = other;43 *this = other;
42 }44 }
@@ -46,13 +48,19 @@
46}48}
4749
48Album::Album(const std::string &title, const std::string &artist)50Album::Album(const std::string &title, const std::string &artist)
49 : Album(title, artist, "", "", "") {51 : Album(title, artist, "", "", "", false) {
50}52}
5153
52Album::Album(const std::string &title, const std::string &artist,54Album::Album(const std::string &title, const std::string &artist,
53 const std::string &date, const std::string &genre,55 const std::string &date, const std::string &genre,
54 const std::string &filename)56 const std::string &filename)
55 : p(new Private(title, artist, date, genre, filename)) {57 : Album(title, artist, date, genre, filename, !filename.empty()) {
58}
59
60Album::Album(const std::string &title, const std::string &artist,
61 const std::string &date, const std::string &genre,
62 const std::string &filename, bool has_thumbnail)
63 : p(new Private(title, artist, date, genre, filename, has_thumbnail)) {
56}64}
5765
58Album::Album(const Album &other) : p(new Private(*other.p)) {66Album::Album(const Album &other) : p(new Private(*other.p)) {
@@ -100,11 +108,19 @@
100 return p->filename;108 return p->filename;
101}109}
102110
111bool Album::getHasThumbnail() const noexcept {
112 return p->has_thumbnail;
113}
114
103std::string Album::getArtUri() const {115std::string Album::getArtUri() const {
104 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 }
105 return make_album_art_uri(p->artist, p->title);123 return make_album_art_uri(p->artist, p->title);
106 } else {
107 return make_thumbnail_uri(getUri(p->filename));
108 }124 }
109}125}
110126
@@ -113,7 +129,8 @@
113 p->artist == other.p->artist &&129 p->artist == other.p->artist &&
114 p->date == other.p->date &&130 p->date == other.p->date &&
115 p->genre == other.p->genre &&131 p->genre == other.p->genre &&
116 p->filename == other.p->filename;132 p->filename == other.p->filename &&
133 p->has_thumbnail == other.p->has_thumbnail;
117}134}
118135
119bool Album::operator!=(const Album &other) const {136bool Album::operator!=(const Album &other) const {
120137
=== modified file 'src/mediascanner/Album.hh'
--- src/mediascanner/Album.hh 2014-09-03 07:00:02 +0000
+++ src/mediascanner/Album.hh 2015-11-23 03:14:55 +0000
@@ -32,6 +32,9 @@
32 Album(const std::string &title, const std::string &artist,32 Album(const std::string &title, const std::string &artist,
33 const std::string &date, const std::string &genre,33 const std::string &date, const std::string &genre,
34 const std::string &filename);34 const std::string &filename);
35 Album(const std::string &title, const std::string &artist,
36 const std::string &date, const std::string &genre,
37 const std::string &filename, bool has_thumbnail);
35 Album(const Album &other);38 Album(const Album &other);
36 Album(Album &&other);39 Album(Album &&other);
37 ~Album();40 ~Album();
@@ -44,6 +47,7 @@
44 const std::string& getDate() const noexcept;47 const std::string& getDate() const noexcept;
45 const std::string& getGenre() const noexcept;48 const std::string& getGenre() const noexcept;
46 const std::string& getArtFile() const noexcept;49 const std::string& getArtFile() const noexcept;
50 bool getHasThumbnail() const noexcept;
47 std::string getArtUri() const;51 std::string getArtUri() const;
48 bool operator==(const Album &other) const;52 bool operator==(const Album &other) const;
49 bool operator!=(const Album &other) const;53 bool operator!=(const Album &other) const;
5054
=== modified file 'src/mediascanner/CMakeLists.txt'
--- src/mediascanner/CMakeLists.txt 2015-07-14 08:59:01 +0000
+++ src/mediascanner/CMakeLists.txt 2015-11-23 03:14:55 +0000
@@ -6,6 +6,7 @@
6 Album.cc6 Album.cc
7 MediaStore.cc7 MediaStore.cc
8 MediaStoreBase.cc8 MediaStoreBase.cc
9 FolderArtCache.cc
9 utils.cc10 utils.cc
10 mozilla/fts3_porter.c11 mozilla/fts3_porter.c
11 mozilla/Normalize.c12 mozilla/Normalize.c
1213
=== added file 'src/mediascanner/FolderArtCache.cc'
--- src/mediascanner/FolderArtCache.cc 1970-01-01 00:00:00 +0000
+++ src/mediascanner/FolderArtCache.cc 2015-11-23 03:14:55 +0000
@@ -0,0 +1,169 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * Authors:
5 * James Henstridge <james.henstridge@canonical.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License version 3 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "internal/FolderArtCache.hh"
21
22#include <algorithm>
23#include <array>
24#include <cctype>
25#include <cerrno>
26#include <cstdlib>
27#include <cstring>
28#include <dirent.h>
29#include <memory>
30#include <stdexcept>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <unistd.h>
34
35using namespace std;
36
37namespace {
38
39const int CACHE_SIZE = 50;
40
41string detect_albumart(string directory) {
42 static const array<const char*, 5> art_basenames = {
43 "cover",
44 "album",
45 "albumart",
46 ".folder",
47 "folder",
48 };
49 static const array<const char*, 3> art_extensions = {
50 "jpeg",
51 "jpg",
52 "png",
53 };
54 if (!directory.empty() && directory[directory.size()-1] != '/') {
55 directory += "/";
56 }
57 unique_ptr<DIR, decltype(&closedir)> dir(
58 opendir(directory.c_str()), &closedir);
59 if (!dir) {
60 return "";
61 }
62
63 const int dirent_size = sizeof(dirent) + fpathconf(dirfd(dir.get()), _PC_NAME_MAX) + 1;
64 unique_ptr<struct dirent, decltype(&free)> entry(
65 reinterpret_cast<struct dirent*>(malloc(dirent_size)), &free);
66
67 string detected;
68 int best_score = 0;
69 struct dirent *de = nullptr;
70 while (readdir_r(dir.get(), entry.get(), &de) == 0 && de) {
71 const string filename(de->d_name);
72 auto dot = filename.rfind('.');
73 // Ignore files with no extension
74 if (dot == string::npos) {
75 continue;
76 }
77 auto basename = filename.substr(0, dot);
78 auto extension = filename.substr(dot+1);
79
80 // Does the file name match one of the required names when
81 // converted to lower case?
82 transform(basename.begin(), basename.end(),
83 basename.begin(), ::tolower);
84 transform(extension.begin(), extension.end(),
85 extension.begin(), ::tolower);
86 auto base_pos = find(art_basenames.begin(), art_basenames.end(), basename);
87 if (base_pos == art_basenames.end()) {
88 continue;
89 }
90 auto ext_pos = find(art_extensions.begin(), art_extensions.end(), extension);
91 if (ext_pos == art_extensions.end()) {
92 continue;
93 }
94
95 int score = (base_pos - art_basenames.begin()) * art_basenames.size() +
96 (ext_pos - art_extensions.begin());
97 if (detected.empty() || score < best_score) {
98 detected = filename;
99 best_score = score;
100 }
101 }
102 if (detected.empty()) {
103 return detected;
104 }
105 return directory + detected;
106}
107
108}
109
110namespace mediascanner {
111
112FolderArtCache::FolderArtCache() = default;
113FolderArtCache::~FolderArtCache() = default;
114
115// Get a singleton instance of the cache
116FolderArtCache& FolderArtCache::get() {
117 static FolderArtCache cache;
118 return cache;
119}
120
121string FolderArtCache::get_art_for_directory(const string &directory) {
122 struct stat s;
123 if (lstat(directory.c_str(), &s) < 0) {
124 return "";
125 }
126 if (!S_ISDIR(s.st_mode)) {
127 return "";
128 }
129 FolderArtInfo info;
130 bool update = false;
131 try {
132 info = cache_.at(directory);
133 } catch (const out_of_range &) {
134 // Fall back to checking the previous iteration of the cache
135 try {
136 info = old_cache_.at(directory);
137 update = true;
138 } catch (const out_of_range &) {
139 }
140 }
141
142 if (info.dir_mtime.tv_sec != s.st_mtim.tv_sec ||
143 info.dir_mtime.tv_nsec != s.st_mtim.tv_nsec) {
144 info.art = detect_albumart(directory);
145 info.dir_mtime = s.st_mtim;
146 update = true;
147 }
148
149 if (update) {
150 cache_[directory] = info;
151 // Start new cache generation if we've exceeded the size.
152 if (cache_.size() > CACHE_SIZE) {
153 old_cache_ = move(cache_);
154 cache_.clear();
155 }
156 }
157 return info.art;
158}
159
160string FolderArtCache::get_art_for_file(const string &filename) {
161 auto slash = filename.rfind('/');
162 if (slash == string::npos) {
163 return "";
164 }
165 auto directory = filename.substr(0, slash + 1);
166 return get_art_for_directory(directory);
167}
168
169}
0170
=== modified file 'src/mediascanner/MediaFile.cc'
--- src/mediascanner/MediaFile.cc 2015-07-07 04:36:02 +0000
+++ src/mediascanner/MediaFile.cc 2015-11-23 03:14:55 +0000
@@ -20,6 +20,7 @@
20#include "MediaFile.hh"20#include "MediaFile.hh"
21#include "MediaFileBuilder.hh"21#include "MediaFileBuilder.hh"
22#include "internal/MediaFilePrivate.hh"22#include "internal/MediaFilePrivate.hh"
23#include "internal/FolderArtCache.hh"
23#include "internal/utils.hh"24#include "internal/utils.hh"
24#include <stdexcept>25#include <stdexcept>
2526
@@ -155,12 +156,17 @@
155156
156std::string MediaFile::getArtUri() const {157std::string MediaFile::getArtUri() const {
157 switch (p->type) {158 switch (p->type) {
158 case AudioMedia:159 case AudioMedia: {
159 if (p->has_thumbnail) {160 if (p->has_thumbnail) {
160 return make_thumbnail_uri(getUri());161 return make_thumbnail_uri(getUri());
161 } else {162 }
162 return make_album_art_uri(getAuthor(), getAlbum());163 auto standalone = FolderArtCache::get().get_art_for_file(p->filename);
163 }164 if(!standalone.empty()) {
165 return make_thumbnail_uri(mediascanner::getUri(standalone));
166 }
167 return make_album_art_uri(getAuthor(), getAlbum());
168 }
169
164 default:170 default:
165 return make_thumbnail_uri(getUri());171 return make_thumbnail_uri(getUri());
166 }172 }
167173
=== modified file 'src/mediascanner/MediaStore.cc'
--- src/mediascanner/MediaStore.cc 2015-10-07 06:49:12 +0000
+++ src/mediascanner/MediaStore.cc 2015-11-23 03:14:55 +0000
@@ -604,7 +604,7 @@
604 const string genre = query.getText(3);604 const string genre = query.getText(3);
605 const string filename = query.getText(4);605 const string filename = query.getText(4);
606 const bool has_thumbnail = query.getInt(5);606 const bool has_thumbnail = query.getInt(5);
607 return Album(album, album_artist, date, genre, has_thumbnail ? filename : "");607 return Album(album, album_artist, date, genre, filename, has_thumbnail);
608}608}
609609
610static vector<Album> collect_albums(Statement &query) {610static vector<Album> collect_albums(Statement &query) {
611611
=== added file 'src/mediascanner/internal/FolderArtCache.hh'
--- src/mediascanner/internal/FolderArtCache.hh 1970-01-01 00:00:00 +0000
+++ src/mediascanner/internal/FolderArtCache.hh 2015-11-23 03:14:55 +0000
@@ -0,0 +1,55 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * Authors:
5 * James Henstridge <james.henstridge@canonical.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License version 3 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef FOLDERARTCACHE_HH
21#define FOLDERARTCACHE_HH
22
23#include <cstdint>
24#include <ctime>
25#include <map>
26#include <string>
27
28namespace mediascanner {
29
30struct FolderArtInfo {
31 std::string art;
32 struct timespec dir_mtime {0, 0};
33};
34
35class FolderArtCache final {
36public:
37 FolderArtCache();
38 ~FolderArtCache();
39
40 FolderArtCache(const FolderArtCache &other) = delete;
41 FolderArtCache& operator=(const FolderArtCache &other) = delete;
42
43 // Get a singleton instance of the cache
44 static FolderArtCache& get();
45
46 std::string get_art_for_directory(const std::string &directory);
47 std::string get_art_for_file(const std::string &filename);
48private:
49 std::map<std::string, FolderArtInfo> cache_;
50 std::map<std::string, FolderArtInfo> old_cache_;
51};
52
53}
54
55#endif
056
=== modified file 'src/ms-dbus/dbus-codec.cc'
--- src/ms-dbus/dbus-codec.cc 2015-07-07 05:00:16 +0000
+++ src/ms-dbus/dbus-codec.cc 2015-11-23 03:14:55 +0000
@@ -104,14 +104,16 @@
104 core::dbus::encode_argument(w, album.getDate());104 core::dbus::encode_argument(w, album.getDate());
105 core::dbus::encode_argument(w, album.getGenre());105 core::dbus::encode_argument(w, album.getGenre());
106 core::dbus::encode_argument(w, album.getArtFile());106 core::dbus::encode_argument(w, album.getArtFile());
107 core::dbus::encode_argument(w, album.getHasThumbnail());
107 out.close_structure(std::move(w));108 out.close_structure(std::move(w));
108}109}
109110
110void Codec<Album>::decode_argument(Message::Reader &in, Album &album) {111void Codec<Album>::decode_argument(Message::Reader &in, Album &album) {
111 auto r = in.pop_structure();112 auto r = in.pop_structure();
112 string title, artist, date, genre, art_file;113 string title, artist, date, genre, art_file;
113 r >> title >> artist >> date >> genre >> art_file;114 bool has_thumbnail;
114 album = Album(title, artist, date, genre, art_file);115 r >> title >> artist >> date >> genre >> art_file >> has_thumbnail;
116 album = Album(title, artist, date, genre, art_file, has_thumbnail);
115}117}
116118
117void Codec<Filter>::encode_argument(Message::Writer &out, const Filter &filter) {119void Codec<Filter>::encode_argument(Message::Writer &out, const Filter &filter) {
118120
=== modified file 'src/ms-dbus/dbus-codec.hh'
--- src/ms-dbus/dbus-codec.hh 2015-07-07 05:00:16 +0000
+++ src/ms-dbus/dbus-codec.hh 2015-11-23 03:14:55 +0000
@@ -82,7 +82,7 @@
82 return true;82 return true;
83 }83 }
84 static const std::string &signature() {84 static const std::string &signature() {
85 static const std::string s = "(sssss)";85 static const std::string s = "(sssssb)";
86 return s;86 return s;
87 }87 }
88};88};
8989
=== modified file 'test/test_dbus.cc'
--- test/test_dbus.cc 2015-07-07 05:00:16 +0000
+++ test/test_dbus.cc 2015-11-23 03:14:55 +0000
@@ -53,10 +53,10 @@
53}53}
5454
55TEST_F(MediaStoreDBusTests, album_codec) {55TEST_F(MediaStoreDBusTests, album_codec) {
56 mediascanner::Album album("title", "artist", "date", "genre", "art_file");56 mediascanner::Album album("title", "artist", "date", "genre", "art_file", true);
57 message->writer() << album;57 message->writer() << album;
5858
59 EXPECT_EQ("(sssss)", message->signature());59 EXPECT_EQ("(sssssb)", message->signature());
60 EXPECT_EQ(core::dbus::helper::TypeMapper<mediascanner::Album>::signature(), message->signature());60 EXPECT_EQ(core::dbus::helper::TypeMapper<mediascanner::Album>::signature(), message->signature());
6161
62 mediascanner::Album album2;62 mediascanner::Album album2;
@@ -66,6 +66,7 @@
66 EXPECT_EQ("date", album2.getDate());66 EXPECT_EQ("date", album2.getDate());
67 EXPECT_EQ("genre", album2.getGenre());67 EXPECT_EQ("genre", album2.getGenre());
68 EXPECT_EQ("art_file", album2.getArtFile());68 EXPECT_EQ("art_file", album2.getArtFile());
69 EXPECT_EQ(true, album2.getHasThumbnail());
69 EXPECT_EQ(album, album2);70 EXPECT_EQ(album, album2);
70}71}
7172
7273
=== modified file 'test/test_mfbuilder.cc'
--- test/test_mfbuilder.cc 2015-07-07 08:21:29 +0000
+++ test/test_mfbuilder.cc 2015-11-23 03:14:55 +0000
@@ -17,26 +17,60 @@
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */18 */
1919
20#include<gtest/gtest.h>20#include "test_config.h"
21#include"mediascanner/MediaFile.hh"21#include "mediascanner/Album.hh"
22#include"mediascanner/MediaFileBuilder.hh"22#include "mediascanner/MediaFile.hh"
23#include<stdexcept>23#include "mediascanner/MediaFileBuilder.hh"
24
25#include <gtest/gtest.h>
26
27#include <chrono>
28#include <cstdlib>
29#include <fcntl.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <stdexcept>
33#include <thread>
34#include <vector>
2435
25using namespace mediascanner;36using namespace mediascanner;
2637
27class MFBTest : public ::testing::Test {38class MFBTest : public ::testing::Test {
28 protected:39protected:
29 MFBTest() {40 MFBTest() = default;
30 }41 virtual ~MFBTest() = default;
3142
32 virtual ~MFBTest() {43 virtual void SetUp() override {
33 }44 tmpdir = TEST_DIR "/mfbuilder-test.XXXXXX";
3445 ASSERT_NE(nullptr, mkdtemp(&tmpdir[0]));
35 virtual void SetUp() {46 }
36 }47
3748 virtual void TearDown() override {
38 virtual void TearDown() {49 if (!tmpdir.empty()) {
39 }50 std::string cmd = "rm -rf " + tmpdir;
51 ASSERT_EQ(0, system(cmd.c_str()));
52 }
53 }
54
55 void touch(const std::string &fname, bool sleep=false) {
56 if (sleep) {
57 // Ensure time stamps change
58 std::this_thread::sleep_for(std::chrono::milliseconds(50));
59 }
60 int fd = open(fname.c_str(), O_CREAT, 0600);
61 ASSERT_GT(fd, 0);
62 ASSERT_EQ(0, close(fd));
63 }
64
65 void remove(const std::string &fname, bool sleep=false) {
66 if (sleep) {
67 // Ensure time stamps change
68 std::this_thread::sleep_for(std::chrono::milliseconds(50));
69 }
70 ASSERT_EQ(0, unlink(fname.c_str()));
71 }
72
73 std::string tmpdir;
40};74};
4175
42TEST_F(MFBTest, basic) {76TEST_F(MFBTest, basic) {
@@ -209,6 +243,120 @@
209 EXPECT_EQ("image://thumbnailer/file:///foo/bar/baz.mp4", mf.getArtUri());243 EXPECT_EQ("image://thumbnailer/file:///foo/bar/baz.mp4", mf.getArtUri());
210}244}
211245
246TEST_F(MFBTest, folder_art) {
247 std::string fname = tmpdir + "/dummy.mp3";
248 MediaFile media = MediaFileBuilder(fname)
249 .setType(AudioMedia)
250 .setTitle("Title")
251 .setAuthor("Artist")
252 .setAlbum("Album");
253
254 EXPECT_EQ("image://albumart/artist=Artist&album=Album", media.getArtUri());
255 touch(tmpdir + "/folder.jpg", true);
256 EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri();
257 remove(tmpdir + "/folder.jpg", true);
258 EXPECT_EQ("image://albumart/artist=Artist&album=Album", media.getArtUri());
259}
260
261TEST_F(MFBTest, folder_art_case_insensitive) {
262 std::string fname = tmpdir + "/dummy.mp3";
263 MediaFile media = MediaFileBuilder(fname)
264 .setType(AudioMedia)
265 .setTitle("Title")
266 .setAuthor("Artist")
267 .setAlbum("Album");
268
269 touch(tmpdir + "/FOLDER.JPG");
270 EXPECT_NE(std::string::npos, media.getArtUri().find("/FOLDER.JPG")) << media.getArtUri();
271}
272
273TEST_F(MFBTest, folder_art_precedence) {
274 std::string fname = tmpdir + "/dummy.mp3";
275 MediaFile media = MediaFileBuilder(fname)
276 .setType(AudioMedia)
277 .setTitle("Title")
278 .setAuthor("Artist")
279 .setAlbum("Album");
280
281 touch(tmpdir + "/cover.jpg");
282 touch(tmpdir + "/album.jpg");
283 touch(tmpdir + "/albumart.jpg");
284 touch(tmpdir + "/.folder.jpg");
285 touch(tmpdir + "/folder.jpeg");
286 touch(tmpdir + "/folder.jpg");
287 touch(tmpdir + "/folder.png");
288
289 EXPECT_NE(std::string::npos, media.getArtUri().find("/cover.jpg")) << media.getArtUri();
290 remove(tmpdir + "/cover.jpg", true);
291
292 EXPECT_NE(std::string::npos, media.getArtUri().find("/album.jpg")) << media.getArtUri();
293 remove(tmpdir + "/album.jpg", true);
294
295 EXPECT_NE(std::string::npos, media.getArtUri().find("/albumart.jpg")) << media.getArtUri();
296 remove(tmpdir + "/albumart.jpg", true);
297
298 EXPECT_NE(std::string::npos, media.getArtUri().find("/.folder.jpg")) << media.getArtUri();
299 remove(tmpdir + "/.folder.jpg", true);
300
301 EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpeg")) << media.getArtUri();
302 remove(tmpdir + "/folder.jpeg", true);
303
304 EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri();
305 remove(tmpdir + "/folder.jpg", true);
306
307 EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.png")) << media.getArtUri();
308}
309
310TEST_F(MFBTest, folder_art_cache_coverage) {
311 std::vector<MediaFile> files;
312 for (int i = 0; i < 100; i++) {
313 std::string directory = tmpdir + "/" + std::to_string(i);
314 ASSERT_EQ(0, mkdir(directory.c_str(), 0700));
315 touch(directory + "/folder.jpg");
316
317 std::string fname = directory + "/dummy.mp3";
318 files.emplace_back(MediaFileBuilder(fname)
319 .setType(AudioMedia)
320 .setTitle("Title")
321 .setAuthor("Artist")
322 .setAlbum("Album"));
323 }
324
325 // Check art for a number of files smaller than the cache size twice
326 for (int i = 0; i < 10; i++) {
327 const auto &media = files[i];
328 EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri();
329 }
330 for (int i = 0; i < 10; i++) {
331 const auto &media = files[i];
332 EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri();
333 }
334
335 // Now check a larger number of files twice
336 for (const auto &media : files) {
337 EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri();
338 }
339 for (const auto &media : files) {
340 EXPECT_NE(std::string::npos, media.getArtUri().find("/folder.jpg")) << media.getArtUri();
341 }
342}
343
344TEST_F(MFBTest, album_art) {
345 std::string fname = tmpdir + "/dummy.mp3";
346
347 // File with embedded art
348 Album album("Album", "Artist", "2015-11-23", "Rock", fname, true);
349 EXPECT_NE(std::string::npos, album.getArtUri().find("/dummy.mp3")) << album.getArtUri();
350
351 // No embedded art
352 album = Album("Album", "Artist", "2015-11-23", "Rock", fname, false);
353 EXPECT_EQ("image://albumart/artist=Artist&album=Album", album.getArtUri());
354
355 // No embedded art, but folder art available
356 touch(tmpdir + "/folder.jpg", true);
357 EXPECT_NE(std::string::npos, album.getArtUri().find("/folder.jpg")) << album.getArtUri();
358}
359
212int main(int argc, char **argv) {360int main(int argc, char **argv) {
213 ::testing::InitGoogleTest(&argc, argv);361 ::testing::InitGoogleTest(&argc, argv);
214 return RUN_ALL_TESTS();362 return RUN_ALL_TESTS();
215363
=== modified file 'test/test_util.cc'
--- test/test_util.cc 2014-05-09 14:04:48 +0000
+++ test/test_util.cc 2015-11-23 03:14:55 +0000
@@ -19,6 +19,9 @@
1919
20#include <gtest/gtest.h>20#include <gtest/gtest.h>
21#include"../src/mediascanner/internal/utils.hh"21#include"../src/mediascanner/internal/utils.hh"
22#include"../src/mediascanner/MediaFile.hh"
23#include"../src/mediascanner/MediaFileBuilder.hh"
24
22#include "test_config.h"25#include "test_config.h"
2326
24using namespace mediascanner;27using namespace mediascanner;

Subscribers

People subscribed via source and target branches