# HG changeset patch # User fabien # Date 1076277324 18000 # Node ID 13f56bb29b96cc514ef1343bed23e5051dcbb4fd # Parent a7f7026f9416193f9d0b97635af4e4e80f380710 [svn] New bestofimms, cleanimms, imms.py and utils.py. diff -r a7f7026f9416 -r 13f56bb29b96 .cvsignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.cvsignore Sun Feb 08 16:55:24 2004 -0500 @@ -0,0 +1,3 @@ +*.pyc +*.tmplc +index.html diff -r a7f7026f9416 -r 13f56bb29b96 Makefile --- a/Makefile Sat Feb 07 13:29:53 2004 -0500 +++ b/Makefile Sun Feb 08 16:55:24 2004 -0500 @@ -1,7 +1,7 @@ REMOTE_DIR=o:public_html/oss/immsview VERSION=$(shell python -c 'from imp import load_source; \ print load_source(".","immsview")._version_' \ - | sed -e 's;\$$Id$$;\1;') + | sed -e 's;\$$\I\d\: immsview,v \(.*\) fabien Exp \$$;\1;') index.html: index.html.in immsview perl -p -e 's;\@IMMSVIEW_VERSION\@;$(VERSION);g' $< > $@ diff -r a7f7026f9416 -r 13f56bb29b96 bestofimms --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bestofimms Sun Feb 08 16:55:24 2004 -0500 @@ -0,0 +1,78 @@ +#!/usr/bin/python + +import sqlite +from htmltmpl import TemplateManager, TemplateProcessor +import os +import ID3 +from sys import stderr +from ogg.vorbis import VorbisFile + +_imms_db = os.environ['HOME'] + '/.imms/imms.db' +_template = __file__ + '.tmpl' + +def rating_to_color(rating): + i = rating - 75 + if i <= 25: + red = 255 + green = i * 255 / 25 + blue = 0 + elif i <= 50: + red = (50-i) * 255 / 25 + green = 255 + blue = 0 + else: + red = 0 + green = 255 + blue = (i-50) * 255 / 25 + return "#%02X%02X%02X" % (red, green, blue) + +def grab_tunes(): + sql = sqlite.connect(_imms_db, autocommit = 1) + cu = sql.cursor() + cu.execute(''' SELECT r.rating, l.path + FROM Library l, Rating r + WHERE r.uid = l.uid AND r.rating >= 125 + ORDER BY r.rating DESC;''') + return map(lambda x: { + 'rating' : x[0], + 'path': x[1]}, + cu.fetchall() ) + +def check_tunes(tunes): + res = [] + for tune in tunes: + song = tune['path'] + try: + os.stat(song) + except: + continue + tune['color'] = rating_to_color(tune['rating']) + if song[-4:] == '.mp3': + id3 = ID3.ID3(song) + try: + tune['path'] = \ + id3['ARTIST'] + \ + ' - ' + id3['TITLE'] + except: + pass + elif song[-4:] == '.ogg': + vf = VorbisFile(song) + vc = vf.comment() + try: + tune['path'] = \ + vc['ARTIST'][0] + \ + u' - ' + vc['TITLE'][0] + except: + pass + res.append(tune) + return res + + +def output_web(): + tproc = TemplateProcessor() + tmpl = TemplateManager().prepare(_template) + tproc.set('Bestof', check_tunes(grab_tunes())) + print tproc.process(tmpl) + +if __name__ == '__main__': + output_web() diff -r a7f7026f9416 -r 13f56bb29b96 bestofimms.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bestofimms.tmpl Sun Feb 08 16:55:24 2004 -0500 @@ -0,0 +1,16 @@ + + + Fabien Niñoles's Top Songs + + +

Fabien Niñoles's Top Songs

