# HG changeset patch # User fabien # Date 1076387348 18000 # Node ID ad808d18c6930e321b3d34677c4a61317e10748e # Parent 85c8f5280d48a0f97f55d8148fc1b4121ad3c945 [svn] Many cleanup, both architecture (division of interface), encoding behavior and also many bugs corrected. diff -r 85c8f5280d48 -r ad808d18c693 bestofimms --- a/bestofimms Sun Feb 08 17:27:21 2004 -0500 +++ b/bestofimms Mon Feb 09 23:29:08 2004 -0500 @@ -1,36 +1,17 @@ #!/usr/bin/python -import imms +import os +from sys import stderr, argv from htmltmpl import TemplateManager, TemplateProcessor -import os -import ID3 -from sys import stderr -from ogg.vorbis import VorbisFile - -_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) +from utils import get_song_info +from imms import IMMSDb, rating_to_color def sort_rating(x, y): return x['rating']-y['rating'] -def grab_tunes(): - db = imms.IMMSDb() - rates = db.get_ratings(125) +def grab_tunes(minrate = 75, maxrate = 150): + db = IMMSDb() + rates = db.get_ratings(minrate, maxrate) uids = map(lambda x: x[0], rates) files = db.get_paths(uids) d = {} @@ -51,37 +32,27 @@ res = [] for tune in tunes: song = tune['path'] - try: - os.stat(song) - except: - continue + if not os.path.isfile(song): + continue + artist, title = get_song_info(song) + if artist and title: + tune['path'] = artist + ' - ' + title 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(): +def output_web(template): tproc = TemplateProcessor() - tmpl = TemplateManager().prepare(_template) - tproc.set('Bestof', check_tunes(grab_tunes())) - print tproc.process(tmpl) + tmpl = TemplateManager().prepare(template) + tproc.set('Bestof', check_tunes(grab_tunes(125))) + return tproc.process(tmpl) + +_template = 'bestofimms.tmpl' +if globals().has_key('__file__'): + _template = __file__ + '.tmpl' if __name__ == '__main__': - output_web() + if len(argv) > 1: + _template = argv[1] + print output_web(_template) diff -r 85c8f5280d48 -r ad808d18c693 bestofimms.tmpl --- a/bestofimms.tmpl Sun Feb 08 17:27:21 2004 -0500 +++ b/bestofimms.tmpl Mon Feb 09 23:29:08 2004 -0500 @@ -1,12 +1,21 @@ - + + + - Fabien Niñoles's Top Songs + Fabien Niñoles's Top Songs + - +

Fabien Niñoles's Top Songs

