[Deskbar] Muine song/album search



Hi,

This adds a handler that allows for playing an music in Muine by
searching for an album or song name (two separate handlers).  It works
fine, but has a number of caveats:

* Requires muine-shell (I'm using python2.3, so can't compile dbus
bindings yet)
* If you are running both the album and song handlers, you can't
distinguish between songs and albums of the same name. Both display
'Play %(song/album) by %(artist)'.  Maybe  albums should say 'Play album
%(album) by %(artist)' or something?
* I don't normally use Python, so things are probably ugly.
Specifically, the database parsing algorithm could use work, because it
is using a nasty regex that embarrasses me a little bit.

All these things could obviously be fixed by someone who has some
knowledge of Python and a desire to use this.  Hopefully somebody finds
this useful.  At the very least, it may inspire someone to hack up the
same thing for Rhythmbox, Banshee, or Quodlibet.

-eric
import os
from os.path import expanduser, exists
from gettext import gettext as _

import gdbm
import re

import gobject
import deskbar.handler
from deskbar.filewatcher import FileWatcher

HANDLERS = {
	"MuineAlbumHandler" : {
		"name": _("Music search"),
		"description": _("Play music albums"),
	},
	"MuineSongHandler" : {
	        "name": _("Music search"),
		"description": _("Play songs")
	},
}

MUINE_SONGS_DB = expanduser("~/.gnome2/muine/songs.db")

class MuineAlbumMatch(deskbar.handler.Match):
        def __init__(self, backend, name, artist, path):
		deskbar.handler.Match.__init__(self, backend, "stock_music-library")
		self._name = name
		self._artist = artist
		self._path = path
		
	def action(self, text=None):
		self._priority = self._priority+1
		gobject.spawn_async(["muine-shell", '-f', self._path[0]], flags=gobject.SPAWN_SEARCH_PATH)
		del self._path[0]
		for song in self._path:
			gobject.spawn_async(["muine-shell", '-q', song], flags=gobject.SPAWN_SEARCH_PATH)
	
	def get_verb(self):
		return _("Play <b>%(name)s</b> by %(artist)s")

	def get_name(self, text=None):
		return {
			"name": self._name,
			"artist": self._artist,
		}

class MuineSongMatch(deskbar.handler.Match):
        def __init__(self, backend, artist, name, path):
		deskbar.handler.Match.__init__(self, backend, "stock_music-library")
		self._artist = artist
		self._name = name
		self._path = path
		
	def action(self, text=None):
		self._priority = self._priority+1
		gobject.spawn_async(["muine-shell", "-f", self._path], flags=gobject.SPAWN_SEARCH_PATH)
	
	def get_verb(self):
		return _("Play <b>%(name)s</b> by %(artist)s")

	def get_name(self, text=None):
		return {
			"name": self._name,
			"artist": self._artist,
		}

class MuineHandler (deskbar.handler.Handler):
	def __init__(self, callback):
		deskbar.handler.Handler.__init__(self, "stock_music-library")
		self.watch_callback = callback
		
	def initialize(self):
		if not hasattr(self, 'watcher'):
			self.watcher = FileWatcher()
			self.watcher.connect('changed', lambda watcher, f: self.watch_callback())

		self.watcher.add(MUINE_SONGS_DB)

	
	def stop(self):
		self.watcher.remove(MUINE_SONGS_DB)

class MuineAlbumHandler(MuineHandler):
        def __init__(self):
		MuineHandler.__init__(self, lambda: self._scan_muine_albums())
		self._albums = {}
		
	def initialize(self):
		MuineHandler.initialize(self)
		self._scan_muine_albums()
		
	def query(self, query, max=5):
		result = []
		query = query.lower()
		for key, (artist, songs) in self._albums.items():
			if key.lower().startswith(query):
				result.append(MuineAlbumMatch(self, key, artist, songs))		
		return result[:max]

	def _scan_muine_albums(self):
		if not exists(MUINE_SONGS_DB):
			return

		try:
			db = gdbm.open(MUINE_SONGS_DB, 'r')

			p = re.compile(r'\A\W.*?(\w.*?)\x00.*?\W.*?(\w.*?)\x00.*?\W.*?(\w.*?)\x00.*?\W.*')
			
			key = db.firstkey()
			
			while key != None:
				match = p.match(db[key])
				if match != None:
					if not self._albums.has_key("%s" % match.group(3)):
						self._albums["%s" % match.group(3)] = (match.group(2), [key])
					else:
						(artist, songs) = self._albums["%s" % match.group(3)]
						songs.append(key)
						self._albums["%s" % match.group(3)] = (artist, songs)
				key = db.nextkey(key)
						
		except Exception, msg:
			print 'Error:_scan_muine_albums:', msg

class MuineSongHandler(MuineHandler):
        def __init__(self):
		MuineHandler.__init__(self, lambda: self._scan_muine_songs())
		self._songs = []
		
	def initialize(self):
		MuineHandler.initialize(self)
		self._scan_muine_songs()
		
	def query(self, query, max=5):
		result = []
		query = query.lower()
		for (artist, song, path) in self._songs:
			if song.lower().startswith(query):
				result.append(MuineSongMatch(self, artist, song, path))
		
		return result[:max]

	def _scan_muine_songs(self):
		if not exists(MUINE_SONGS_DB):
			return

		try:
			db = gdbm.open(MUINE_SONGS_DB, 'r')

			p = re.compile(r'\A\W.*?(\w.*?)\x00.*?\W.*?(\w.*?)\x00.*?\W.*?(\w.*?)\x00.*?\W.*')
			
			key = db.firstkey()
			
			while key != None:
				match = p.match(db[key])
				if match != None:
					self._songs.append((match.group(2), match.group(1), key))
				key = db.nextkey(key)
						
		except Exception, msg:
			print 'Error:_scan_muine_songs:', msg


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]