Merge lp:~mhr3/unity-lens-music/rb-parse-tdb into lp:unity-lens-music

Proposed by Michal Hruby
Status: Merged
Approved by: Michal Hruby
Approved revision: 80
Merged at revision: 80
Proposed branch: lp:~mhr3/unity-lens-music/rb-parse-tdb
Merge into: lp:unity-lens-music
Diff against target: 549 lines (+330/-44)
7 files modified
configure.ac (+2/-1)
src/Makefile.am (+5/-0)
src/daemon.vala (+13/-5)
src/rhythmbox-collection.vala (+160/-37)
src/rhythmbox-scope.vala (+24/-1)
src/tdb.deps (+1/-0)
src/tdb.vapi (+125/-0)
To merge this branch: bzr merge lp:~mhr3/unity-lens-music/rb-parse-tdb
Reviewer Review Type Date Requested Status
Gord Allott (community) Approve
Review via email: mp+101394@code.launchpad.net

Commit message

Added rhythmbox's TDB parsing so we'll have more sources of album arts in the lens

Description of the change

Added rhythmbox's TDB parsing so we'll have more sources of album arts in the lens.

To post a comment you must log in.
Revision history for this message
Gord Allott (gordallott) wrote :

seems good to me, +1

review: Approve
Revision history for this message
Unity Merger (unity-merger) wrote :

No commit message specified.

Revision history for this message
Unity Merger (unity-merger) wrote :

