[gnome-music] refactor albumart
- From: Vadim Rutkovsky <vrutkovsky src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music] refactor albumart
- Date: Wed, 16 Apr 2014 12:36:26 +0000 (UTC)
commit 3f23e2ef79137c7f53305eba6ceb1afe507266db
Author: Vadim Rutkovsky <vrutkovs redhat com>
Date: Wed Apr 16 14:36:19 2014 +0200
refactor albumart
gnomemusic/albumArtCache.py | 271 ++++++++++++-------------------------------
gnomemusic/grilo.py | 16 +--
gnomemusic/notification.py | 3 +-
gnomemusic/player.py | 22 +++-
gnomemusic/view.py | 24 +----
gnomemusic/widgets.py | 4 +-
6 files changed, 101 insertions(+), 239 deletions(-)
---
diff --git a/gnomemusic/albumArtCache.py b/gnomemusic/albumArtCache.py
index f447dc1..a73c390 100644
--- a/gnomemusic/albumArtCache.py
+++ b/gnomemusic/albumArtCache.py
@@ -28,211 +28,60 @@
# delete this exception statement from your version.
-from gi.repository import Gtk, GdkPixbuf, Gio, GLib, Grl, Gdk, MediaArt
+from gi.repository import Gtk, GdkPixbuf, Gio, GLib, Gdk, MediaArt
from gettext import gettext as _
import cairo
from math import pi
-import threading
import os
+from threading import Thread
+from queue import Queue, Empty
from gnomemusic import log
import logging
logger = logging.getLogger(__name__)
-frame_lock = threading.Lock()
@log
def _make_icon_frame(pixbuf, path=None):
-
border = 1.5
+ degrees = pi / 180
+ radius = 3
+
w = pixbuf.get_width()
h = pixbuf.get_height()
- pixbuf = pixbuf.scale_simple(w - border * 2,
- h - border * 2,
- 0)
-
- result = _draw_rounded_path(0, 0, w, h, 3)
-
- pixbuf.copy_area(border, border,
- w - border * 4,
- h - border * 4,
- result,
- border * 2, border * 2)
- return pixbuf
-
-
- log
-def _draw_rounded_path(x, y, width, height, radius):
- degrees = pi / 180
+ new_pixbuf = pixbuf.scale_simple(w - border * 2,
+ h - border * 2,
+ 0)
- global frame_lock
- frame_lock.acquire()
- #if key not in frame_cache:
- surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
ctx = cairo.Context(surface)
ctx.new_sub_path()
- ctx.arc(x + width - radius, y + radius, radius - 0.5,
- -90 * degrees, 0 * degrees)
- ctx.arc(x + width - radius, y + height - radius, radius - 0.5,
- 0 * degrees, 90 * degrees)
- ctx.arc(x + radius, y + height - radius, radius - 0.5,
- 90 * degrees, 180 * degrees)
- ctx.arc(x + radius, y + radius, radius - 0.5, 180 * degrees,
- 270 * degrees)
+ ctx.arc(w - radius, radius, radius - 0.5, -90 * degrees, 0 * degrees)
+ ctx.arc(w - radius, h - radius, radius - 0.5, 0 * degrees, 90 * degrees)
+ ctx.arc(radius, h - radius, radius - 0.5, 90 * degrees, 180 * degrees)
+ ctx.arc(radius, radius, radius - 0.5, 180 * degrees, 270 * degrees)
ctx.close_path()
ctx.set_line_width(0.6)
ctx.set_source_rgb(0.2, 0.2, 0.2)
ctx.stroke_preserve()
ctx.set_source_rgb(1, 1, 1)
ctx.fill()
- res = Gdk.pixbuf_get_from_surface(surface, 0, 0, width, height)
- frame_lock.release()
- return res
-
-
-class LookupRequest:
-
- @log
- def __init__(self, item, width, height, callback, data=None):
- self.item = item
- self.width = width or -1
- self.height = height or -1
- self.callback = callback
- self.data = data
- self.artist = item.get_string(Grl.METADATA_KEY_ARTIST) or item.get_string(Grl.METADATA_KEY_AUTHOR)
or ''
- self.album = item.get_string(Grl.METADATA_KEY_ALBUM) or ''
- self.path = MediaArt.get_path(self.artist, self.album, "album", None)[0]
- self.started = False
-
- @log
- def start(self):
- self.started = True
- f = Gio.File.new_for_path(self.path)
- f.read_async(GLib.PRIORITY_DEFAULT, None, self._on_read_ready, None)
-
- @log
- def finish(self, pixbuf):
- self.callback(pixbuf, self.path, self.data)
-
- @log
- def _on_read_ready(self, obj, res, data=None):
- try:
- stream = obj.read_finish(res)
-
- GdkPixbuf.Pixbuf.new_from_stream_async(stream, None, self._on_pixbuf_ready, None)
- return
-
- except Exception as error:
- if AlbumArtCache.get_default().logLookupErrors:
- print('ERROR:', error)
-
- self._on_load_fail()
-
- @log
- def _on_pixbuf_ready(self, source, res, data=None):
- try:
- pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(res)
- if self.width < 0 and self.height < 0:
- self.finish(pixbuf)
- return
-
- width = pixbuf.get_width()
- height = pixbuf.get_height()
- if width >= self.width or height >= self.height:
- if width > height and self.width < 0:
- self.height *= (height / width)
- elif height > width and self.height < 0:
- self.width *= (width / height)
- scale = max(width / self.width, height / self.height)
- pixbuf = pixbuf.scale_simple(width / scale, height / scale, 2)
- pixbuf = _make_icon_frame(pixbuf)
- self.finish(pixbuf)
- return
- except Exception as error:
- if AlbumArtCache.get_default().logLookupErrors:
- print('ERROR:', error)
-
- self._on_load_fail()
-
- @log
- def _on_load_fail(self):
- options = Grl.OperationOptions()
- options.set_flags(Grl.ResolutionFlags.FULL |
- Grl.ResolutionFlags.IDLE_RELAY)
-
- uri = self.item.get_thumbnail()
- if uri is None:
- self.finish(None)
- return
+ border_pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0, w, h)
- AlbumArtCache.get_default().get_from_uri(
- uri, self.artist, self.album, self.width, self.height,
- self.callback, self.data
- )
-
-
-class GetUriRequest:
-
- @log
- def __init__(self, uri, artist, album, callback, data=None):
- self.uri = uri
- self.artist = artist
- self.album = album
- self.callback = callback
- self.data = data
- self.callbacks = []
- self.path = ''
- self.path = MediaArt.get_path(artist, album, "album", None)[0]
- self.stream = None
- self.started = False
-
- @log
- def start(self):
- self.started = True
- f = Gio.File.new_for_uri(self.uri)
- f.read_async(300, None, self._on_read_ready, None)
-
- @log
- def _on_read_ready(self, outstream, res, user_data=None):
- try:
- self.stream = outstream.read_finish(res)
- newFile = Gio.File.new_for_path(self.path)
- newFile.replace_async(None, False,
- Gio.FileCreateFlags.REPLACE_DESTINATION,
- 300, None, self._on_replace_ready, None)
-
- except Exception as e:
- print(e)
-
- @log
- def _on_replace_ready(self, new_file, res, user_data=None):
- outstream = new_file.replace_finish(res)
- outstream.splice_async(self.stream,
- Gio.IOStreamSpliceFlags.NONE,
- 300, None, self._on_splice_ready, None)
-
- @log
- def _on_splice_ready(self, outstream, res, user_data=None):
- for values in self.callbacks:
- width, height, callback, data = values
- try:
- pixbuf =\
- GdkPixbuf.Pixbuf.new_from_file_at_scale(
- self.path, height, width, True)
- callback(pixbuf, self.path, data)
- except Exception as e:
- print('Failed to load image: %s' % e.message)
- callback(None, None, data)
- self.callback(self, self.data)
+ new_pixbuf.copy_area(border, border,
+ w - border * 4,
+ h - border * 4,
+ border_pixbuf,
+ border * 2, border * 2)
+ return new_pixbuf
class AlbumArtCache:
instance = None
+ num_worker_threads = 5
@classmethod
def get_default(self):
- if self.instance:
- return self.instance
- else:
+ if not self.instance:
self.instance = AlbumArtCache()
return self.instance
@@ -263,14 +112,17 @@ class AlbumArtCache:
@log
def __init__(self):
- self.logLookupErrors = False
- self.requested_uris = {}
- self.cacheDir = os.path.join(GLib.get_user_cache_dir(), 'media-art')
-
try:
+ self.cacheDir = os.path.join(GLib.get_user_cache_dir(), 'media-art')
Gio.file_new_for_path(self.cacheDir).make_directory(None)
except:
- pass
+ logger.warn("Cannot create media-art dir")
+
+ self.q = Queue()
+ for i in range(self.num_worker_threads):
+ t = Thread(target=self.lookup_worker)
+ t.daemon = True
+ t.start()
@log
def get_default_icon(self, width, height):
@@ -293,28 +145,55 @@ class AlbumArtCache:
icon.get_height() * 3 / 2,
1, 1,
GdkPixbuf.InterpType.NEAREST, 0xff)
- return result
+ return _make_icon_frame(result)
@log
- def lookup(self, item, width, height, callback, data=None):
- request = LookupRequest(item, width, height, callback, data)
- request.start()
+ def lookup(self, item, width, height, callback, itr, artist, album):
+ self.q.put((item, width, height, callback, itr, artist, album))
@log
- def get_from_uri(self, uri, artist, album, width, height, callback, data=None):
- if not uri:
- return
+ def lookup_worker(self):
+ while True:
+ try:
+ (item, width, height, callback, itr, artist, album) = self.q.get_nowait()
+ width = width or -1
+ height = height or -1
+ path = MediaArt.get_path(artist, album, "album", None)[0]
+ if not os.path.exists(path):
+ self.cached_thumb_not_found(item, album, artist, path, callback, itr)
+ self.read_cached_pixbuf(path, width, height, callback, itr)
+ except Empty:
+ pass
+ except Exception as e:
+ logger.warn("Error: %s" % e)
- if uri not in self.requested_uris:
- request = GetUriRequest(uri, artist, album, self._on_get_uri_request_finish, data)
- self.requested_uris[uri] = request
- else:
- request = self.requested_uris[uri]
+ @log
+ def read_cached_pixbuf(self, path, width, height, callback, itr):
+ try:
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(path, width, height, True)
+ self.finish(_make_icon_frame(pixbuf), path, callback, itr)
+ except Exception as e:
+ logger.debug("Error: %s" % e)
- request.callbacks.append([width, height, callback, data])
- if not request.started:
- request.start()
+ @log
+ def finish(self, pixbuf, path, callback, itr):
+ try:
+ callback(pixbuf, path, itr)
+ except Exception as e:
+ logger.warn("Error: %s" % e)
+ self.q.task_done()
@log
- def _on_get_uri_request_finish(self, request, data=None):
- del self.requested_uris[request.uri]
+ def cached_thumb_not_found(self, item, album, artist, path, callback, itr):
+ try:
+ uri = item.get_thumbnail()
+ if uri is None:
+ logger.warn("can't find URL for album '%s' by %s" % (album, artist))
+ self.finish(None, path, callback, itr)
+ return
+
+ src = Gio.File.new_for_uri(uri)
+ dest = Gio.File.new_for_path(path)
+ src.copy(dest, Gio.FileCopyFlags.OVERWRITE)
+ except Exception as e:
+ logger.warn("Error: %s" % e)
diff --git a/gnomemusic/grilo.py b/gnomemusic/grilo.py
index 4e59326..4deaf75 100644
--- a/gnomemusic/grilo.py
+++ b/gnomemusic/grilo.py
@@ -43,12 +43,8 @@ class Grilo(GObject.GObject):
Grl.METADATA_KEY_ID, Grl.METADATA_KEY_TITLE,
Grl.METADATA_KEY_ARTIST, Grl.METADATA_KEY_ALBUM,
Grl.METADATA_KEY_DURATION,
- Grl.METADATA_KEY_CREATION_DATE]
-
- METADATA_THUMBNAIL_KEYS = [
- Grl.METADATA_KEY_ID,
- Grl.METADATA_KEY_THUMBNAIL,
- ]
+ Grl.METADATA_KEY_CREATION_DATE,
+ Grl.METADATA_KEY_THUMBNAIL]
CHANGED_MEDIA_MAX_ITEMS = 500
CHANGED_MEDIA_SIGNAL_TIMEOUT = 2000
@@ -61,7 +57,7 @@ class Grilo(GObject.GObject):
if not (GLib.file_test(self.playlist_path, GLib.FileTest.IS_DIR)):
GLib.mkdir_with_parents(self.playlist_path, int("0755", 8))
self.options = Grl.OperationOptions()
- self.options.set_flags(Grl.ResolutionFlags.FULL |
+ self.options.set_flags(Grl.ResolutionFlags.FAST_ONLY |
Grl.ResolutionFlags.IDLE_RELAY)
self.sources = {}
@@ -180,12 +176,6 @@ class Grilo(GObject.GObject):
options, self._search_callback, source)
@log
- def get_album_art_for_album_id(self, album_id, _callback):
- options = self.options.copy()
- query = Query.get_album_for_id(album_id)
- self.tracker.query(query, self.METADATA_THUMBNAIL_KEYS, options, _callback, None)
-
- @log
def get_media_from_uri(self, uri, callback):
options = self.options.copy()
query = Query.get_song_with_url(uri)
diff --git a/gnomemusic/notification.py b/gnomemusic/notification.py
index 49e3ac7..7535439 100644
--- a/gnomemusic/notification.py
+++ b/gnomemusic/notification.py
@@ -94,7 +94,8 @@ class NotificationManager:
'<i>' + album + '</i>'),
'gnome-music')
- self._albumArtCache.lookup(item, IMAGE_SIZE, IMAGE_SIZE, self._album_art_loaded)
+ self._albumArtCache.lookup(
+ item, IMAGE_SIZE, IMAGE_SIZE, self._album_art_loaded, None, artist, album)
@log
def _album_art_loaded(self, image, path, data):
diff --git a/gnomemusic/player.py b/gnomemusic/player.py
index 1ff807d..ee43438 100644
--- a/gnomemusic/player.py
+++ b/gnomemusic/player.py
@@ -376,17 +376,25 @@ class Player(GObject.GObject):
self.playBtn.set_sensitive(True)
self._sync_prev_next()
- self.coverImg.set_from_pixbuf(self._symbolicIcon)
- self.cache.lookup(media, ART_SIZE, ART_SIZE, self._on_cache_lookup)
-
- self.titleLabel.set_label(AlbumArtCache.get_media_title(media))
-
+ artist = _("Unknown Artist")
try:
+ assert media.get_artist() is not None
artist = media.get_artist()
- assert artist is not None
+ finally:
self.artistLabel.set_label(artist)
+
+ album = _("Unknown Album")
+ try:
+ assert media.get_album() is not None
+ album = media.get_album()
except:
- self.artistLabel.set_label(_("Unknown Artist"))
+ pass
+
+ self.coverImg.set_from_pixbuf(self._symbolicIcon)
+ self.cache.lookup(
+ media, ART_SIZE, ART_SIZE, self._on_cache_lookup, None, artist, album)
+
+ self.titleLabel.set_label(AlbumArtCache.get_media_title(media))
url = media.get_url()
if url != self.player.get_value('current-uri', 0):
diff --git a/gnomemusic/view.py b/gnomemusic/view.py
index 621e563..bee691b 100644
--- a/gnomemusic/view.py
+++ b/gnomemusic/view.py
@@ -284,28 +284,11 @@ class ViewContainer(Gtk.Stack):
[str(item.get_id()), '', title,
artist, self._symbolicIcon, item,
0, icon_name, False, icon_name == self.errorIconName])
- self._update_album_art(item, _iter)
-
- GLib.idle_add(add_new_item)
-
- @log
- def _insert_album_art(self, item, cb_item, itr, x=False):
- if item and cb_item and not item.get_thumbnail():
- if cb_item.get_thumbnail():
- item.set_thumbnail(cb_item.get_thumbnail())
albumArtCache.get_default().lookup(
- item,
- self._iconWidth,
- self._iconHeight,
- self._on_lookup_ready, itr)
+ item, self._iconWidth, self._iconHeight, self._on_lookup_ready,
+ _iter, artist, title)
- @log
- def _update_album_art(self, item, itr):
- grilo.get_album_art_for_album_id(
- item.get_id(),
- lambda source, count, cb_item, x, y, z:
- self._insert_album_art(item, cb_item, itr, True)
- )
+ GLib.idle_add(add_new_item)
@log
def _on_lookup_ready(self, icon, path, _iter):
@@ -313,7 +296,6 @@ class ViewContainer(Gtk.Stack):
self._model.set_value(
_iter, 4,
icon)
- self.view.queue_draw()
@log
def _add_list_renderers(self):
diff --git a/gnomemusic/widgets.py b/gnomemusic/widgets.py
index ddc0209..71a72ea 100644
--- a/gnomemusic/widgets.py
+++ b/gnomemusic/widgets.py
@@ -632,7 +632,9 @@ class ArtistAlbumWidget(Gtk.HBox):
@log
def _update_album_art(self):
- ALBUM_ART_CACHE.lookup(self.album, 128, 128, self._get_album_cover)
+ ALBUM_ART_CACHE.lookup(
+ self.album, 128, 128, self._get_album_cover, None,
+ self.artist, self.album.get_title())
@log
def _get_album_cover(self, pixbuf, path, data=None):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]