diff -r 85c8f5280d48 -r ad808d18c693 cleanimms --- a/cleanimms Sun Feb 08 17:27:21 2004 -0500 +++ b/cleanimms Mon Feb 09 23:29:08 2004 -0500 @@ -1,39 +1,41 @@ #!/usr/bin/python import os -import sqlite -from ogg.vorbis import VorbisFile -import ID3 import readline -import imms +from imms import IMMSCleaner, IMMSDb 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) +class CLICleaner(IMMSCleaner): + def check_if_uid_exist(self, uid): + other_paths = self.db.get_library_entry(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) + # if one, release it. + return 1 + return 0 + def check_and_edit_path(self, path, uid, sid): + """Must return the new file name, None to remove + it. If the new file name is already in the Db, + it will be skip.""" + # if path exist, skip it. + if os.path.isfile(path): + return path + # if exist another uid with a valid path, remove it. + if self.check_if_uid_exist(uid): + return None + # Elsewhere, first build a list of valid candidate from sid... + other_paths = self.db.get_library_entry(sid = sid) other_paths = unique(map(lambda x: x[0], other_paths)) + return self.edit_filename(path, other_paths) + def edit_filename(self, path, other_paths): + # Add them to the history for editing for opath in other_paths: if os.path.isfile(opath): readline.add_history(opath) + # And also add the current file as a base for editing. readline.add_history(path) + # Sniff! This doesn't seems to work readline.insert_text('Edit') while 1: cmd = raw_input("I can't find '%s'.\n" @@ -42,56 +44,18 @@ cmd = cmd.lstrip()[0].lower() if cmd == 'e': newpath = raw_input() + # Already exist, so delete. if newpath in other_paths: return None + # new file so keep it. if os.path.isfile(newpath): return newpath + # Elsewhere move it. 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() @@ -100,11 +64,15 @@ db_backup = db_file + '.bak' copy_file(db_file, db_backup).close() try: - clean_up = CleanupIMMS(db_file) + CLICleaner(IMMSDb(db_file)).clean_all() 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) + while 1: + ans = raw_input('Do you want to get back the backup file? ') + if len(ans) > 0: + ans = ans.lstrip()[0].lower() + if ans == 'y': + copy_file(db_backup, db_file) + elif ans == 'n': + break print "backup file preserved: %s." % db_backup diff -r 85c8f5280d48 -r ad808d18c693 imms.py --- a/imms.py Sun Feb 08 17:27:21 2004 -0500 +++ b/imms.py Mon Feb 09 23:29:08 2004 -0500 @@ -1,18 +1,32 @@ -import os +import os.path +from sys import stderr import sqlite +from utils import sql_quote, unique + +_log = stderr -def quote_sql(str): - return str.replace("'", "''") - +def rating_to_color(rating): + i = rating - 75 + red = green = blue = 0 + if i <= 25: + red = 255 + green = i * 255 / 25 + elif i <= 50: + red = (50-i) * 255 / 25 + green = 255 + else: + green = 255 + blue = (i-50) * 255 / 25 + return "#%02x%02x%02x" % (red, green, blue) + 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.cx = sqlite.connect(dbname, autocommit = 1, timeout = 5) self.cu = self.cx.cursor() - def get_library_entry(self): + def get_library_entry(self, **kw): qry = "SELECT path, uid, sid FROM Library"; first = 1 for key in kw.keys(): @@ -24,18 +38,19 @@ if key in ['uid', 'sid']: qry += "%s = %d" % (key, kw[key]) else: - qry += "%s = '%s'" % (key, quote_sql(kw[key])) + qry += "%s = '%s'" % (key, sql_quote(kw[key])) qry += ";" self.cu.execute(qry) return self.cu.fetchall() + return res def update_filename(self, oldname, newname): self.cu.execute("""UPDATE Library SET path = '%s' - WHERE path = '%s';""" % (quote_sql(newname), - quote_sql(oldname))) + WHERE path = '%s';""" % (sql_quote(newname), + sql_quote(oldname))) def erase_filename(self, name): self.cu.execute("""DELETE FROM Library - WHERE path = '%s';""" % quote_sql(name)) + WHERE path = '%s';""" % sql_quote(name)) def erase_uid(self, uid): self.cu.execute("""BEGIN TRANSACTION; DELETE FROM Library WHERE uid = %d; @@ -47,10 +62,10 @@ DELETE FROM Library WHERE sid = %d; DELETE FROM Info WHERE sid = %d; DELETE FROM Last WHERE sid = %d; - COMMIT;""") + COMMIT;""" % (sid, sid, sid)) def erase_path(self, path): self.cu.execute("DELETE FROM Library WHERE path = '%s';" \ - % quote_sql(path)) + % sql_quote(path)) def get_paths(self, uids = None, sids = None): qry = "SELECT uid, sid, path FROM Library" first = 1 @@ -79,48 +94,51 @@ ORDER BY Rating.rating;''' % (min, max)) return self.cu.fetchall() def get_acoustics(self, uids = None): - qry = "SELECT uid, bpm. spectrum FROM Acoustic" + 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 + if uids: + for uid in uids: + if first: + qry += ' WHERE' + first = 0 + else: + qry += ' OR' + qry += " uid = %d" % uid qry += ';' self.cu.execute(qry) return self.cu.fetchall() def get_infos(self, sids = None): - qry = "SELECT sid, artist, title FROM Infos" + qry = "SELECT sid, artist, title FROM Info" first = 1 - for sid in sids: - if first: - qry += ' WHERE' - first = 0 - else: - qry += ' OR' - qry += " sid = %d" % id + if sids: + for sid in sids: + if first: + qry += ' WHERE' + first = 0 + else: + qry += ' OR' + qry += " sid = %d" % id qry += ';' self.cu.execute(qry) return self.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 + if sids: + for sid in sids: + if first: + qry += ' WHERE' + first = 0 + else: + qry += ' OR' + qry += " sid = %d" % id qry += ';' self.cu.execute(qry) return self.cu.fetchall() def get_uid_by_path(self, path): - entries = self.get_library_entries(path = path) + entries = self.get_library_entry(path = path) return map(lambda x: x[1], entries) - def get_ratings_and_info(self, uids = None): + def get_ratings_and_paths(self, uids = None): 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''' @@ -136,17 +154,99 @@ results = {} tune = self.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 = self.cu.fetchone() + uid = int(tune[0]) + if results.has_key(uid): + results[uid]['path'].append(tune[2]) + else: + results[uid] = { + 'rating' : int(tune[1]), + 'path' : [ tune[2] ], + 'last' : int(tune[3])} + tune = self.cu.fetchone() return results + def get_ratings_and_infos(self): + self.cu.execute('''SELECT r.rating, i.artist, i.title + FROM Library l, Rating r, Info i + WHERE l.uid = r.uid AND l.sid = i.sid;''') + return self.cu.fetchall() + +class IMMSCleaner: + def __init__(self, db): + self.db = db + def check_uid(self, uid): + lib = self.db.get_library_entry(uid = uid) + if len(lib) == 0: + print >> _log, "Erased uid = ", uid + self.db.erase_uid(uid) + def check_sid(self, sid): + lib = self.db.get_library_entry(sid = sid) + if len(lib) == 0: + print >> _log, "Erased sid = ", sid + self.db.erase_sid(sid) + def is_path_in_db(self, path): + return len(self.db.get_library_entry(path = path)) + # Note: I doesn't much how I handle the two following functions... + # May be I must just have the second one and handle everything + # else in the derived class. + def check_and_edit_path(self, path, uid, sid): + """Must return the new path, None to remove + it. If the new file name is already in the Db, + it will be skip. The skip is more efficient if path + is return. + This is the default handler which always skip the file by + returning path directly. + """ + # The right thing (but not safe) would be to erase the + # file if it already exist in the db... But I find it + # too much unsafe... Erasing a file shouldn't be easy to + # do. + return path + def clean_library(self): + lib = self.db.get_library_entry() + print >> _log, "Processing %d entries" % len(lib) + deleted_uids = [] + deleted_sids = [] + for entry in lib: + path, uid, sid = entry + uid = int(uid) + sid = int(sid) + newfile = self.check_and_edit_path(path, uid, sid) + if not newfile: + print >> _log, "Erasing ", path + self.db.erase_filename(path) + deleted_uids.append(uid) + deleted_sids.append(sid) + elif (path == newfile): + pass + elif self.is_path_in_db(newfile): + print >> _log, "Skipping ", path + pass + else: + print >> _log, "Renaming ", path, " into ", newfile + self.db.update_filename(path, newfile) + map(self.check_uid, unique(deleted_uids)) + map(self.check_sid, unique(deleted_sids)) + def clean_rating(self): + print >> _log, "Clean Rating" + rates = self.db.get_ratings() + rates = unique(map(lambda x: x[0], rates)) + map(self.check_uid, rates) + def clean_acoustic(self): + print >> _log, "Clean Acoustic" + uids = self.db.get_acoustics() + uids = map(lambda x: x[0], uids ) + map(self.check_uid, uids) + def clean_info(self): + print >> _log, "Clean Info" + sids = map(lambda x: x[0], self.db.get_infos()) + map(self.check_sid, sids) + def clean_last(self): + print >> _log, "Clean Last" + 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() diff -r 85c8f5280d48 -r ad808d18c693 immsview --- a/immsview Sun Feb 08 17:27:21 2004 -0500 +++ b/immsview Mon Feb 09 23:29:08 2004 -0500 @@ -20,9 +20,13 @@ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. -_version_ = "$Id: immsview 1713 2004-02-08 21:55:24Z fabien $" +_version_ = "$Id: immsview 1715 2004-02-10 04:29:08Z fabien $" # $Log$ +# Revision 1.30 2004/02/10 04:29:08 fabien +# Many cleanup, both architecture (division of interface), encoding +# behavior and also many bugs corrected. +# # Revision 1.29 2004/02/08 21:55:24 fabien # New bestofimms, cleanimms, imms.py and utils.py. # @@ -149,34 +153,13 @@ import xmms.control import time import Gnuplot -from imms import IMMSDb +from imms import IMMSDb, rating_to_color +from utils import strdelay gtk.glade.bindtextdomain('immsview', '/usr/share/immsview/LANG') gtk.glade.textdomain('immsview') _ = gettext.gettext - -def strtime(seconds): - secs = abs(round(seconds)) - minutes = secs / 60; - hours = minutes / 60; - days = hours / 24; - secs = secs % 60; - minutes %= 60; - hours %= 24; - - if seconds < 0: - s = "-" - else: - s = "" - if days >= 1: - s += "%dd %dh" % (days, hours) - elif hours >= 1: - s += "%dh%02d" % (hours, minutes) - elif minutes >= 1: - s += "%d'%02d\"" % (minutes, secs) - else: - s += "%d\"" % (secs) - return s +stderr = sys.stderr class XMMSControl: def __getattr__(self, name): @@ -196,24 +179,11 @@ else: self.set_playlist_pos(idx) +_gdk_colors = [] +for i in range(75,150+1): + _gdk_colors.append(rating_to_color(i)) -_gdk_colors = [] -for i in range(76): - 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 - _gdk_colors.append("#%02X%02X%02X" % (red, green, blue)) - -def rating_to_color(rate): +def gdk_rating_to_color(rate): rate = min(max(rate,75),150) return _gdk_colors[rate-75] @@ -227,7 +197,7 @@ COL_SELECT = 4 COL_UID = 5 COL_RATING_COLOR = 6 - COL_PATHS_NUMBER = 7 + COL_PATHS = 7 def __init__(self, db): gtk.ListStore.__init__(self, gobject.TYPE_INT, @@ -237,7 +207,7 @@ gobject.TYPE_BOOLEAN, gobject.TYPE_INT, gobject.TYPE_STRING, - gobject.TYPE_INT, + gobject.TYPE_PYOBJECT, ) self.db = db self.set_default_sort_func(self.default_sort) @@ -265,11 +235,11 @@ self.set(giter, IMMSStore.COL_UID, uid, IMMSStore.COL_RATING, tune['rating'], - IMMSStore.COL_PATH, fn, + IMMSStore.COL_PATH, fn.decode('utf-8','replace'), IMMSStore.COL_LAST, tune['last'], - IMMSStore.COL_LAST_STR, strtime(self.curtime-tune['last']), - IMMSStore.COL_RATING_COLOR, rating_to_color(tune['rating']), - IMMSStore.COL_PATHS_NUMBER, len(tune['path']), + IMMSStore.COL_LAST_STR, strdelay(self.curtime-tune['last']), + IMMSStore.COL_RATING_COLOR, gdk_rating_to_color(tune['rating']), + IMMSStore.COL_PATHS, tune['path'], IMMSStore.COL_SELECT, gtk.FALSE) return giter def update(self): @@ -278,8 +248,8 @@ col, order = self.get_sort_column_id() if col: self.set_sort_column_id(-1, gtk.SORT_ASCENDING) - tunes = self.db.get_ratings_and_info() - print time.ctime(time.time()) + ": inserting" + tunes = self.db.get_ratings_and_paths() + print >> stderr, time.ctime(time.time()) + ": inserting" self._update_me(tunes) print time.ctime(time.time()) + ": end insert" if col: @@ -300,38 +270,6 @@ next = self.iter_next(giter) self.remove(giter) giter = next - def refresh(self, tunes): - "refresh only get new data and update last time and rating." - freed_giters = [] - # first, try to recycle current giter. - giter = self.get_iter_first() - while giter: - # check if giter exist in DB. - guid = self.get_value(giter, IMMSStore.COL_UID) - if tunes.has_key(guid): - tune = tunes[guid] - self.set(giter, - IMMSStore.COL_RATING, tune['rating'], - IMMSStore.COL_LAST, tune['last'], - IMMSStore.COL_LAST_STR, - strtime(self.curtime-tune['last']), - IMMSStore.COL_RATING_COLOR, - rating_to_color(tune['rating'])) - else: - # Elsewhere, collect them for further use... - # Hopefully, ListStore have persistent giter! - freed_giters.append(giter) - giter = self.iter_next(giter) - # populate the remeaning tunes into the collected giters - for uid, tune in tunes.iteritems(): - if len(freed_giters) > 0: - giter = freed_giters.pop() - else: - giter = None - self.tune_to_giter(uid, tune, None, giter) - # then erase the remeaning giter. - for giter in freed_giters: - self.remove(giter) def find_selected_giter(self): giter = self.get_iter_first() while giter: @@ -353,7 +291,7 @@ uid = uids[0] giter = self.find_giter_from_uid(uid) if not giter: - tunes = self.db.get_ratings_and_info([uid]) + tunes = self.db.get_ratings_and_paths([uid]) if tunes > 0: giter = self.tune_to_giter(uid, tunes[uid], song) else: @@ -370,14 +308,14 @@ return res def update_giter(self, giter, path = None): uid = self.get_value(giter, IMMSStore.COL_UID) - tunes = self.db.get_ratings_and_info([uid,]) + tunes = self.db.get_ratings_and_paths([uid,]) if len(tunes) > 0: return self.tune_to_giter(uid, tunes[uid], path, giter) return giter ## def get_value(self, giter, col): ## # sniff! Can't override built-ins ## if col == IMMSStore.COL_LAST_STR: -## return strtime(time.time() - +## return strdelay(time.time() - ## self.get_value(giter, IMMSStore.COL_LAST)) ## else: ## return gtk.ListStore.get_value(self, giter, col) @@ -421,10 +359,6 @@ text = IMMSStore.COL_LAST_STR) column.set_sort_column_id(IMMSStore.COL_LAST) self.append_column(column) - column = gtk.TreeViewColumn(_("#"), renderer, - weight_set = IMMSStore.COL_SELECT, - text = IMMSStore.COL_PATHS_NUMBER) - self.append_column(column) column = gtk.TreeViewColumn(_("File"), renderer, weight_set = IMMSStore.COL_SELECT, text = IMMSStore.COL_PATH) @@ -446,12 +380,19 @@ self.set_cursor(model.get_path(giter)) def get_filename(self, giter): model = self.get_model() - fn = model.get_value(model.update_giter(giter), IMMSStore.COL_PATH) - try: - os.stat(fn) - except OSError: + model.update_giter(giter) + paths = model.get_value(giter, IMMSStore.COL_PATHS) + enc_path = model.get_value(giter, IMMSStore.COL_PATH) + paths = filter(os.path.isfile, paths) + # No valid file in list + if len(paths) == 0: return None - return fn + # Try to find the currently display file + for fn in paths: + if enc_path == fn.decode('utf8', 'replace'): + return fn + # Else, return the first find + return paths[0] def get_file_selected(self): model, giter = self.get_selection().get_selected() if giter: @@ -494,8 +435,8 @@ song = self.xmms.get_current_file() try: self.iview.set_current_song(song) - except: - pass + except Exception, e: + print >> stderr, e def do_play(self, dummy): fn = self.iview.get_file_selected() if fn: diff -r 85c8f5280d48 -r ad808d18c693 utils.py --- a/utils.py Sun Feb 08 17:27:21 2004 -0500 +++ b/utils.py Mon Feb 09 23:29:08 2004 -0500 @@ -1,9 +1,62 @@ -#!/usr/bin/python - import dircache, os.path from sys import stderr import readline +def sql_quote(str): + return str.replace("'", "''") + +def get_song_info(path): + "Return (artist, title) pair from path." + artist, title = None, None + if os.path.isfile(path): + if path[-4:] == '.mp3': + from ID3 import ID3 + id3 = ID3(path) + try: + artist = id3['ARTIST'] + except: + pass + try: + title = id3['TITLE'] + except: + pass + elif path[-4:] == '.ogg': + from ogg.vorbis import VorbisFile + vf = VorbisFile(path) + vc = vf.comment() + try: + artist = vc['ARTIST'][0] + except: + pass + try: + title = vc['TITLE'][0] + except: + pass + return artist, title + +def strdelay(seconds): + secs = abs(round(seconds)) + minutes = secs / 60; + hours = minutes / 60; + days = hours / 24; + secs = secs % 60; + minutes %= 60; + hours %= 24; + + if seconds < 0: + s = "-" + else: + s = "" + if days >= 1: + s += "%dd %dh" % (days, hours) + elif hours >= 1: + s += "%dh%02d" % (hours, minutes) + elif minutes >= 1: + s += "%d'%02d\"" % (minutes, secs) + else: + s += "%d\"" % (secs) + return s + def set_file_completer(): readline.set_completer_delims('') readline.set_completer(_file_completer)