Merge lp:~elementary-apps/noise/async-pixbuf-cache into lp:~elementary-apps/noise/trunk
- async-pixbuf-cache
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 1058 |
Proposed branch: | lp:~elementary-apps/noise/async-pixbuf-cache |
Merge into: | lp:~elementary-apps/noise/trunk |
Diff against target: |
1920 lines (+625/-544) 8 files modified
core/Utils/FileUtils.vala (+79/-36) plugins/LastFM/Core.vala (+117/-109) src/GStreamer/GStreamerTagger.vala (+282/-281) src/LibraryWindow.vala (+17/-8) src/Objects/CoverartCache.vala (+66/-78) src/Objects/MediaArtCache.vala (+14/-6) src/Utils/PixbufCache.vala (+43/-20) src/Utils/PixbufUtils.vala (+7/-6) |
To merge this branch: | bzr merge lp:~elementary-apps/noise/async-pixbuf-cache |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
David Gomes (community) | Approve | ||
Review via email: mp+128154@code.launchpad.net |
Commit message
Description of the change
This branch should fix bug #1060122: noise crashed while re-scanning Music folder.
Previously, the code used synchronous (i.e. blocking) I/O to load the cached images from disk, and was run from a different thread to avoid freezing the UI. Since we run into many thread-safety issues, and considering there's not enough time to hunt them down one by one, using Idle calls seems to be the most reliable and quick solution to the problem; the code now uses GLib's Idle scheduler to avoid the need of a second thread here.
Also updates:
+ Last.fm plugin (no longer uses threads.)
Opinions and testers are welcome!
- 1055. By Victor Martinez
-
[CMake] Fix libnotify support
This revision adds back notification support. Recent changes to the CMake files introduced a bug that would override the HAVE_LIBNOTIFY option, since the default options were being set below in the same file. Enjoy the notifications again.
- 1056. By Victor Martinez
-
Opps. Search is smart again.
Fix a regression from revision 1049 that would make the searches fail if the search string contained any whitespace character.
- 1057. By Victor Martinez
-
Merge translations
- 1058. By David Gomes
-
Small code fixes.
- 1059. By Victor Martinez
-
CoverartCache.
lookup_ folder_ image_async: rv => image_file
Preview Diff
1 | === modified file 'core/Utils/FileUtils.vala' |
2 | --- core/Utils/FileUtils.vala 2012-09-17 22:14:19 +0000 |
3 | +++ core/Utils/FileUtils.vala 2012-10-06 03:24:21 +0000 |
4 | @@ -46,6 +46,26 @@ |
5 | } |
6 | |
7 | /** |
8 | + * Asynchronously checks whether a file exists or not. |
9 | + * It follows symbolic links. |
10 | + */ |
11 | + public async bool query_exists_async (File file_or_dir, Cancellable? cancellable = null) { |
12 | + FileInfo? info = null; |
13 | + |
14 | + try { |
15 | + info = yield file_or_dir.query_info_async (FileAttribute.STANDARD_NAME, |
16 | + FileQueryInfoFlags.NONE, |
17 | + Priority.DEFAULT, |
18 | + cancellable); |
19 | + } catch (Error err) { |
20 | + if (err is IOError.NOT_FOUND) |
21 | + return false; |
22 | + } |
23 | + |
24 | + return info != null; |
25 | + } |
26 | + |
27 | + /** |
28 | * Convenience method to get the size of a file or directory (recursively) |
29 | * |
30 | * @param file a {@link GLib.File} representing the file or directory to be queried |
31 | @@ -53,12 +73,12 @@ |
32 | * @return size in bytes of file. It is recommended to use GLib.format_size() in case |
33 | * you want to convert it to a string representation. |
34 | */ |
35 | - public uint64 get_size (File file_or_dir, Cancellable? cancellable = null) { |
36 | + public async uint64 get_size_async (File file_or_dir, Cancellable? cancellable = null) { |
37 | uint64 size = 0; |
38 | Gee.Collection<File> files; |
39 | |
40 | - if (is_directory (file_or_dir, cancellable)) { |
41 | - enumerate_files (file_or_dir, null, true, out files, cancellable); |
42 | + if (yield is_directory_async (file_or_dir, cancellable)) { |
43 | + yield enumerate_files_async (file_or_dir, null, true, out files, cancellable); |
44 | } else { |
45 | files = new Gee.LinkedList<File> (); |
46 | files.add (file_or_dir); |
47 | @@ -69,8 +89,10 @@ |
48 | break; |
49 | |
50 | try { |
51 | - var info = file.query_info (FileAttribute.STANDARD_SIZE, |
52 | - FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable); |
53 | + var info = yield file.query_info_async (FileAttribute.STANDARD_SIZE, |
54 | + FileQueryInfoFlags.NOFOLLOW_SYMLINKS, |
55 | + Priority.DEFAULT, |
56 | + cancellable); |
57 | size += info.get_attribute_uint64 (FileAttribute.STANDARD_SIZE); |
58 | } catch (Error err) { |
59 | warning ("Could not get size of '%s': %s", file.get_uri (), err.message); |
60 | @@ -80,9 +102,23 @@ |
61 | return size; |
62 | } |
63 | |
64 | - public bool is_directory (File file, Cancellable? cancellable = null) { |
65 | - var type = file.query_file_type (FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable); |
66 | - return type == FileType.DIRECTORY; |
67 | + /** |
68 | + * Checks whether //dir// is a directory. |
69 | + * Does not follow symbolic links. |
70 | + */ |
71 | + public async bool is_directory_async (File dir, Cancellable? cancellable = null) { |
72 | + FileInfo? info = null; |
73 | + |
74 | + try { |
75 | + info = yield dir.query_info_async (FileAttribute.STANDARD_TYPE, |
76 | + FileQueryInfoFlags.NOFOLLOW_SYMLINKS, |
77 | + Priority.DEFAULT, |
78 | + cancellable); |
79 | + } catch (Error err) { |
80 | + warning (err.message); |
81 | + } |
82 | + |
83 | + return info != null && info.get_file_type () == FileType.DIRECTORY; |
84 | } |
85 | |
86 | /** |
87 | @@ -90,24 +126,25 @@ |
88 | * |
89 | * @param folder a {@link GLib.File} representing the folder you wish to query |
90 | * @param types a string array containing the formats you want to limit the search to, or null |
91 | - * to allow any file type. e.g. [[[string[] types = {"mp3", "jpg"}]]] [allow-none] |
92 | + * to allow any file type. e.g. string[] types = {"mp3", "jpg"} [allow-none] |
93 | * @param recursive whether to query the whole directory tree or only immediate children. [allow-none] |
94 | * @param files the data container for the files found. This only includes files, not directories [allow-none] |
95 | - * @param cancellable a cancellable object for cancelling the operation. [allow-none] |
96 | + * @param cancellable a cancellable object for canceling the operation. [allow-none] |
97 | * |
98 | * @return total number of files found (should be the same as files.size) |
99 | */ |
100 | - public uint enumerate_files (File folder, string[]? types = null, |
101 | - bool recursive = true, |
102 | - out Gee.Collection<File>? files = null, |
103 | - Cancellable? cancellable = null) { |
104 | - return_val_if_fail (is_directory (folder), 0); |
105 | + public async uint enumerate_files_async (File folder, string[]? types = null, |
106 | + bool recursive = true, |
107 | + out Gee.Collection<File>? files = null, |
108 | + Cancellable? cancellable = null) |
109 | + { |
110 | + return_val_if_fail (yield is_directory_async (folder), 0); |
111 | var counter = new FileEnumerator (); |
112 | - return counter.enumerate_files (folder, types, out files, recursive, cancellable); |
113 | + return yield counter.enumerate_files_async (folder, types, out files, recursive, cancellable); |
114 | } |
115 | |
116 | /** |
117 | - * Comprobates whether a filename matches a given extension. |
118 | + * Queries whether a filename matches a given extension. |
119 | * |
120 | * @param name path, URI or name of the file to verify |
121 | * @param types a string array containing the expected file extensions (without dot). |
122 | @@ -143,17 +180,18 @@ |
123 | * the directories descendant from folder. In case you only want the first-level |
124 | * descendants, set recursive to false. |
125 | */ |
126 | - public uint enumerate_files (File folder, string[]? types, |
127 | - out Gee.Collection<File>? files, |
128 | - bool recursive = true, |
129 | - Cancellable? cancellable = null) { |
130 | + public async uint enumerate_files_async (File folder, string[]? types, |
131 | + out Gee.Collection<File>? files, |
132 | + bool recursive = true, |
133 | + Cancellable? cancellable = null) |
134 | + { |
135 | assert (file_count == 0); |
136 | |
137 | this.types = types; |
138 | this.cancellable = cancellable; |
139 | |
140 | files = new Gee.LinkedList<File> (); |
141 | - enumerate_files_internal (folder, ref files, recursive); |
142 | + yield enumerate_files_internal_async (folder, files, recursive); |
143 | return file_count; |
144 | } |
145 | |
146 | @@ -161,19 +199,25 @@ |
147 | return Utils.is_cancelled (cancellable); |
148 | } |
149 | |
150 | - private void enumerate_files_internal (File folder, ref Gee.Collection<File>? files, |
151 | - bool recursive) { |
152 | - |
153 | + private async void enumerate_files_internal_async (File folder, Gee.Collection<File>? files, |
154 | + bool recursive) |
155 | + { |
156 | if (is_cancelled ()) |
157 | return; |
158 | |
159 | try { |
160 | - var enumerator = folder.enumerate_children (ATTRIBUTES, |
161 | - FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable); |
162 | - |
163 | - FileInfo? file_info = null; |
164 | - |
165 | - while ((file_info = enumerator.next_file ()) != null && !is_cancelled ()) { |
166 | + var enumerator = yield folder.enumerate_children_async (ATTRIBUTES, |
167 | + FileQueryInfoFlags.NOFOLLOW_SYMLINKS, |
168 | + Priority.DEFAULT, |
169 | + cancellable); |
170 | + |
171 | + while (!is_cancelled ()) { |
172 | + var enum_files = yield enumerator.next_files_async (1, Priority.DEFAULT, cancellable); |
173 | + FileInfo? file_info = enum_files.nth_data (0); |
174 | + |
175 | + if (file_info == null) |
176 | + break; |
177 | + |
178 | var file_name = file_info.get_name (); |
179 | var file_type = file_info.get_file_type (); |
180 | var file = folder.get_child (file_name); |
181 | @@ -183,15 +227,14 @@ |
182 | continue; |
183 | |
184 | file_count++; |
185 | + |
186 | if (files != null) |
187 | files.add (file); |
188 | - } |
189 | - else if (recursive && file_type == FileType.DIRECTORY) { |
190 | - enumerate_files_internal (file, ref files, true); |
191 | + } else if (recursive && file_type == FileType.DIRECTORY) { |
192 | + yield enumerate_files_internal_async (file, files, true); |
193 | } |
194 | } |
195 | - } |
196 | - catch (Error err) { |
197 | + } catch (Error err) { |
198 | warning ("Could not scan folder: %s", err.message); |
199 | } |
200 | } |
201 | |
202 | === modified file 'plugins/LastFM/Core.vala' |
203 | --- plugins/LastFM/Core.vala 2012-09-18 10:14:54 +0000 |
204 | +++ plugins/LastFM/Core.vala 2012-10-06 03:24:21 +0000 |
205 | @@ -40,26 +40,26 @@ |
206 | public Settings lastfm_settings { get; private set; } |
207 | |
208 | public string token; |
209 | - |
210 | + |
211 | public signal void logged_in(); |
212 | public signal void similar_retrieved(LinkedList<int> similarIDs, LinkedList<Noise.Media> similarDont); |
213 | - |
214 | + |
215 | LastFM.SimilarMedias similarMedias; |
216 | - |
217 | + |
218 | Mutex _artists_lock; |
219 | Mutex _albums_lock; |
220 | Mutex _tracks_lock; |
221 | HashMap<string, LastFM.ArtistInfo> _artists;//key:artist |
222 | HashMap<string, LastFM.AlbumInfo> _albums;//key:artist<sep>album |
223 | HashMap<string, LastFM.TrackInfo> _tracks;//key:artist<sep>album<sep>track |
224 | - |
225 | + |
226 | public Core(Noise.LibraryManager lmm) { |
227 | lm = lmm; |
228 | - |
229 | + |
230 | lastfm_settings = new LastFM.Settings (); |
231 | - |
232 | + |
233 | similarMedias = new LastFM.SimilarMedias(lm); |
234 | - |
235 | + |
236 | _artists = new HashMap<string, LastFM.ArtistInfo>(); |
237 | _albums = new HashMap<string, LastFM.AlbumInfo>(); |
238 | _tracks = new HashMap<string, LastFM.TrackInfo>(); |
239 | @@ -69,13 +69,13 @@ |
240 | _artists.set(a.name, (LastFM.ArtistInfo)a); |
241 | } |
242 | _artists_lock.unlock(); |
243 | - |
244 | + |
245 | _albums_lock.lock(); |
246 | foreach(Noise.AlbumInfo a in lm.dbm.load_albums()) { |
247 | _albums.set(a.name + " by " + a.artist, (LastFM.AlbumInfo)a); |
248 | } |
249 | _albums_lock.unlock(); |
250 | - |
251 | + |
252 | _tracks_lock.lock(); |
253 | foreach(Noise.TrackInfo t in lm.dbm.load_tracks()) { |
254 | _tracks.set(t.name + " by " + t.artist, (LastFM.TrackInfo)t); |
255 | @@ -84,98 +84,98 @@ |
256 | |
257 | similarMedias.similar_retrieved.connect(similar_retrieved_signal); |
258 | } |
259 | - |
260 | + |
261 | /************* Last FM Artist Stuff ************/ |
262 | public GLib.List<LastFM.ArtistInfo> artists() { |
263 | var rv = new GLib.List<LastFM.ArtistInfo>(); |
264 | foreach(var artist in _artists.values) |
265 | rv.append(artist); |
266 | - |
267 | + |
268 | return rv; |
269 | } |
270 | - |
271 | + |
272 | public void save_artist(LastFM.ArtistInfo artist) { |
273 | _artists_lock.lock(); |
274 | _artists.set(artist.name.down(), artist); |
275 | _artists_lock.unlock(); |
276 | } |
277 | - |
278 | + |
279 | public bool artist_info_exists(string artist_key) { |
280 | return _artists.get(artist_key.down()) != null; |
281 | } |
282 | - |
283 | + |
284 | public LastFM.ArtistInfo? get_artist(string artist_key) { |
285 | LastFM.ArtistInfo? rv = null; |
286 | - |
287 | + |
288 | _artists_lock.lock(); |
289 | - if(artist_info_exists(artist_key.down())) |
290 | + if(artist_info_exists(artist_key.down())) |
291 | rv = _artists.get(artist_key.down()); |
292 | _artists_lock.unlock(); |
293 | - |
294 | + |
295 | return rv; |
296 | } |
297 | - |
298 | + |
299 | /************** LastFM Album stuff **************/ |
300 | public GLib.List<LastFM.AlbumInfo> albums() { |
301 | var rv = new GLib.List<LastFM.AlbumInfo>(); |
302 | foreach(var album in _albums.values) |
303 | rv.append(album); |
304 | - |
305 | + |
306 | return rv; |
307 | } |
308 | - |
309 | + |
310 | public void save_album(LastFM.AlbumInfo album) { |
311 | _albums_lock.lock(); |
312 | _albums.set(album.name.down() + " by " + album.artist.down(), album); |
313 | _albums_lock.unlock(); |
314 | } |
315 | - |
316 | + |
317 | public bool album_info_exists(string album_key) { |
318 | return _albums.get(album_key) != null; |
319 | } |
320 | - |
321 | + |
322 | public LastFM.AlbumInfo? get_album(string album_key) { |
323 | LastFM.AlbumInfo? rv = null; |
324 | - |
325 | + |
326 | _albums_lock.lock(); |
327 | - if(album_info_exists(album_key.down())) |
328 | + if(album_info_exists(album_key.down())) |
329 | rv = _albums.get(album_key.down()); |
330 | _albums_lock.unlock(); |
331 | - |
332 | + |
333 | return rv; |
334 | } |
335 | - |
336 | + |
337 | /************** Last FM Track Stuff ***************/ |
338 | public GLib.List<LastFM.TrackInfo> tracks() { |
339 | var rv = new GLib.List<LastFM.TrackInfo>(); |
340 | foreach(var track in _tracks.values) |
341 | rv.append(track); |
342 | - |
343 | + |
344 | return rv; |
345 | } |
346 | - |
347 | + |
348 | public void save_track(LastFM.TrackInfo track) { |
349 | _tracks_lock.lock(); |
350 | if (track != null && track.name != null && track.artist != null) |
351 | _tracks.set(track.name.down() + " by " + track.artist.down(), track); |
352 | _tracks_lock.unlock(); |
353 | } |
354 | - |
355 | + |
356 | public bool track_info_exists(string track_key) { |
357 | return _tracks.get(track_key.down()) != null; |
358 | } |
359 | - |
360 | + |
361 | public LastFM.TrackInfo? get_track(string track_key) { |
362 | LastFM.TrackInfo? rv = null; |
363 | - |
364 | + |
365 | _tracks_lock.lock(); |
366 | if(track_info_exists(track_key.down())) |
367 | rv = _tracks.get(track_key.down()); |
368 | _tracks_lock.unlock(); |
369 | - |
370 | + |
371 | return rv; |
372 | } |
373 | - |
374 | + |
375 | /** Last.FM Api functions **/ |
376 | public static string fix_for_url(string fix) { |
377 | var fix1 = fix.replace(" ", "%20"); |
378 | @@ -190,40 +190,40 @@ |
379 | var fix0 = fix9.replace("*", "%2A"); |
380 | return fix0; |
381 | } |
382 | - |
383 | + |
384 | public string generate_md5(string text) { |
385 | return GLib.Checksum.compute_for_string(ChecksumType.MD5, text, text.length); |
386 | } |
387 | - |
388 | + |
389 | public string generate_getsession_signature(string token) { |
390 | return generate_md5("api_key" + API + "methodauth.getSessiontoken" + token + SECRET); |
391 | } |
392 | - |
393 | + |
394 | public string generate_tracklove_signature(string artist, string track) { |
395 | return generate_md5("api_key" + API + "artist" + artist + "methodtrack.lovesk" + lastfm_settings.session_key + "track" + track + SECRET); |
396 | } |
397 | - |
398 | + |
399 | public string generate_trackban_signature(string artist, string track) { |
400 | return generate_md5("api_key" + API + "artist" + artist + "methodtrack.bansk" + lastfm_settings.session_key + "track" + track + SECRET); |
401 | } |
402 | - |
403 | + |
404 | public string generate_trackscrobble_signature(string artist, string track, int timestamp) { |
405 | return generate_md5("api_key" + API + "artist" + artist + "methodtrack.scrobblesk" + lastfm_settings.session_key + "timestamp" + timestamp.to_string() + "track" + track + SECRET); |
406 | } |
407 | - |
408 | + |
409 | public string generate_trackupdatenowplaying_signature(string artist, string track) { |
410 | return generate_md5("api_key" + API + "artist" + artist + "methodtrack.updateNowPlayingsk" + lastfm_settings.session_key + "track" + track + SECRET); |
411 | } |
412 | - |
413 | + |
414 | public string? getToken() { |
415 | var url = "http://ws.audioscrobbler.com/2.0/?method=auth.gettoken&api_key=" + API; |
416 | - |
417 | + |
418 | Xml.Doc* doc = Parser.parse_file (url); |
419 | if(doc == null) return null; |
420 | - |
421 | + |
422 | Xml.Node* root = doc->get_root_element(); |
423 | if(root == null) return null; |
424 | - |
425 | + |
426 | for (Xml.Node* iter = root->children; iter != null; iter = iter->next) { |
427 | if(iter->name == "token") { |
428 | return iter->get_content(); |
429 | @@ -231,19 +231,19 @@ |
430 | } |
431 | return null; |
432 | } |
433 | - |
434 | + |
435 | public string? getSessionKey(string token) { |
436 | var sig = generate_getsession_signature(token); |
437 | var url = "http://ws.audioscrobbler.com/2.0/?method=auth.getSession&api_key=" + API + "&api_sig=" + sig + "&token=" + token; |
438 | - |
439 | + |
440 | message ("url: %s\n", url); |
441 | - |
442 | + |
443 | Xml.Doc* doc = Parser.parse_file (url); |
444 | if(doc == null) return null; |
445 | - |
446 | + |
447 | Xml.Node* root = doc->get_root_element(); |
448 | if(root == null) return null; |
449 | - |
450 | + |
451 | for (Xml.Node* iter = root->children; iter != null; iter = iter->next) { |
452 | if(iter->name == "session") { |
453 | for(Xml.Node* n = iter->children; n != null; n = n->next) { |
454 | @@ -255,18 +255,18 @@ |
455 | } |
456 | return null; |
457 | } |
458 | - |
459 | + |
460 | public bool loveTrack(string title, string artist) { |
461 | if(lastfm_settings.session_key == null || lastfm_settings.session_key == "") { |
462 | warning ("User tried to ban a track, but is not logged into Last FM\n"); |
463 | return false; |
464 | } |
465 | - |
466 | + |
467 | var uri = "http://ws.audioscrobbler.com/2.0/?api_key=" + API + "&api_sig=" + generate_tracklove_signature(artist, title) + "&artist=" + fix_for_url(artist) + "&method=track.love&sk=" + lastfm_settings.session_key + "&track=" + fix_for_url(title); |
468 | - |
469 | + |
470 | Soup.SessionSync session = new Soup.SessionSync(); |
471 | Soup.Message message = new Soup.Message ("POST", uri); |
472 | - |
473 | + |
474 | var headers = new Soup.MessageHeaders(MessageHeadersType.REQUEST); |
475 | headers.append("api_key", API); |
476 | headers.append("api_sig", generate_tracklove_signature(artist, title)); |
477 | @@ -274,29 +274,29 @@ |
478 | headers.append("method", "track.love"); |
479 | headers.append("sk", lastfm_settings.session_key); |
480 | headers.append("track", title); |
481 | - |
482 | + |
483 | message.request_headers = headers; |
484 | - |
485 | + |
486 | /* send the HTTP request */ |
487 | session.send_message(message); |
488 | - |
489 | + |
490 | if(message.response_body.length == 0) |
491 | return false; |
492 | - |
493 | + |
494 | return true; |
495 | } |
496 | - |
497 | + |
498 | public bool banTrack(string title, string artist) { |
499 | if(lastfm_settings.session_key == null || lastfm_settings.session_key == "") { |
500 | warning ("User tried to ban a track, but is not logged into Last FM\n"); |
501 | return false; |
502 | } |
503 | - |
504 | + |
505 | var uri = "http://ws.audioscrobbler.com/2.0/?api_key=" + API + "&api_sig=" + generate_trackban_signature(artist, title) + "&artist=" + fix_for_url(artist) + "&method=track.ban&sk=" + lastfm_settings.session_key + "&track=" + fix_for_url(title); |
506 | - |
507 | + |
508 | Soup.SessionSync session = new Soup.SessionSync(); |
509 | Soup.Message message = new Soup.Message ("POST", uri); |
510 | - |
511 | + |
512 | var headers = new Soup.MessageHeaders(MessageHeadersType.REQUEST); |
513 | headers.append("api_key", API); |
514 | headers.append("api_sig", generate_trackban_signature(artist, title)); |
515 | @@ -304,31 +304,31 @@ |
516 | headers.append("method", "track.ban"); |
517 | headers.append("sk", lastfm_settings.session_key); |
518 | headers.append("track", title); |
519 | - |
520 | + |
521 | message.request_headers = headers; |
522 | - |
523 | + |
524 | /* send the HTTP request */ |
525 | session.send_message(message); |
526 | - |
527 | + |
528 | if(message.response_body.length == 0) |
529 | return false; |
530 | - |
531 | + |
532 | return true; |
533 | } |
534 | - |
535 | + |
536 | /** Fetches the current track's info from last.fm |
537 | */ |
538 | |
539 | Mutex fetch_info_guard; |
540 | |
541 | public void fetchCurrentTrackInfo() { |
542 | - Noise.Threads.add (track_thread_function); |
543 | + Idle.add (track_thread_function); |
544 | } |
545 | - |
546 | - void track_thread_function () { |
547 | + |
548 | + private bool track_thread_function () { |
549 | var current_media = Noise.App.player.media_info.media; |
550 | if (current_media == null) |
551 | - return; |
552 | + return false; |
553 | |
554 | string album_artist_s = current_media.album_artist; |
555 | string track_s = current_media.title; |
556 | @@ -350,17 +350,19 @@ |
557 | Noise.App.player.media_info.track = track; |
558 | fetch_info_guard.unlock (); |
559 | } |
560 | + |
561 | + return false; |
562 | } |
563 | - |
564 | + |
565 | public void fetchCurrentAlbumInfo() { |
566 | - Noise.Threads.add (album_thread_function); |
567 | + Idle.add (album_thread_function); |
568 | } |
569 | |
570 | - void album_thread_function () { |
571 | + private bool album_thread_function () { |
572 | |
573 | var current_media = Noise.App.player.media_info.media; |
574 | if (current_media == null) |
575 | - return; |
576 | + return false; |
577 | |
578 | string album_artist_s = current_media.album_artist; |
579 | string album_s = current_media.album; |
580 | @@ -373,10 +375,10 @@ |
581 | // This does the fetching to internet. may take a few seconds |
582 | var album = new LastFM.AlbumInfo.with_info (album_artist_s, album_s); |
583 | |
584 | - if (album != null) |
585 | + if (album != null) |
586 | save_album (album); |
587 | else |
588 | - return; |
589 | + return false; |
590 | |
591 | /* If on same song, update Noise.App.player.media_info.album */ |
592 | fetch_info_guard.lock (); |
593 | @@ -391,33 +393,33 @@ |
594 | var coverart_cache = Noise.CoverartCache.instance; |
595 | |
596 | if (coverart_cache.has_image (current_media)) |
597 | - return; |
598 | + return false; |
599 | |
600 | if (album.image_uri != "") { |
601 | message ("Caching last.fm image from URL: %s", album.image_uri); |
602 | |
603 | - fetch_info_guard.lock (); |
604 | var image_file = File.new_for_uri (album.image_uri); |
605 | - coverart_cache.cache_image_from_file (current_media, image_file); |
606 | - fetch_info_guard.unlock (); |
607 | + coverart_cache.cache_image_from_file_async.begin (current_media, image_file); |
608 | } |
609 | |
610 | } |
611 | else { |
612 | message ("Not fetching album info or art"); |
613 | } |
614 | + |
615 | + return false; |
616 | } |
617 | - |
618 | + |
619 | /** Fetches artist info for currently playing song's artist |
620 | */ |
621 | public void fetchCurrentArtistInfo() { |
622 | - Noise.Threads.add (artist_thread_function); |
623 | + Idle.add (artist_thread_function); |
624 | } |
625 | - |
626 | - void artist_thread_function () { |
627 | + |
628 | + private bool artist_thread_function () { |
629 | var current_media = Noise.App.player.media_info.media; |
630 | if (current_media == null) |
631 | - return; |
632 | + return false; |
633 | |
634 | string album_artist_s = current_media.album_artist; |
635 | if (album_artist_s == "") |
636 | @@ -442,30 +444,32 @@ |
637 | |
638 | fetch_info_guard.unlock (); |
639 | } |
640 | + |
641 | + return false; |
642 | } |
643 | - |
644 | + |
645 | /** Update's the user's currently playing track on last.fm |
646 | - * |
647 | + * |
648 | */ |
649 | public void postNowPlaying() { |
650 | - Noise.Threads.add (update_nowplaying_thread_function); |
651 | + Idle.add (update_nowplaying_thread_function); |
652 | } |
653 | - |
654 | - void update_nowplaying_thread_function() { |
655 | + |
656 | + private bool update_nowplaying_thread_function() { |
657 | if(lastfm_settings.session_key == null || lastfm_settings.session_key == "") { |
658 | message ("Last.FM user not logged in\n"); |
659 | - return; |
660 | + return false; |
661 | } |
662 | if(!Noise.App.player.media_active) |
663 | - return; |
664 | - |
665 | + return false; |
666 | + |
667 | var artist = Noise.App.player.media_info.media.artist; |
668 | var title = Noise.App.player.media_info.media.title; |
669 | var uri = "http://ws.audioscrobbler.com/2.0/?api_key=" + API + "&api_sig=" + generate_trackupdatenowplaying_signature(artist, title) + "&artist=" + fix_for_url(artist) + "&method=track.updateNowPlaying&sk=" + lastfm_settings.session_key + "&track=" + fix_for_url(title); |
670 | - |
671 | + |
672 | Soup.SessionSync session = new Soup.SessionSync(); |
673 | Soup.Message message = new Soup.Message ("POST", uri); |
674 | - |
675 | + |
676 | var headers = new Soup.MessageHeaders(MessageHeadersType.REQUEST); |
677 | headers.append("api_key", API); |
678 | headers.append("api_sig", generate_trackupdatenowplaying_signature(artist, title)); |
679 | @@ -473,38 +477,40 @@ |
680 | headers.append("method", "track.updateNowPlaying"); |
681 | headers.append("sk", lastfm_settings.session_key); |
682 | headers.append("track", title); |
683 | - |
684 | + |
685 | message.request_headers = headers; |
686 | - |
687 | + |
688 | /* send the HTTP request */ |
689 | session.send_message(message); |
690 | + |
691 | + return false; |
692 | } |
693 | - |
694 | + |
695 | /** |
696 | * Scrobbles the currently playing track to last.fm |
697 | */ |
698 | public void postScrobbleTrack() { |
699 | - Noise.Threads.add (scrobble_thread_function); |
700 | + Idle.add (scrobble_thread_function); |
701 | } |
702 | - |
703 | - void scrobble_thread_function () { |
704 | + |
705 | + private bool scrobble_thread_function () { |
706 | if(lastfm_settings.session_key == null || lastfm_settings.session_key == "") { |
707 | message ("Last.FM user not logged in\n"); |
708 | - return; |
709 | + return false; |
710 | } |
711 | if(!Noise.App.player.media_active) |
712 | - return; |
713 | + return false; |
714 | |
715 | var current_media = Noise.App.player.media_info.media; |
716 | - |
717 | + |
718 | var timestamp = (int)time_t(); |
719 | var artist = current_media.artist; |
720 | var title = current_media.title; |
721 | var uri = "http://ws.audioscrobbler.com/2.0/?api_key=" + API + "&api_sig=" + generate_trackscrobble_signature(artist, title, timestamp) + "&artist=" + fix_for_url(artist) + "&method=track.scrobble&sk=" + lastfm_settings.session_key + "×tamp=" + timestamp.to_string() + "&track=" + fix_for_url(title); |
722 | - |
723 | + |
724 | Soup.SessionSync session = new Soup.SessionSync(); |
725 | Soup.Message message = new Soup.Message ("POST", uri); |
726 | - |
727 | + |
728 | var headers = new Soup.MessageHeaders(MessageHeadersType.REQUEST); |
729 | headers.append("api_key", API); |
730 | headers.append("api_sig", generate_trackscrobble_signature(artist, title, timestamp)); |
731 | @@ -513,17 +519,19 @@ |
732 | headers.append("sk", lastfm_settings.session_key); |
733 | headers.append("timestamp", timestamp.to_string()); |
734 | headers.append("track", title); |
735 | - |
736 | + |
737 | message.request_headers = headers; |
738 | - |
739 | + |
740 | /* send the HTTP request */ |
741 | session.send_message(message); |
742 | + |
743 | + return false; |
744 | } |
745 | - |
746 | + |
747 | public void fetchCurrentSimilarSongs() { |
748 | similarMedias.queryForSimilar(Noise.App.player.media_info.media); |
749 | } |
750 | - |
751 | + |
752 | void similar_retrieved_signal(LinkedList<int> similarIDs, LinkedList<Noise.Media> similarDont) { |
753 | similar_retrieved(similarIDs, similarDont); |
754 | } |
755 | |
756 | === modified file 'src/GStreamer/GStreamerTagger.vala' |
757 | --- src/GStreamer/GStreamerTagger.vala 2012-08-06 17:12:33 +0000 |
758 | +++ src/GStreamer/GStreamerTagger.vala 2012-10-06 03:24:21 +0000 |
759 | @@ -24,146 +24,147 @@ |
760 | using Gee; |
761 | |
762 | public class Noise.GStreamerTagger : GLib.Object { |
763 | - LibraryManager lm; |
764 | - static int DISCOVER_SET_SIZE = 50; |
765 | - Gst.Discoverer d; |
766 | - HashMap<string, int> uri_to_id; |
767 | - LinkedList<string> path_queue; |
768 | - |
769 | - public signal void media_imported(Media m); |
770 | - public signal void import_error(string file); |
771 | - public signal void queue_finished(); |
772 | - |
773 | - bool cancelled; |
774 | - |
775 | - public GStreamerTagger(LibraryManager lm) { |
776 | - this.lm = lm; |
777 | + |
778 | + LibraryManager lm; |
779 | + static int DISCOVER_SET_SIZE = 50; |
780 | + Gst.Discoverer d; |
781 | + HashMap<string, int> uri_to_id; |
782 | + LinkedList<string> path_queue; |
783 | + |
784 | + public signal void media_imported(Media m); |
785 | + public signal void import_error(string file); |
786 | + public signal void queue_finished(); |
787 | + |
788 | + bool cancelled; |
789 | + |
790 | + public GStreamerTagger(LibraryManager lm) { |
791 | + this.lm = lm; |
792 | |
793 | d = create_discoverer (); |
794 | |
795 | - |
796 | - uri_to_id = new HashMap<string, int>(); |
797 | - path_queue = new LinkedList<string>(); |
798 | - } |
799 | + |
800 | + uri_to_id = new HashMap<string, int>(); |
801 | + path_queue = new LinkedList<string>(); |
802 | + } |
803 | |
804 | private Gst.Discoverer? create_discoverer () { |
805 | Gst.Discoverer? discoverer = null; |
806 | |
807 | - try { |
808 | - discoverer = new Gst.Discoverer ((ClockTime)(30*Gst.SECOND)); |
809 | - } |
810 | - catch (Error err) { |
811 | - critical ("Metadata reader could not create discoverer object: %s\n", err.message); |
812 | - } |
813 | + try { |
814 | + discoverer = new Gst.Discoverer ((ClockTime)(30*Gst.SECOND)); |
815 | + } |
816 | + catch (Error err) { |
817 | + critical ("Metadata reader could not create discoverer object: %s\n", err.message); |
818 | + } |
819 | |
820 | - discoverer.discovered.connect (import_media); |
821 | - discoverer.finished.connect (finished); |
822 | + discoverer.discovered.connect (import_media); |
823 | + discoverer.finished.connect (finished); |
824 | |
825 | return discoverer; |
826 | } |
827 | |
828 | - void finished() { |
829 | - if(!cancelled && path_queue.size > 0) { |
830 | - d = create_discoverer (); |
831 | - |
832 | - d.start(); |
833 | - for(int i = 0; i < DISCOVER_SET_SIZE && i < path_queue.size; ++i) { |
834 | - d.discover_uri_async("file://" + path_queue.get(i)); |
835 | - } |
836 | - } |
837 | - else { |
838 | - debug("queue finished\n"); |
839 | - queue_finished(); |
840 | - } |
841 | - } |
842 | - |
843 | - public void cancel_operations() { |
844 | - cancelled = true; |
845 | - } |
846 | - |
847 | - public void discoverer_import_media (LinkedList<string> files) { |
848 | - int size = 0; |
849 | - cancelled = false; |
850 | - path_queue.clear(); |
851 | - |
852 | - foreach(string s in files) { |
853 | - path_queue.add(s); |
854 | - |
855 | - d.start(); |
856 | - if(size < DISCOVER_SET_SIZE) { |
857 | - ++size; |
858 | - d.discover_uri_async("file://" + s); |
859 | - } |
860 | - } |
861 | - } |
862 | - |
863 | - void import_media(DiscovererInfo info, Error err) { |
864 | - path_queue.remove(info.get_uri().replace("file://","")); |
865 | + void finished() { |
866 | + if(!cancelled && path_queue.size > 0) { |
867 | + d = create_discoverer (); |
868 | + |
869 | + d.start(); |
870 | + for(int i = 0; i < DISCOVER_SET_SIZE && i < path_queue.size; ++i) { |
871 | + d.discover_uri_async("file://" + path_queue.get(i)); |
872 | + } |
873 | + } |
874 | + else { |
875 | + debug("queue finished\n"); |
876 | + queue_finished(); |
877 | + } |
878 | + } |
879 | + |
880 | + public void cancel_operations() { |
881 | + cancelled = true; |
882 | + } |
883 | + |
884 | + public void discoverer_import_media (LinkedList<string> files) { |
885 | + int size = 0; |
886 | + cancelled = false; |
887 | + path_queue.clear(); |
888 | + |
889 | + foreach(string s in files) { |
890 | + path_queue.add(s); |
891 | + |
892 | + d.start(); |
893 | + if(size < DISCOVER_SET_SIZE) { |
894 | + ++size; |
895 | + d.discover_uri_async("file://" + s); |
896 | + } |
897 | + } |
898 | + } |
899 | + |
900 | + private async void import_media(DiscovererInfo info, Error err) { |
901 | + path_queue.remove(info.get_uri().replace("file://","")); |
902 | |
903 | Media? s = null; |
904 | |
905 | - if(info != null && info.get_tags() != null) { |
906 | - s = new Media(info.get_uri()); |
907 | - |
908 | - string title = ""; |
909 | - string artist, composer, album_artist, album, grouping, genre, comment, lyrics; |
910 | - uint track, track_count, album_number, album_count, bitrate, rating; |
911 | - double bpm; |
912 | - GLib.Date? date = GLib.Date(); |
913 | - |
914 | - // get title, artist, album artist, album, genre, comment, lyrics strings |
915 | - if(info.get_tags().get_string(TAG_TITLE, out title)) |
916 | - s.title = title; |
917 | - if(info.get_tags().get_string(TAG_ARTIST, out artist)) |
918 | - s.artist = artist; |
919 | - if(info.get_tags().get_string(TAG_COMPOSER, out composer)) |
920 | - s.composer = composer; |
921 | - |
922 | - if(info.get_tags().get_string(TAG_ALBUM_ARTIST, out album_artist)) |
923 | - s.album_artist = album_artist; |
924 | - else |
925 | - s.album_artist = s.artist; |
926 | - |
927 | - if(info.get_tags().get_string(TAG_ALBUM, out album)) |
928 | - s.album = album; |
929 | - if(info.get_tags().get_string(TAG_GROUPING, out grouping)) |
930 | - s.grouping = grouping; |
931 | - if(info.get_tags().get_string(TAG_GENRE, out genre)) |
932 | - s.genre = genre; |
933 | - if(info.get_tags().get_string(TAG_COMMENT, out comment)) |
934 | - s.comment = comment; |
935 | - if(info.get_tags().get_string(TAG_LYRICS, out lyrics)) |
936 | - s.lyrics = lyrics; |
937 | - |
938 | - // get the year |
939 | - if(info.get_tags().get_date(TAG_DATE, out date)) { |
940 | - if(date != null) |
941 | - s.year = (int)date.get_year(); |
942 | - } |
943 | - // get track/album number/count, bitrating, rating, bpm |
944 | - if(info.get_tags().get_uint(TAG_TRACK_NUMBER, out track)) |
945 | - s.track = (int)track; |
946 | - if(info.get_tags().get_uint(TAG_TRACK_COUNT, out track_count)) |
947 | - s.track_count = track_count; |
948 | - |
949 | - if(info.get_tags().get_uint(TAG_ALBUM_VOLUME_NUMBER, out album_number)) |
950 | - s.album_number = album_number; |
951 | - if(info.get_tags().get_uint(TAG_ALBUM_VOLUME_COUNT, out album_count)) |
952 | - s.album_count = album_count; |
953 | - |
954 | - if(info.get_tags().get_uint(TAG_BITRATE, out bitrate)) |
955 | - s.bitrate = (int)(bitrate/1000); |
956 | - if(info.get_tags().get_uint(TAG_USER_RATING, out rating)) |
957 | - s.rating = (int)((rating > 0 && rating <= 5) ? rating : 0); |
958 | - if(info.get_tags().get_double(TAG_BEATS_PER_MINUTE, out bpm)) |
959 | - s.bpm = (int)bpm; |
960 | - |
961 | - |
962 | - // get length |
963 | + if(info != null && info.get_tags() != null) { |
964 | + s = new Media(info.get_uri()); |
965 | + |
966 | + string title = ""; |
967 | + string artist, composer, album_artist, album, grouping, genre, comment, lyrics; |
968 | + uint track, track_count, album_number, album_count, bitrate, rating; |
969 | + double bpm; |
970 | + GLib.Date? date = GLib.Date(); |
971 | + |
972 | + // get title, artist, album artist, album, genre, comment, lyrics strings |
973 | + if(info.get_tags().get_string(TAG_TITLE, out title)) |
974 | + s.title = title; |
975 | + if(info.get_tags().get_string(TAG_ARTIST, out artist)) |
976 | + s.artist = artist; |
977 | + if(info.get_tags().get_string(TAG_COMPOSER, out composer)) |
978 | + s.composer = composer; |
979 | + |
980 | + if(info.get_tags().get_string(TAG_ALBUM_ARTIST, out album_artist)) |
981 | + s.album_artist = album_artist; |
982 | + else |
983 | + s.album_artist = s.artist; |
984 | + |
985 | + if(info.get_tags().get_string(TAG_ALBUM, out album)) |
986 | + s.album = album; |
987 | + if(info.get_tags().get_string(TAG_GROUPING, out grouping)) |
988 | + s.grouping = grouping; |
989 | + if(info.get_tags().get_string(TAG_GENRE, out genre)) |
990 | + s.genre = genre; |
991 | + if(info.get_tags().get_string(TAG_COMMENT, out comment)) |
992 | + s.comment = comment; |
993 | + if(info.get_tags().get_string(TAG_LYRICS, out lyrics)) |
994 | + s.lyrics = lyrics; |
995 | + |
996 | + // get the year |
997 | + if(info.get_tags().get_date(TAG_DATE, out date)) { |
998 | + if(date != null) |
999 | + s.year = (int)date.get_year(); |
1000 | + } |
1001 | + // get track/album number/count, bitrating, rating, bpm |
1002 | + if(info.get_tags().get_uint(TAG_TRACK_NUMBER, out track)) |
1003 | + s.track = (int)track; |
1004 | + if(info.get_tags().get_uint(TAG_TRACK_COUNT, out track_count)) |
1005 | + s.track_count = track_count; |
1006 | + |
1007 | + if(info.get_tags().get_uint(TAG_ALBUM_VOLUME_NUMBER, out album_number)) |
1008 | + s.album_number = album_number; |
1009 | + if(info.get_tags().get_uint(TAG_ALBUM_VOLUME_COUNT, out album_count)) |
1010 | + s.album_count = album_count; |
1011 | + |
1012 | + if(info.get_tags().get_uint(TAG_BITRATE, out bitrate)) |
1013 | + s.bitrate = (int)(bitrate/1000); |
1014 | + if(info.get_tags().get_uint(TAG_USER_RATING, out rating)) |
1015 | + s.rating = (int)((rating > 0 && rating <= 5) ? rating : 0); |
1016 | + if(info.get_tags().get_double(TAG_BEATS_PER_MINUTE, out bpm)) |
1017 | + s.bpm = (int)bpm; |
1018 | + |
1019 | + |
1020 | + // get length |
1021 | uint64 duration = info.get_duration (); |
1022 | |
1023 | if (duration == 0) |
1024 | - info.get_tags ().get_uint64 (TAG_DURATION, out duration); |
1025 | + info.get_tags ().get_uint64 (TAG_DURATION, out duration); |
1026 | |
1027 | // we convert from nanoseconds (10E-9) to miliseconds (10E-3); |
1028 | s.length = (uint)((duration * Numeric.MILI_INV) / Numeric.NANO_INV); |
1029 | @@ -188,63 +189,63 @@ |
1030 | } |
1031 | |
1032 | // Get cover art |
1033 | - import_art (s, info); |
1034 | - } |
1035 | - else { |
1036 | - s = taglib_import_media(info.get_uri()); |
1037 | + import_art_async.begin (s, info); |
1038 | + } |
1039 | + else { |
1040 | + s = taglib_import_media(info.get_uri()); |
1041 | |
1042 | - if (s == null) { |
1043 | - import_error (info.get_uri().replace("file://", "")); |
1044 | - return; |
1045 | + if (s == null) { |
1046 | + import_error (info.get_uri().replace("file://", "")); |
1047 | + return; |
1048 | } |
1049 | - } |
1050 | - |
1051 | - // get the size |
1052 | - s.file_size = FileUtils.get_size (s.file); |
1053 | - s.date_added = (int)time_t(); |
1054 | - |
1055 | - media_imported(s); |
1056 | - } |
1057 | - |
1058 | - public Media? taglib_import_media(string uri) { |
1059 | - Media s = new Media(uri); |
1060 | - TagLib.File tag_file; |
1061 | - |
1062 | - tag_file = new TagLib.File(uri.replace("file://","")); |
1063 | - |
1064 | - if(tag_file != null && tag_file.tag != null && tag_file.audioproperties != null) { |
1065 | - try { |
1066 | - s.title = tag_file.tag.title; |
1067 | - s.artist = tag_file.tag.artist; |
1068 | - s.album = tag_file.tag.album; |
1069 | - s.genre = tag_file.tag.genre; |
1070 | - s.comment = tag_file.tag.comment; |
1071 | - s.year = (int)tag_file.tag.year; |
1072 | - s.track = (int)tag_file.tag.track; |
1073 | - s.bitrate = tag_file.audioproperties.bitrate; |
1074 | - |
1075 | - s.length = (uint)(tag_file.audioproperties.length * Numeric.MILI_INV); |
1076 | - s.samplerate = tag_file.audioproperties.samplerate; |
1077 | - } |
1078 | - finally { |
1079 | - if(s.title == null || s.title == "") { |
1080 | - string[] paths = uri.split("/", 0); |
1081 | - s.title = paths[paths.length - 1]; |
1082 | - } |
1083 | - if(s.artist == null || s.artist == "") s.artist = Media.UNKNOWN_ARTIST; |
1084 | - |
1085 | - s.album_artist = s.artist; |
1086 | - s.album_number = 1; |
1087 | - } |
1088 | - } |
1089 | - else { |
1090 | - return null; |
1091 | - } |
1092 | - |
1093 | - return s; |
1094 | - } |
1095 | - |
1096 | - void import_art (Media m, DiscovererInfo info) { |
1097 | + } |
1098 | + |
1099 | + // get the size |
1100 | + s.file_size = yield FileUtils.get_size_async (s.file); |
1101 | + s.date_added = (int)time_t(); |
1102 | + |
1103 | + media_imported(s); |
1104 | + } |
1105 | + |
1106 | + public Media? taglib_import_media(string uri) { |
1107 | + Media s = new Media(uri); |
1108 | + TagLib.File tag_file; |
1109 | + |
1110 | + tag_file = new TagLib.File(uri.replace("file://","")); |
1111 | + |
1112 | + if(tag_file != null && tag_file.tag != null && tag_file.audioproperties != null) { |
1113 | + try { |
1114 | + s.title = tag_file.tag.title; |
1115 | + s.artist = tag_file.tag.artist; |
1116 | + s.album = tag_file.tag.album; |
1117 | + s.genre = tag_file.tag.genre; |
1118 | + s.comment = tag_file.tag.comment; |
1119 | + s.year = (int)tag_file.tag.year; |
1120 | + s.track = (int)tag_file.tag.track; |
1121 | + s.bitrate = tag_file.audioproperties.bitrate; |
1122 | + |
1123 | + s.length = (uint)(tag_file.audioproperties.length * Numeric.MILI_INV); |
1124 | + s.samplerate = tag_file.audioproperties.samplerate; |
1125 | + } |
1126 | + finally { |
1127 | + if(s.title == null || s.title == "") { |
1128 | + string[] paths = uri.split("/", 0); |
1129 | + s.title = paths[paths.length - 1]; |
1130 | + } |
1131 | + if(s.artist == null || s.artist == "") s.artist = Media.UNKNOWN_ARTIST; |
1132 | + |
1133 | + s.album_artist = s.artist; |
1134 | + s.album_number = 1; |
1135 | + } |
1136 | + } |
1137 | + else { |
1138 | + return null; |
1139 | + } |
1140 | + |
1141 | + return s; |
1142 | + } |
1143 | + |
1144 | + private async void import_art_async (Media m, DiscovererInfo info) { |
1145 | var cache = CoverartCache.instance; |
1146 | |
1147 | if (cache.has_image (m)) |
1148 | @@ -253,10 +254,10 @@ |
1149 | var pix = get_image (info.get_tags ()); |
1150 | |
1151 | if (pix != null) |
1152 | - cache.cache_image (m, pix); |
1153 | + yield cache.cache_image_async (m, pix); |
1154 | else |
1155 | warning ("import_art: null pixbuf"); |
1156 | - } |
1157 | + } |
1158 | |
1159 | private static Gdk.Pixbuf? get_image (Gst.TagList tag) { |
1160 | Gst.Buffer? buffer = null; |
1161 | @@ -274,14 +275,14 @@ |
1162 | continue; |
1163 | |
1164 | int image_type; |
1165 | - structure.get_enum ("image-type", typeof (Gst.TagImageType), out image_type); |
1166 | + structure.get_enum ("image-type", typeof (Gst.TagImageType), out image_type); |
1167 | |
1168 | - if (image_type == Gst.TagImageType.FRONT_COVER) { |
1169 | - buffer = loop_buffer; |
1170 | - break; |
1171 | - } else if (image_type == Gst.TagImageType.UNDEFINED || buffer == null) { |
1172 | + if (image_type == Gst.TagImageType.FRONT_COVER) { |
1173 | + buffer = loop_buffer; |
1174 | + break; |
1175 | + } else if (image_type == Gst.TagImageType.UNDEFINED || buffer == null) { |
1176 | buffer = loop_buffer; |
1177 | - } |
1178 | + } |
1179 | } |
1180 | |
1181 | if (buffer == null) { |
1182 | @@ -306,100 +307,100 @@ |
1183 | |
1184 | return pix; |
1185 | } |
1186 | - |
1187 | - public bool save_media(Media s) { |
1188 | - return false; |
1189 | - |
1190 | - /*Gst.Pipeline pipe = new Pipeline("pipe"); |
1191 | - Element src = Element.make_from_uri(URIType.SRC, "file://" + s.file, null); |
1192 | - Element decoder = ElementFactory.make("decodebin", "decoder"); |
1193 | - |
1194 | - GLib.Signal.connect(decoder, "new-decoded-pad", (GLib.Callback)newDecodedPad, this); |
1195 | - |
1196 | - if(!((Gst.Bin)pipe).add_many(src, decoder)) { |
1197 | - message ("Could not add src and decoder to pipeline to save metadata\n"); |
1198 | - return false; |
1199 | - } |
1200 | - |
1201 | - if(!src.link_many(decoder)) { |
1202 | - message ("Could not link src to decoder to save metadata\n"); |
1203 | - return false; |
1204 | - } |
1205 | - |
1206 | - |
1207 | - Gst.Element queue = ElementFactory.make("queue", "queue"); |
1208 | - Gst.Element queue2 = ElementFactory.make("queue", "queue2"); |
1209 | - |
1210 | - if(queue == null || queue2 == null) { |
1211 | - message ("could not add create queues to save metadata\n"); |
1212 | - return false; |
1213 | - } |
1214 | - |
1215 | - if(!((Gst.Bin)pipe).add_many(queue, queue2)) { |
1216 | - warning ("Could not add queue's to save metadata\n"); |
1217 | - return false; |
1218 | - } |
1219 | - |
1220 | - queue.set("max-size-time", 120 * Gst.SECOND); |
1221 | - |
1222 | - |
1223 | - |
1224 | - |
1225 | - //Element encoder = new_element_from_uri(URIType.SINK, "file://" + s.file, null); |
1226 | - |
1227 | - Gst.TagList tags; |
1228 | - bool rv = true; |
1229 | - //long day; |
1230 | - |
1231 | - tags = new TagList(); |
1232 | - tags.add(TagMergeMode.REPLACE, TAG_TITLE, s.title, |
1233 | - TAG_ARTIST, s.artist, |
1234 | - TAG_COMPOSER, s.composer, |
1235 | - TAG_ALBUM_ARTIST, s.album_artist, |
1236 | - TAG_ALBUM, s.album, |
1237 | - TAG_GROUPING, s.grouping, |
1238 | - TAG_GENRE, s.genre, |
1239 | - TAG_COMMENT, s.comment, |
1240 | - TAG_LYRICS, s.lyrics, |
1241 | - TAG_TRACK_NUMBER, s.track, |
1242 | - TAG_TRACK_COUNT, s.track_count, |
1243 | - TAG_ALBUM_VOLUME_NUMBER, s.album_number, |
1244 | - TAG_ALBUM_VOLUME_COUNT, s.album_count, |
1245 | - TAG_USER_RATING, s.rating); |
1246 | - |
1247 | - /* fetch date, set new year to s.year, set date */ |
1248 | - |
1249 | - // now find a tag setter interface and use it |
1250 | - /*Gst.Iterator iter; |
1251 | - bool done; |
1252 | - |
1253 | - iter = ((Gst.Bin)pipeline).iterate_all_by_interface(typeof(Gst.TagSetter)); |
1254 | - done = false; |
1255 | - while (!done) { |
1256 | - Gst.TagSetter tagger = null; |
1257 | - |
1258 | - switch (iter.next(out tagger) { |
1259 | - case GST_ITERATOR_OK: |
1260 | - tagger.merge_tags (tags, GST_TAG_MERGE_REPLACE_ALL); |
1261 | - break; |
1262 | - case GST_ITERATOR_RESYNC: |
1263 | - iter.resync(); |
1264 | - break; |
1265 | - case GST_ITERATOR_ERROR: |
1266 | - warning("Could not update metadata on media\n"); |
1267 | - rv = false; |
1268 | - done = true; |
1269 | - break; |
1270 | - case GST_ITERATOR_DONE: |
1271 | - done = true; |
1272 | - break; |
1273 | - } |
1274 | - } |
1275 | - |
1276 | - return rv; */ |
1277 | - } |
1278 | - |
1279 | - public bool save_embeddeart_d(Gdk.Pixbuf pix) { |
1280 | - return false; |
1281 | - } |
1282 | + |
1283 | + public bool save_media(Media s) { |
1284 | + return false; |
1285 | + |
1286 | + /*Gst.Pipeline pipe = new Pipeline("pipe"); |
1287 | + Element src = Element.make_from_uri(URIType.SRC, "file://" + s.file, null); |
1288 | + Element decoder = ElementFactory.make("decodebin", "decoder"); |
1289 | + |
1290 | + GLib.Signal.connect(decoder, "new-decoded-pad", (GLib.Callback)newDecodedPad, this); |
1291 | + |
1292 | + if(!((Gst.Bin)pipe).add_many(src, decoder)) { |
1293 | + message ("Could not add src and decoder to pipeline to save metadata\n"); |
1294 | + return false; |
1295 | + } |
1296 | + |
1297 | + if(!src.link_many(decoder)) { |
1298 | + message ("Could not link src to decoder to save metadata\n"); |
1299 | + return false; |
1300 | + } |
1301 | + |
1302 | + |
1303 | + Gst.Element queue = ElementFactory.make("queue", "queue"); |
1304 | + Gst.Element queue2 = ElementFactory.make("queue", "queue2"); |
1305 | + |
1306 | + if(queue == null || queue2 == null) { |
1307 | + message ("could not add create queues to save metadata\n"); |
1308 | + return false; |
1309 | + } |
1310 | + |
1311 | + if(!((Gst.Bin)pipe).add_many(queue, queue2)) { |
1312 | + warning ("Could not add queue's to save metadata\n"); |
1313 | + return false; |
1314 | + } |
1315 | + |
1316 | + queue.set("max-size-time", 120 * Gst.SECOND); |
1317 | + |
1318 | + |
1319 | + |
1320 | + |
1321 | + //Element encoder = new_element_from_uri(URIType.SINK, "file://" + s.file, null); |
1322 | + |
1323 | + Gst.TagList tags; |
1324 | + bool rv = true; |
1325 | + //long day; |
1326 | + |
1327 | + tags = new TagList(); |
1328 | + tags.add(TagMergeMode.REPLACE, TAG_TITLE, s.title, |
1329 | + TAG_ARTIST, s.artist, |
1330 | + TAG_COMPOSER, s.composer, |
1331 | + TAG_ALBUM_ARTIST, s.album_artist, |
1332 | + TAG_ALBUM, s.album, |
1333 | + TAG_GROUPING, s.grouping, |
1334 | + TAG_GENRE, s.genre, |
1335 | + TAG_COMMENT, s.comment, |
1336 | + TAG_LYRICS, s.lyrics, |
1337 | + TAG_TRACK_NUMBER, s.track, |
1338 | + TAG_TRACK_COUNT, s.track_count, |
1339 | + TAG_ALBUM_VOLUME_NUMBER, s.album_number, |
1340 | + TAG_ALBUM_VOLUME_COUNT, s.album_count, |
1341 | + TAG_USER_RATING, s.rating); |
1342 | + |
1343 | + /* fetch date, set new year to s.year, set date */ |
1344 | + |
1345 | + // now find a tag setter interface and use it |
1346 | + /*Gst.Iterator iter; |
1347 | + bool done; |
1348 | + |
1349 | + iter = ((Gst.Bin)pipeline).iterate_all_by_interface(typeof(Gst.TagSetter)); |
1350 | + done = false; |
1351 | + while (!done) { |
1352 | + Gst.TagSetter tagger = null; |
1353 | + |
1354 | + switch (iter.next(out tagger) { |
1355 | + case GST_ITERATOR_OK: |
1356 | + tagger.merge_tags (tags, GST_TAG_MERGE_REPLACE_ALL); |
1357 | + break; |
1358 | + case GST_ITERATOR_RESYNC: |
1359 | + iter.resync(); |
1360 | + break; |
1361 | + case GST_ITERATOR_ERROR: |
1362 | + warning("Could not update metadata on media\n"); |
1363 | + rv = false; |
1364 | + done = true; |
1365 | + break; |
1366 | + case GST_ITERATOR_DONE: |
1367 | + done = true; |
1368 | + break; |
1369 | + } |
1370 | + } |
1371 | + |
1372 | + return rv; */ |
1373 | + } |
1374 | + |
1375 | + public bool save_embeddeart_d(Gdk.Pixbuf pix) { |
1376 | + return false; |
1377 | + } |
1378 | } |
1379 | |
1380 | === modified file 'src/LibraryWindow.vala' |
1381 | --- src/LibraryWindow.vala 2012-09-25 20:11:04 +0000 |
1382 | +++ src/LibraryWindow.vala 2012-10-06 03:24:21 +0000 |
1383 | @@ -362,7 +362,7 @@ |
1384 | #endif |
1385 | } |
1386 | |
1387 | - public void show_notification_from_media (Media media) { |
1388 | + public async void show_notification_from_media_async (Media media) { |
1389 | if (media == null) |
1390 | return; |
1391 | |
1392 | @@ -372,8 +372,8 @@ |
1393 | Gdk.Pixbuf? pixbuf = null; |
1394 | |
1395 | try { |
1396 | - string path = CoverartCache.instance.get_cached_image_path_for_media (media); |
1397 | - pixbuf = new Gdk.Pixbuf.from_file_at_size (path, 64, 64); |
1398 | + var file = File.new_for_path (CoverartCache.instance.get_cached_image_path_for_media (media)); |
1399 | + pixbuf = yield PixbufUtils.get_pixbuf_from_file_at_scale_async (file, 64, 64, false); |
1400 | } catch (Error err) { |
1401 | // Media often doesn't have an associated album art, |
1402 | // so we shouldn't treat this as an unexpected error. |
1403 | @@ -383,9 +383,9 @@ |
1404 | show_notification (primary_text, secondary_text, pixbuf); |
1405 | } |
1406 | |
1407 | - private void notify_current_media () { |
1408 | + private async void notify_current_media_async () { |
1409 | if (App.player.media_info != null && App.player.media_info.media != null) |
1410 | - show_notification_from_media (App.player.media_info.media); |
1411 | + yield show_notification_from_media_async (App.player.media_info.media); |
1412 | } |
1413 | |
1414 | /** |
1415 | @@ -539,11 +539,17 @@ |
1416 | } |
1417 | |
1418 | |
1419 | + private bool update_sensitivities_pending = false; |
1420 | |
1421 | /** |
1422 | * This is handled more carefully inside each ViewWrapper object. |
1423 | */ |
1424 | public async void update_sensitivities () { |
1425 | + if (update_sensitivities_pending) |
1426 | + return; |
1427 | + |
1428 | + update_sensitivities_pending = true; |
1429 | + |
1430 | Idle.add_full (Priority.HIGH_IDLE, update_sensitivities.callback); |
1431 | yield; |
1432 | |
1433 | @@ -590,7 +596,10 @@ |
1434 | |
1435 | if(!App.player.media_active || have_media && !App.player.playing) { |
1436 | playButton.set_stock_id(Gtk.Stock.MEDIA_PLAY); |
1437 | + |
1438 | } |
1439 | + |
1440 | + update_sensitivities_pending = false; |
1441 | } |
1442 | |
1443 | public virtual void progressNotification(string? message, double progress) { |
1444 | @@ -742,7 +751,7 @@ |
1445 | App.player.player.play(); |
1446 | |
1447 | if (!inhibit_notifications) |
1448 | - notify_current_media (); |
1449 | + notify_current_media_async.begin (); |
1450 | } |
1451 | else { |
1452 | if(App.player.playing) { |
1453 | @@ -788,7 +797,7 @@ |
1454 | } |
1455 | |
1456 | if (!inhibit_notifications) |
1457 | - notify_current_media (); |
1458 | + notify_current_media_async.begin (); |
1459 | } |
1460 | |
1461 | public virtual void play_previous_media (bool inhibit_notifications = false) { |
1462 | @@ -804,7 +813,7 @@ |
1463 | return; |
1464 | } |
1465 | else if (play && !inhibit_notifications) { |
1466 | - notify_current_media (); |
1467 | + notify_current_media_async.begin (); |
1468 | } |
1469 | } |
1470 | else |
1471 | |
1472 | === modified file 'src/Objects/CoverartCache.vala' |
1473 | --- src/Objects/CoverartCache.vala 2012-10-04 22:32:58 +0000 |
1474 | +++ src/Objects/CoverartCache.vala 2012-10-06 03:24:21 +0000 |
1475 | @@ -44,7 +44,6 @@ |
1476 | |
1477 | private Gdk.Pixbuf default_image; |
1478 | |
1479 | - |
1480 | public CoverartCache () { |
1481 | assert (_instance == null); |
1482 | |
1483 | @@ -54,13 +53,13 @@ |
1484 | default_image = filter_func (default_pix); |
1485 | } |
1486 | |
1487 | - |
1488 | - // add a shadow to every image |
1489 | + /** |
1490 | + * Adds a shadow to every image. |
1491 | + */ |
1492 | protected override Gdk.Pixbuf? filter_func (Gdk.Pixbuf pix) { |
1493 | return PixbufUtils.get_pixbuf_shadow (pix, Icons.ALBUM_VIEW_IMAGE_SIZE); |
1494 | } |
1495 | |
1496 | - |
1497 | protected override string get_key (Media m) { |
1498 | string album_name = m.album; |
1499 | string artist_name = m.album_artist; |
1500 | @@ -71,137 +70,126 @@ |
1501 | return @"$artist_name-$album_name"; |
1502 | } |
1503 | |
1504 | - |
1505 | public Gdk.Pixbuf get_cover (Media m) { |
1506 | - var image = get_image (m, false); |
1507 | - return image ?? default_image; |
1508 | + return get_image (m) ?? default_image; |
1509 | } |
1510 | |
1511 | - |
1512 | public async void fetch_all_cover_art_async (Gee.Collection<Media> media) { |
1513 | yield fetch_folder_images_async (media); |
1514 | yield load_for_media_async (media); |
1515 | } |
1516 | |
1517 | - |
1518 | + /** |
1519 | + * Retrieves images from the cache for the specified media. |
1520 | + */ |
1521 | public async void load_for_media_async (Gee.Collection<Media> media) { |
1522 | - SourceFunc callback = load_for_media_async.callback; |
1523 | - |
1524 | - Threads.add ( () => { |
1525 | - load_for_media (media); |
1526 | - Idle.add ((owned)callback); |
1527 | - }); |
1528 | - |
1529 | - yield; |
1530 | - } |
1531 | - |
1532 | - |
1533 | - public void load_for_media (Gee.Collection<Media> media) { |
1534 | - debug ("READING CACHED COVERART"); |
1535 | - |
1536 | + // get_key() can yield a similar key for different media files, so we keep |
1537 | + // track of all the keys we've explored in order to lookup images only once |
1538 | + // for every equivalent media. |
1539 | var used_keys_set = new Gee.HashSet<string> (); |
1540 | |
1541 | foreach (var m in media) { |
1542 | string key = get_key (m); |
1543 | - |
1544 | if (!used_keys_set.contains (key) && !has_image (m)) { |
1545 | - debug ("Getting [%s]", key); |
1546 | - |
1547 | - // Pass true to lookup_file in order to fetch the images for the first time |
1548 | - get_image (m, true); |
1549 | - |
1550 | + yield get_image_async (m, true); |
1551 | used_keys_set.add (key); |
1552 | } |
1553 | } |
1554 | |
1555 | - debug ("FINISHED LOADING CACHED COVERART"); |
1556 | - |
1557 | queue_notify (); |
1558 | } |
1559 | |
1560 | - |
1561 | + /** |
1562 | + * Looks up for image types in the media's directory. |
1563 | + */ |
1564 | public async void fetch_folder_images_async (Gee.Collection<Media> media) { |
1565 | - SourceFunc callback = fetch_folder_images_async.callback; |
1566 | - |
1567 | - Threads.add ( () => { |
1568 | - fetch_folder_images (media); |
1569 | - |
1570 | - Idle.add ((owned) callback); |
1571 | - }); |
1572 | - |
1573 | - yield; |
1574 | - } |
1575 | - |
1576 | - |
1577 | - /** |
1578 | - * Looks up for image types in the media's directory. We look for image files |
1579 | - * that follow certain name patterns, like "album.png", "folder.jpg", etc. |
1580 | - */ |
1581 | - public void fetch_folder_images (Gee.Collection<Media> media) { |
1582 | + // get_key() can yield a similar key for different media files, so we keep |
1583 | + // track of all the keys we've explored in order to lookup images only once |
1584 | + // for every equivalent media. |
1585 | + var used_keys_set = new Gee.HashSet<string> (); |
1586 | + |
1587 | foreach (var m in media) { |
1588 | - if (!has_image (m)) { |
1589 | - var art_file = lookup_folder_image_file (m); |
1590 | + string key = get_key (m); |
1591 | + if (!used_keys_set.contains (key) && !has_image (m)) { |
1592 | + var art_file = yield lookup_folder_image_file_async (m); |
1593 | if (art_file != null) |
1594 | - cache_image_from_file (m, art_file); |
1595 | + yield cache_image_from_file_async (m, art_file); |
1596 | + |
1597 | + used_keys_set.add (key); |
1598 | } |
1599 | } |
1600 | - |
1601 | - queue_notify (); |
1602 | } |
1603 | |
1604 | |
1605 | - // Awesome method taken from BeatBox's FileOperator.vala (adapted to use Noise's internal API) |
1606 | - private static File? lookup_folder_image_file (Media m) { |
1607 | - File? rv = null, media_file = m.file; |
1608 | + /** |
1609 | + * Looks up a valid album image in a media's directory. |
1610 | + * |
1611 | + * It tries to find image files that follow certain name patterns, like "album.png", |
1612 | + * "folder.jpg", the album name, etc. If no image matching the pattern is found, null |
1613 | + * is returned. |
1614 | + */ |
1615 | + private static async File? lookup_folder_image_file_async (Media m) { |
1616 | + File? media_file = m.file; |
1617 | + return_val_if_fail (media_file != null, null); |
1618 | |
1619 | - if (!media_file.query_exists ()) |
1620 | - return rv; |
1621 | + // Check file existence |
1622 | + return_val_if_fail (yield FileUtils.query_exists_async (media_file), null); |
1623 | |
1624 | var album_folder = media_file.get_parent (); |
1625 | - |
1626 | - if (album_folder == null) |
1627 | - return rv; |
1628 | + return_val_if_fail (album_folder != null, null); |
1629 | |
1630 | // Don't consider generic image names if the album folder doesn't contain the name of |
1631 | // the media's album. This is probably the simpler way to prevent considering images |
1632 | // from folders that contain multiple unrelated tracks. |
1633 | bool generic_folder = !album_folder.get_path ().contains (m.album); |
1634 | |
1635 | - string[] image_types = { "jpg", "jpeg", "png" }; |
1636 | + string[] image_types = { "jpg", "jpeg", "png", "tiff" }; |
1637 | Gee.Collection<File> image_files; |
1638 | - FileUtils.enumerate_files (album_folder, image_types, false, out image_files); |
1639 | + yield FileUtils.enumerate_files_async (album_folder, image_types, false, out image_files); |
1640 | |
1641 | + File? image_file = null; |
1642 | + bool good_image_found = false; |
1643 | // Choose an image based on priorities. |
1644 | + |
1645 | foreach (var file in image_files) { |
1646 | - string file_path = file.get_path ().down (); |
1647 | + // We don't want to be fooled by strange characters or whitespace |
1648 | + string file_path = String.canonicalize_for_search (file.get_path ().down ()); |
1649 | + string album_name = String.canonicalize_for_search ((m.album ?? "").down ()); |
1650 | |
1651 | if (generic_folder) { |
1652 | - if (m.album in file_path) { |
1653 | - rv = file; |
1654 | + if (!String.is_white_space (album_name) && album_name in file_path) { |
1655 | + image_file = file; |
1656 | break; |
1657 | } |
1658 | |
1659 | continue; |
1660 | } |
1661 | |
1662 | - |
1663 | if ("folder" in file_path) { |
1664 | - rv = file; |
1665 | + image_file = file; |
1666 | + good_image_found = true; |
1667 | break; |
1668 | } |
1669 | |
1670 | if ("cover" in file_path) { |
1671 | - rv = file; |
1672 | - } else if (rv != null) { |
1673 | - if (!("cover" in rv.get_path ()) && "album" in file_path) |
1674 | - rv = file; |
1675 | - else if (!("album" in rv.get_path ()) && "front" in file_path) |
1676 | - rv = file; |
1677 | - else if (!("front" in rv.get_path ()) && m.album in file_path) |
1678 | - rv = file; |
1679 | + good_image_found = true; |
1680 | + image_file = file; |
1681 | + continue; |
1682 | + } |
1683 | + |
1684 | + // Let's use whatever we found |
1685 | + if (image_file == null) |
1686 | + image_file = file; |
1687 | + |
1688 | + if (!("cover" in image_file.get_path ()) && "album" in file_path) { |
1689 | + good_image_found = true; |
1690 | + image_file = file; |
1691 | + } else if (!("album" in image_file.get_path ()) && "front" in file_path) { |
1692 | + good_image_found = true; |
1693 | + image_file = file; |
1694 | } |
1695 | } |
1696 | |
1697 | - return rv; |
1698 | + return good_image_found ? image_file : null; |
1699 | } |
1700 | } |
1701 | |
1702 | === modified file 'src/Objects/MediaArtCache.vala' |
1703 | --- src/Objects/MediaArtCache.vala 2012-09-24 19:58:02 +0000 |
1704 | +++ src/Objects/MediaArtCache.vala 2012-10-06 03:24:21 +0000 |
1705 | @@ -103,8 +103,8 @@ |
1706 | * (e.g. due to metadata changes, etc.), since the old pixbuf and cached image |
1707 | * are overwritten. |
1708 | */ |
1709 | - public void cache_image (Media m, Gdk.Pixbuf image) { |
1710 | - pixbuf_cache.cache_image (get_key (m), image); |
1711 | + public async void cache_image_async (Media m, Gdk.Pixbuf image) { |
1712 | + yield pixbuf_cache.cache_image_async (get_key (m), image); |
1713 | queue_notify (); |
1714 | } |
1715 | |
1716 | @@ -112,8 +112,8 @@ |
1717 | * This method does the same as cache_image(), with the only difference that it |
1718 | * first fetches the image from the given file. |
1719 | */ |
1720 | - public void cache_image_from_file (Media m, File image_file, Cancellable? c = null) { |
1721 | - pixbuf_cache.cache_image_from_file (get_key (m), image_file, c); |
1722 | + public async void cache_image_from_file_async (Media m, File image_file, Cancellable? c = null) { |
1723 | + yield pixbuf_cache.cache_image_from_file_async (get_key (m), image_file, c); |
1724 | queue_notify (); |
1725 | } |
1726 | |
1727 | @@ -131,8 +131,16 @@ |
1728 | * @return null if the media's corresponding image was not found; otherwise |
1729 | * a valid {@link Gdk.Pixbuf} |
1730 | */ |
1731 | - protected Gdk.Pixbuf? get_image (Media m, bool lookup_file) { |
1732 | - return pixbuf_cache.get_image (get_key (m), lookup_file); |
1733 | + protected Gdk.Pixbuf? get_image (Media m) { |
1734 | + return pixbuf_cache.get_image (get_key (m)); |
1735 | + } |
1736 | + |
1737 | + /** |
1738 | + * @return null if the media's corresponding image was not found; otherwise |
1739 | + * a valid {@link Gdk.Pixbuf} |
1740 | + */ |
1741 | + protected async Gdk.Pixbuf? get_image_async (Media m, bool lookup_file) { |
1742 | + return yield pixbuf_cache.get_image_async (get_key (m), lookup_file); |
1743 | } |
1744 | |
1745 | protected void queue_notify () { |
1746 | |
1747 | === modified file 'src/Utils/PixbufCache.vala' |
1748 | --- src/Utils/PixbufCache.vala 2012-10-04 22:32:58 +0000 |
1749 | +++ src/Utils/PixbufCache.vala 2012-10-06 03:24:21 +0000 |
1750 | @@ -50,12 +50,13 @@ |
1751 | * to be generic). It should be easy to re-use it on another application. |
1752 | */ |
1753 | public class Noise.PixbufCache { |
1754 | + private const string DEFAULT_FORMAT_NAME = "jpeg"; |
1755 | |
1756 | public Gee.Map<string, Gdk.Pixbuf> images { |
1757 | owned get { return image_map.read_only_view; } |
1758 | } |
1759 | |
1760 | - public string image_format { get; private set; default = "jpeg"; } |
1761 | + public Gdk.PixbufFormat image_format { get; private set; } |
1762 | |
1763 | private File image_dir; |
1764 | private Gee.HashMap<string, Gdk.Pixbuf> image_map; |
1765 | @@ -68,11 +69,20 @@ |
1766 | * @param image_format a string specifying the image format, or null to use the default |
1767 | * format (JPEG). Valid image formats are those supported by {@link Gdk.Pixbuf.save}. |
1768 | */ |
1769 | - public PixbufCache (File image_dir, string? image_format = null) { |
1770 | - image_map = new Gee.HashMap<string, Gdk.Pixbuf> (); |
1771 | - |
1772 | - if (image_format != null) |
1773 | + public PixbufCache (File image_dir, Gdk.PixbufFormat? image_format = null) { |
1774 | + if (image_format == null) { |
1775 | + foreach (var format in Gdk.Pixbuf.get_formats ()) { |
1776 | + if (format.get_name () == DEFAULT_FORMAT_NAME) { |
1777 | + this.image_format = format; |
1778 | + break; |
1779 | + } |
1780 | + } |
1781 | + } else { |
1782 | this.image_format = image_format; |
1783 | + } |
1784 | + |
1785 | + // We need to be able to write images to disk for permanent storage |
1786 | + assert (this.image_format != null && this.image_format.is_writable ()); |
1787 | |
1788 | this.image_dir = image_dir; |
1789 | |
1790 | @@ -82,6 +92,8 @@ |
1791 | if (!(err is IOError.EXISTS)) |
1792 | warning ("Could not create image cache directory: %s", err.message); |
1793 | } |
1794 | + |
1795 | + image_map = new Gee.HashMap<string, Gdk.Pixbuf> (); |
1796 | } |
1797 | |
1798 | /** |
1799 | @@ -113,7 +125,7 @@ |
1800 | * recommended to use {@link Noise.PixbufCache.has_image} to check for that. |
1801 | */ |
1802 | public string get_cached_image_path (string key) { |
1803 | - string filename = Checksum.compute_for_string (ChecksumType.MD5, key + image_format); |
1804 | + string filename = Checksum.compute_for_string (ChecksumType.MD5, key + image_format.get_name ()); |
1805 | return image_dir.get_child (filename).get_path (); |
1806 | } |
1807 | |
1808 | @@ -141,19 +153,30 @@ |
1809 | * This method can also be used to update image buffers when they have changed, |
1810 | * since the old image is overwritten (in both primary memory and disk.) |
1811 | */ |
1812 | - public void cache_image (string key, Gdk.Pixbuf image) { |
1813 | - cache_image_internal (key, image, true); |
1814 | + public async void cache_image_async (string key, Gdk.Pixbuf image) { |
1815 | + yield cache_image_internal_async (key, image, true); |
1816 | } |
1817 | |
1818 | /** |
1819 | * This method does the same as {@link Noise.PixbufCache.cache_image}, with the only |
1820 | * difference that it first fetches the image from the given file. |
1821 | */ |
1822 | - public void cache_image_from_file (string key, File image_file, Cancellable? c = null) { |
1823 | - var image = load_image_from_file (image_file, c); |
1824 | + public async void cache_image_from_file_async (string key, File image_file, Cancellable? c = null) { |
1825 | + var image = yield load_image_from_file_async (image_file, c); |
1826 | if (image != null) |
1827 | - cache_image (key, image); |
1828 | - } |
1829 | + yield cache_image_async (key, image); |
1830 | + } |
1831 | + |
1832 | + |
1833 | + /** |
1834 | + * Retrieves the image for the given key from the cache. |
1835 | + * |
1836 | + * @return A valid {@link Gdk.Pixbuf}, or null if the image is not found. |
1837 | + */ |
1838 | + public Gdk.Pixbuf? get_image (string key) { |
1839 | + return image_map.get (key); |
1840 | + } |
1841 | + |
1842 | |
1843 | /** |
1844 | * Retrieves the image for the given key from the cache. If lookup_file |
1845 | @@ -165,12 +188,12 @@ |
1846 | * @return null if the key's corresponding image was not found; Otherwise |
1847 | * a valid {@link Gdk.Pixbuf} |
1848 | */ |
1849 | - public Gdk.Pixbuf? get_image (string key, bool lookup_file = true) { |
1850 | - if (lookup_file && !image_map.has_key (key)) { |
1851 | + public async Gdk.Pixbuf? get_image_async (string key, bool lookup_file = true) { |
1852 | + if (lookup_file && !has_image (key)) { |
1853 | var image_file = File.new_for_path (get_cached_image_path (key)); |
1854 | - var image = load_image_from_file (image_file, null); |
1855 | + var image = yield load_image_from_file_async (image_file, null); |
1856 | if (image != null) |
1857 | - cache_image_internal (key, image, false); |
1858 | + yield cache_image_internal_async (key, image, false); |
1859 | } |
1860 | |
1861 | return image_map.get (key); |
1862 | @@ -179,7 +202,7 @@ |
1863 | /** |
1864 | * Adds an image to the hash map and also writes the image to disk if save_to_disk is true. |
1865 | */ |
1866 | - private void cache_image_internal (string key, Gdk.Pixbuf image, bool save_to_disk) { |
1867 | + private async void cache_image_internal_async (string key, Gdk.Pixbuf image, bool save_to_disk) { |
1868 | Gdk.Pixbuf? modified_image = (filter_func != null) ? filter_func (key, image) : image; |
1869 | |
1870 | if (modified_image != null) { |
1871 | @@ -198,11 +221,11 @@ |
1872 | * Central place for retrieving images from permanent-storage locations. This is not |
1873 | * limited to this cache's local directory. |
1874 | */ |
1875 | - private Gdk.Pixbuf? load_image_from_file (File image_file, Cancellable? cancellable) { |
1876 | + private async Gdk.Pixbuf? load_image_from_file_async (File image_file, Cancellable? cancellable) { |
1877 | Gdk.Pixbuf? image = null; |
1878 | |
1879 | try { |
1880 | - image = PixbufUtils.get_pixbuf_from_file (image_file, cancellable); |
1881 | + image = yield PixbufUtils.get_pixbuf_from_file_async (image_file, cancellable); |
1882 | } catch (Error err) { |
1883 | warning ("Could not get image from file [%s]: %s", image_file.get_uri (), err.message); |
1884 | } |
1885 | @@ -219,7 +242,7 @@ |
1886 | try { |
1887 | string path = get_cached_image_path (key); |
1888 | if (delete_file (path)) |
1889 | - to_save.save (path, image_format); |
1890 | + to_save.save (path, image_format.get_name ()); |
1891 | } catch (Error err) { |
1892 | warning ("Could not save pixbuf: %s", err.message); |
1893 | } |
1894 | |
1895 | === modified file 'src/Utils/PixbufUtils.vala' |
1896 | --- src/Utils/PixbufUtils.vala 2012-09-15 07:04:34 +0000 |
1897 | +++ src/Utils/PixbufUtils.vala 2012-10-06 03:24:21 +0000 |
1898 | @@ -93,15 +93,16 @@ |
1899 | return buffer_surface.load_to_pixbuf(); |
1900 | } |
1901 | |
1902 | - public Gdk.Pixbuf? get_pixbuf_from_file (File file, Cancellable? c) throws Error { |
1903 | - return get_pixbuf_from_file_at_scale (file, -1, -1, false, c); |
1904 | + public async Gdk.Pixbuf? get_pixbuf_from_file_async (File file, Cancellable? c = null) throws Error { |
1905 | + return yield get_pixbuf_from_file_at_scale_async (file, -1, -1, false, c); |
1906 | } |
1907 | |
1908 | - public Gdk.Pixbuf? get_pixbuf_from_file_at_scale (File file, int width, int height, |
1909 | - bool preserve_aspect_ratio, |
1910 | - Cancellable? c) throws Error { |
1911 | + public async Gdk.Pixbuf? get_pixbuf_from_file_at_scale_async (File file, int width, int height, |
1912 | + bool preserve_aspect_ratio, |
1913 | + Cancellable? c = null) throws Error |
1914 | + { |
1915 | Gdk.Pixbuf? image = null; |
1916 | - var filestream = file.read (c); |
1917 | + var filestream = yield file.read_async (Priority.DEFAULT, c); |
1918 | |
1919 | if (filestream != null); |
1920 | image = new Gdk.Pixbuf.from_stream_at_scale (filestream, width, height, |
Good branch, GStreamerTagger .vala needs some work, but I can do it tomorrow.