The Jenkins job https://jenkins.qa.ubuntu.com/job/automerge-unity-lens-music/16/console reported an error when processing this lp:~mhr3/unity-lens-music/rb-parse-tdb branch.
Not merging it.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'configure.ac'
--- configure.ac 2012-04-12 07:57:42 +0000
+++ configure.ac 2012-04-23 12:39:24 +0000
@@ -64,7 +64,8 @@
64 sqlite3 >= 3.7.764 sqlite3 >= 3.7.7
65 gee-1.065 gee-1.0
66 json-glib-1.066 json-glib-1.0
67 unity >= 4.99.0)67 unity >= 4.99.0
68 tdb >= 1.2.6)
6869
69AC_SUBST(LENS_DAEMON_CFLAGS)70AC_SUBST(LENS_DAEMON_CFLAGS)
70AC_SUBST(LENS_DAEMON_LIBS)71AC_SUBST(LENS_DAEMON_LIBS)
7172
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2012-03-27 21:51:45 +0000
+++ src/Makefile.am 2012-04-23 12:39:24 +0000
@@ -28,6 +28,9 @@
28 --pkg gio-2.0 \28 --pkg gio-2.0 \
29 --pkg gio-unix-2.0 \29 --pkg gio-unix-2.0 \
30 --pkg glib-2.0 \30 --pkg glib-2.0 \
31 --vapidir $(srcdir) \
32 --pkg tdb \
33 --target-glib=2.26 \
31 $(MAINTAINER_VALAFLAGS)34 $(MAINTAINER_VALAFLAGS)
3235
33unity_music_daemon_LDADD = \36unity_music_daemon_LDADD = \
@@ -98,6 +101,8 @@
98 unity_musicstore_daemon.vala.stamp \101 unity_musicstore_daemon.vala.stamp \
99 $(unity_music_daemon_VALASOURCES) \102 $(unity_music_daemon_VALASOURCES) \
100 $(unity_musicstore_daemon_VALASOURCES) \103 $(unity_musicstore_daemon_VALASOURCES) \
104 tdb.vapi \
105 tdb.deps \
101 $(NULL)106 $(NULL)
102107
103unity_music_daemon.vala.stamp: $(unity_music_daemon_VALASOURCES)108unity_music_daemon.vala.stamp: $(unity_music_daemon_VALASOURCES)
104109
=== modified file 'src/daemon.vala'
--- src/daemon.vala 2012-03-20 14:59:27 +0000
+++ src/daemon.vala 2012-04-23 12:39:24 +0000
@@ -32,9 +32,6 @@
32 32
33 construct33 construct
34 {34 {
35 banshee = new BansheeScopeProxy ();
36 rb = new RhythmboxScope ();
37
38 lens = new Unity.Lens("/com/canonical/unity/lens/music", "music");35 lens = new Unity.Lens("/com/canonical/unity/lens/music", "music");
39 lens.search_in_global = true;36 lens.search_in_global = true;
40 lens.search_hint = _("Search Music Collection");37 lens.search_hint = _("Search Music Collection");
@@ -44,8 +41,19 @@
44 populate_categories ();41 populate_categories ();
45 populate_filters();42 populate_filters();
4643
47 lens.add_local_scope (banshee.scope);44 var app_check = new DesktopAppInfo ("banshee.desktop");
48 lens.add_local_scope (rb.scope);45 if (app_check != null)
46 {
47 banshee = new BansheeScopeProxy ();
48 lens.add_local_scope (banshee.scope);
49 }
50
51 app_check = new DesktopAppInfo ("rhythmbox.desktop");
52 if (app_check != null)
53 {
54 rb = new RhythmboxScope ();
55 lens.add_local_scope (rb.scope);
56 }
4957
50 try {58 try {
51 lens.export ();59 lens.export ();
5260
=== modified file 'src/rhythmbox-collection.vala'
--- src/rhythmbox-collection.vala 2012-03-27 21:51:45 +0000
+++ src/rhythmbox-collection.vala 2012-04-23 12:39:24 +0000
@@ -45,14 +45,19 @@
45 {45 {
4646
47 SequenceModel all_tracks;47 SequenceModel all_tracks;
48 ModelTag<int> album_art_tag;
48 FilterModel tracks_by_play_count;49 FilterModel tracks_by_play_count;
4950
51 TDB.Database album_art_tdb;
52 FileMonitor tdb_monitor;
53 int current_album_art_tag;
54
50 HashTable<unowned string, Variant> variant_store;55 HashTable<unowned string, Variant> variant_store;
51 HashTable<int, Variant> int_variant_store;56 HashTable<int, Variant> int_variant_store;
52 Variant row_buffer[11];57 Variant row_buffer[11];
5358
54 Analyzer analyzer;59 Analyzer analyzer;
55 Index index;60 Index? index;
56 ICUTermFilter ascii_filter;61 ICUTermFilter ascii_filter;
5762
58 string media_art_dir;63 string media_art_dir;
@@ -203,9 +208,10 @@
203 direct_equal);208 direct_equal);
204 all_tracks = new SequenceModel ();209 all_tracks = new SequenceModel ();
205 // the columns correspond to the Columns enum210 // the columns correspond to the Columns enum
206 all_tracks.set_schema ("s", "s", "s", "s", "s", "s", "s", "s", "i", "i", "i");211 all_tracks.set_schema ("s", "s", "s", "s", "s", "s",
212 "s", "s", "i", "i", "i");
207 assert (all_tracks.get_schema ().length == Columns.N_COLUMNS);213 assert (all_tracks.get_schema ().length == Columns.N_COLUMNS);
208 214 album_art_tag = new ModelTag<int> (all_tracks);
209215
210 var filter = Dee.Filter.new_sort ((row1, row2) =>216 var filter = Dee.Filter.new_sort ((row1, row2) =>
211 {217 {
@@ -227,6 +233,11 @@
227 if (folded != term) terms_out.add_term (folded);233 if (folded != term) terms_out.add_term (folded);
228 }234 }
229 });235 });
236 initialize_index ();
237 }
238
239 private void initialize_index ()
240 {
230 var reader = ModelReader.new ((model, iter) =>241 var reader = ModelReader.new ((model, iter) =>
231 {242 {
232 var s ="%s\n%s\n%s".printf (model.get_string (iter, Columns.TITLE),243 var s ="%s\n%s\n%s".printf (model.get_string (iter, Columns.TITLE),
@@ -237,21 +248,63 @@
237248
238 index = new TreeIndex (all_tracks, analyzer, reader);249 index = new TreeIndex (all_tracks, analyzer, reader);
239 }250 }
251
252 private string? check_album_art_tdb (string artist, string album)
253 {
254 if (album_art_tdb == null) return null;
255
256 uint8 null_helper[1] = { 0 };
257 ByteArray byte_arr = new ByteArray ();
258 byte_arr.append ("album".data);
259 byte_arr.append (null_helper);
260 byte_arr.append (album.data);
261 byte_arr.append (null_helper);
262 byte_arr.append ("artist".data);
263 byte_arr.append (null_helper);
264 byte_arr.append (artist.data);
265 byte_arr.append (null_helper);
266
267 TDB.Data key = TDB.NULL_DATA;
268 key.data = byte_arr.data;
269 var val = album_art_tdb.fetch (key);
270
271 if (val.data != null)
272 {
273 Variant v = Variant.new_from_data<int> (new VariantType ("a{sv}"), val.data, false);
274 var file_variant = v.lookup_value ("file", VariantType.STRING);
275 if (file_variant != null)
276 {
277 return file_variant.get_string ();
278 }
279 }
280
281 return null;
282 }
240 283
241 private string? get_albumart (Track track)284 private string? get_albumart (Track track)
242 {285 {
286 string filename;
243 var artist = track.album_artist ?? track.artist;287 var artist = track.album_artist ?? track.artist;
244 var album = track.album;288 var album = track.album;
245289
246 var artist_norm = artist.normalize (-1, NormalizeMode.NFKD);290 var artist_norm = artist.normalize (-1, NormalizeMode.NFKD);
247 var album_norm = album.normalize (-1, NormalizeMode.NFKD);291 var album_norm = album.normalize (-1, NormalizeMode.NFKD);
248292
293 filename = check_album_art_tdb (artist, album);
294 if (filename != null)
295 {
296 filename = Path.build_filename (Environment.get_user_cache_dir (),
297 "rhythmbox", "album-art",
298 filename);
299
300 if (FileUtils.test (filename, FileTest.EXISTS)) return filename;
301 }
302
249 var artist_md5 = Checksum.compute_for_string (ChecksumType.MD5,303 var artist_md5 = Checksum.compute_for_string (ChecksumType.MD5,
250 artist_norm);304 artist_norm);
251 var album_md5 = Checksum.compute_for_string (ChecksumType.MD5,305 var album_md5 = Checksum.compute_for_string (ChecksumType.MD5,
252 album_norm);306 album_norm);
253307
254 string filename;
255 filename = Path.build_filename (media_art_dir,308 filename = Path.build_filename (media_art_dir,
256 "album-%s-%s".printf (artist_md5, album_md5));309 "album-%s-%s".printf (artist_md5, album_md5));
257 if (FileUtils.test (filename, FileTest.EXISTS)) return filename;310 if (FileUtils.test (filename, FileTest.EXISTS)) return filename;
@@ -331,8 +384,59 @@
331 row_buffer[10] = play_count;384 row_buffer[10] = play_count;
332 }385 }
333386
387 public void parse_metadata_file (string path)
388 {
389 if (album_art_tdb != null) return;
390
391 if (tdb_monitor == null)
392 {
393 var tdb_file = File.new_for_path (path);
394 try
395 {
396 tdb_monitor = tdb_file.monitor (FileMonitorFlags.NONE);
397 tdb_monitor.changed.connect (() =>
398 {
399 if (album_art_tdb == null) parse_metadata_file (path);
400 else current_album_art_tag++;
401 });
402 }
403 catch (Error err)
404 {
405 warning ("%s", err.message);
406 }
407 }
408
409 var flags = TDB.OpenFlags.INCOMPATIBLE_HASH | TDB.OpenFlags.SEQNUM | TDB.OpenFlags.NOLOCK;
410 album_art_tdb = new TDB.Database (path, 999, flags,
411 Posix.O_RDONLY, 0600);
412 if (album_art_tdb == null)
413 {
414 warning ("Unable to open album-art DB!");
415 return;
416 }
417
418 /*
419 album_art_tdb.traverse ((db, key, val) =>
420 {
421 var byte_arr = new ByteArray.sized ((uint) val.data_size);
422 byte_arr.append (val.data);
423 Variant v = Variant.new_from_data<ByteArray> (new VariantType ("a{sv}"), byte_arr.data, false, byte_arr);
424 message ("value: %s", v.print (true));
425
426 return 0;
427 });
428 */
429 }
430
334 public void parse_file (string path)431 public void parse_file (string path)
335 {432 {
433 // this could be really expensive if the index was already built, so
434 // we'll destroy it first
435 index = null;
436 all_tracks.clear ();
437 initialize_index ();
438 current_album_art_tag = 0;
439
336 var parser = new XmlParser ();440 var parser = new XmlParser ();
337 parser.track_info_ready.connect ((track) =>441 parser.track_info_ready.connect ((track) =>
338 {442 {
@@ -366,6 +470,48 @@
366 }470 }
367 }471 }
368472
473 private void add_result (Model results_model, Model model,
474 ModelIter iter, Columns title_col,
475 uint category_id)
476 {
477 // check for updated album art
478 var tag = album_art_tag[model, iter];
479 if (tag < current_album_art_tag)
480 {
481 unowned string album = model.get_string (iter, Columns.ALBUM);
482 unowned string artist = model.get_string (iter,
483 Columns.ALBUM_ARTIST);
484 if (artist == "")
485 artist = model.get_string (iter, Columns.ARTIST);
486
487 var album_art_string = check_album_art_tdb (artist, album);
488 if (album_art_string != null)
489 {
490 string filename;
491 filename = Path.build_filename (Environment.get_user_cache_dir (),
492 "rhythmbox", "album-art",
493 album_art_string);
494 album_art_string = FileUtils.test (filename, FileTest.EXISTS) ?
495 filename : "audio-x-generic";
496
497 if (album_art_string != model.get_string (iter, Columns.ARTWORK))
498 {
499 model.set_value (iter, Columns.ARTWORK,
500 cached_variant_for_string (album_art_string));
501 }
502 }
503 album_art_tag[model, iter] = current_album_art_tag;
504 }
505
506 results_model.append (model.get_string (iter, Columns.URI),
507 model.get_string (iter, Columns.ARTWORK),
508 category_id,
509 model.get_string (iter, Columns.MIMETYPE),
510 model.get_string (iter, title_col),
511 model.get_string (iter, Columns.ARTIST),
512 model.get_string (iter, Columns.URI));
513 }
514
369 public void search (LensSearch search, 515 public void search (LensSearch search,
370 SearchType search_type, 516 SearchType search_type,
371 GLib.List<FilterParser>? filters = null,517 GLib.List<FilterParser>? filters = null,
@@ -420,28 +566,16 @@
420 {566 {
421 category_id = category_override >= 0 ?567 category_id = category_override >= 0 ?
422 category_override : Category.ALBUMS;568 category_override : Category.ALBUMS;
423569
424 search.results_model.append (570 add_result (search.results_model, model, iter,
425 model.get_string (iter, Columns.URI),571 Columns.ALBUM, category_id);
426 model.get_string (iter, Columns.ARTWORK),
427 category_id,
428 model.get_string (iter, Columns.MIMETYPE),
429 model.get_string (iter, Columns.ALBUM),
430 model.get_string (iter, Columns.ARTIST),
431 model.get_string (iter, Columns.URI));
432 }572 }
433573
434 category_id = category_override >= 0 ?574 category_id = category_override >= 0 ?
435 category_override : Category.SONGS;575 category_override : Category.SONGS;
436576
437 search.results_model.append (577 add_result (search.results_model, model, iter,
438 model.get_string (iter, Columns.URI),578 Columns.TITLE, category_id);
439 model.get_string (iter, Columns.ARTWORK),
440 category_id,
441 model.get_string (iter, Columns.MIMETYPE),
442 model.get_string (iter, Columns.TITLE),
443 model.get_string (iter, Columns.ARTIST),
444 model.get_string (iter, Columns.URI));
445579
446 num_results++;580 num_results++;
447 if (max_results >= 0 && num_results >= max_results) break;581 if (max_results >= 0 && num_results >= max_results) break;
@@ -515,6 +649,7 @@
515649
516 unowned string album = model.get_string (model_iter,650 unowned string album = model.get_string (model_iter,
517 Columns.ALBUM);651 Columns.ALBUM);
652
518 // it's not first as in track #1, but first found from album653 // it's not first as in track #1, but first found from album
519 bool first_track_from_album = !(album in albums_list);654 bool first_track_from_album = !(album in albums_list);
520 albums_list.add (album);655 albums_list.add (album);
@@ -524,27 +659,15 @@
524 category_id = category_override >= 0 ?659 category_id = category_override >= 0 ?
525 category_override : Category.ALBUMS;660 category_override : Category.ALBUMS;
526661
527 search.results_model.append (662 add_result (search.results_model, model, model_iter,
528 model.get_string (model_iter, Columns.URI),663 Columns.ALBUM, category_id);
529 model.get_string (model_iter, Columns.ARTWORK),
530 category_id,
531 model.get_string (model_iter, Columns.MIMETYPE),
532 model.get_string (model_iter, Columns.ALBUM),
533 model.get_string (model_iter, Columns.ARTIST),
534 model.get_string (model_iter, Columns.URI));
535 }664 }
536665
537 category_id = category_override >= 0 ?666 category_id = category_override >= 0 ?
538 category_override : Category.SONGS;667 category_override : Category.SONGS;
539668
540 search.results_model.append (669 add_result (search.results_model, model, model_iter,
541 model.get_string (model_iter, Columns.URI),670 Columns.TITLE, category_id);
542 model.get_string (model_iter, Columns.ARTWORK),
543 category_id,
544 model.get_string (model_iter, Columns.MIMETYPE),
545 model.get_string (model_iter, Columns.TITLE),
546 model.get_string (model_iter, Columns.ARTIST),
547 model.get_string (model_iter, Columns.URI));
548671
549 num_results++;672 num_results++;
550 if (max_results >= 0 && num_results >= max_results) break;673 if (max_results >= 0 && num_results >= max_results) break;
551674
=== modified file 'src/rhythmbox-scope.vala'
--- src/rhythmbox-scope.vala 2012-03-20 14:59:27 +0000
+++ src/rhythmbox-scope.vala 2012-04-23 12:39:24 +0000
@@ -25,6 +25,7 @@
25 { 25 {
26 private RhythmboxCollection collection;26 private RhythmboxCollection collection;
27 private bool db_ready;27 private bool db_ready;
28 private FileMonitor rb_xml_monitor;
2829
29 public RhythmboxScope ()30 public RhythmboxScope ()
30 {31 {
@@ -105,7 +106,29 @@
105 if (!db_ready)106 if (!db_ready)
106 {107 {
107 // parse the DB lazily108 // parse the DB lazily
108 collection.parse_file ("%s/.local/share/rhythmbox/rhythmdb.xml".printf (Environment.get_home_dir ()));109 var tdb_path = Path.build_filename (Environment.get_user_cache_dir (),
110 "rhythmbox", "album-art",
111 "store.tdb");
112 collection.parse_metadata_file (tdb_path);
113
114 var xml_path = Path.build_filename (Environment.get_user_data_dir (),
115 "rhythmbox", "rhythmdb.xml");
116 collection.parse_file (xml_path);
117 if (rb_xml_monitor == null)
118 {
119 // re-parse the file if it changes
120 File xml_file = File.new_for_path (xml_path);
121 try
122 {
123 rb_xml_monitor = xml_file.monitor (FileMonitorFlags.NONE);
124 rb_xml_monitor.changed.connect (() => { db_ready = false; });
125 }
126 catch (Error err)
127 {
128 warning ("%s", err.message);
129 }
130 }
131
109 db_ready = true;132 db_ready = true;
110 }133 }
111134
112135
=== added file 'src/tdb.deps'
--- src/tdb.deps 1970-01-01 00:00:00 +0000
+++ src/tdb.deps 2012-04-23 12:39:24 +0000
@@ -0,0 +1,1 @@
1posix
02
=== added file 'src/tdb.vapi'
--- src/tdb.vapi 1970-01-01 00:00:00 +0000
+++ src/tdb.vapi 2012-04-23 12:39:24 +0000
@@ -0,0 +1,125 @@
1/* tdb.vapi
2 *
3 * Copyright (C) 2012 Canonical Ltd.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Author:
20 * Michal Hruby <michal.hruby@canonical.com>
21 */
22
23[CCode (lower_case_cprefix = "tdb_", cheader_filename = "tdb.h")]
24namespace TDB {
25 /* Database Connection Handle */
26 [Compact]
27 [CCode (free_function = "tdb_close", cname = "TDB_CONTEXT", cprefix = "tdb_")]
28 public class Database {
29 [CCode (cname = "tdb_open")]
30 public Database (string name, int hash_size, TDB.OpenFlags tdb_flags, int open_flags, Posix.mode_t mode);
31 [CCode (cname = "tdb_open_ex")]
32 public Database.open_ex (string name, int hash_size, int tdb_flags, int open_flags, Posix.mode_t mode, TDB.LogFunc log_fn);
33
34 public int reopen ();
35 public static int reopen_all ();
36
37 public TDB.Error error ();
38 public unowned string errorstr ();
39
40 public TDB.Data fetch (TDB.Data key);
41 public int @delete (TDB.Data key);
42 public int store (TDB.Data key, TDB.Data dbuf, TDB.StoreType type_flag);
43 public TDB.Data firstkey ();
44 public TDB.Data nextkey (TDB.Data key);
45 public int traverse (TDB.TraverseFunc traverse_func);
46 public int exists (TDB.Data key);
47 public int lockkeys (TDB.Data[] keys);
48 public void unlockkeys ();
49 public int lockall ();
50 public void unlockall ();
51
52 public int chainlock (TDB.Data key);
53 public void chainunlock (TDB.Data key);
54 }
55
56 [CCode (cname = "TDB_DATA")]
57 [SimpleType]
58 public struct Data {
59 [CCode (array_length_cname = "dsize", array_length_type = "size_t", cname = "dptr")]
60 public unowned uint8[] data;
61 [CCode (cname = "dsize")]
62 public size_t data_size;
63 }
64
65 [CCode (cname = "tdb_null")]
66 public const TDB.Data NULL_DATA;
67
68 [CCode (has_target = false)]
69 public delegate void LogFunc (TDB.Database db, TDB.DebugLevel debug_level, string format, ...);
70
71 public delegate int TraverseFunc (TDB.Database db, TDB.Data key, TDB.Data @value);
72
73 [CCode (cname = "SQLITE_ANY")]
74 public const int ANY;
75
76 [CCode (cname = "enum tdb_debug_level", cprefix = "TDB_DEBUG_")]
77 public enum DebugLevel {
78 FATAL,
79 ERROR,
80 WARNING,
81 TRACE
82 }
83
84 [CCode (cname = "int", cprefix = "TDB_")]
85 public enum StoreType {
86 REPLACE,
87 INSERT,
88 MODIFY
89 }
90
91 [CCode (cname = "int", cprefix = "TDB_")]
92 public enum OpenFlags {
93 DEFAULT,
94 CLEAR_IF_FIRST,
95 INTERNAL,
96 NOLOCK,
97 NOMMAP,
98 CONVERT,
99 BIGENDIAN,
100 NOSYNC,
101 SEQNUM,
102 VOLATILE,
103 ALLOW_NESTING,
104 DISALLOW_NESTING,
105 INCOMPATIBLE_HASH
106 }
107
108 [CCode (cname = "enum TDB_ERROR", cprefix = "TDB_ERR_")]
109 public enum Error {
110 [CCode (cname = "TDB_SUCCESS")]
111 SUCCESS,
112 CORRUPT,
113 IO,
114 LOCK,
115 OOM,
116 EXISTS,
117 NOLOCK,
118 LOCK_TIMEOUT,
119 NOEXISTS,
120 EINVAL,
121 RDONLY,
122 NESTING
123 }
124}
125

Subscribers

People subscribed via source and target branches

to all changes: