Skip to content

Commit

Permalink
Merge pull request #337 from genodeftest/moodbar-plugin
Browse files Browse the repository at this point in the history
Plugin moodbar: rework cache filenames, cleanups and fixes
  • Loading branch information
sjohannes committed Apr 26, 2017
2 parents c3014db + c50d6d7 commit a0c9d08
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 66 deletions.
19 changes: 11 additions & 8 deletions plugins/moodbar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,15 @@

from cache import ExaileMoodbarCache
from generator import SpectrumMoodbarGenerator
from painter import MoodbarPainter
from widget import Moodbar


class MoodbarPlugin:

exaile = None
generator = None
cache = None

def __init__(self):
self.main_controller = self.preview_controller = None

Expand All @@ -51,7 +55,6 @@ def enable(self, exaile):

self.exaile = exaile
self.cache = ExaileMoodbarCache(os.path.join(xl.xdg.get_cache_dir(), 'moods'))
self.painter = MoodbarPainter()

xl.event.add_ui_callback(self._on_preview_device_enabled, 'preview_device_enabled')
xl.event.add_ui_callback(self._on_preview_device_disabling, 'preview_device_disabling')
Expand All @@ -71,7 +74,7 @@ def disable(self, exaile):
if self.preview_controller:
self.preview_controller.destroy()
self.main_controller = self.preview_controller = None
del self.exaile, self.cache, self.generator, self.painter
del self.exaile, self.cache, self.generator

# Preview Device events

Expand All @@ -86,10 +89,10 @@ def _on_preview_device_disabling(self, event, plugin, _=None):


# TRANSLATORS: Time format for playback progress
def format_time(seconds, format=_("{minutes}:{seconds:02}")):
def format_time(seconds, time_format=_("{minutes}:{seconds:02}")):
seconds = int(round(seconds))
minutes, seconds = divmod(seconds, 60)
return format.format(minutes=int(minutes), seconds=seconds)
return time_format.format(minutes=int(minutes), seconds=seconds)


class MoodbarController:
Expand All @@ -99,7 +102,7 @@ def __init__(self, plugin, player, orig_seekbar):
self.orig_seekbar = orig_seekbar
self.timer = self.seeking = False

self.moodbar = moodbar = Moodbar(plugin.painter)
self.moodbar = moodbar = Moodbar()
moodbar.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON1_MOTION_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK)
xlgui.guiutil.gtk_widget_replace(self.orig_seekbar, moodbar)
moodbar.show()
Expand Down Expand Up @@ -144,12 +147,12 @@ def _on_timer(self):
return False
current_time = self.player.get_time()
if total_time:
format = dict(
time_format = dict(
current=format_time(current_time),
remaining=format_time(total_time - current_time),
)
# TRANSLATORS: Format for playback progress text
self.moodbar.set_text(_("{current} / {remaining}").format(**format))
self.moodbar.set_text(_("{current} / {remaining}").format(**time_format))
if not self.seeking:
self.moodbar.seek_position = current_time / total_time
else:
Expand Down
40 changes: 14 additions & 26 deletions plugins/moodbar/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,12 @@

from __future__ import division, print_function, unicode_literals

import hashlib
import os
import sys


if sys.platform == 'win32':
import string
invalid_chars = b'*/:<>?\\'
SANITIZE_TRANS_TABLE = string.maketrans(invalid_chars, b'-' * len(invalid_chars))


class MoodbarCache:
class MoodbarCache(object):
""" Abstract class for a cache for moodbar metadata files """
def get(self, uri):
"""
:type uri: bytes
Expand All @@ -44,40 +39,33 @@ def put(self, uri, data):


class ExaileMoodbarCache(MoodbarCache):
""" Exaile's implementation of a cache for moodbar files """

__loc = None

def __init__(self, location):
try:
os.mkdir(location)
except OSError:
pass
self.loc = location
self.__loc = location

def get(self, uri):
try:
with open(self._get_cache_path(uri), 'rb') as f:
return f.read()
with open(self._get_cache_path(uri), 'rb') as cachefile:
return cachefile.read()
except IOError:
return None

def put(self, uri, data):
if data is None:
return
with open(self._get_cache_path(uri), 'wb') as f:
f.write(data)
with open(self._get_cache_path(uri), 'wb') as cachefile:
cachefile.write(data)

def _get_cache_path(self, uri):
"""
:type uri: bytes
:rtype: bytes
"""
assert isinstance(uri, bytes)
if uri.startswith(b'file://'):
uri = uri[7:]
uri = uri.replace(b"'", b"")
if sys.platform == 'win32':
uri = uri.translate(SANITIZE_TRANS_TABLE, b'"')
else:
uri = uri.replace(b'/', b'-')
return os.path.join(self.loc, uri + b'.mood')
hex_str = hashlib.sha256(uri).hexdigest()
return os.path.join(self.__loc, hex_str + b'.mood')


# vi: et sts=4 sw=4 tw=99
16 changes: 9 additions & 7 deletions plugins/moodbar/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@
from gi.repository import Gio


class MoodbarGeneratorError(Exception): pass
class MoodbarGeneratorError(Exception):
pass


class MoodbarGenerator:
class MoodbarGenerator(object):
def check(self):
"""Check whether the generator works.
Expand All @@ -55,10 +56,10 @@ def generate_async(self, uri, callback=None):
:type uri: bytes
:type callback: Callable[[bytes, bytes], None]
"""
t = threading.Thread(name=self.__class__.__name__,
target=self.generate, args=(uri, callback))
t.daemon = True
t.start()
thread = threading.Thread(name=self.__class__.__name__,
target=self.generate, args=(uri, callback))
thread.daemon = True
thread.start()


class SpectrumMoodbarGenerator(MoodbarGenerator):
Expand All @@ -84,11 +85,12 @@ def generate(self, uri, callback=None):
f = open(tmppath, 'rb')
data = f.read()
except Exception as e:
# TODO propagate this error to UI, make sure to install required GStreamer plugins
raise MoodbarGeneratorError(e)
finally:
if f:
f.close()
if tmppath:
if tmppath and os.path.exists(tmppath):
os.remove(tmppath)
if callback:
callback(uri, data)
Expand Down
41 changes: 20 additions & 21 deletions plugins/moodbar/painter.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,29 @@

from __future__ import division, print_function, unicode_literals

# It seems like cairo does not support GObject Introspection.
import cairo


class MoodbarPainter:
"""Turn moodbar data into Cairo surface, ready to be drawn"""

def paint(self, data):
"""Paint moodbar to a surface.
:param data: Moodbar data
:type data: bytes
:return: Cairo surface containing the image to be drawn
:rtype: cairo.ImageSurface
"""
surf = cairo.ImageSurface(cairo.FORMAT_RGB24, 1000, 1)
arr = surf.get_data()
for p in xrange(0, 1000):
p4 = p * 4
p3 = p * 3
# Cairo RGB24 is BGRX
arr[p4 + 0] = data[p3 + 2]
arr[p4 + 1] = data[p3 + 1]
arr[p4 + 2] = data[p3 + 0]
return surf
def paint(data):
"""Paint moodbar to a surface.
:param data: Moodbar data
:type data: bytes
:return: Cairo surface containing the image to be drawn
:rtype: cairo.ImageSurface
"""
width = len(data) // 3
surf = cairo.ImageSurface(cairo.FORMAT_RGB24, width, 1)
arr = surf.get_data()
for index in xrange(0, width):
index4 = index * 4
index3 = index * 3
# Cairo RGB24 is BGRX
arr[index4 + 0] = data[index3 + 2]
arr[index4 + 1] = data[index3 + 1]
arr[index4 + 2] = data[index3 + 0]
return surf


# vi: et sts=4 sw=4 tw=99
9 changes: 5 additions & 4 deletions plugins/moodbar/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
PangoCairo,
)

from plugins.moodbar import painter


Extents = collections.namedtuple('Extents', 'x_bearing y_bearing width height x_advance y_advance')

Expand All @@ -37,9 +39,8 @@ class Moodbar(Gtk.DrawingArea):
pos_size = 0.4 # Size of seek notch, in fraction of bar height
pos_linesize = 2

def __init__(self, loader):
def __init__(self):
super(Moodbar, self).__init__()
self.loader = loader
# TODO: Handle screen-changed.
# See https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-create-pango-layout
self.pango_layout = self.create_pango_layout()
Expand All @@ -54,7 +55,7 @@ def set_mood(self, data):
:rtype: bool
"""
if data:
self.surf = self.loader.paint(data)
self.surf = painter.paint(data)
self._invalidate()
return bool(self.surf)
else:
Expand Down Expand Up @@ -117,7 +118,7 @@ def do_draw(self, cr):
if self.surf:
# Mood
cr.save()
cr.scale(width / 1000, height)
cr.scale(width / self.surf.get_width(), height)
cr.set_source_surface(self.surf, 0, 0)
cr.paint()
cr.restore()
Expand Down

0 comments on commit a0c9d08

Please sign in to comment.