Merge lp:~jamesh/mediascanner2/media-filter into lp:mediascanner2
- media-filter
- Merge into trunk
Proposed by
James Henstridge
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 232 | ||||||||
Proposed branch: | lp:~jamesh/mediascanner2/media-filter | ||||||||
Merge into: | lp:mediascanner2 | ||||||||
Prerequisite: | lp:~jamesh/mediascanner2/dbus-apparmor | ||||||||
Diff against target: |
2163 lines (+1313/-184) 33 files modified
src/mediascanner/CMakeLists.txt (+1/-0) src/mediascanner/Filter.cc (+154/-0) src/mediascanner/Filter.hh (+66/-0) src/mediascanner/MediaStore.cc (+93/-34) src/mediascanner/MediaStore.hh (+6/-3) src/ms-dbus/dbus-codec.cc (+45/-0) src/ms-dbus/dbus-codec.hh (+24/-0) src/ms-dbus/dbus-interface.hh (+26/-0) src/ms-dbus/service-skeleton.cc (+64/-14) src/ms-dbus/service-stub.cc (+30/-15) src/ms-dbus/service-stub.hh (+6/-3) src/qml/Ubuntu/MediaScanner/AlbumModelBase.cc (+1/-0) src/qml/Ubuntu/MediaScanner/AlbumModelBase.hh (+3/-1) src/qml/Ubuntu/MediaScanner/AlbumsModel.cc (+61/-22) src/qml/Ubuntu/MediaScanner/AlbumsModel.hh (+11/-7) src/qml/Ubuntu/MediaScanner/ArtistsModel.cc (+27/-2) src/qml/Ubuntu/MediaScanner/ArtistsModel.hh (+8/-1) src/qml/Ubuntu/MediaScanner/CMakeLists.txt (+1/-0) src/qml/Ubuntu/MediaScanner/GenresModel.cc (+86/-0) src/qml/Ubuntu/MediaScanner/GenresModel.hh (+71/-0) src/qml/Ubuntu/MediaScanner/MediaFileModelBase.cc (+1/-0) src/qml/Ubuntu/MediaScanner/MediaFileModelBase.hh (+3/-1) src/qml/Ubuntu/MediaScanner/SongsModel.cc (+82/-34) src/qml/Ubuntu/MediaScanner/SongsModel.hh (+14/-10) src/qml/Ubuntu/MediaScanner/plugin.cc (+2/-0) src/qml/Ubuntu/MediaScanner/plugin.qmltypes (+29/-5) src/qml/Ubuntu/MediaScanner/qmldir (+1/-0) test/qml/tst_albumsmodel.qml (+91/-0) test/qml/tst_artistsmodel.qml (+68/-0) test/qml/tst_genresmodel.qml (+44/-0) test/qml/tst_songsmodel.qml (+104/-0) test/test_dbus.cc (+39/-12) test/test_mediastore.cc (+51/-20) |
||||||||
To merge this branch: | bzr merge lp:~jamesh/mediascanner2/media-filter | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Mediascanner Team | Pending | ||
Review via email:
|
Commit message
Introduce a mediascanner:
Description of the change
Introduce a mediascanner:
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
review:
Approve
(continuous-integration)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/mediascanner/CMakeLists.txt' |
2 | --- src/mediascanner/CMakeLists.txt 2014-05-29 09:17:39 +0000 |
3 | +++ src/mediascanner/CMakeLists.txt 2014-05-29 09:17:39 +0000 |
4 | @@ -2,6 +2,7 @@ |
5 | MediaFile.cc |
6 | MediaFileBuilder.cc |
7 | MediaFilePrivate.cc |
8 | + Filter.cc |
9 | Album.cc |
10 | MediaStore.cc |
11 | utils.cc |
12 | |
13 | === added file 'src/mediascanner/Filter.cc' |
14 | --- src/mediascanner/Filter.cc 1970-01-01 00:00:00 +0000 |
15 | +++ src/mediascanner/Filter.cc 2014-05-29 09:17:39 +0000 |
16 | @@ -0,0 +1,154 @@ |
17 | +/* |
18 | + * Copyright (C) 2014 Canonical, Ltd. |
19 | + * |
20 | + * Authors: |
21 | + * James Henstridge <james.henstridge@canonical.com> |
22 | + * |
23 | + * This program is free software: you can redistribute it and/or modify |
24 | + * it under the terms of the GNU Lesser General Public License version 3 as |
25 | + * published by the Free Software Foundation. |
26 | + * |
27 | + * This program is distributed in the hope that it will be useful, |
28 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
29 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
30 | + * GNU Lesser General Public License for more details. |
31 | + * |
32 | + * You should have received a copy of the GNU Lesser General Public License |
33 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
34 | + */ |
35 | + |
36 | +#include "Filter.hh" |
37 | + |
38 | +using std::string; |
39 | + |
40 | +namespace mediascanner { |
41 | + |
42 | +struct Filter::Private { |
43 | + string artist; |
44 | + string album; |
45 | + string album_artist; |
46 | + string genre; |
47 | + |
48 | + bool have_artist; |
49 | + bool have_album; |
50 | + bool have_album_artist; |
51 | + bool have_genre; |
52 | + |
53 | + Private() : |
54 | + have_artist(false), have_album(false), have_album_artist(false), |
55 | + have_genre(false) { |
56 | + } |
57 | +}; |
58 | + |
59 | +Filter::Filter() : p(new Private) { |
60 | +} |
61 | + |
62 | +Filter::Filter(const Filter &other) : Filter() { |
63 | + *p = *other.p; |
64 | +} |
65 | + |
66 | +Filter::~Filter() { |
67 | + delete p; |
68 | +} |
69 | + |
70 | +bool Filter::operator==(const Filter &other) const { |
71 | + return |
72 | + p->have_artist == other.p->have_artist && |
73 | + p->have_album == other.p->have_album && |
74 | + p->have_album_artist == other.p->have_album_artist && |
75 | + p->have_genre == other.p->have_genre && |
76 | + p->artist == other.p->artist && |
77 | + p->album == other.p->album && |
78 | + p->album_artist == other.p->album_artist && |
79 | + p->genre == other.p->genre; |
80 | +} |
81 | + |
82 | +bool Filter::operator!=(const Filter &other) const { |
83 | + return !(*this == other); |
84 | +} |
85 | + |
86 | +Filter &Filter::operator=(const Filter &other) { |
87 | + *p = *other.p; |
88 | + return *this; |
89 | +} |
90 | + |
91 | +void Filter::clear() { |
92 | + unsetArtist(); |
93 | + unsetAlbum(); |
94 | + unsetAlbumArtist(); |
95 | + unsetGenre(); |
96 | +} |
97 | + |
98 | +void Filter::setArtist(const std::string &artist) { |
99 | + p->artist = artist; |
100 | + p->have_artist = true; |
101 | +} |
102 | + |
103 | +void Filter::unsetArtist() { |
104 | + p->artist = ""; |
105 | + p->have_artist = false; |
106 | +} |
107 | + |
108 | +bool Filter::hasArtist() const { |
109 | + return p->have_artist; |
110 | +} |
111 | + |
112 | +const std::string &Filter::getArtist() const { |
113 | + return p->artist; |
114 | +} |
115 | + |
116 | +void Filter::setAlbum(const std::string &album) { |
117 | + p->album = album; |
118 | + p->have_album = true; |
119 | +} |
120 | + |
121 | +void Filter::unsetAlbum() { |
122 | + p->album = ""; |
123 | + p->have_album = false; |
124 | +} |
125 | + |
126 | +bool Filter::hasAlbum() const { |
127 | + return p->have_album; |
128 | +} |
129 | + |
130 | +const std::string &Filter::getAlbum() const { |
131 | + return p->album; |
132 | +} |
133 | + |
134 | +void Filter::setAlbumArtist(const std::string &album_artist) { |
135 | + p->album_artist = album_artist; |
136 | + p->have_album_artist = true; |
137 | +} |
138 | + |
139 | +void Filter::unsetAlbumArtist() { |
140 | + p->album_artist = ""; |
141 | + p->have_album_artist = false; |
142 | +} |
143 | + |
144 | +bool Filter::hasAlbumArtist() const { |
145 | + return p->have_album_artist; |
146 | +} |
147 | + |
148 | +const std::string &Filter::getAlbumArtist() const { |
149 | + return p->album_artist; |
150 | +} |
151 | + |
152 | +void Filter::setGenre(const std::string &genre) { |
153 | + p->genre = genre; |
154 | + p->have_genre = true; |
155 | +} |
156 | + |
157 | +void Filter::unsetGenre() { |
158 | + p->genre = ""; |
159 | + p->have_genre = false; |
160 | +} |
161 | + |
162 | +bool Filter::hasGenre() const { |
163 | + return p->have_genre; |
164 | +} |
165 | + |
166 | +const std::string &Filter::getGenre() const { |
167 | + return p->genre; |
168 | +} |
169 | + |
170 | +} |
171 | |
172 | === added file 'src/mediascanner/Filter.hh' |
173 | --- src/mediascanner/Filter.hh 1970-01-01 00:00:00 +0000 |
174 | +++ src/mediascanner/Filter.hh 2014-05-29 09:17:39 +0000 |
175 | @@ -0,0 +1,66 @@ |
176 | +/* |
177 | + * Copyright (C) 2014 Canonical, Ltd. |
178 | + * |
179 | + * Authors: |
180 | + * James Henstridge <james.henstridge@canonical.com> |
181 | + * |
182 | + * This program is free software: you can redistribute it and/or modify |
183 | + * it under the terms of the GNU Lesser General Public License version 3 as |
184 | + * published by the Free Software Foundation. |
185 | + * |
186 | + * This program is distributed in the hope that it will be useful, |
187 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
188 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
189 | + * GNU Lesser General Public License for more details. |
190 | + * |
191 | + * You should have received a copy of the GNU Lesser General Public License |
192 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
193 | + */ |
194 | + |
195 | +#ifndef MEDIAFILTER_H_ |
196 | +#define MEDIAFILTER_H_ |
197 | + |
198 | +#include <string> |
199 | + |
200 | +namespace mediascanner { |
201 | + |
202 | +class Filter final { |
203 | +public: |
204 | + Filter(); |
205 | + Filter(const Filter &other); |
206 | + ~Filter(); |
207 | + |
208 | + Filter &operator=(const Filter &other); |
209 | + bool operator==(const Filter &other) const; |
210 | + bool operator!=(const Filter &other) const; |
211 | + |
212 | + void clear(); |
213 | + |
214 | + void setArtist(const std::string &artist); |
215 | + void unsetArtist(); |
216 | + bool hasArtist() const; |
217 | + const std::string &getArtist() const; |
218 | + |
219 | + void setAlbum(const std::string &album); |
220 | + void unsetAlbum(); |
221 | + bool hasAlbum() const; |
222 | + const std::string &getAlbum() const; |
223 | + |
224 | + void setAlbumArtist(const std::string &album_artist); |
225 | + void unsetAlbumArtist(); |
226 | + bool hasAlbumArtist() const; |
227 | + const std::string &getAlbumArtist() const; |
228 | + |
229 | + void setGenre(const std::string &genre); |
230 | + void unsetGenre(); |
231 | + bool hasGenre() const; |
232 | + const std::string &getGenre() const; |
233 | + |
234 | +private: |
235 | + struct Private; |
236 | + Private *p; |
237 | +}; |
238 | + |
239 | +} |
240 | + |
241 | +#endif |
242 | |
243 | === modified file 'src/mediascanner/MediaStore.cc' |
244 | --- src/mediascanner/MediaStore.cc 2014-05-29 09:17:39 +0000 |
245 | +++ src/mediascanner/MediaStore.cc 2014-05-29 09:17:39 +0000 |
246 | @@ -33,6 +33,7 @@ |
247 | #include "MediaFile.hh" |
248 | #include "MediaFileBuilder.hh" |
249 | #include "Album.hh" |
250 | +#include "Filter.hh" |
251 | #include "internal/sqliteutils.hh" |
252 | #include "internal/utils.hh" |
253 | |
254 | @@ -448,21 +449,24 @@ |
255 | } |
256 | } |
257 | |
258 | -std::vector<MediaFile> MediaStore::listSongs(const std::string& artist, const std::string& album, const std::string& album_artist, int limit) const { |
259 | +std::vector<MediaFile> MediaStore::listSongs(const Filter &filter, int limit) const { |
260 | std::string qs(R"( |
261 | SELECT filename, content_type, etag, title, date, artist, album, album_artist, genre, disc_number, track_number, duration, type |
262 | FROM media |
263 | WHERE type = ? |
264 | )"); |
265 | - if (!artist.empty()) { |
266 | + if (filter.hasArtist()) { |
267 | qs += " AND artist = ?"; |
268 | } |
269 | - if (!album.empty()) { |
270 | + if (filter.hasAlbum()) { |
271 | qs += " AND album = ?"; |
272 | } |
273 | - if (!album_artist.empty()) { |
274 | + if (filter.hasAlbumArtist()) { |
275 | qs += " AND album_artist = ?"; |
276 | } |
277 | + if (filter.hasGenre()) { |
278 | + qs += " AND genre = ?"; |
279 | + } |
280 | qs += R"( |
281 | ORDER BY album_artist, album, disc_number, track_number, title |
282 | LIMIT ? |
283 | @@ -470,31 +474,37 @@ |
284 | Statement query(p->db, qs.c_str()); |
285 | int param = 1; |
286 | query.bind(param++, (int)AudioMedia); |
287 | - if (!artist.empty()) { |
288 | - query.bind(param++, artist); |
289 | - } |
290 | - if (!album.empty()) { |
291 | - query.bind(param++, album); |
292 | - } |
293 | - if (!album_artist.empty()) { |
294 | - query.bind(param++, album_artist); |
295 | + if (filter.hasArtist()) { |
296 | + query.bind(param++, filter.getArtist()); |
297 | + } |
298 | + if (filter.hasAlbum()) { |
299 | + query.bind(param++, filter.getAlbum()); |
300 | + } |
301 | + if (filter.hasAlbumArtist()) { |
302 | + query.bind(param++, filter.getAlbumArtist()); |
303 | + } |
304 | + if (filter.hasGenre()) { |
305 | + query.bind(param++, filter.getGenre()); |
306 | } |
307 | query.bind(param++, limit); |
308 | |
309 | return collect_media(query); |
310 | } |
311 | |
312 | -std::vector<Album> MediaStore::listAlbums(const std::string& artist, const std::string& album_artist, int limit) const { |
313 | +std::vector<Album> MediaStore::listAlbums(const Filter &filter, int limit) const { |
314 | std::string qs(R"( |
315 | SELECT album, album_artist FROM media |
316 | WHERE type = ? |
317 | )"); |
318 | - if (!artist.empty()) { |
319 | + if (filter.hasArtist()) { |
320 | qs += " AND artist = ?"; |
321 | } |
322 | - if (!album_artist.empty()) { |
323 | + if (filter.hasAlbumArtist()) { |
324 | qs += " AND album_artist = ?"; |
325 | } |
326 | + if (filter.hasGenre()) { |
327 | + qs += "AND genre = ?"; |
328 | + } |
329 | qs += R"( |
330 | GROUP BY album, album_artist |
331 | ORDER BY album_artist, album |
332 | @@ -503,37 +513,68 @@ |
333 | Statement query(p->db, qs.c_str()); |
334 | int param = 1; |
335 | query.bind(param++, (int)AudioMedia); |
336 | - if (!artist.empty()) { |
337 | - query.bind(param++, artist); |
338 | - } |
339 | - if (!album_artist.empty()) { |
340 | - query.bind(param++, album_artist); |
341 | + if (filter.hasArtist()) { |
342 | + query.bind(param++, filter.getArtist()); |
343 | + } |
344 | + if (filter.hasAlbumArtist()) { |
345 | + query.bind(param++, filter.getAlbumArtist()); |
346 | + } |
347 | + if (filter.hasGenre()) { |
348 | + query.bind(param++, filter.getGenre()); |
349 | } |
350 | query.bind(param++, limit); |
351 | |
352 | return collect_albums(query); |
353 | } |
354 | |
355 | -vector<std::string> MediaStore::listArtists(bool album_artists, int limit) const { |
356 | - const char *qs; |
357 | - |
358 | - if (album_artists) { |
359 | - qs = R"( |
360 | +vector<std::string> MediaStore::listArtists(const Filter &filter, int limit) const { |
361 | + string qs(R"( |
362 | +SELECT artist FROM media |
363 | + WHERE type = ? |
364 | +)"); |
365 | + if (filter.hasGenre()) { |
366 | + qs += " AND genre = ?"; |
367 | + } |
368 | + qs += R"( |
369 | + GROUP BY artist |
370 | + ORDER BY artist |
371 | + LIMIT ? |
372 | +)"; |
373 | + Statement query(p->db, qs.c_str()); |
374 | + int param = 1; |
375 | + query.bind(param++, (int)AudioMedia); |
376 | + if (filter.hasGenre()) { |
377 | + query.bind(param++, filter.getGenre()); |
378 | + } |
379 | + query.bind(param++, limit); |
380 | + |
381 | + vector<string> artists; |
382 | + while (query.step()) { |
383 | + artists.push_back(query.getText(0)); |
384 | + } |
385 | + return artists; |
386 | +} |
387 | + |
388 | +vector<std::string> MediaStore::listAlbumArtists(const Filter &filter, int limit) const { |
389 | + string qs(R"( |
390 | SELECT album_artist FROM media |
391 | + WHERE type = ? |
392 | +)"); |
393 | + if (filter.hasGenre()) { |
394 | + qs += " AND genre = ?"; |
395 | + } |
396 | + qs += R"( |
397 | GROUP BY album_artist |
398 | ORDER BY album_artist |
399 | LIMIT ? |
400 | )"; |
401 | - } else { |
402 | - qs = R"( |
403 | -SELECT artist FROM media |
404 | - GROUP BY artist |
405 | - ORDER BY artist |
406 | - LIMIT ? |
407 | -)"; |
408 | + Statement query(p->db, qs.c_str()); |
409 | + int param = 1; |
410 | + query.bind(param++, (int)AudioMedia); |
411 | + if (filter.hasGenre()) { |
412 | + query.bind(param++, filter.getGenre()); |
413 | } |
414 | - Statement query(p->db, qs); |
415 | - query.bind(1, limit); |
416 | + query.bind(param++, limit); |
417 | |
418 | vector<string> artists; |
419 | while (query.step()) { |
420 | @@ -542,6 +583,24 @@ |
421 | return artists; |
422 | } |
423 | |
424 | +vector<std::string> MediaStore::listGenres(int limit) const { |
425 | + Statement query(p->db, R"( |
426 | +SELECT genre FROM media |
427 | + WHERE type = ? |
428 | + GROUP BY genre |
429 | + ORDER BY genre |
430 | + LIMIT ? |
431 | +)"); |
432 | + query.bind(1, (int)AudioMedia); |
433 | + query.bind(2, limit); |
434 | + |
435 | + vector<string> genres; |
436 | + while (query.step()) { |
437 | + genres.push_back(query.getText(0)); |
438 | + } |
439 | + return genres; |
440 | +} |
441 | + |
442 | void MediaStorePrivate::pruneDeleted() { |
443 | vector<string> deleted; |
444 | Statement query(db, "SELECT filename FROM media"); |
445 | |
446 | === modified file 'src/mediascanner/MediaStore.hh' |
447 | --- src/mediascanner/MediaStore.hh 2014-05-29 09:17:39 +0000 |
448 | +++ src/mediascanner/MediaStore.hh 2014-05-29 09:17:39 +0000 |
449 | @@ -29,6 +29,7 @@ |
450 | struct MediaStorePrivate; |
451 | class MediaFile; |
452 | class Album; |
453 | +class Filter; |
454 | |
455 | enum OpenType { |
456 | MS_READ_ONLY, |
457 | @@ -53,9 +54,11 @@ |
458 | std::vector<Album> queryAlbums(const std::string &core_term, int limit=-1) const; |
459 | std::vector<MediaFile> getAlbumSongs(const Album& album) const; |
460 | std::string getETag(const std::string &filename) const; |
461 | - std::vector<MediaFile> listSongs(const std::string& artist="", const std::string& album="", const std::string& album_artist="", int limit=-1) const; |
462 | - std::vector<Album> listAlbums(const std::string& artist="", const std::string& album_artist="", int limit=-1) const; |
463 | - std::vector<std::string> listArtists(bool album_artists, int limit=-1) const; |
464 | + std::vector<MediaFile> listSongs(const Filter &filter, int limit=-1) const; |
465 | + std::vector<Album> listAlbums(const Filter &filter, int limit=-1) const; |
466 | + std::vector<std::string> listArtists(const Filter &filter, int limit=-1) const; |
467 | + std::vector<std::string>listAlbumArtists(const Filter &filter, int limit=-1) const; |
468 | + std::vector<std::string>listGenres(int limit=-1) const; |
469 | |
470 | size_t size() const; |
471 | void pruneDeleted(); |
472 | |
473 | === modified file 'src/ms-dbus/dbus-codec.cc' |
474 | --- src/ms-dbus/dbus-codec.cc 2014-05-29 09:17:39 +0000 |
475 | +++ src/ms-dbus/dbus-codec.cc 2014-05-29 09:17:39 +0000 |
476 | @@ -24,6 +24,7 @@ |
477 | #include <mediascanner/MediaFile.hh> |
478 | #include <mediascanner/MediaFileBuilder.hh> |
479 | #include <mediascanner/Album.hh> |
480 | +#include <mediascanner/Filter.hh> |
481 | #include "dbus-codec.hh" |
482 | |
483 | using core::dbus::Message; |
484 | @@ -32,6 +33,7 @@ |
485 | using mediascanner::MediaFileBuilder; |
486 | using mediascanner::MediaType; |
487 | using mediascanner::Album; |
488 | +using mediascanner::Filter; |
489 | using std::string; |
490 | |
491 | void Codec<MediaFile>::encode_argument(Message::Writer &out, const MediaFile &file) { |
492 | @@ -88,3 +90,46 @@ |
493 | r >> title >> artist; |
494 | album = Album(title, artist); |
495 | } |
496 | + |
497 | +void Codec<Filter>::encode_argument(Message::Writer &out, const Filter &filter) { |
498 | + auto w = out.open_array(core::dbus::types::Signature("{ss}")); |
499 | + |
500 | + if (filter.hasArtist()) { |
501 | + w.close_dict_entry( |
502 | + w.open_dict_entry() << string("artist") << filter.getArtist()); |
503 | + } |
504 | + if (filter.hasAlbum()) { |
505 | + w.close_dict_entry( |
506 | + w.open_dict_entry() << string("album") << filter.getAlbum()); |
507 | + } |
508 | + if (filter.hasAlbumArtist()) { |
509 | + w.close_dict_entry( |
510 | + w.open_dict_entry() << string("album_artist") << filter.getAlbumArtist()); |
511 | + } |
512 | + if (filter.hasGenre()) { |
513 | + w.close_dict_entry( |
514 | + w.open_dict_entry() << string("genre") << filter.getGenre()); |
515 | + } |
516 | + |
517 | + out.close_array(std::move(w)); |
518 | +} |
519 | + |
520 | +void Codec<Filter>::decode_argument(Message::Reader &in, Filter &filter) { |
521 | + auto r = in.pop_array(); |
522 | + |
523 | + filter.clear(); |
524 | + while (r.type() != ArgumentType::invalid) { |
525 | + string key, value; |
526 | + r.pop_dict_entry() >> key >> value; |
527 | + |
528 | + if (key == "artist") { |
529 | + filter.setArtist(value); |
530 | + } else if (key == "album") { |
531 | + filter.setAlbum(value); |
532 | + } else if (key == "album_artist") { |
533 | + filter.setAlbumArtist(value); |
534 | + } else if (key == "genre") { |
535 | + filter.setGenre(value); |
536 | + } |
537 | + } |
538 | +} |
539 | |
540 | === modified file 'src/ms-dbus/dbus-codec.hh' |
541 | --- src/ms-dbus/dbus-codec.hh 2014-05-29 09:17:39 +0000 |
542 | +++ src/ms-dbus/dbus-codec.hh 2014-05-29 09:17:39 +0000 |
543 | @@ -27,6 +27,7 @@ |
544 | namespace mediascanner { |
545 | class MediaFile; |
546 | class Album; |
547 | +class Filter; |
548 | } |
549 | |
550 | namespace core { |
551 | @@ -44,6 +45,12 @@ |
552 | static void decode_argument(Message::Reader &in, mediascanner::Album &album); |
553 | }; |
554 | |
555 | +template <> |
556 | +struct Codec<mediascanner::Filter> { |
557 | + static void encode_argument(Message::Writer &out, const mediascanner::Filter &filter); |
558 | + static void decode_argument(Message::Reader &in, mediascanner::Filter &filter); |
559 | +}; |
560 | + |
561 | namespace helper { |
562 | |
563 | template<> |
564 | @@ -80,6 +87,23 @@ |
565 | } |
566 | }; |
567 | |
568 | +template<> |
569 | +struct TypeMapper<mediascanner::Filter> { |
570 | + constexpr static ArgumentType type_value() { |
571 | + return ArgumentType::array; |
572 | + } |
573 | + constexpr static bool is_basic_type() { |
574 | + return false; |
575 | + } |
576 | + constexpr static bool requires_signature() { |
577 | + return true; |
578 | + } |
579 | + static const std::string &signature() { |
580 | + static const std::string s = "a{ss}"; |
581 | + return s; |
582 | + } |
583 | +}; |
584 | + |
585 | } |
586 | |
587 | } |
588 | |
589 | === modified file 'src/ms-dbus/dbus-interface.hh' |
590 | --- src/ms-dbus/dbus-interface.hh 2014-05-29 09:17:39 +0000 |
591 | +++ src/ms-dbus/dbus-interface.hh 2014-05-29 09:17:39 +0000 |
592 | @@ -153,6 +153,32 @@ |
593 | return std::chrono::seconds{1}; |
594 | } |
595 | }; |
596 | + |
597 | + struct ListAlbumArtists { |
598 | + typedef MediaStoreInterface Interface; |
599 | + |
600 | + inline static const std::string& name() { |
601 | + static std::string s = "ListAlbumArtists"; |
602 | + return s; |
603 | + } |
604 | + |
605 | + inline static const std::chrono::milliseconds default_timeout() { |
606 | + return std::chrono::seconds{1}; |
607 | + } |
608 | + }; |
609 | + |
610 | + struct ListGenres { |
611 | + typedef MediaStoreInterface Interface; |
612 | + |
613 | + inline static const std::string& name() { |
614 | + static std::string s = "ListGenres"; |
615 | + return s; |
616 | + } |
617 | + |
618 | + inline static const std::chrono::milliseconds default_timeout() { |
619 | + return std::chrono::seconds{1}; |
620 | + } |
621 | + }; |
622 | }; |
623 | |
624 | } |
625 | |
626 | === modified file 'src/ms-dbus/service-skeleton.cc' |
627 | --- src/ms-dbus/service-skeleton.cc 2014-05-29 09:17:39 +0000 |
628 | +++ src/ms-dbus/service-skeleton.cc 2014-05-29 09:17:39 +0000 |
629 | @@ -6,6 +6,7 @@ |
630 | #include <sys/apparmor.h> |
631 | |
632 | #include <mediascanner/Album.hh> |
633 | +#include <mediascanner/Filter.hh> |
634 | #include <mediascanner/MediaFile.hh> |
635 | #include <mediascanner/MediaStore.hh> |
636 | |
637 | @@ -88,6 +89,16 @@ |
638 | &Private::handle_list_artists, |
639 | this, |
640 | std::placeholders::_1)); |
641 | + object->install_method_handler<MediaStoreInterface::ListAlbumArtists>( |
642 | + std::bind( |
643 | + &Private::handle_list_album_artists, |
644 | + this, |
645 | + std::placeholders::_1)); |
646 | + object->install_method_handler<MediaStoreInterface::ListGenres>( |
647 | + std::bind( |
648 | + &Private::handle_list_genres, |
649 | + this, |
650 | + std::placeholders::_1)); |
651 | } |
652 | |
653 | std::string get_client_apparmor_context(const Message::Ptr &message) { |
654 | @@ -253,12 +264,12 @@ |
655 | if (!check_access(message, AudioMedia)) |
656 | return; |
657 | |
658 | - std::string artist, album, album_artist; |
659 | + Filter filter; |
660 | int32_t limit; |
661 | - message->reader() >> artist >> album >> album_artist >> limit; |
662 | + message->reader() >> filter >> limit; |
663 | Message::Ptr reply; |
664 | try { |
665 | - auto results = store->listSongs(artist, album, album_artist, limit); |
666 | + auto results = store->listSongs(filter, limit); |
667 | reply = Message::make_method_return(message); |
668 | reply->writer() << results; |
669 | } catch (const std::exception &e) { |
670 | @@ -273,12 +284,12 @@ |
671 | if (!check_access(message, AudioMedia)) |
672 | return; |
673 | |
674 | - std::string artist, album_artist; |
675 | + Filter filter; |
676 | int32_t limit; |
677 | - message->reader() >> artist >> album_artist >> limit; |
678 | + message->reader() >> filter >> limit; |
679 | Message::Ptr reply; |
680 | try { |
681 | - auto albums = store->listAlbums(artist, album_artist, limit); |
682 | + auto albums = store->listAlbums(filter, limit); |
683 | reply = Message::make_method_return(message); |
684 | reply->writer() << albums; |
685 | } catch (const std::exception &e) { |
686 | @@ -293,14 +304,53 @@ |
687 | if (!check_access(message, AudioMedia)) |
688 | return; |
689 | |
690 | - bool album_artists; |
691 | - int32_t limit; |
692 | - message->reader() >> album_artists >> limit; |
693 | - Message::Ptr reply; |
694 | - try { |
695 | - auto artists = store->listArtists(album_artists, limit); |
696 | - reply = Message::make_method_return(message); |
697 | - reply->writer() << artists; |
698 | + Filter filter; |
699 | + int32_t limit; |
700 | + message->reader() >> filter >> limit; |
701 | + Message::Ptr reply; |
702 | + try { |
703 | + auto artists = store->listArtists(filter, limit); |
704 | + reply = Message::make_method_return(message); |
705 | + reply->writer() << artists; |
706 | + } catch (const std::exception &e) { |
707 | + reply = Message::make_error( |
708 | + message, MediaStoreInterface::Errors::Error::name(), |
709 | + e.what()); |
710 | + } |
711 | + impl->access_bus()->send(reply); |
712 | + } |
713 | + |
714 | + void handle_list_album_artists(const Message::Ptr &message) { |
715 | + if (!check_access(message, AudioMedia)) |
716 | + return; |
717 | + |
718 | + Filter filter; |
719 | + int32_t limit; |
720 | + message->reader() >> filter >> limit; |
721 | + Message::Ptr reply; |
722 | + try { |
723 | + auto artists = store->listAlbumArtists(filter, limit); |
724 | + reply = Message::make_method_return(message); |
725 | + reply->writer() << artists; |
726 | + } catch (const std::exception &e) { |
727 | + reply = Message::make_error( |
728 | + message, MediaStoreInterface::Errors::Error::name(), |
729 | + e.what()); |
730 | + } |
731 | + impl->access_bus()->send(reply); |
732 | + } |
733 | + |
734 | + void handle_list_genres(const Message::Ptr &message) { |
735 | + if (!check_access(message, AudioMedia)) |
736 | + return; |
737 | + |
738 | + int32_t limit; |
739 | + message->reader() >> limit; |
740 | + Message::Ptr reply; |
741 | + try { |
742 | + auto genres = store->listGenres(limit); |
743 | + reply = Message::make_method_return(message); |
744 | + reply->writer() << genres; |
745 | } catch (const std::exception &e) { |
746 | reply = Message::make_error( |
747 | message, MediaStoreInterface::Errors::Error::name(), |
748 | |
749 | === modified file 'src/ms-dbus/service-stub.cc' |
750 | --- src/ms-dbus/service-stub.cc 2014-05-29 09:17:39 +0000 |
751 | +++ src/ms-dbus/service-stub.cc 2014-05-29 09:17:39 +0000 |
752 | @@ -20,6 +20,7 @@ |
753 | #include <stdexcept> |
754 | |
755 | #include <mediascanner/Album.hh> |
756 | +#include <mediascanner/Filter.hh> |
757 | #include <mediascanner/MediaFile.hh> |
758 | #include "service-stub.hh" |
759 | #include "dbus-interface.hh" |
760 | @@ -79,21 +80,35 @@ |
761 | return result.value(); |
762 | } |
763 | |
764 | -std::vector<MediaFile> ServiceStub::listSongs(const string &artist, const string &album, const string &album_artist, int limit) const { |
765 | - auto result = p->object->invoke_method_synchronously<MediaStoreInterface::ListSongs, std::vector<MediaFile>>(artist, album, album_artist, (int32_t)limit); |
766 | - if (result.is_error()) |
767 | - throw std::runtime_error(result.error().print()); |
768 | - return result.value(); |
769 | -} |
770 | - |
771 | -std::vector<Album> ServiceStub::listAlbums(const string &artist, const string &album_artist, int limit) const { |
772 | - auto result = p->object->invoke_method_synchronously<MediaStoreInterface::ListAlbums, std::vector<Album>>(artist, album_artist, (int32_t)limit); |
773 | - if (result.is_error()) |
774 | - throw std::runtime_error(result.error().print()); |
775 | - return result.value(); |
776 | -} |
777 | -std::vector<string> ServiceStub::listArtists(bool album_artists, int limit) const { |
778 | - auto result = p->object->invoke_method_synchronously<MediaStoreInterface::ListArtists, std::vector<string>>(album_artists, (int32_t)limit); |
779 | +std::vector<MediaFile> ServiceStub::listSongs(const Filter &filter, int limit) const { |
780 | + auto result = p->object->invoke_method_synchronously<MediaStoreInterface::ListSongs, std::vector<MediaFile>>(filter, (int32_t)limit); |
781 | + if (result.is_error()) |
782 | + throw std::runtime_error(result.error().print()); |
783 | + return result.value(); |
784 | +} |
785 | + |
786 | +std::vector<Album> ServiceStub::listAlbums(const Filter &filter, int limit) const { |
787 | + auto result = p->object->invoke_method_synchronously<MediaStoreInterface::ListAlbums, std::vector<Album>>(filter, (int32_t)limit); |
788 | + if (result.is_error()) |
789 | + throw std::runtime_error(result.error().print()); |
790 | + return result.value(); |
791 | +} |
792 | +std::vector<string> ServiceStub::listArtists(const Filter &filter, int limit) const { |
793 | + auto result = p->object->invoke_method_synchronously<MediaStoreInterface::ListArtists, std::vector<string>>(filter, (int32_t)limit); |
794 | + if (result.is_error()) |
795 | + throw std::runtime_error(result.error().print()); |
796 | + return result.value(); |
797 | +} |
798 | + |
799 | +std::vector<string> ServiceStub::listAlbumArtists(const Filter &filter, int limit) const { |
800 | + auto result = p->object->invoke_method_synchronously<MediaStoreInterface::ListAlbumArtists, std::vector<string>>(filter, (int32_t)limit); |
801 | + if (result.is_error()) |
802 | + throw std::runtime_error(result.error().print()); |
803 | + return result.value(); |
804 | +} |
805 | + |
806 | +std::vector<string> ServiceStub::listGenres(int limit) const { |
807 | + auto result = p->object->invoke_method_synchronously<MediaStoreInterface::ListGenres, std::vector<string>>((int32_t)limit); |
808 | if (result.is_error()) |
809 | throw std::runtime_error(result.error().print()); |
810 | return result.value(); |
811 | |
812 | === modified file 'src/ms-dbus/service-stub.hh' |
813 | --- src/ms-dbus/service-stub.hh 2014-05-29 09:17:39 +0000 |
814 | +++ src/ms-dbus/service-stub.hh 2014-05-29 09:17:39 +0000 |
815 | @@ -32,6 +32,7 @@ |
816 | namespace mediascanner { |
817 | |
818 | class Album; |
819 | +class Filter; |
820 | class MediaFile; |
821 | |
822 | namespace dbus { |
823 | @@ -46,9 +47,11 @@ |
824 | std::vector<Album> queryAlbums(const std::string &core_term, int limit=-1) const; |
825 | std::vector<MediaFile> getAlbumSongs(const Album& album) const; |
826 | std::string getETag(const std::string &filename) const; |
827 | - std::vector<MediaFile> listSongs(const std::string& artist="", const std::string& album="", const std::string& album_artist="", int limit=-1) const; |
828 | - std::vector<Album> listAlbums(const std::string& artist="", const std::string& album_artist="", int limit=-1) const; |
829 | - std::vector<std::string> listArtists(bool album_artists, int limit=-1) const; |
830 | + std::vector<MediaFile> listSongs(const Filter &filter, int limit=-1) const; |
831 | + std::vector<Album> listAlbums(const Filter &filter, int limit=-1) const; |
832 | + std::vector<std::string> listArtists(const Filter &filter, int limit=-1) const; |
833 | + std::vector<std::string> listAlbumArtists(const Filter &filter, int limit=-1) const; |
834 | + std::vector<std::string> listGenres(int limit=-1) const; |
835 | |
836 | private: |
837 | struct Private; |
838 | |
839 | === modified file 'src/qml/Ubuntu/MediaScanner/AlbumModelBase.cc' |
840 | --- src/qml/Ubuntu/MediaScanner/AlbumModelBase.cc 2014-05-06 09:09:49 +0000 |
841 | +++ src/qml/Ubuntu/MediaScanner/AlbumModelBase.cc 2014-05-29 09:17:39 +0000 |
842 | @@ -62,4 +62,5 @@ |
843 | beginResetModel(); |
844 | this->results = results; |
845 | endResetModel(); |
846 | + Q_EMIT rowCountChanged(); |
847 | } |
848 | |
849 | === modified file 'src/qml/Ubuntu/MediaScanner/AlbumModelBase.hh' |
850 | --- src/qml/Ubuntu/MediaScanner/AlbumModelBase.hh 2014-05-06 09:09:49 +0000 |
851 | +++ src/qml/Ubuntu/MediaScanner/AlbumModelBase.hh 2014-05-29 09:17:39 +0000 |
852 | @@ -31,7 +31,7 @@ |
853 | class AlbumModelBase : public QAbstractListModel { |
854 | Q_OBJECT |
855 | Q_ENUMS(Roles) |
856 | - Q_PROPERTY(int rowCount READ rowCount) // NOTIFY modelReset |
857 | + Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged) |
858 | public: |
859 | enum Roles { |
860 | RoleTitle, |
861 | @@ -44,6 +44,8 @@ |
862 | QVariant data(const QModelIndex &index, int role) const override; |
863 | |
864 | Q_INVOKABLE QVariant get(int row, Roles role) const; |
865 | +Q_SIGNALS: |
866 | + void rowCountChanged(); |
867 | protected: |
868 | QHash<int, QByteArray> roleNames() const override; |
869 | void updateResults(const std::vector<mediascanner::Album> &results); |
870 | |
871 | === modified file 'src/qml/Ubuntu/MediaScanner/AlbumsModel.cc' |
872 | --- src/qml/Ubuntu/MediaScanner/AlbumsModel.cc 2014-02-27 07:02:21 +0000 |
873 | +++ src/qml/Ubuntu/MediaScanner/AlbumsModel.cc 2014-05-29 09:17:39 +0000 |
874 | @@ -24,8 +24,6 @@ |
875 | AlbumsModel::AlbumsModel(QObject *parent) |
876 | : AlbumModelBase(parent), |
877 | store(nullptr), |
878 | - artist(""), |
879 | - album_artist(""), |
880 | limit(-1) { |
881 | } |
882 | |
883 | @@ -40,25 +38,66 @@ |
884 | } |
885 | } |
886 | |
887 | -QString AlbumsModel::getArtist() { |
888 | - return artist; |
889 | -} |
890 | - |
891 | -void AlbumsModel::setArtist(const QString artist) { |
892 | - if (this->artist != artist) { |
893 | - this->artist = artist; |
894 | - update(); |
895 | - } |
896 | -} |
897 | - |
898 | -QString AlbumsModel::getAlbumArtist() { |
899 | - return album_artist; |
900 | -} |
901 | - |
902 | -void AlbumsModel::setAlbumArtist(const QString album_artist) { |
903 | - if (this->album_artist != album_artist) { |
904 | - this->album_artist = album_artist; |
905 | - update(); |
906 | +QVariant AlbumsModel::getArtist() { |
907 | + if (!filter.hasArtist()) |
908 | + return QVariant(); |
909 | + return QString::fromStdString(filter.getArtist()); |
910 | +} |
911 | + |
912 | +void AlbumsModel::setArtist(const QVariant artist) { |
913 | + if (artist.isNull()) { |
914 | + if (filter.hasArtist()) { |
915 | + filter.unsetArtist(); |
916 | + update(); |
917 | + } |
918 | + } else { |
919 | + const std::string std_artist = artist.value<QString>().toStdString(); |
920 | + if (!filter.hasArtist() || filter.getArtist() != std_artist) { |
921 | + filter.setArtist(std_artist); |
922 | + update(); |
923 | + } |
924 | + } |
925 | +} |
926 | + |
927 | +QVariant AlbumsModel::getAlbumArtist() { |
928 | + if (!filter.hasAlbumArtist()) |
929 | + return QVariant(); |
930 | + return QString::fromStdString(filter.getAlbumArtist()); |
931 | +} |
932 | + |
933 | +void AlbumsModel::setAlbumArtist(const QVariant album_artist) { |
934 | + if (album_artist.isNull()) { |
935 | + if (filter.hasAlbumArtist()) { |
936 | + filter.unsetAlbumArtist(); |
937 | + update(); |
938 | + } |
939 | + } else { |
940 | + const std::string std_album_artist = album_artist.value<QString>().toStdString(); |
941 | + if (!filter.hasAlbumArtist() || filter.getAlbumArtist() != std_album_artist) { |
942 | + filter.setAlbumArtist(std_album_artist); |
943 | + update(); |
944 | + } |
945 | + } |
946 | +} |
947 | + |
948 | +QVariant AlbumsModel::getGenre() { |
949 | + if (!filter.hasGenre()) |
950 | + return QVariant(); |
951 | + return QString::fromStdString(filter.getGenre()); |
952 | +} |
953 | + |
954 | +void AlbumsModel::setGenre(const QVariant genre) { |
955 | + if (genre.isNull()) { |
956 | + if (filter.hasGenre()) { |
957 | + filter.unsetGenre(); |
958 | + update(); |
959 | + } |
960 | + } else { |
961 | + const std::string std_genre = genre.value<QString>().toStdString(); |
962 | + if (!filter.hasGenre() || filter.getGenre() != std_genre) { |
963 | + filter.setGenre(std_genre); |
964 | + update(); |
965 | + } |
966 | } |
967 | } |
968 | |
969 | @@ -77,6 +116,6 @@ |
970 | if (store == nullptr) { |
971 | updateResults(std::vector<mediascanner::Album>()); |
972 | } else { |
973 | - updateResults(store->store.listAlbums(artist.toStdString(), album_artist.toStdString(), limit)); |
974 | + updateResults(store->store.listAlbums(filter, limit)); |
975 | } |
976 | } |
977 | |
978 | === modified file 'src/qml/Ubuntu/MediaScanner/AlbumsModel.hh' |
979 | --- src/qml/Ubuntu/MediaScanner/AlbumsModel.hh 2014-02-27 07:02:21 +0000 |
980 | +++ src/qml/Ubuntu/MediaScanner/AlbumsModel.hh 2014-05-29 09:17:39 +0000 |
981 | @@ -22,6 +22,7 @@ |
982 | |
983 | #include <QString> |
984 | |
985 | +#include <mediascanner/Filter.hh> |
986 | #include "MediaStoreWrapper.hh" |
987 | #include "AlbumModelBase.hh" |
988 | |
989 | @@ -31,8 +32,9 @@ |
990 | class AlbumsModel : public AlbumModelBase { |
991 | Q_OBJECT |
992 | Q_PROPERTY(mediascanner::qml::MediaStoreWrapper* store READ getStore WRITE setStore) |
993 | - Q_PROPERTY(QString artist READ getArtist WRITE setArtist) |
994 | - Q_PROPERTY(QString albumArtist READ getAlbumArtist WRITE setAlbumArtist) |
995 | + Q_PROPERTY(QVariant artist READ getArtist WRITE setArtist) |
996 | + Q_PROPERTY(QVariant albumArtist READ getAlbumArtist WRITE setAlbumArtist) |
997 | + Q_PROPERTY(QVariant genre READ getGenre WRITE setGenre) |
998 | Q_PROPERTY(int limit READ getLimit WRITE setLimit) |
999 | public: |
1000 | explicit AlbumsModel(QObject *parent=0); |
1001 | @@ -40,17 +42,19 @@ |
1002 | MediaStoreWrapper *getStore(); |
1003 | void setStore(MediaStoreWrapper *store); |
1004 | |
1005 | - QString getArtist(); |
1006 | - void setArtist(const QString artist); |
1007 | - QString getAlbumArtist(); |
1008 | - void setAlbumArtist(const QString album_artist); |
1009 | + QVariant getArtist(); |
1010 | + void setArtist(const QVariant artist); |
1011 | + QVariant getAlbumArtist(); |
1012 | + void setAlbumArtist(const QVariant album_artist); |
1013 | + QVariant getGenre(); |
1014 | + void setGenre(const QVariant genre); |
1015 | int getLimit(); |
1016 | void setLimit(int limit); |
1017 | private: |
1018 | void update(); |
1019 | |
1020 | MediaStoreWrapper *store; |
1021 | - QString artist, album_artist; |
1022 | + Filter filter; |
1023 | int limit; |
1024 | }; |
1025 | |
1026 | |
1027 | === modified file 'src/qml/Ubuntu/MediaScanner/ArtistsModel.cc' |
1028 | --- src/qml/Ubuntu/MediaScanner/ArtistsModel.cc 2014-05-06 09:09:49 +0000 |
1029 | +++ src/qml/Ubuntu/MediaScanner/ArtistsModel.cc 2014-05-29 09:17:39 +0000 |
1030 | @@ -75,6 +75,27 @@ |
1031 | } |
1032 | } |
1033 | |
1034 | +QVariant ArtistsModel::getGenre() { |
1035 | + if (!filter.hasGenre()) |
1036 | + return QVariant(); |
1037 | + return QString::fromStdString(filter.getGenre()); |
1038 | +} |
1039 | + |
1040 | +void ArtistsModel::setGenre(const QVariant genre) { |
1041 | + if (genre.isNull()) { |
1042 | + if (filter.hasGenre()) { |
1043 | + filter.unsetGenre(); |
1044 | + update(); |
1045 | + } |
1046 | + } else { |
1047 | + const std::string std_genre = genre.value<QString>().toStdString(); |
1048 | + if (!filter.hasGenre() || filter.getGenre() != std_genre) { |
1049 | + filter.setGenre(std_genre); |
1050 | + update(); |
1051 | + } |
1052 | + } |
1053 | +} |
1054 | + |
1055 | int ArtistsModel::getLimit() { |
1056 | return limit; |
1057 | } |
1058 | @@ -91,8 +112,12 @@ |
1059 | if (store == nullptr) { |
1060 | this->results.clear(); |
1061 | } else { |
1062 | - this->results = store->store.listArtists(album_artists, limit); |
1063 | + if (album_artists) { |
1064 | + this->results = store->store.listAlbumArtists(filter, limit); |
1065 | + } else { |
1066 | + this->results = store->store.listArtists(filter, limit); |
1067 | + } |
1068 | } |
1069 | - this->results = results; |
1070 | endResetModel(); |
1071 | + Q_EMIT rowCountChanged(); |
1072 | } |
1073 | |
1074 | === modified file 'src/qml/Ubuntu/MediaScanner/ArtistsModel.hh' |
1075 | --- src/qml/Ubuntu/MediaScanner/ArtistsModel.hh 2014-05-06 09:09:49 +0000 |
1076 | +++ src/qml/Ubuntu/MediaScanner/ArtistsModel.hh 2014-05-29 09:17:39 +0000 |
1077 | @@ -24,6 +24,7 @@ |
1078 | #include <QAbstractListModel> |
1079 | #include <QString> |
1080 | |
1081 | +#include <mediascanner/Filter.hh> |
1082 | #include "MediaStoreWrapper.hh" |
1083 | |
1084 | namespace mediascanner { |
1085 | @@ -34,8 +35,9 @@ |
1086 | Q_ENUMS(Roles) |
1087 | Q_PROPERTY(mediascanner::qml::MediaStoreWrapper* store READ getStore WRITE setStore) |
1088 | Q_PROPERTY(bool albumArtists READ getAlbumArtists WRITE setAlbumArtists) |
1089 | + Q_PROPERTY(QVariant genre READ getGenre WRITE setGenre) |
1090 | Q_PROPERTY(int limit READ getLimit WRITE setLimit) |
1091 | - Q_PROPERTY(int rowCount READ rowCount) // NOTIFY modelReset |
1092 | + Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged) |
1093 | public: |
1094 | enum Roles { |
1095 | RoleArtist, |
1096 | @@ -46,6 +48,8 @@ |
1097 | QVariant data(const QModelIndex &index, int role) const override; |
1098 | |
1099 | Q_INVOKABLE QVariant get(int row, Roles role) const; |
1100 | +Q_SIGNALS: |
1101 | + void rowCountChanged(); |
1102 | protected: |
1103 | QHash<int, QByteArray> roleNames() const override; |
1104 | |
1105 | @@ -53,6 +57,8 @@ |
1106 | void setStore(MediaStoreWrapper *store); |
1107 | bool getAlbumArtists(); |
1108 | void setAlbumArtists(bool album_artists); |
1109 | + QVariant getGenre(); |
1110 | + void setGenre(QVariant genre); |
1111 | int getLimit(); |
1112 | void setLimit(int limit); |
1113 | |
1114 | @@ -62,6 +68,7 @@ |
1115 | QHash<int, QByteArray> roles; |
1116 | std::vector<std::string> results; |
1117 | MediaStoreWrapper *store; |
1118 | + Filter filter; |
1119 | bool album_artists; |
1120 | int limit; |
1121 | }; |
1122 | |
1123 | === modified file 'src/qml/Ubuntu/MediaScanner/CMakeLists.txt' |
1124 | --- src/qml/Ubuntu/MediaScanner/CMakeLists.txt 2014-05-29 09:17:39 +0000 |
1125 | +++ src/qml/Ubuntu/MediaScanner/CMakeLists.txt 2014-05-29 09:17:39 +0000 |
1126 | @@ -12,6 +12,7 @@ |
1127 | AlbumModelBase.cc |
1128 | AlbumsModel.cc |
1129 | ArtistsModel.cc |
1130 | + GenresModel.cc |
1131 | SongsModel.cc |
1132 | SongsSearchModel.cc |
1133 | ) |
1134 | |
1135 | === added file 'src/qml/Ubuntu/MediaScanner/GenresModel.cc' |
1136 | --- src/qml/Ubuntu/MediaScanner/GenresModel.cc 1970-01-01 00:00:00 +0000 |
1137 | +++ src/qml/Ubuntu/MediaScanner/GenresModel.cc 2014-05-29 09:17:39 +0000 |
1138 | @@ -0,0 +1,86 @@ |
1139 | +/* |
1140 | + * Copyright (C) 2014 Canonical, Ltd. |
1141 | + * |
1142 | + * Authors: |
1143 | + * James Henstridge <james.henstridge@canonical.com> |
1144 | + * |
1145 | + * This program is free software: you can redistribute it and/or modify |
1146 | + * it under the terms of the GNU Lesser General Public License version 3 as |
1147 | + * published by the Free Software Foundation. |
1148 | + * |
1149 | + * This program is distributed in the hope that it will be useful, |
1150 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1151 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1152 | + * GNU Lesser General Public License for more details. |
1153 | + * |
1154 | + * You should have received a copy of the GNU Lesser General Public License |
1155 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1156 | + */ |
1157 | + |
1158 | +#include "GenresModel.hh" |
1159 | + |
1160 | +using namespace mediascanner::qml; |
1161 | + |
1162 | +GenresModel::GenresModel(QObject *parent) |
1163 | + : QAbstractListModel(parent), |
1164 | + store(nullptr), |
1165 | + limit(-1) { |
1166 | + roles[Roles::RoleGenre] = "genre"; |
1167 | +} |
1168 | + |
1169 | +int GenresModel::rowCount(const QModelIndex &) const { |
1170 | + return results.size(); |
1171 | +} |
1172 | + |
1173 | +QVariant GenresModel::data(const QModelIndex &index, int role) const { |
1174 | + if (index.row() < 0 || index.row() >= (ptrdiff_t)results.size()) { |
1175 | + return QVariant(); |
1176 | + } |
1177 | + switch (role) { |
1178 | + case RoleGenre: |
1179 | + return QString::fromStdString(results[index.row()]); |
1180 | + default: |
1181 | + return QVariant(); |
1182 | + } |
1183 | +} |
1184 | + |
1185 | +QVariant GenresModel::get(int row, GenresModel::Roles role) const { |
1186 | + return data(index(row, 0), role); |
1187 | +} |
1188 | + |
1189 | +QHash<int, QByteArray> GenresModel::roleNames() const { |
1190 | + return roles; |
1191 | +} |
1192 | + |
1193 | +MediaStoreWrapper *GenresModel::getStore() { |
1194 | + return store; |
1195 | +} |
1196 | + |
1197 | +void GenresModel::setStore(MediaStoreWrapper *store) { |
1198 | + if (this->store != store) { |
1199 | + this->store = store; |
1200 | + update(); |
1201 | + } |
1202 | +} |
1203 | + |
1204 | +int GenresModel::getLimit() { |
1205 | + return limit; |
1206 | +} |
1207 | + |
1208 | +void GenresModel::setLimit(int limit) { |
1209 | + if (this->limit != limit) { |
1210 | + this->limit = limit; |
1211 | + update(); |
1212 | + } |
1213 | +} |
1214 | + |
1215 | +void GenresModel::update() { |
1216 | + beginResetModel(); |
1217 | + if (store == nullptr) { |
1218 | + this->results.clear(); |
1219 | + } else { |
1220 | + this->results = store->store.listGenres(limit); |
1221 | + } |
1222 | + endResetModel(); |
1223 | + Q_EMIT rowCountChanged(); |
1224 | +} |
1225 | |
1226 | === added file 'src/qml/Ubuntu/MediaScanner/GenresModel.hh' |
1227 | --- src/qml/Ubuntu/MediaScanner/GenresModel.hh 1970-01-01 00:00:00 +0000 |
1228 | +++ src/qml/Ubuntu/MediaScanner/GenresModel.hh 2014-05-29 09:17:39 +0000 |
1229 | @@ -0,0 +1,71 @@ |
1230 | +/* |
1231 | + * Copyright (C) 2014 Canonical, Ltd. |
1232 | + * |
1233 | + * Authors: |
1234 | + * James Henstridge <james.henstridge@canonical.com> |
1235 | + * |
1236 | + * This program is free software: you can redistribute it and/or modify |
1237 | + * it under the terms of the GNU Lesser General Public License version 3 as |
1238 | + * published by the Free Software Foundation. |
1239 | + * |
1240 | + * This program is distributed in the hope that it will be useful, |
1241 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1242 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1243 | + * GNU Lesser General Public License for more details. |
1244 | + * |
1245 | + * You should have received a copy of the GNU Lesser General Public License |
1246 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1247 | + */ |
1248 | + |
1249 | +#ifndef MEDIASCANNER_QML_GENRESMODEL_H |
1250 | +#define MEDIASCANNER_QML_GENRESMODEL_H |
1251 | + |
1252 | +#include <string> |
1253 | +#include <QAbstractListModel> |
1254 | +#include <QString> |
1255 | + |
1256 | +#include <mediascanner/Filter.hh> |
1257 | +#include "MediaStoreWrapper.hh" |
1258 | + |
1259 | +namespace mediascanner { |
1260 | +namespace qml { |
1261 | + |
1262 | +class GenresModel : public QAbstractListModel { |
1263 | + Q_OBJECT |
1264 | + Q_ENUMS(Roles) |
1265 | + Q_PROPERTY(mediascanner::qml::MediaStoreWrapper* store READ getStore WRITE setStore) |
1266 | + Q_PROPERTY(int limit READ getLimit WRITE setLimit) |
1267 | + Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged) |
1268 | +public: |
1269 | + enum Roles { |
1270 | + RoleGenre, |
1271 | + }; |
1272 | + |
1273 | + explicit GenresModel(QObject *parent = 0); |
1274 | + int rowCount(const QModelIndex &parent=QModelIndex()) const override; |
1275 | + QVariant data(const QModelIndex &index, int role) const override; |
1276 | + |
1277 | + Q_INVOKABLE QVariant get(int row, Roles role) const; |
1278 | +Q_SIGNALS: |
1279 | + void rowCountChanged(); |
1280 | +protected: |
1281 | + QHash<int, QByteArray> roleNames() const override; |
1282 | + |
1283 | + MediaStoreWrapper *getStore(); |
1284 | + void setStore(MediaStoreWrapper *store); |
1285 | + int getLimit(); |
1286 | + void setLimit(int limit); |
1287 | + |
1288 | +private: |
1289 | + void update(); |
1290 | + |
1291 | + QHash<int, QByteArray> roles; |
1292 | + std::vector<std::string> results; |
1293 | + MediaStoreWrapper *store; |
1294 | + int limit; |
1295 | +}; |
1296 | + |
1297 | +} |
1298 | +} |
1299 | + |
1300 | +#endif |
1301 | |
1302 | === modified file 'src/qml/Ubuntu/MediaScanner/MediaFileModelBase.cc' |
1303 | --- src/qml/Ubuntu/MediaScanner/MediaFileModelBase.cc 2014-05-06 09:09:49 +0000 |
1304 | +++ src/qml/Ubuntu/MediaScanner/MediaFileModelBase.cc 2014-05-29 09:17:39 +0000 |
1305 | @@ -99,4 +99,5 @@ |
1306 | beginResetModel(); |
1307 | this->results = results; |
1308 | endResetModel(); |
1309 | + Q_EMIT rowCountChanged(); |
1310 | } |
1311 | |
1312 | === modified file 'src/qml/Ubuntu/MediaScanner/MediaFileModelBase.hh' |
1313 | --- src/qml/Ubuntu/MediaScanner/MediaFileModelBase.hh 2014-05-06 09:09:49 +0000 |
1314 | +++ src/qml/Ubuntu/MediaScanner/MediaFileModelBase.hh 2014-05-29 09:17:39 +0000 |
1315 | @@ -31,7 +31,7 @@ |
1316 | class MediaFileModelBase : public QAbstractListModel { |
1317 | Q_OBJECT |
1318 | Q_ENUMS(Roles) |
1319 | - Q_PROPERTY(int rowCount READ rowCount) // NOTIFY modelReset |
1320 | + Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged) |
1321 | public: |
1322 | enum Roles { |
1323 | RoleModelData, |
1324 | @@ -56,6 +56,8 @@ |
1325 | QVariant data(const QModelIndex &index, int role) const override; |
1326 | |
1327 | Q_INVOKABLE QVariant get(int row, Roles role) const; |
1328 | +Q_SIGNALS: |
1329 | + void rowCountChanged(); |
1330 | protected: |
1331 | QHash<int, QByteArray> roleNames() const override; |
1332 | void updateResults(const std::vector<mediascanner::MediaFile> &results); |
1333 | |
1334 | === modified file 'src/qml/Ubuntu/MediaScanner/SongsModel.cc' |
1335 | --- src/qml/Ubuntu/MediaScanner/SongsModel.cc 2014-02-27 05:37:36 +0000 |
1336 | +++ src/qml/Ubuntu/MediaScanner/SongsModel.cc 2014-05-29 09:17:39 +0000 |
1337 | @@ -24,9 +24,6 @@ |
1338 | SongsModel::SongsModel(QObject *parent) |
1339 | : MediaFileModelBase(parent), |
1340 | store(nullptr), |
1341 | - artist(""), |
1342 | - album(""), |
1343 | - album_artist(""), |
1344 | limit(-1) { |
1345 | } |
1346 | |
1347 | @@ -41,36 +38,87 @@ |
1348 | } |
1349 | } |
1350 | |
1351 | -QString SongsModel::getArtist() { |
1352 | - return artist; |
1353 | -} |
1354 | - |
1355 | -void SongsModel::setArtist(const QString artist) { |
1356 | - if (this->artist != artist) { |
1357 | - this->artist = artist; |
1358 | - update(); |
1359 | - } |
1360 | -} |
1361 | - |
1362 | -QString SongsModel::getAlbum() { |
1363 | - return album; |
1364 | -} |
1365 | - |
1366 | -void SongsModel::setAlbum(const QString album) { |
1367 | - if (this->album != album) { |
1368 | - this->album = album; |
1369 | - update(); |
1370 | - } |
1371 | -} |
1372 | - |
1373 | -QString SongsModel::getAlbumArtist() { |
1374 | - return album_artist; |
1375 | -} |
1376 | - |
1377 | -void SongsModel::setAlbumArtist(const QString album_artist) { |
1378 | - if (this->album_artist != album_artist) { |
1379 | - this->album_artist = album_artist; |
1380 | - update(); |
1381 | +QVariant SongsModel::getArtist() { |
1382 | + if (!filter.hasArtist()) |
1383 | + return QVariant(); |
1384 | + return QString::fromStdString(filter.getArtist()); |
1385 | +} |
1386 | + |
1387 | +void SongsModel::setArtist(const QVariant artist) { |
1388 | + if (artist.isNull()) { |
1389 | + if (filter.hasArtist()) { |
1390 | + filter.unsetArtist(); |
1391 | + update(); |
1392 | + } |
1393 | + } else { |
1394 | + const std::string std_artist = artist.value<QString>().toStdString(); |
1395 | + if (!filter.hasArtist() || filter.getArtist() != std_artist) { |
1396 | + filter.setArtist(std_artist); |
1397 | + update(); |
1398 | + } |
1399 | + } |
1400 | +} |
1401 | + |
1402 | +QVariant SongsModel::getAlbum() { |
1403 | + if (!filter.hasAlbum()) |
1404 | + return QVariant(); |
1405 | + return QString::fromStdString(filter.getAlbum()); |
1406 | +} |
1407 | + |
1408 | +void SongsModel::setAlbum(const QVariant album) { |
1409 | + if (album.isNull()) { |
1410 | + if (filter.hasAlbum()) { |
1411 | + filter.unsetAlbum(); |
1412 | + update(); |
1413 | + } |
1414 | + } else { |
1415 | + const std::string std_album = album.value<QString>().toStdString(); |
1416 | + if (!filter.hasAlbum() || filter.getAlbum() != std_album) { |
1417 | + filter.setAlbum(std_album); |
1418 | + update(); |
1419 | + } |
1420 | + } |
1421 | +} |
1422 | + |
1423 | +QVariant SongsModel::getAlbumArtist() { |
1424 | + if (!filter.hasAlbumArtist()) |
1425 | + return QVariant(); |
1426 | + return QString::fromStdString(filter.getAlbumArtist()); |
1427 | +} |
1428 | + |
1429 | +void SongsModel::setAlbumArtist(const QVariant album_artist) { |
1430 | + if (album_artist.isNull()) { |
1431 | + if (filter.hasAlbumArtist()) { |
1432 | + filter.unsetAlbumArtist(); |
1433 | + update(); |
1434 | + } |
1435 | + } else { |
1436 | + const std::string std_album_artist = album_artist.value<QString>().toStdString(); |
1437 | + if (!filter.hasAlbumArtist() || filter.getAlbumArtist() != std_album_artist) { |
1438 | + filter.setAlbumArtist(std_album_artist); |
1439 | + update(); |
1440 | + } |
1441 | + } |
1442 | +} |
1443 | + |
1444 | +QVariant SongsModel::getGenre() { |
1445 | + if (!filter.hasGenre()) |
1446 | + return QVariant(); |
1447 | + return QString::fromStdString(filter.getGenre()); |
1448 | +} |
1449 | + |
1450 | +void SongsModel::setGenre(const QVariant genre) { |
1451 | + if (genre.isNull()) { |
1452 | + if (filter.hasGenre()) { |
1453 | + filter.unsetGenre(); |
1454 | + update(); |
1455 | + } |
1456 | + } else { |
1457 | + const std::string std_genre = genre.value<QString>().toStdString(); |
1458 | + if (!filter.hasGenre() || filter.getGenre() != std_genre) { |
1459 | + filter.setGenre(std_genre); |
1460 | + update(); |
1461 | + } |
1462 | } |
1463 | } |
1464 | |
1465 | @@ -89,6 +137,6 @@ |
1466 | if (store == nullptr) { |
1467 | updateResults(std::vector<mediascanner::MediaFile>()); |
1468 | } else { |
1469 | - updateResults(store->store.listSongs(artist.toStdString(), album.toStdString(), album_artist.toStdString(), limit)); |
1470 | + updateResults(store->store.listSongs(filter, limit)); |
1471 | } |
1472 | } |
1473 | |
1474 | === modified file 'src/qml/Ubuntu/MediaScanner/SongsModel.hh' |
1475 | --- src/qml/Ubuntu/MediaScanner/SongsModel.hh 2014-02-27 05:37:36 +0000 |
1476 | +++ src/qml/Ubuntu/MediaScanner/SongsModel.hh 2014-05-29 09:17:39 +0000 |
1477 | @@ -22,6 +22,7 @@ |
1478 | |
1479 | #include <QString> |
1480 | |
1481 | +#include <mediascanner/Filter.hh> |
1482 | #include "MediaStoreWrapper.hh" |
1483 | #include "MediaFileModelBase.hh" |
1484 | |
1485 | @@ -31,9 +32,10 @@ |
1486 | class SongsModel : public MediaFileModelBase { |
1487 | Q_OBJECT |
1488 | Q_PROPERTY(mediascanner::qml::MediaStoreWrapper* store READ getStore WRITE setStore) |
1489 | - Q_PROPERTY(QString artist READ getArtist WRITE setArtist) |
1490 | - Q_PROPERTY(QString album READ getAlbum WRITE setAlbum) |
1491 | - Q_PROPERTY(QString albumArtist READ getAlbumArtist WRITE setAlbumArtist) |
1492 | + Q_PROPERTY(QVariant artist READ getArtist WRITE setArtist) |
1493 | + Q_PROPERTY(QVariant album READ getAlbum WRITE setAlbum) |
1494 | + Q_PROPERTY(QVariant albumArtist READ getAlbumArtist WRITE setAlbumArtist) |
1495 | + Q_PROPERTY(QVariant genre READ getGenre WRITE setGenre) |
1496 | Q_PROPERTY(int limit READ getLimit WRITE setLimit) |
1497 | public: |
1498 | explicit SongsModel(QObject *parent=0); |
1499 | @@ -41,19 +43,21 @@ |
1500 | MediaStoreWrapper *getStore(); |
1501 | void setStore(MediaStoreWrapper *store); |
1502 | |
1503 | - QString getArtist(); |
1504 | - void setArtist(const QString artist); |
1505 | - QString getAlbum(); |
1506 | - void setAlbum(const QString album); |
1507 | - QString getAlbumArtist(); |
1508 | - void setAlbumArtist(const QString album_artist); |
1509 | + QVariant getArtist(); |
1510 | + void setArtist(const QVariant artist); |
1511 | + QVariant getAlbum(); |
1512 | + void setAlbum(const QVariant album); |
1513 | + QVariant getAlbumArtist(); |
1514 | + void setAlbumArtist(const QVariant album_artist); |
1515 | + QVariant getGenre(); |
1516 | + void setGenre(const QVariant genre); |
1517 | int getLimit(); |
1518 | void setLimit(int limit); |
1519 | private: |
1520 | void update(); |
1521 | |
1522 | MediaStoreWrapper *store; |
1523 | - QString artist, album, album_artist; |
1524 | + Filter filter; |
1525 | int limit; |
1526 | }; |
1527 | |
1528 | |
1529 | === modified file 'src/qml/Ubuntu/MediaScanner/plugin.cc' |
1530 | --- src/qml/Ubuntu/MediaScanner/plugin.cc 2014-02-28 07:01:44 +0000 |
1531 | +++ src/qml/Ubuntu/MediaScanner/plugin.cc 2014-05-29 09:17:39 +0000 |
1532 | @@ -22,6 +22,7 @@ |
1533 | #include "MediaStoreWrapper.hh" |
1534 | #include "AlbumsModel.hh" |
1535 | #include "ArtistsModel.hh" |
1536 | +#include "GenresModel.hh" |
1537 | #include "SongsModel.hh" |
1538 | #include "SongsSearchModel.hh" |
1539 | |
1540 | @@ -33,6 +34,7 @@ |
1541 | "Use a MediaStore to retrieve MediaFiles"); |
1542 | qmlRegisterType<AlbumsModel>(uri, 0, 1, "AlbumsModel"); |
1543 | qmlRegisterType<ArtistsModel>(uri, 0, 1, "ArtistsModel"); |
1544 | + qmlRegisterType<GenresModel>(uri, 0, 1, "GenresModel"); |
1545 | qmlRegisterType<SongsModel>(uri, 0, 1, "SongsModel"); |
1546 | qmlRegisterType<SongsSearchModel>(uri, 0, 1, "SongsSearchModel"); |
1547 | } |
1548 | |
1549 | === modified file 'src/qml/Ubuntu/MediaScanner/plugin.qmltypes' |
1550 | --- src/qml/Ubuntu/MediaScanner/plugin.qmltypes 2014-05-08 07:17:11 +0000 |
1551 | +++ src/qml/Ubuntu/MediaScanner/plugin.qmltypes 2014-05-29 09:17:39 +0000 |
1552 | @@ -32,8 +32,9 @@ |
1553 | exports: ["Ubuntu.MediaScanner/AlbumsModel 0.1"] |
1554 | exportMetaObjectRevisions: [0] |
1555 | Property { name: "store"; type: "mediascanner::qml::MediaStoreWrapper"; isPointer: true } |
1556 | - Property { name: "artist"; type: "string" } |
1557 | - Property { name: "albumArtist"; type: "string" } |
1558 | + Property { name: "artist"; type: "QVariant" } |
1559 | + Property { name: "albumArtist"; type: "QVariant" } |
1560 | + Property { name: "genre"; type: "QVariant" } |
1561 | Property { name: "limit"; type: "int" } |
1562 | } |
1563 | Component { |
1564 | @@ -49,6 +50,28 @@ |
1565 | } |
1566 | Property { name: "store"; type: "mediascanner::qml::MediaStoreWrapper"; isPointer: true } |
1567 | Property { name: "albumArtists"; type: "bool" } |
1568 | + Property { name: "genre"; type: "QVariant" } |
1569 | + Property { name: "limit"; type: "int" } |
1570 | + Property { name: "rowCount"; type: "int"; isReadonly: true } |
1571 | + Method { |
1572 | + name: "get" |
1573 | + type: "QVariant" |
1574 | + Parameter { name: "row"; type: "int" } |
1575 | + Parameter { name: "role"; type: "Roles" } |
1576 | + } |
1577 | + } |
1578 | + Component { |
1579 | + name: "mediascanner::qml::GenresModel" |
1580 | + prototype: "QAbstractListModel" |
1581 | + exports: ["Ubuntu.MediaScanner/GenresModel 0.1"] |
1582 | + exportMetaObjectRevisions: [0] |
1583 | + Enum { |
1584 | + name: "Roles" |
1585 | + values: { |
1586 | + "RoleGenre": 0 |
1587 | + } |
1588 | + } |
1589 | + Property { name: "store"; type: "mediascanner::qml::MediaStoreWrapper"; isPointer: true } |
1590 | Property { name: "limit"; type: "int" } |
1591 | Property { name: "rowCount"; type: "int"; isReadonly: true } |
1592 | Method { |
1593 | @@ -140,9 +163,10 @@ |
1594 | exports: ["Ubuntu.MediaScanner/SongsModel 0.1"] |
1595 | exportMetaObjectRevisions: [0] |
1596 | Property { name: "store"; type: "mediascanner::qml::MediaStoreWrapper"; isPointer: true } |
1597 | - Property { name: "artist"; type: "string" } |
1598 | - Property { name: "album"; type: "string" } |
1599 | - Property { name: "albumArtist"; type: "string" } |
1600 | + Property { name: "artist"; type: "QVariant" } |
1601 | + Property { name: "album"; type: "QVariant" } |
1602 | + Property { name: "albumArtist"; type: "QVariant" } |
1603 | + Property { name: "genre"; type: "QVariant" } |
1604 | Property { name: "limit"; type: "int" } |
1605 | } |
1606 | Component { |
1607 | |
1608 | === modified file 'src/qml/Ubuntu/MediaScanner/qmldir' |
1609 | --- src/qml/Ubuntu/MediaScanner/qmldir 2014-01-02 07:48:47 +0000 |
1610 | +++ src/qml/Ubuntu/MediaScanner/qmldir 2014-05-29 09:17:39 +0000 |
1611 | @@ -1,2 +1,3 @@ |
1612 | module Ubuntu.MediaScanner |
1613 | plugin mediascanner-qml |
1614 | +typeinfo plugin.qmltypes |
1615 | |
1616 | === added file 'test/qml/tst_albumsmodel.qml' |
1617 | --- test/qml/tst_albumsmodel.qml 1970-01-01 00:00:00 +0000 |
1618 | +++ test/qml/tst_albumsmodel.qml 2014-05-29 09:17:39 +0000 |
1619 | @@ -0,0 +1,91 @@ |
1620 | +import QtQuick 2.0 |
1621 | +import QtTest 1.0 |
1622 | +import Ubuntu.MediaScanner 0.1 |
1623 | + |
1624 | +Item { |
1625 | + id: root |
1626 | + |
1627 | + MediaStore { |
1628 | + id: store |
1629 | + } |
1630 | + |
1631 | + AlbumsModel { |
1632 | + id: model |
1633 | + store: store |
1634 | + } |
1635 | + |
1636 | + TestCase { |
1637 | + name: "AlbumsModelTests" |
1638 | + |
1639 | + function cleanup() { |
1640 | + model.artist = undefined; |
1641 | + model.albumArtist = undefined; |
1642 | + model.genre = undefined; |
1643 | + model.limit = -1; |
1644 | + } |
1645 | + |
1646 | + function test_initial_state() { |
1647 | + compare(model.artist, undefined); |
1648 | + compare(model.albumArtist, undefined); |
1649 | + compare(model.genre, undefined); |
1650 | + compare(model.limit, -1); |
1651 | + |
1652 | + compare(model.rowCount, 4); |
1653 | + compare(model.get(0, AlbumsModel.RoleTitle), "Ivy and the Big Apples"); |
1654 | + compare(model.get(0, AlbumsModel.RoleArtist), "Spiderbait"); |
1655 | + |
1656 | + compare(model.get(1, AlbumsModel.RoleTitle), "Spiderbait"); |
1657 | + compare(model.get(1, AlbumsModel.RoleArtist), "Spiderbait"); |
1658 | + |
1659 | + compare(model.get(2, AlbumsModel.RoleTitle), "April Uprising"); |
1660 | + compare(model.get(2, AlbumsModel.RoleArtist), "The John Butler Trio"); |
1661 | + |
1662 | + compare(model.get(3, AlbumsModel.RoleTitle), "Sunrise Over Sea"); |
1663 | + compare(model.get(3, AlbumsModel.RoleArtist), "The John Butler Trio"); |
1664 | + } |
1665 | + |
1666 | + function test_limit() { |
1667 | + model.limit = 2; |
1668 | + compare(model.rowCount, 2); |
1669 | + |
1670 | + model.limit = 42; |
1671 | + compare(model.rowCount, 4); |
1672 | + |
1673 | + model.limit = -1; |
1674 | + compare(model.rowCount, 4); |
1675 | + } |
1676 | + |
1677 | + function test_artist() { |
1678 | + model.artist = "The John Butler Trio"; |
1679 | + compare(model.rowCount, 2); |
1680 | + |
1681 | + compare(model.get(0, AlbumsModel.RoleTitle), "April Uprising"); |
1682 | + compare(model.get(0, AlbumsModel.RoleArtist), "The John Butler Trio"); |
1683 | + |
1684 | + model.artist = "unknown"; |
1685 | + compare(model.rowCount, 0); |
1686 | + } |
1687 | + |
1688 | + function test_album_artist() { |
1689 | + model.albumArtist = "The John Butler Trio"; |
1690 | + compare(model.rowCount, 2); |
1691 | + |
1692 | + compare(model.get(0, AlbumsModel.RoleTitle), "April Uprising"); |
1693 | + compare(model.get(0, AlbumsModel.RoleArtist), "The John Butler Trio"); |
1694 | + |
1695 | + model.albumArtist = "unknown"; |
1696 | + compare(model.rowCount, 0); |
1697 | + } |
1698 | + |
1699 | + function test_genre() { |
1700 | + model.genre = "rock"; |
1701 | + compare(model.rowCount, 2); |
1702 | + compare(model.get(0, AlbumsModel.RoleTitle), "Ivy and the Big Apples"); |
1703 | + compare(model.get(1, AlbumsModel.RoleTitle), "Spiderbait"); |
1704 | + |
1705 | + model.genre = "unknown"; |
1706 | + compare(model.rowCount, 0); |
1707 | + } |
1708 | + |
1709 | + } |
1710 | +} |
1711 | |
1712 | === added file 'test/qml/tst_artistsmodel.qml' |
1713 | --- test/qml/tst_artistsmodel.qml 1970-01-01 00:00:00 +0000 |
1714 | +++ test/qml/tst_artistsmodel.qml 2014-05-29 09:17:39 +0000 |
1715 | @@ -0,0 +1,68 @@ |
1716 | +import QtQuick 2.0 |
1717 | +import QtTest 1.0 |
1718 | +import Ubuntu.MediaScanner 0.1 |
1719 | + |
1720 | +Item { |
1721 | + id: root |
1722 | + |
1723 | + MediaStore { |
1724 | + id: store |
1725 | + } |
1726 | + |
1727 | + ArtistsModel { |
1728 | + id: model |
1729 | + store: store |
1730 | + } |
1731 | + |
1732 | + TestCase { |
1733 | + name: "ArtistsModelTests" |
1734 | + |
1735 | + function cleanup() { |
1736 | + model.albumArtists = false; |
1737 | + model.genre = undefined; |
1738 | + model.limit = -1; |
1739 | + } |
1740 | + |
1741 | + function test_initial_state() { |
1742 | + compare(model.albumArtists, false); |
1743 | + compare(model.limit, -1); |
1744 | + |
1745 | + compare(model.rowCount, 2); |
1746 | + compare(model.get(0, ArtistsModel.RoleArtist), "Spiderbait"); |
1747 | + compare(model.get(1, ArtistsModel.RoleArtist), "The John Butler Trio"); |
1748 | + } |
1749 | + |
1750 | + function test_limit() { |
1751 | + model.limit = 1; |
1752 | + compare(model.rowCount, 1); |
1753 | + |
1754 | + model.limit = 42; |
1755 | + compare(model.rowCount, 2); |
1756 | + |
1757 | + model.limit = -1; |
1758 | + compare(model.rowCount, 2); |
1759 | + } |
1760 | + |
1761 | + function test_album_artists() { |
1762 | + model.albumArtists = true; |
1763 | + compare(model.rowCount, 2); |
1764 | + |
1765 | + compare(model.get(0, ArtistsModel.RoleArtist), "Spiderbait"); |
1766 | + compare(model.get(1, ArtistsModel.RoleArtist), "The John Butler Trio"); |
1767 | + } |
1768 | + |
1769 | + function test_genre() { |
1770 | + model.genre = "rock"; |
1771 | + compare(model.rowCount, 1); |
1772 | + compare(model.get(0, ArtistsModel.RoleArtist), "Spiderbait"); |
1773 | + |
1774 | + model.genre = "roots"; |
1775 | + compare(model.rowCount, 1); |
1776 | + compare(model.get(0, ArtistsModel.RoleArtist), "The John Butler Trio"); |
1777 | + |
1778 | + model.genre = "unknown"; |
1779 | + compare(model.rowCount, 0); |
1780 | + } |
1781 | + |
1782 | + } |
1783 | +} |
1784 | |
1785 | === added file 'test/qml/tst_genresmodel.qml' |
1786 | --- test/qml/tst_genresmodel.qml 1970-01-01 00:00:00 +0000 |
1787 | +++ test/qml/tst_genresmodel.qml 2014-05-29 09:17:39 +0000 |
1788 | @@ -0,0 +1,44 @@ |
1789 | +import QtQuick 2.0 |
1790 | +import QtTest 1.0 |
1791 | +import Ubuntu.MediaScanner 0.1 |
1792 | + |
1793 | +Item { |
1794 | + id: root |
1795 | + |
1796 | + MediaStore { |
1797 | + id: store |
1798 | + } |
1799 | + |
1800 | + GenresModel { |
1801 | + id: model |
1802 | + store: store |
1803 | + } |
1804 | + |
1805 | + TestCase { |
1806 | + name: "GenresModelTests" |
1807 | + |
1808 | + function cleanup() { |
1809 | + model.limit = -1; |
1810 | + } |
1811 | + |
1812 | + function test_initial_state() { |
1813 | + compare(model.limit, -1); |
1814 | + |
1815 | + compare(model.rowCount, 2); |
1816 | + compare(model.get(0, ArtistsModel.RoleGenre), "rock"); |
1817 | + compare(model.get(1, ArtistsModel.RoleGenre), "roots"); |
1818 | + } |
1819 | + |
1820 | + function test_limit() { |
1821 | + model.limit = 1; |
1822 | + compare(model.rowCount, 1); |
1823 | + |
1824 | + model.limit = 42; |
1825 | + compare(model.rowCount, 2); |
1826 | + |
1827 | + model.limit = -1; |
1828 | + compare(model.rowCount, 2); |
1829 | + } |
1830 | + |
1831 | + } |
1832 | +} |
1833 | |
1834 | === added file 'test/qml/tst_songsmodel.qml' |
1835 | --- test/qml/tst_songsmodel.qml 1970-01-01 00:00:00 +0000 |
1836 | +++ test/qml/tst_songsmodel.qml 2014-05-29 09:17:39 +0000 |
1837 | @@ -0,0 +1,104 @@ |
1838 | +import QtQuick 2.0 |
1839 | +import QtTest 1.0 |
1840 | +import Ubuntu.MediaScanner 0.1 |
1841 | + |
1842 | +Item { |
1843 | + id: root |
1844 | + |
1845 | + MediaStore { |
1846 | + id: store |
1847 | + } |
1848 | + |
1849 | + SongsModel { |
1850 | + id: model |
1851 | + store: store |
1852 | + } |
1853 | + |
1854 | + TestCase { |
1855 | + name: "SongsModelTests" |
1856 | + |
1857 | + function cleanup() { |
1858 | + model.artist = undefined; |
1859 | + model.albumArtist = undefined; |
1860 | + model.album = undefined; |
1861 | + model.genre = undefined; |
1862 | + model.limit = -1; |
1863 | + } |
1864 | + |
1865 | + function test_initial_state() { |
1866 | + compare(model.artist, undefined); |
1867 | + compare(model.albumArtist, undefined); |
1868 | + compare(model.album, undefined); |
1869 | + compare(model.limit, -1); |
1870 | + |
1871 | + compare(model.rowCount, 7); |
1872 | + compare(model.get(0, SongsModel.RoleTitle), "Buy Me a Pony") |
1873 | + compare(model.get(0, SongsModel.RoleAlbum), "Ivy and the Big Apples"); |
1874 | + compare(model.get(0, SongsModel.RoleAuthor), "Spiderbait"); |
1875 | + |
1876 | + compare(model.get(1, SongsModel.RoleTitle), "Straight Through The Sun"); |
1877 | + compare(model.get(2, SongsModel.RoleTitle), "It's Beautiful"); |
1878 | + compare(model.get(3, SongsModel.RoleTitle), "Revolution"); |
1879 | + compare(model.get(4, SongsModel.RoleTitle), "One Way Road"); |
1880 | + compare(model.get(5, SongsModel.RoleTitle), "Peaches & Cream"); |
1881 | + compare(model.get(6, SongsModel.RoleTitle), "Zebra"); |
1882 | + } |
1883 | + |
1884 | + function test_limit() { |
1885 | + model.limit = 2; |
1886 | + compare(model.rowCount, 2); |
1887 | + |
1888 | + model.limit = 42; |
1889 | + compare(model.rowCount, 7); |
1890 | + |
1891 | + model.limit = -1; |
1892 | + compare(model.rowCount, 7); |
1893 | + } |
1894 | + |
1895 | + function test_artist() { |
1896 | + model.artist = "The John Butler Trio"; |
1897 | + compare(model.rowCount, 4); |
1898 | + |
1899 | + compare(model.get(0, SongsModel.RoleTitle), "Revolution"); |
1900 | + compare(model.get(0, SongsModel.RoleAuthor), "The John Butler Trio"); |
1901 | + |
1902 | + model.artist = "unknown"; |
1903 | + compare(model.rowCount, 0); |
1904 | + } |
1905 | + |
1906 | + function test_album_artist() { |
1907 | + model.albumArtist = "The John Butler Trio"; |
1908 | + compare(model.rowCount, 4); |
1909 | + |
1910 | + compare(model.get(0, SongsModel.RoleTitle), "Revolution"); |
1911 | + compare(model.get(0, SongsModel.RoleAuthor), "The John Butler Trio"); |
1912 | + |
1913 | + model.albumArtist = "unknown"; |
1914 | + compare(model.rowCount, 0); |
1915 | + } |
1916 | + |
1917 | + function test_album() { |
1918 | + model.album = "Sunrise Over Sea"; |
1919 | + compare(model.rowCount, 2); |
1920 | + |
1921 | + compare(model.get(0, SongsModel.RoleTitle), "Peaches & Cream"); |
1922 | + compare(model.get(0, SongsModel.RoleAuthor), "The John Butler Trio"); |
1923 | + |
1924 | + model.albumArtist = "unknown"; |
1925 | + compare(model.rowCount, 0); |
1926 | + } |
1927 | + |
1928 | + function test_genre() { |
1929 | + model.genre = "rock"; |
1930 | + compare(model.rowCount, 3); |
1931 | + |
1932 | + compare(model.get(0, SongsModel.RoleTitle), "Buy Me a Pony"); |
1933 | + compare(model.get(1, SongsModel.RoleTitle), "Straight Through The Sun"); |
1934 | + compare(model.get(2, SongsModel.RoleTitle), "It's Beautiful"); |
1935 | + |
1936 | + model.albumArtist = "unknown"; |
1937 | + compare(model.rowCount, 0); |
1938 | + } |
1939 | + |
1940 | + } |
1941 | +} |
1942 | |
1943 | === modified file 'test/test_dbus.cc' |
1944 | --- test/test_dbus.cc 2014-05-29 09:17:39 +0000 |
1945 | +++ test/test_dbus.cc 2014-05-29 09:17:39 +0000 |
1946 | @@ -6,18 +6,24 @@ |
1947 | #include <mediascanner/Album.hh> |
1948 | #include <mediascanner/MediaFile.hh> |
1949 | #include <mediascanner/MediaFileBuilder.hh> |
1950 | +#include <mediascanner/Filter.hh> |
1951 | #include <ms-dbus/dbus-codec.hh> |
1952 | |
1953 | class MediaStoreDBusTests : public ::testing::Test { |
1954 | +protected: |
1955 | + virtual void SetUp() override { |
1956 | + ::testing::Test::SetUp(); |
1957 | + message = core::dbus::Message::make_method_call( |
1958 | + "org.example.Name", |
1959 | + core::dbus::types::ObjectPath("/org/example/Path"), |
1960 | + "org.example.Interface", |
1961 | + "Method"); |
1962 | + } |
1963 | + |
1964 | + core::dbus::Message::Ptr message; |
1965 | }; |
1966 | |
1967 | TEST_F(MediaStoreDBusTests, mediafile_codec) { |
1968 | - auto message = core::dbus::Message::make_method_call( |
1969 | - "org.example.Name", |
1970 | - core::dbus::types::ObjectPath("/org/example/Path"), |
1971 | - "org.example.Interface", |
1972 | - "Method"); |
1973 | - |
1974 | mediascanner::MediaFile media = mediascanner::MediaFileBuilder("a") |
1975 | .setContentType("type") |
1976 | .setETag("etag") |
1977 | @@ -42,12 +48,6 @@ |
1978 | } |
1979 | |
1980 | TEST_F(MediaStoreDBusTests, album_codec) { |
1981 | - auto message = core::dbus::Message::make_method_call( |
1982 | - "org.example.Name", |
1983 | - core::dbus::types::ObjectPath("/org/example/Path"), |
1984 | - "org.example.Interface", |
1985 | - "Method"); |
1986 | - |
1987 | mediascanner::Album album("title", "artist"); |
1988 | message->writer() << album; |
1989 | |
1990 | @@ -61,6 +61,33 @@ |
1991 | EXPECT_EQ(album, album2); |
1992 | } |
1993 | |
1994 | +TEST_F(MediaStoreDBusTests, filter_codec) { |
1995 | + mediascanner::Filter filter; |
1996 | + filter.setArtist("Artist1"); |
1997 | + filter.setAlbum("Album1"); |
1998 | + filter.setAlbumArtist("AlbumArtist1"); |
1999 | + filter.setGenre("Genre"); |
2000 | + message->writer() << filter; |
2001 | + |
2002 | + EXPECT_EQ("a{ss}", message->signature()); |
2003 | + EXPECT_EQ(core::dbus::helper::TypeMapper<mediascanner::Filter>::signature(), message->signature()); |
2004 | + |
2005 | + mediascanner::Filter other; |
2006 | + message->reader() >> other; |
2007 | + EXPECT_EQ(filter, other); |
2008 | +} |
2009 | + |
2010 | +TEST_F(MediaStoreDBusTests, filter_codec_empty) { |
2011 | + mediascanner::Filter empty; |
2012 | + message->writer() << empty; |
2013 | + |
2014 | + EXPECT_EQ("a{ss}", message->signature()); |
2015 | + |
2016 | + mediascanner::Filter other; |
2017 | + message->reader() >> other; |
2018 | + EXPECT_EQ(empty, other); |
2019 | +} |
2020 | + |
2021 | int main(int argc, char **argv) { |
2022 | ::testing::InitGoogleTest(&argc, argv); |
2023 | return RUN_ALL_TESTS(); |
2024 | |
2025 | === modified file 'test/test_mediastore.cc' |
2026 | --- test/test_mediastore.cc 2014-05-29 09:17:39 +0000 |
2027 | +++ test/test_mediastore.cc 2014-05-29 09:17:39 +0000 |
2028 | @@ -20,6 +20,7 @@ |
2029 | #include <mediascanner/MediaFile.hh> |
2030 | #include <mediascanner/MediaFileBuilder.hh> |
2031 | #include <mediascanner/Album.hh> |
2032 | +#include <mediascanner/Filter.hh> |
2033 | #include <mediascanner/MediaStore.hh> |
2034 | #include <mediascanner/internal/utils.hh> |
2035 | |
2036 | @@ -617,34 +618,56 @@ |
2037 | store.insert(audio5); |
2038 | store.insert(audio6); |
2039 | |
2040 | - vector<MediaFile> tracks = store.listSongs(); |
2041 | + Filter filter; |
2042 | + vector<MediaFile> tracks = store.listSongs(filter); |
2043 | ASSERT_EQ(6, tracks.size()); |
2044 | EXPECT_EQ("TitleOne", tracks[0].getTitle()); |
2045 | |
2046 | // Apply a limit |
2047 | - tracks = store.listSongs("", "", "", 4); |
2048 | + tracks = store.listSongs(filter, 4); |
2049 | EXPECT_EQ(4, tracks.size()); |
2050 | |
2051 | // List songs by artist |
2052 | - tracks = store.listSongs("ArtistOne"); |
2053 | + filter.setArtist("ArtistOne"); |
2054 | + tracks = store.listSongs(filter); |
2055 | EXPECT_EQ(4, tracks.size()); |
2056 | |
2057 | // List songs by album |
2058 | - tracks = store.listSongs("", "AlbumOne"); |
2059 | + filter.clear(); |
2060 | + filter.setAlbum("AlbumOne"); |
2061 | + tracks = store.listSongs(filter); |
2062 | EXPECT_EQ(2, tracks.size()); |
2063 | |
2064 | // List songs by album artist |
2065 | - tracks = store.listSongs("", "", "Various Artists"); |
2066 | + filter.clear(); |
2067 | + filter.setAlbumArtist("Various Artists"); |
2068 | + tracks = store.listSongs(filter); |
2069 | EXPECT_EQ(2, tracks.size()); |
2070 | |
2071 | // Combinations |
2072 | - tracks = store.listSongs("ArtistOne", "AlbumOne", ""); |
2073 | - EXPECT_EQ(2, tracks.size()); |
2074 | - tracks = store.listSongs("", "AlbumOne", "ArtistOne"); |
2075 | - EXPECT_EQ(2, tracks.size()); |
2076 | - tracks = store.listSongs("ArtistOne", "AlbumOne", "ArtistOne"); |
2077 | - EXPECT_EQ(2, tracks.size()); |
2078 | - tracks = store.listSongs("ArtistOne", "", "ArtistOne"); |
2079 | + filter.clear(); |
2080 | + filter.setArtist("ArtistOne"); |
2081 | + filter.setAlbum("AlbumOne"); |
2082 | + tracks = store.listSongs(filter); |
2083 | + EXPECT_EQ(2, tracks.size()); |
2084 | + |
2085 | + filter.clear(); |
2086 | + filter.setAlbum("AlbumOne"); |
2087 | + filter.setAlbumArtist("ArtistOne"); |
2088 | + tracks = store.listSongs(filter); |
2089 | + EXPECT_EQ(2, tracks.size()); |
2090 | + |
2091 | + filter.clear(); |
2092 | + filter.setArtist("ArtistOne"); |
2093 | + filter.setAlbum("AlbumOne"); |
2094 | + filter.setAlbumArtist("ArtistOne"); |
2095 | + tracks = store.listSongs(filter); |
2096 | + EXPECT_EQ(2, tracks.size()); |
2097 | + |
2098 | + filter.clear(); |
2099 | + filter.setArtist("ArtistOne"); |
2100 | + filter.setAlbumArtist("ArtistOne"); |
2101 | + tracks = store.listSongs(filter); |
2102 | EXPECT_EQ(3, tracks.size()); |
2103 | } |
2104 | |
2105 | @@ -687,24 +710,31 @@ |
2106 | store.insert(audio4); |
2107 | store.insert(audio5); |
2108 | |
2109 | - vector<Album> albums = store.listAlbums(); |
2110 | + Filter filter; |
2111 | + vector<Album> albums = store.listAlbums(filter); |
2112 | ASSERT_EQ(4, albums.size()); |
2113 | EXPECT_EQ("AlbumOne", albums[0].getTitle()); |
2114 | |
2115 | // test limit |
2116 | - albums = store.listAlbums("", "", 2); |
2117 | + albums = store.listAlbums(filter, 2); |
2118 | EXPECT_EQ(2, albums.size()); |
2119 | |
2120 | // Songs by artist |
2121 | - albums = store.listAlbums("ArtistOne"); |
2122 | + filter.setArtist("ArtistOne"); |
2123 | + albums = store.listAlbums(filter); |
2124 | EXPECT_EQ(3, albums.size()); |
2125 | |
2126 | // Songs by album artist |
2127 | - albums = store.listAlbums("", "ArtistOne"); |
2128 | + filter.clear(); |
2129 | + filter.setAlbumArtist("ArtistOne"); |
2130 | + albums = store.listAlbums(filter); |
2131 | EXPECT_EQ(2, albums.size()); |
2132 | |
2133 | // Combination |
2134 | - albums = store.listAlbums("ArtistOne", "Various Artists"); |
2135 | + filter.clear(); |
2136 | + filter.setArtist("ArtistOne"); |
2137 | + filter.setAlbumArtist("Various Artists"); |
2138 | + albums = store.listAlbums(filter); |
2139 | EXPECT_EQ(1, albums.size()); |
2140 | } |
2141 | |
2142 | @@ -747,17 +777,18 @@ |
2143 | store.insert(audio4); |
2144 | store.insert(audio5); |
2145 | |
2146 | - vector<string> artists = store.listArtists(false); |
2147 | + Filter filter; |
2148 | + vector<string> artists = store.listArtists(filter); |
2149 | ASSERT_EQ(2, artists.size()); |
2150 | EXPECT_EQ("ArtistOne", artists[0]); |
2151 | EXPECT_EQ("ArtistTwo", artists[1]); |
2152 | |
2153 | // Test limit clause |
2154 | - artists = store.listArtists(false, 1); |
2155 | + artists = store.listArtists(filter, 1); |
2156 | EXPECT_EQ(1, artists.size()); |
2157 | |
2158 | // List "album artists" |
2159 | - artists = store.listArtists(true); |
2160 | + artists = store.listAlbumArtists(filter); |
2161 | ASSERT_EQ(3, artists.size()); |
2162 | EXPECT_EQ("ArtistOne", artists[0]); |
2163 | EXPECT_EQ("ArtistTwo", artists[1]); |
PASSED: Continuous integration, rev:267 jenkins. qa.ubuntu. com/job/ mediascanner2- ci/81/ jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- amd64-ci/ 22 jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- armhf-ci/ 22 jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- armhf-ci/ 22/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- i386-ci/ 22
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mediascanne r2-ci/81/ rebuild
http://