summaryrefslogtreecommitdiff
path: root/multimedia
diff options
context:
space:
mode:
authorPav Lucistnik <pav@FreeBSD.org>2005-12-28 01:00:32 +0000
committerPav Lucistnik <pav@FreeBSD.org>2005-12-28 01:00:32 +0000
commit16f70785f5f7a55a3942467717bbb934a7ea2668 (patch)
tree8d1f47f1c874d9011dea0f56336987ddd32e1dc3 /multimedia
parentBump PORTREVISION to bring in the REQUIRE: ldconfig change. (diff)
Quod Libet is a GTK+-based audio player written in Python.
PR: ports/90627 Submitted by: Byung-Hee HWANG <bh@izb.knu.ac.kr>
Notes
Notes: svn path=/head/; revision=152245
Diffstat (limited to 'multimedia')
-rw-r--r--multimedia/Makefile1
-rw-r--r--multimedia/quodlibet/Makefile69
-rw-r--r--multimedia/quodlibet/distinfo3
-rw-r--r--multimedia/quodlibet/files/patch-Makefile24
-rw-r--r--multimedia/quodlibet/files/qlscrobbler.py482
-rw-r--r--multimedia/quodlibet/pkg-descr3
-rw-r--r--multimedia/quodlibet/pkg-plist37
7 files changed, 619 insertions, 0 deletions
diff --git a/multimedia/Makefile b/multimedia/Makefile
index 4f979848b3a0..e9dabc6646e0 100644
--- a/multimedia/Makefile
+++ b/multimedia/Makefile
@@ -150,6 +150,7 @@
SUBDIR += pvr250
SUBDIR += py-gstreamer
SUBDIR += qdvdauthor
+ SUBDIR += quodlibet
SUBDIR += quark
SUBDIR += recmpeg
SUBDIR += replex
diff --git a/multimedia/quodlibet/Makefile b/multimedia/quodlibet/Makefile
new file mode 100644
index 000000000000..5f374f982f66
--- /dev/null
+++ b/multimedia/quodlibet/Makefile
@@ -0,0 +1,69 @@
+# New ports collection makefile for: quodlibet
+# Date created: 19 December 2005
+# Whom: Byung-Hee HWANG <bh@izb.knu.ac.kr>
+#
+# $FreeBSD$
+#
+
+PORTNAME= quodlibet
+PORTVERSION= 0.15
+CATEGORIES= multimedia audio python
+MASTER_SITES= http://www.sacredchao.net/~piman/software/
+
+MAINTAINER= bh@izb.knu.ac.kr
+COMMENT= A GTK+-based audio player written in Python
+
+BUILD_DEPENDS= ${PYTHON_SITELIBDIR}/gst/__init__.py:${PORTSDIR}/multimedia/py-gstreamer \
+ ${PYTHON_SITELIBDIR}/ogg/_ogg.so:${PORTSDIR}/audio/py-ogg \
+ ${PYTHON_SITELIBDIR}/ogg/vorbis.so:${PORTSDIR}/audio/py-vorbis \
+ ${PYTHON_SITELIBDIR}/madmodule.so:${PORTSDIR}/audio/py-mad
+RUN_DEPENDS= ${PYTHON_SITELIBDIR}/gst/__init__.py:${PORTSDIR}/multimedia/py-gstreamer \
+ ${PYTHON_SITELIBDIR}/ogg/_ogg.so:${PORTSDIR}/audio/py-ogg \
+ ${PYTHON_SITELIBDIR}/ogg/vorbis.so:${PORTSDIR}/audio/py-vorbis \
+ ${PYTHON_SITELIBDIR}/madmodule.so:${PORTSDIR}/audio/py-mad
+
+USE_PYTHON= 2.3+
+USE_GNOME= pygtk2
+USE_GMAKE= yes
+USE_X_PREFIX= yes
+USE_GSTREAMER= ogg vorbis mad
+
+MAN1= exfalso.1 quodlibet.1
+
+.if defined(PACKAGE_BUILDING)
+BUILD_DEPENDS+= Xvfb:${X_VFBSERVER_PORT} \
+ ${X11BASE}/lib/X11/fonts/misc/8x13O.pcf.gz:${X_FONTS_MISC_PORT}
+.endif
+
+.if defined(WITH_EXTENSIONS)
+BUILD_DEPENDS+= ${PYTHON_SITELIBDIR}/gtk-2.0/egg/__init__.py:${PORTSDIR}/x11-toolkits/py-gnome-extras
+RUN_DEPENDS+= ${PYTHON_SITELIBDIR}/gtk-2.0/egg/__init__.py:${PORTSDIR}/x11-toolkits/py-gnome-extras
+PLIST_SUB+= EXTENSIONS:=""
+.else
+PLIST_SUB+= EXTENSIONS:="@comment "
+.endif
+
+.include <bsd.port.pre.mk>
+
+.if ${OSVERSION} < 500000
+IGNORE= is unsupported on FreeBSD 4.x
+.endif
+
+do-configure:
+ @${PYTHON_CMD} ${WRKSRC}/check.py
+
+do-build:
+.if defined(WITH_EXTENSIONS)
+ @cd ${WRKSRC} && ${GMAKE} extensions
+.else
+ @${ECHO_MSG} "Build done."
+.endif
+
+pre-install:
+ @${MKDIR} ${PREFIX}/share/applications
+
+post-install:
+ @${MKDIR} ${PREFIX}/lib/${PORTNAME}/plugins
+ @${INSTALL_DATA} ${FILESDIR}/qlscrobbler.py ${PREFIX}/lib/${PORTNAME}/plugins
+
+.include <bsd.port.post.mk>
diff --git a/multimedia/quodlibet/distinfo b/multimedia/quodlibet/distinfo
new file mode 100644
index 000000000000..79c6e2338105
--- /dev/null
+++ b/multimedia/quodlibet/distinfo
@@ -0,0 +1,3 @@
+MD5 (quodlibet-0.15.tar.gz) = 09b14d03e587af6ba929b18ed8e0df56
+SHA256 (quodlibet-0.15.tar.gz) = 20851cc270f17330204683b0a29ab86dcfbbfdda0e1c399c8f97836347063cf5
+SIZE (quodlibet-0.15.tar.gz) = 476236
diff --git a/multimedia/quodlibet/files/patch-Makefile b/multimedia/quodlibet/files/patch-Makefile
new file mode 100644
index 000000000000..e6e12b3320ee
--- /dev/null
+++ b/multimedia/quodlibet/files/patch-Makefile
@@ -0,0 +1,24 @@
+--- Makefile.orig Fri Nov 11 16:06:26 2005
++++ Makefile Wed Dec 28 01:50:43 2005
+@@ -10,7 +10,6 @@
+ @./check.py
+
+ make-install-dirs:
+- mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1
+ mkdir -p $(DESTDIR)$(PREFIX)/bin
+ mkdir -p $(DESTDIR)$(PREFIX)/$(TO)
+
+@@ -27,10 +26,10 @@
+
+ install-%: make-install-dirs
+ install -m 755 $*.py $(DESTDIR)$(PREFIX)/$(TO)
+- install -m 644 $*.1 $(DESTDIR)$(PREFIX)/share/man/man1/$*.1
+- install -D -m 644 $*.png $(DESTDIR)$(PREFIX)/share/pixmaps/$*.png
++ install -m 644 $*.1 $(DESTDIR)$(PREFIX)/man/man1/$*.1
++ install -m 644 $*.png $(DESTDIR)$(PREFIX)/share/pixmaps/$*.png
+ install -m 644 $*.png $(DESTDIR)$(PREFIX)/$(TO)
+- install -D -m 644 $*.desktop $(DESTDIR)$(PREFIX)/share/applications/$*.desktop
++ install -m 644 $*.desktop $(DESTDIR)$(PREFIX)/share/applications/$*.desktop
+ ln -sf ../$(TO)/$*.py $(DESTDIR)$(PREFIX)/bin/$*
+
+ clean:
diff --git a/multimedia/quodlibet/files/qlscrobbler.py b/multimedia/quodlibet/files/qlscrobbler.py
new file mode 100644
index 000000000000..a52f534cec8d
--- /dev/null
+++ b/multimedia/quodlibet/files/qlscrobbler.py
@@ -0,0 +1,482 @@
+# QLScrobbler: an Audioscrobbler client plugin for Quod Libet.
+# version 0.7
+# (C) 2005 by Joshua Kwan <joshk@triplehelix.org>,
+# Joe Wreschnig <piman@sacredchao.net>
+# Licensed under GPLv2. See Quod Libet's COPYING for more information.
+
+import random
+import md5, urllib, urllib2, time, threading, os
+import player, config, const
+import gobject, gtk
+from qltk import Message
+from util import to
+
+class QLScrobbler(object):
+ # session invariants
+ PLUGIN_NAME = "QLScrobbler"
+ PLUGIN_DESC = "Audioscrobbler client for Quod Libet"
+ PLUGIN_ICON = gtk.STOCK_CONNECT
+ PLUGIN_VERSION = "0.7"
+ CLIENT = "qlb"
+ PROTOCOL_VERSION = "1.1"
+ DUMP = os.path.join(const.DIR, "scrobbler_cache")
+
+ # things that could change
+
+ username = ""
+ password = ""
+ pwhash = ""
+
+ timeout_id = -1
+ submission_tid = -1
+
+ challenge = ""
+ submit_url = ""
+
+ # state management
+ waiting = False
+ challenge_sent = False
+ broken = False
+ need_config = False
+ need_update = False
+ already_submitted = False
+ locked = False
+ flushing = False
+ disabled = False
+
+ # we need to store this because not all events get the song
+ song = None
+
+ queue = []
+
+ def __init__(self):
+ # Read dumped queue and delete it
+ try:
+ dump = open(self.DUMP, 'r')
+ self.read_dump(dump)
+ except: pass
+
+ # Set up exit hook to dump queue
+ gtk.quit_add(0, self.dump_queue)
+
+ def read_dump(self, dump):
+ print "Loading dumped queue."
+
+ current = {}
+
+ for line in dump.readlines():
+ key = ""
+ value = ""
+
+ line = line.rstrip()
+ try: (key, value) = line.split(" = ", 1)
+ except:
+ if line == "-":
+ for key in ["album", "mbid"]:
+ if key not in current:
+ current[key] = ""
+
+ for reqkey in ["artist", "title", "length", "stamp"]:
+ # discard if somehow invalid
+ if reqkey not in current:
+ current = {}
+
+ if current != {}:
+ self.queue.append(current)
+ current = {}
+ continue
+
+ if key == "length": current[key] = int(value)
+ else: current[key] = value
+
+ dump.close()
+
+ os.remove(self.DUMP)
+
+# print "Queue contents:"
+# for item in self.queue:
+# print "\t%s - %s" % (item['artist'], item['title'])
+
+ # Try to flush it immediately
+ if len(self.queue) > 0:
+ self.flushing = True
+ self.submit_song()
+ else: print "Queue was empty!"
+
+ def dump_queue(self):
+ if len(self.queue) == 0: return 0
+
+ print "Dumping offline queue, will submit next time."
+
+ dump = open(self.DUMP, 'w')
+
+ for item in self.queue:
+ for key in item:
+ dump.write("%s = %s\n" % (key, item[key]))
+
+ dump.close()
+
+ return 0
+
+ def plugin_on_removed(self, songs):
+ try:
+ if self.song in songs:
+ self.already_submitted = True
+ except:
+ # Older version compatibility.
+ if self.song is songs:
+ self.already_submitted = True
+
+ def plugin_on_song_ended(self, song, stopped):
+ if song is None: return
+
+ if self.timeout_id > 0:
+ gobject.source_remove(self.timeout_id)
+ self.timeout_id = -1
+
+ def plugin_on_song_started(self, song):
+ if song is None: return
+
+ self.already_submitted = False
+ if self.timeout_id > 0:
+ gobject.source_remove(self.timeout_id)
+
+ self.timeout_id = -1
+
+ # Protocol stipulation:
+ # * don't submit when length < 00:30
+ # NOTE: >30:00 stipulation has been REMOVED as of Protocol1.1
+ # * don't submit if artist and title are not available
+ if song["~#length"] < 30: return
+ elif 'title' not in song: return
+ elif "artist" not in song:
+ if ("composer" not in song) and ("performer" not in song): return
+
+ self.song = song
+ if player.playlist.paused == False:
+ self.prepare()
+
+ def plugin_on_paused(self):
+ if self.timeout_id > 0:
+ gobject.source_remove(self.timeout_id)
+ # special value that will tell on_unpaused to check song length
+ self.timeout_id = -2
+
+ def plugin_on_unpaused(self):
+ if self.already_submitted == False and self.timeout_id == -1: self.prepare()
+
+ def plugin_on_seek(self, song, msec):
+ if self.timeout_id > 0:
+ gobject.source_remove(self.timeout_id)
+ self.timeout_id = -1
+
+ if msec == 0: #I think this is okay!
+ self.prepare()
+ else:
+ self.already_submitted = True # cancel
+
+ def prepare(self):
+ if self.song is None: return
+
+ # Protocol stipulations:
+ # * submit 240 seconds in or at 50%, whichever comes first
+ delay = int(min(self.song["~#length"] / 2, 240))
+
+ if self.timeout_id == -2: # change delta based on current progress
+ # assumption is that self.already_submitted == 0, therefore
+ # delay - progress > 0
+ progress = int(player.playlist.info.time[0] / 1000)
+ delay -= progress
+
+ self.timeout_id = gobject.timeout_add(delay * 1000, self.submit_song)
+
+ def read_config(self):
+ username = ""
+ password = ""
+ try:
+ username = config.get("plugins", "scrobbler_username")
+ password = config.get("plugins", "scrobbler_password")
+ except:
+ if self.need_config == False:
+ self.quick_info("Please visit the Preferences window to set QLScrobbler up. Until then, songs will not be submitted.")
+ self.need_config = True
+ return
+
+ self.username = username
+
+ hasher = md5.new()
+ hasher.update(password);
+ self.password = hasher.hexdigest()
+ self.need_config = False
+
+ def __destroy_cb(self, dialog, response_id):
+ dialog.destroy()
+
+ def quick_error_helper(self, str):
+ dialog = Message(gtk.MESSAGE_ERROR, None, "QLScrobbler", str)
+ dialog.connect('response', self.__destroy_cb)
+ dialog.show()
+
+ def quick_error(self, str):
+ gobject.idle_add(self.quick_error_helper, str)
+
+ def quick_info_helper(self, str):
+ dialog = Message(gtk.MESSAGE_INFO, widgets.widgets.main, "QLScrobbler", str).run()
+ dialog.connect('response', self.__destroy_cb)
+ dialog.show()
+
+ def quick_info(self, str):
+ gobject.idle_add(self.quick_info_helper, str)
+
+ def clear_waiting(self):
+ self.waiting = False
+
+ def send_handshake(self):
+ # construct url
+ url = "http://post.audioscrobbler.com/?hs=true&p=%s&c=%s&v=%s&u=%s" % ( self.PROTOCOL_VERSION, self.CLIENT, self.PLUGIN_VERSION, self.username )
+
+ print "Sending handshake to Audioscrobbler."
+
+ resp = None
+
+ try:
+ resp = urllib2.urlopen(url);
+ except:
+ print "Server not responding, handshake failed."
+ return # challenge_sent is NOT set to 1
+
+ # check response
+ lines = resp.read().rstrip().split("\n")
+ status = lines.pop(0)
+
+ print "Handshake status: %s" % status
+
+ if status == "UPTODATE" or status.startswith("UPDATE"):
+ if status.startswith("UPDATE"):
+ self.quick_info("A new plugin is available at %s! Please download it, or your Audioscrobbler stats may not be updated, and this message will be displayed every session." % status.split()[1])
+ self.need_update = True
+
+ # Scan for submit URL and challenge.
+ self.challenge = lines.pop(0)
+
+ print "Challenge: %s" % self.challenge
+
+ # determine password
+ hasher = md5.new()
+ hasher.update(self.password)
+ hasher.update(self.challenge)
+ self.pwhash = hasher.hexdigest()
+
+ self.submit_url = lines.pop(0)
+
+ self.challenge_sent = True
+ elif status == "BADUSER":
+ self.quick_error("Authentication failed: invalid username %s or bad password." % self.username)
+
+ self.broken = True
+
+ # Honor INTERVAL if available
+ try: interval = int(lines.pop(0).split()[1])
+ except: interval = 0
+
+ if interval > 1:
+ self.waiting = True
+ gobject.timeout_add(interval * 1000, self.clear_waiting)
+ print "Server says to wait for %d seconds." % interval
+
+ def submit_song(self):
+ bg = threading.Thread(None, self.submit_song_helper)
+ bg.setDaemon(True)
+ bg.start()
+
+ def submit_song_helper(self):
+ enabled = getattr(self, 'PMEnFlag', False)
+ if enabled and self.disabled:
+ print "Plugin re-enabled - accepting new songs."
+ self.disabled = False
+ if self.submission_tid != -1:
+ gobject.source_remove(self.submission_tid);
+ self.submission_tid = -1
+ elif not enabled and not self.disabled: #if we've already printed
+ print "Plugin disabled - not accepting any new songs."
+ self.disabled = True
+ if len(self.queue) > 0:
+ self.submission_tid = gobject.timeout_add(120 * 1000, self.submit_song_helper)
+ print "Attempts will continue to submit the last %d songs." % len(self.queue)
+
+ if self.already_submitted == True or self.broken == True: return
+
+ if self.flushing == False:
+ stamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
+
+ store = {
+ "title": self.song.comma("title"),
+ "length": str(self.song["~#length"]),
+ "album": self.song.comma("album"),
+ "mbid": "", # will be correctly set if available
+ "stamp": stamp
+ }
+
+ if "artist" in self.song:
+ store["artist"] = self.song.comma("artist")
+ elif "composer" in self.song:
+ store["artist"] = self.song.comma("composer")
+ elif "performer" in self.song:
+ performer = self.song.comma('performer')
+ if performer[-1] == ")" and "(" in performer:
+ store["artist"] = performer[:performer.rindex("(")].strip()
+ else:
+ store["artist"] = performer
+ elif "musicbrainz_trackid" in self.song:
+ store["mbid"] = self.song["musicbrainz_trackid"]
+
+ self.queue.append(store)
+ else: self.flushing = False
+
+ if self.locked == True:
+ # another instance running, let it deal with this
+ return
+
+ self.locked = True
+
+ while self.waiting == True: time.sleep(1)
+
+ # Read config, handshake, and send challenge if not already done
+ if self.challenge_sent == False:
+ self.read_config()
+ if self.broken == False and self.need_config == False:
+ self.send_handshake()
+
+ # INTERVAL may have been set during handshake.
+ while self.waiting == True: time.sleep(1)
+
+ if self.challenge_sent == False:
+ self.locked = False
+ return
+
+ data = {
+ 'u': self.username,
+ 's': self.pwhash
+ }
+
+ # Flush the cache
+ for i in range(len(self.queue)):
+ print to("Sending song: %s - %s" % (self.queue[i]['artist'], self.queue[i]['title']))
+ data["a[%d]" % i] = self.queue[i]['artist'].encode('utf-8')
+ data["t[%d]" % i] = self.queue[i]['title'].encode('utf-8')
+ data["l[%d]" % i] = str(self.queue[i]['length'])
+ data["b[%d]" % i] = self.queue[i]['album'].encode('utf-8')
+ data["m[%d]" % i] = self.queue[i]['mbid']
+ data["i[%d]" % i] = self.queue[i]['stamp']
+
+ (host, file) = self.submit_url[7:].split("/")
+
+ resp = None
+
+ try:
+ data_str = urllib.urlencode(data)
+ resp = urllib2.urlopen("http://" + host + "/" + file, data_str)
+ except:
+ print "Audioscrobbler server not responding, will try later."
+ self.locked = False
+ return # preserve the queue, yadda yadda
+
+ resp_save = resp.read()
+ lines = resp_save.rstrip().split("\n")
+
+ try: (status, interval) = lines
+ except:
+ try: status = lines[0]
+ except:
+ print "Truncated server response, will try later..."
+ self.locked = False
+ return
+ interval = None
+
+ print "Submission status: %s" % status
+
+ if status == "BADAUTH":
+ print "Attempting to re-authenticate."
+ self.challenge_sent = False
+ self.send_handshake()
+ if self.challenge_sent == False:
+ self.quick_error("Your Audioscrobbler login data is incorrect, so you must re-enter it before any songs will be submitted.\n\nThis message will not be shown again.")
+ self.broken = True
+ elif status == "OK":
+ self.queue = []
+ elif status.startswith("FAILED"):
+ if status.startswith("FAILED Plugin bug"):
+ print "Plugin bug!? Ridiculous! Dumping queue contents."
+ for item in self.queue:
+ for key in item:
+ print "%s = %s" % (key, item[key])
+ # possibly handle other specific cases here for debugging later
+ else:
+ print "Unknown response from server: %s" % status
+ print "Dumping full response:"
+ print resp_save
+
+ if interval != None: interval_secs = int(interval.split()[1])
+ else: interval_secs = 0
+
+ if interval_secs > 1:
+ self.waiting = True
+ gobject.timeout_add(interval_secs * 1000, self.clear_waiting)
+ print "Server says to wait for %d seconds." % interval_secs
+
+ if self.disabled and len(self.queue) == 0 and self.submission_tid != -1:
+ print "All songs submitted, disabling retries."
+ gobject.source_remove(self.submission_tid)
+ self.submission_tid = -1
+
+ self.already_submitted = True
+ self.locked = False
+
+ def PluginPreferences(self, parent):
+ def changed(entry, key):
+ # having two functions is unnecessary..
+ config.set("plugins", "scrobbler_" + key, entry.get_text())
+
+ def destroyed(*args):
+ # if changed, let's say that things just got better and we should
+ # try everything again
+ newu = None
+ newp = None
+ try:
+ newu = config.get("plugins", "scrobbler_username")
+ newp = config.get("plugins", "scrobbler_password")
+ except:
+ return
+
+ if self.username != newu or self.password != newp:
+ self.broken = False
+
+ table = gtk.Table(3, 2)
+ table.set_col_spacings(3)
+ lt = gtk.Label(_("Please enter your Audioscrobbler username and password."))
+ lt.set_size_request(260, -1)
+ lu = gtk.Label(_("Username:"))
+ lp = gtk.Label(_("Password:"))
+ for l in [lt, lu, lp]:
+ l.set_line_wrap(True)
+ l.set_alignment(0.0, 0.5)
+ table.attach(lt, 0, 2, 0, 1, xoptions=gtk.FILL)
+ table.attach(lu, 0, 1, 1, 2, xoptions=gtk.FILL)
+ table.attach(lp, 0, 1, 2, 3, xoptions=gtk.FILL)
+ userent = gtk.Entry()
+ pwent = gtk.Entry()
+ pwent.set_visibility(False)
+ pwent.set_invisible_char('*')
+ table.set_border_width(6)
+
+ try: userent.set_text(config.get("plugins", "scrobbler_username"))
+ except: pass
+ try: pwent.set_text(config.get("plugins", "scrobbler_password"))
+ except: pass
+
+ table.attach(userent, 1, 2, 1, 2)
+ table.attach(pwent, 1, 2, 2, 3)
+ pwent.connect('changed', changed, 'password')
+ userent.connect('changed', changed, 'username')
+ table.connect('destroy', destroyed)
+ return table
diff --git a/multimedia/quodlibet/pkg-descr b/multimedia/quodlibet/pkg-descr
new file mode 100644
index 000000000000..5f3011b0e0ec
--- /dev/null
+++ b/multimedia/quodlibet/pkg-descr
@@ -0,0 +1,3 @@
+Quod Libet is a GTK+-based audio player written in Python.
+
+WWW: http://www.sacredchao.net/quodlibet/
diff --git a/multimedia/quodlibet/pkg-plist b/multimedia/quodlibet/pkg-plist
new file mode 100644
index 000000000000..013b238a3760
--- /dev/null
+++ b/multimedia/quodlibet/pkg-plist
@@ -0,0 +1,37 @@
+bin/exfalso
+bin/quodlibet
+lib/quodlibet/exfalso.png
+lib/quodlibet/exfalso.py
+%%EXTENSIONS:%%lib/quodlibet/mmkeys.so
+lib/quodlibet/plugins/qlscrobbler.py
+lib/quodlibet/ql-volume-max.png
+lib/quodlibet/ql-volume-medium.png
+lib/quodlibet/ql-volume-min.png
+lib/quodlibet/ql-volume-zero.png
+lib/quodlibet/quodlibet.png
+lib/quodlibet/quodlibet.py
+lib/quodlibet/quodlibet.zip
+%%EXTENSIONS:%%lib/quodlibet/trayicon.so
+share/applications/exfalso.desktop
+share/applications/quodlibet.desktop
+share/locale/bg/LC_MESSAGES/quodlibet.mo
+share/locale/de/LC_MESSAGES/quodlibet.mo
+share/locale/en_CA/LC_MESSAGES/quodlibet.mo
+share/locale/en_GB/LC_MESSAGES/quodlibet.mo
+share/locale/es/LC_MESSAGES/quodlibet.mo
+share/locale/fi/LC_MESSAGES/quodlibet.mo
+share/locale/fr/LC_MESSAGES/quodlibet.mo
+share/locale/gl/LC_MESSAGES/quodlibet.mo
+share/locale/he/LC_MESSAGES/quodlibet.mo
+share/locale/it/LC_MESSAGES/quodlibet.mo
+share/locale/nl/LC_MESSAGES/quodlibet.mo
+share/locale/pl/LC_MESSAGES/quodlibet.mo
+share/locale/pt/LC_MESSAGES/quodlibet.mo
+share/locale/ru/LC_MESSAGES/quodlibet.mo
+share/pixmaps/exfalso.png
+share/pixmaps/quodlibet.png
+@unexec rmdir %D/share/applications 2>/dev/null || true
+@exec mkdir -p %D/lib/quodlibet/plugins
+@dirrm lib/quodlibet/plugins
+@exec mkdir -p %D/lib/quodlibet
+@dirrm lib/quodlibet