coverart/: Prepare for proper release.
[autoys] / coverart / coverart
diff --git a/coverart/coverart b/coverart/coverart
deleted file mode 100755 (executable)
index 789b06d..0000000
+++ /dev/null
@@ -1,483 +0,0 @@
-#! /usr/bin/python
-# -*- coding: utf-8 -*-
-
-import sys as SYS
-import os as OS
-from cStringIO import StringIO
-
-import gobject as G
-import gtk as GTK
-GDK = GTK.gdk
-import cairo as XR
-
-import urllib as U
-import urllib2 as U2
-import json as JS
-
-THUMBSZ = 96
-
-class ImageCache (object):
-
-  THRESH = 128*1024*1024
-
-  def __init__(me):
-    me._total = 0
-    me._first = me._last = None
-
-  def add(me, img):
-    me._total += img.size
-    while me._first and me._total > me.THRESH:
-      me._first.evict()
-    img._prev = me._last
-    img._next = None
-    if me._last:
-      me._last._next = img
-    else:
-      me._first = img
-    me._last = img
-
-  def rm(me, img):
-    if img._prev:
-      img._prev._next = img._next
-    else:
-      me._first = img._next
-    if img._next:
-      img._next._prev = img._prev
-    else:
-      img._last = img._prev
-    me._total -= img.size
-
-CACHE = ImageCache()
-
-class CacheableImage (object):
-
-  def __init__(me):
-    me._pixbuf = None
-    me._prev = me._next = None
-    me._thumb = None
-
-  @property
-  def pixbuf(me):
-    if not me._pixbuf:
-      me._pixbuf = me._acquire()
-      me.size = me._pixbuf.get_pixels_array().nbytes
-      CACHE.add(me)
-    return me._pixbuf
-
-  def evict(me):
-    me._pixbuf = None
-    CACHE.rm(me)
-
-  def flush(me):
-    me.evict()
-    me._thumb = None
-
-  @property
-  def thumbnail(me):
-    if not me._thumb:
-      me._thumb = Thumbnail(me)
-    return me._thumb
-
-class Thumbnail (object):
-
-  def __init__(me, img):
-    pix = img.pixbuf
-    wd, ht = pix.get_width(), pix.get_height()
-    m = max(wd, ht)
-    if m <= THUMBSZ:
-      me.pixbuf = pix
-    else:
-      twd, tht = [(x*THUMBSZ + m//2)//m for x in [wd, ht]]
-      me.pixbuf = pix.scale_simple(twd, tht, GDK.INTERP_HYPER)
-
-class NullImage (CacheableImage):
-
-  MAP = {}
-
-  def __init__(me, size, text):
-    CacheableImage.__init__(me)
-    me._size = size
-    me._text = text
-
-  @staticmethod
-  def get(cls, size):
-    try:
-      return cls.MAP[size]
-    except KeyError:
-      img = cls.MAP[size] = cls(size)
-      return img
-
-  def _acquire(me):
-
-    surf = XR.ImageSurface(XR.FORMAT_ARGB32, me._size, me._size)
-    xr = XR.Context(surf)
-
-    xr.set_source_rgb(0.3, 0.3, 0.3)
-    xr.paint()
-
-    xr.move_to(me._size/2.0, me._size/2.0)
-    xr.select_font_face('sans-serif',
-                        XR.FONT_SLANT_NORMAL, XR.FONT_WEIGHT_BOLD)
-    xb, yb, wd, ht, xa, ya = xr.text_extents(me._text)
-    m = max(wd, ht)
-    z = me._size/float(m) * 2.0/3.0
-    xr.scale(z, z)
-
-    xr.set_source_rgb(0.8, 0.8, 0.8)
-    xr.move_to(3.0*m/4.0 - wd/2.0 - xb, 3.0*m/4.0 - ht/2.0 - yb)
-    xr.show_text(me._text)
-
-    surf.flush()
-    pix = GDK.pixbuf_new_from_data(surf.get_data(),
-                                   GDK.COLORSPACE_RGB, True, 8,
-                                   me._size, me._size, surf.get_stride())
-    return pix
-
-class FileImage (CacheableImage):
-
-  def __init__(me, file):
-    CacheableImage.__init__(me)
-    me._file = file
-
-  def _acquire(me):
-    return GDK.pixbuf_new_from_file(me._file)
-
-def fetch_url(url):
-  out = StringIO()
-  with U.urlopen(url) as u:
-    while True:
-      stuff = u.read(16384)
-      if not stuff:
-        break
-      out.write(stuff)
-  return out.getvalue()
-
-def fix_background(w):
-  style = w.get_style().copy()
-  style.base[GTK.STATE_NORMAL] = BLACK
-  style.bg[GTK.STATE_NORMAL] = BLACK
-  style.text[GTK.STATE_NORMAL] = WHITE
-  w.set_style(style)
-
-class BaseCoverViewer (object):
-
-  def __init__(me):
-    me.scr = GTK.ScrolledWindow()
-    me.scr.set_policy(GTK.POLICY_AUTOMATIC, GTK.POLICY_AUTOMATIC)
-    me.iv = GTK.IconView()
-    me.iv.connect('item-activated',
-                  lambda iv, p: me.activate(me._frompath(p)))
-    me.iv.connect('selection-changed', me._select)
-    me.iv.set_pixbuf_column(0)
-    me.iv.set_text_column(1)
-    me.iv.set_orientation(GTK.ORIENTATION_VERTICAL)
-    me.iv.set_item_width(THUMBSZ + 32)
-    fix_background(me.iv)
-    me.scr.add(me.iv)
-    me.reset()
-
-  def reset(me):
-    me.list = GTK.ListStore(GDK.Pixbuf, G.TYPE_STRING, G.TYPE_PYOBJECT)
-    me.iv.set_model(me.list)
-    me.iv.unselect_all()
-
-  def add(me, item):
-    item.it = me.list.append([item.img.thumbnail.pixbuf,
-                              item.text,
-                              item])
-
-  def _frompath(me, path):
-    return me.list[path][2]
-
-  def _select(me, iv):
-    sel = me.iv.get_selected_items()
-    if len(sel) != 1:
-      me.select(None)
-    else:
-      me.select(me._frompath(sel[0]))
-
-class SearchCover (object):
-  def __init__(me, img):
-    me.img = img
-    pix = img.pixbuf
-    me.text = '%d×%d*' % (pix.get_width(), pix.get_height())
-
-class SearchViewer (BaseCoverViewer):
-
-  def __init__(me, chooser):
-    BaseCoverViewer.__init__(me)
-    me._chooser = chooser
-
-  def switch(me, current):
-    me.reset()
-    if current:
-      cov = SearchCover(current)
-      me.add(cov)
-      me.iv.select_path(me.list.get_path(cov.it))
-
-  def activate(me, cov):
-    me._chooser.activated(cov)
-
-  def select(me, cov):
-    me._chooser.selected(cov)
-
-class RemoteImage (CacheableImage):
-
-  ERRIMG = NullImage(256, '!')
-
-  def __init__(me, url, ref = None):
-    CacheableImage.__init__(me)
-    me._url = url
-    me._ref = ref
-    me._data = None
-
-  def _fetch(me):
-    if me._data:
-      return
-    d = StringIO()
-    rq = U2.Request(me._url)
-    if me._ref:
-      rq.add_header('Referer', me._ref)
-    rs = U2.urlopen(rq)
-    while True:
-      stuff = rs.read(16384)
-      if not stuff:
-        break
-      d.write(stuff)
-    me._data = d.getvalue()
-    ld = GDK.PixbufLoader()
-    try:
-      o = 0
-      n = len(me._data)
-      while True:
-        if o >= n:
-          raise ValueError, 'not going to work'
-        l = min(n, o + 16384)
-        ld.write(me._data[o:l])
-        o = l
-        f = ld.get_format()
-        if f:
-          break
-      me._format = f
-      if 'image/gif' in f['mime_types']:
-        raise ValueError, 'boycotting GIF image'
-    finally:
-      try:
-        ld.close()
-      except G.GError:
-        pass
-
-  def _acquire(me):
-    try:
-      me._fetch()
-      ld = GDK.PixbufLoader()
-      try:
-        ld.write(me._data)
-      finally:
-        ld.close()
-      return ld.get_pixbuf()
-    except Exception, e:
-      print e
-      return me.ERRIMG.pixbuf
-
-  @property
-  def ext(me):
-    exts = me._format['extensions']
-    for i in ['jpg']:
-      if i in exts:
-        return i
-    return exts[0]
-
-class SearchImage (RemoteImage):
-
-  def __init__(me, url, ref, tburl):
-    RemoteImage.__init__(me, url, ref)
-    me._tburl = tburl
-
-  @property
-  def thumbnail(me):
-    if not me._thumb:
-      me._thumb = Thumbnail(RemoteImage(me._tburl))
-    return me._thumb
-
-class SearchResult (SearchCover):
-
-  def __init__(me, r):
-    w = int(r['width'])
-    h = int(r['height'])
-    url = r['unescapedUrl']
-    ref = r['originalContextUrl']
-    tburl = r['tbUrl']
-    me.img = SearchImage(url, ref, tburl)
-    me.text = '%d×%d' % (w, h)
-
-class SearchFail (Exception):
-  pass
-
-class CoverChooser (object):
-
-  SEARCHURL = \
-    'http://ajax.googleapis.com/ajax/services/search/images?v=1.0&rsz=8&q='
-
-  def __init__(me):
-    me.win = GTK.Window()
-    box = GTK.VBox()
-    top = GTK.HBox()
-    me.query = GTK.Entry()
-    top.pack_start(me.query, True, True, 2)
-    srch = GTK.Button('_Search')
-    srch.set_flags(GTK.CAN_DEFAULT)
-    srch.connect('clicked', me.search)
-    top.pack_start(srch, False, False, 2)
-    box.pack_start(top, False, False, 2)
-    me.sv = SearchViewer(me)
-    panes = GTK.HPaned()
-    panes.pack1(me.sv.scr, False, True)
-    scr = GTK.ScrolledWindow()
-    scr.set_policy(GTK.POLICY_AUTOMATIC, GTK.POLICY_AUTOMATIC)
-    me.img = GTK.Image()
-    evb = GTK.EventBox()
-    evb.add(me.img)
-    fix_background(evb)
-    scr.add_with_viewport(evb)
-    panes.pack2(scr, True, True)
-    panes.set_position(THUMBSZ + 64)
-    box.pack_start(panes, True, True, 0)
-    me.win.add(box)
-    me.win.connect('destroy', me.destroyed)
-    me.win.set_default_size(800, 550)
-    srch.grab_default()
-
-  def update(me, view, which, dir, current):
-    me.view = view
-    me.dir = dir
-    me.which = which
-    me.current = current
-    me.img.clear()
-    me.sv.switch(current)
-    me.query.set_text(me.makequery(dir))
-    me.win.show_all()
-
-  def search(me, w):
-    q = me.query.get_text()
-    try:
-      try:
-        rq = U2.Request(me.SEARCHURL + U.quote_plus(q),
-                        None,
-                        { 'Referer':
-                          'http://www.distorted.org.uk/~mdw/coverart' })
-        rs = U2.urlopen(rq)
-      except U2.URLError, e:
-        raise SearchFail(e.reason)
-      result = JS.load(rs)
-      if result['responseStatus'] != 200:
-        raise SearchFail('%s (status = %d)' %
-                         (result['responseDetails'],
-                          result['responseStatus']))
-      d = result['responseData']
-      me.sv.switch(me.current)
-      for r in d['results']:
-        try:
-          me.sv.add(SearchResult(r))
-        except (U2.URLError, U2.HTTPError):
-          pass
-    except SearchFail, e:
-      print e.args[0]
-
-  def makequery(me, path):
-    bits = path.split(OS.path.sep)
-    return ' '.join(['"%s"' % p for p in bits[-2:]])
-
-  def selected(me, cov):
-    if cov:
-      me.img.set_from_pixbuf(cov.img.pixbuf)
-    else:
-      me.img.clear()
-
-  def activated(me, cov):
-    if isinstance(cov, SearchCover):
-      me.view.replace(me.which, cov.img)
-
-  def destroyed(me, w):
-    global CHOOSER
-    CHOOSER = None
-
-CHOOSER = None
-
-class ViewCover (object):
-
-  NULLIMG = NullImage(THUMBSZ, '?')
-
-  def __init__(me, dir, path, leaf):
-    me.text = dir
-    me.path = path
-    me.leaf = leaf
-    if me.leaf:
-      me.img = me.covimg = FileImage(OS.path.join(me.path, me.leaf))
-    else:
-      me.img = me.NULLIMG
-      me.covimg = None
-
-class MainViewer (BaseCoverViewer):
-
-  ITERATTR = 'vit'
-
-  def __init__(me, root):
-    BaseCoverViewer.__init__(me)
-    me.root = root
-    me.walk('')
-
-  def walk(me, dir):
-    leafp = True
-    b = OS.path.join(me.root, dir)
-    imgfile = None
-    for l in sorted(OS.listdir(b)):
-      if OS.path.isdir(OS.path.join(b, l)):
-        leafp = False
-        me.walk(OS.path.join(dir, l))
-      else:
-        base, ext = OS.path.splitext(l)
-        if base == 'cover' and ext in ['.jpg', '.png', '.gif']:
-          imgfile = l
-    if leafp:
-      me.add(ViewCover(dir, OS.path.join(me.root, dir), imgfile))
-
-  def select(me, cov):
-    pass
-
-  def activate(me, cov):
-    global CHOOSER
-    if not CHOOSER:
-      CHOOSER = CoverChooser()
-    CHOOSER.update(me, cov, cov.text, cov.covimg)
-
-  def replace(me, cov, img):
-    leaf = 'cover.%s' % img.ext
-    out = OS.path.join(cov.path, leaf)
-    new = out + '.new'
-    with open(new, 'wb') as f:
-      f.write(img._data)
-    OS.rename(new, out)
-    if cov.leaf not in [None, leaf]:
-      OS.unlink(OS.path.join(cov.path, cov.leaf))
-    ncov = ViewCover(cov.text, cov.path, leaf)
-    ncov.it = cov.it
-    me.list[ncov.it] = [ncov.img.thumbnail.pixbuf, ncov.text, ncov]
-    me.activate(ncov)
-
-ROOT = SYS.argv[1]
-
-LOOP = G.MainLoop()
-
-BLACK = GDK.Color(0, 0, 0)
-WHITE = GDK.Color(65535, 65535, 65535)
-
-WIN = GTK.Window()
-VIEW = MainViewer(ROOT)
-WIN.add(VIEW.scr)
-WIN.set_default_size(814, 660)
-WIN.set_title('coverart')
-WIN.connect('destroy', lambda _: LOOP.quit())
-WIN.show_all()
-
-LOOP.run()