+ + + + diff -r a7f7026f9416 -r 13f56bb29b96 cleanimms --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cleanimms Sun Feb 08 16:55:24 2004 -0500 @@ -0,0 +1,110 @@ +#!/usr/bin/python + +import os +import sqlite +from ogg.vorbis import VorbisFile +import ID3 +import readline +import imms +from utils import unique, copy_file, set_file_completer + + +class CleanupIMMS: + def __init__(self, db_file): + self.db = imms.IMMSDb(db_file) + def check_uid(self, uid): + lib = self.db.get_library_entries(uid = uid) + if len(lib) == 0: + print "Erased uid = ", uid + self.db.erase_uid(uid) + def check_sid(self, sid): + lib = self.db.get_library_entries(sid = sid) + if len(lib) == 0: + print "Erased sid = ", uid + self.db.erase_sid(sid) + def handle_no_file(self, path, uid, sid): + other_paths = self.db.get_library_entries(uid = uid) + other_paths = unique(map(lambda x: x[0], other_paths)) + for opath in other_paths: + if os.path.isfile(opath): + return None + other_paths = self.db.get_library_entries(sid = sid) + other_paths = unique(map(lambda x: x[0], other_paths)) + for opath in other_paths: + if os.path.isfile(opath): + readline.add_history(opath) + readline.add_history(path) + readline.insert_text('Edit') + while 1: + cmd = raw_input("I can't find '%s'.\n" + "Edit, Skip or Remove? " % path) + if len(cmd) > 0: + cmd = cmd.lstrip()[0].lower() + if cmd == 'e': + newpath = raw_input() + if newpath in other_paths: + return None + if os.path.isfile(newpath): + return newpath + print "Invalid filename!" + elif cmd == 's': + return path; + elif cmd == 'r': + return None + def clean_library(self): + lib = self.db.get_library_entries() + deleted_uids = [] + deleted_sids = [] + for entry in lib: + path, uid, sid = entry + if not os.path.isfile(path): + uid = int(uid) + sid = int(sid) + newfile = self.handle_no_file(path, uid, sid) + if not newfile: + print "Erasing ", path + self.db.erase_filename(path) + deleted_uids.append(uid) + deleted_sids.append(sid) + elif newfile != path: + print "Renaming ", path, " into ", newfile + self.db.update_filename(path, newfile) + else: + print "Skipping ", path + map(self.check_uid, unique(deleted_uids)) + map(self.check_sid, unique(deleted_sids)) + def clean_rating(self): + rates = self.db.get_ratings() + rates = unique(map(lambda x: x[0], rates)) + map(self.check_uid, rates) + def clean_acoustic(self): + uids = map(lambda x: x[0], self.db.get_acoustics()) + map(self.check_uid, uids) + def clean_info(self): + sids = map(lambda x: x[0], self.db.get_infos()) + map(self.check_sid, sids) + def clean_last(self): + sids = map(lambda x: x[0], self.db.get_last()) + map(self.check_sid, sids) + def clean_all(self): + self.clean_library() + self.clean_rating() + self.clean_acoustic() + self.clean_info() + self.clean_last() + +if __name__ == '__main__': + set_file_completer() + readline.parse_and_bind('tab: complete') + db_file = os.environ['HOME'] + '/.imms/imms.db' + db_backup = db_file + '.bak' + copy_file(db_file, db_backup).close() + try: + clean_up = CleanupIMMS(db_file) + except Exception, inst: + print inst + ans = raw_input('Do you want to get back the backup file? ') + if len(ans) > 0: + if ans.lstrip()[0].lower() == 'y': + copy_file(db_backup, db_file) + print "backup file preserved: %s." % db_backup diff -r a7f7026f9416 -r 13f56bb29b96 imms.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imms.py Sun Feb 08 16:55:24 2004 -0500 @@ -0,0 +1,133 @@ +import os +import sqlite + +def quote_sql(str): + return str.replace("'", "''") + +class IMMSDb: + def __init__(self, dbname = None): + if not dbname: + dbname = os.environ['HOME'] + '/.imms/imms.db' + # autocommit = 1 disable autocommit! + self.cx = sqlite.connect(dbname, autocommit = 1, + timeout = 2, encoding = ('utf-8', 'replace')) + self.cu = self.cx.cursor() + def get_library_entries(self, **kw): + qry = "SELECT path, uid, sid FROM Library"; + first = 1 + for key in kw.keys(): + if first: + qry += " WHERE " + first = 0 + else: + qry += " AND " + if key in ['uid', 'sid']: + qry += "%s = %d" % (key, kw[key]) + else: + qry += "%s = '%s'" % (key, quote_sql(kw[key])) + qry += ";" + self.cu.execute(qry) + return self.cu.fetchall() + def update_filename(self, oldname, newname): + self.cu.execute("""UPDATE Library + SET path = '%s' + WHERE path = '%s';""" % (quote_sql(newname), + quote_sql(oldname))) + def erase_filename(self, name): + self.cu.execute("""DELETE FROM Library + WHERE path = '%s';""" % quote_sql(name)) + def erase_uid(self, uid): + self.cu.execute("""BEGIN TRANSACTION; + DELETE FROM Library WHERE uid = %d; + DELETE FROM Rating WHERE uid = %d; + DELETE FROM Acoustic WHERE uid = %d; + COMMIT;""" % (uid, uid, uid)) + def erase_sid(self, sid): + self.cu.execute("""BEGIN TRANSACTION; + DELETE FROM Library WHERE sid = %d; + DELETE FROM Info WHERE sid = %d; + DELETE FROM Last WHERE sid = %d; + COMMIT;""") + def erase_path(self, path): + self.cu.execute("DELETE FROM Library WHERE path = '%s';" \ + % quote_sql(path)) + def get_ratings(self, min = 0, max = 250): + self.cu.execute('''SELECT Rating.uid, Rating.rating + FROM Rating + WHERE Rating.rating >= %d + AND Rating.rating <= %d + ORDER BY Rating.rating;''' % (min, max)) + return cu.fetchall() + def get_acoustics(self, uids = None): + qry = "SELECT uid, bpm. spectrum FROM Acoustic" + first = 1 + for uid in uids: + if first: + qry += ' WHERE' + first = 0 + else: + qry += ' OR' + qry += " uid = %d" % uid + qry += ';' + self.cu.execute(qry) + return cu.fetchall() + def get_infos(self, sids = None): + qry = "SELECT sid, artist, title FROM Infos" + first = 1 + for sid in sids: + if first: + qry += ' WHERE' + first = 0 + else: + qry += ' OR' + qry += " sid = %d" % id + qry += ';' + self.cu.execute(qry) + return cu.fetchall() + def get_last(self, sids = None): + qry = "SELECT sid, last FROM Last" + first = 1 + for sid in sids: + if first: + qry += ' WHERE' + first = 0 + else: + qry += ' OR' + qry += " sid = %d" % id + qry += ';' + self.cu.execute(qry) + return cu.fetchall() + def get_uid_by_path(self, path): + entries = self.get_library_entries(path = path) + return map(lambda x: x[1], entries) + def get_ratings_and_info(self, uids = None): + cu = self.cx.cursor() + qry = '''SELECT l.uid, r.rating, l.path, ls.last + FROM Library l, Rating r, Last ls + WHERE l.uid = r.uid AND l.sid = ls.sid''' + if uids: + qry += ' AND (l.uid = %d' % (uids.pop()) + for uid in uids: + qry += ' OR l.uid = %d' % uid + qry += ')' + qry += ';' + cu.execute(qry) + # Better to fetch everything since locking can really mess + # things in imms plugin. + results = {} + tune = cu.fetchone() + while tune: + try: + uid = int(tune[0]) + if results.has_key(uid): + results[uid]['path'].append( + tune[2].decode('utf-8', 'replace')) + else: + results[uid] = { + 'rating' : int(tune[1]), + 'path' : [ tune[2].decode('utf-8', 'replace') ], + 'last' : int(tune[3])} + except UnicodeDecodeError: + print tune[2] + tune = cu.fetchone() + return results diff -r a7f7026f9416 -r 13f56bb29b96 immsview --- a/immsview Sat Feb 07 13:29:53 2004 -0500 +++ b/immsview Sun Feb 08 16:55:24 2004 -0500 @@ -20,9 +20,12 @@ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. -_version_ = "$Id: immsview 1710 2004-02-07 15:58:54Z fabien $" +_version_ = "$Id: immsview 1713 2004-02-08 21:55:24Z fabien $" # $Log$ +# Revision 1.29 2004/02/08 21:55:24 fabien +# New bestofimms, cleanimms, imms.py and utils.py. +# # Revision 1.28 2004/02/07 15:58:54 fabien # Use Python-Gnuplot instead of immsplot. # @@ -139,7 +142,6 @@ import sys import os -import sqlite import gobject import gtk import gtk.glade @@ -147,6 +149,7 @@ import xmms.control import time import Gnuplot +from imms import IMMSDb gtk.glade.bindtextdomain('immsview', '/usr/share/immsview/LANG') gtk.glade.textdomain('immsview') @@ -193,71 +196,6 @@ else: self.set_playlist_pos(idx) -def quote_sql(str): - return str.replace("'", "''") - -class IMMSDb: - _dbname = os.environ['HOME'] + '/.imms/imms.db' - # _dbname = os.environ['HOME'] + '/.imms/imms.backup.db' - def __init__(self): - # autocommit = 1 disable autocommit! - self.cx = sqlite.connect(IMMSDb._dbname, autocommit = 1, - timeout = 2, encoding = ('utf-8', 'replace')) - def commit(self): - # self.cx.commit() - pass - def _get_ratings(self, min = 0, max = 250): - cu = self.cx.cursor() - cu.execute('''SELECT Rating.uid, Rating.rating - FROM Rating - WHERE Rating.rating >= %d - AND Rating.rating <= %d - ORDER BY Rating.rating;''' % (min, max)) - return cu.fetchall() - def _get_library_uid(self, uid): - cu = self.cx.cursor() - cu.execute('''SELECT Library.path - FROM Library - WHERE Library.uid = %d;''' % (uid,)) - return cu.fetchone() - def get_uid_by_path(self, path): - cu = self.cx.cursor() - cu.execute("""SELECT Library.uid FROM Library - WHERE Library.path = '%s';""" % quote_sql(path)) - return map(lambda x: x[0], cu.fetchall()) - def get_ratings_and_info(self, uids = None): - print time.ctime(time.time()) + ": querying" - cu = self.cx.cursor() - qry = '''SELECT l.uid, r.rating, l.path, ls.last - FROM Library l, Rating r, Last ls - WHERE l.uid = r.uid AND l.sid = ls.sid''' - if uids: - qry += ' AND (l.uid = %d' % (uids.pop()) - for uid in uids: - qry += ' OR l.uid = %d' % uid - qry += ')' - qry += ';' - cu.execute(qry) - # Better to fetch everything since locking can really mess - # things in imms plugin. - print time.ctime(time.time()) + ": mapping" - results = {} - tune = cu.fetchone() - while tune: - try: - uid = int(tune[0]) - if results.has_key(uid): - results[uid]['path'].append( - tune[2].decode('utf-8', 'replace')) - else: - results[uid] = { - 'rating' : int(tune[1]), - 'path' : [ tune[2].decode('utf-8', 'replace') ], - 'last' : int(tune[3])} - except UnicodeDecodeError: - print tune[2] - tune = cu.fetchone() - return results _gdk_colors = [] for i in range(76): @@ -554,7 +492,10 @@ self.iview.get_model().update() def do_get_current(self, dummy): song = self.xmms.get_current_file() - self.iview.set_current_song(song) + try: + self.iview.set_current_song(song) + except: + pass def do_play(self, dummy): fn = self.iview.get_file_selected() if fn: diff -r a7f7026f9416 -r 13f56bb29b96 index.html.in --- a/index.html.in Sat Feb 07 13:29:53 2004 -0500 +++ b/index.html.in Sun Feb 08 16:55:24 2004 -0500 @@ -34,15 +34,16 @@

