# 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
+
+