Requirements

-

As well as any requirements for those programs (that's mean python, - gnuplot and xmms too!).

-
Fabien Niñoles <fabien@tzone.org>
diff -r a7f7026f9416 -r 13f56bb29b96 utils.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/utils.py Sun Feb 08 16:55:24 2004 -0500 @@ -0,0 +1,103 @@ +#!/usr/bin/python + +import dircache, os.path +from sys import stderr +import readline + +def set_file_completer(): + readline.set_completer_delims('') + readline.set_completer(_file_completer) + +def _file_completer(text, state): + dirname, filename = os.path.split(text) + if not os.path.isdir(dirname): + return None + paths = dircache.listdir(dirname) + tlen = len(filename) + checkpaths = [] + if tlen > 0: + for fn in paths: + if fn[:len(filename)] == filename: + checkpaths.append(fn) + else: + checkpaths = paths + if len(checkpaths) > state: + return os.path.join(dirname, checkpaths[state]) + return None + +def copy_file(origname, copyname): + orig = file(origname, 'r') + new = file(copyname, 'w') + new.write(orig.read()) + new.flush() + return new + +def unique(s): + """Return a list of the elements in s, but without duplicates. + + For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3], + unique("abcabc") some permutation of ["a", "b", "c"], and + unique(([1, 2], [2, 3], [1, 2])) some permutation of + [[2, 3], [1, 2]]. + + For best speed, all sequence elements should be hashable. Then + unique() will usually work in linear time. + + If not possible, the sequence elements should enjoy a total + ordering, and if list(s).sort() doesn't raise TypeError it's + assumed that they do enjoy a total ordering. Then unique() will + usually work in O(N*log2(N)) time. + + If that's not possible either, the sequence elements must support + equality-testing. Then unique() will usually work in quadratic + time. + """ + + n = len(s) + if n == 0: + return [] + + # Try using a dict first, as that's the fastest and will usually + # work. If it doesn't work, it will usually fail quickly, so it + # usually doesn't cost much to *try* it. It requires that all the + # sequence elements be hashable, and support equality comparison. + u = {} + try: + for x in s: + u[x] = 1 + except TypeError: + del u # move on to the next method + else: + return u.keys() + + # We can't hash all the elements. Second fastest is to sort, + # which brings the equal elements together; then duplicates are + # easy to weed out in a single pass. + # NOTE: Python's list.sort() was designed to be efficient in the + # presence of many duplicate elements. This isn't true of all + # sort functions in all languages or libraries, so this approach + # is more effective in Python than it may be elsewhere. + try: + t = list(s) + t.sort() + except TypeError: + del t # move on to the next method + else: + assert n > 0 + last = t[0] + lasti = i = 1 + while i < n: + if t[i] != last: + t[lasti] = last = t[i] + lasti += 1 + i += 1 + return t[:lasti] + + # Brute force is all that's left. + u = [] + for x in s: + if x not in u: + u.append(x) + return u + +