diff --git a/libsaria/__init__.py b/libsaria/__init__.py index 76d73a6e..ee7e8f79 100644 --- a/libsaria/__init__.py +++ b/libsaria/__init__.py @@ -36,8 +36,10 @@ def startup(): global plugin import music import plugin + event.start("PRESTART") music.init() plugin.load_all() + event.start("POSTSTART") def shutdown(): diff --git a/libsaria/collection/__init__.py b/libsaria/collection/__init__.py index 9ead0558..7e0f25be 100644 --- a/libsaria/collection/__init__.py +++ b/libsaria/collection/__init__.py @@ -14,12 +14,18 @@ PLAYLIST = 1 QUEUE = 2 import collection -source = collection.Collection() +library = collection.Collection() def new_source(path, bg=True): - global source + global library path = expand(path) if not exists(path): return 0 - return call("NEWSOURCE", source.scan, arg=path, bg=bg) + return call("NEWSOURCE", library.scan, path) + + +def walk_library(): + global library + for track in library: + yield track diff --git a/libsaria/collection/collection.py b/libsaria/collection/collection.py index 5ddd5894..22c8d907 100644 --- a/libsaria/collection/collection.py +++ b/libsaria/collection/collection.py @@ -10,8 +10,14 @@ ins_tree = None class Collection: def __init__(self): - pass + self.tree = None + #self.load() + def __iter__(self): + if not self.tree: + self.load() + for track in self.tree.walk(): + yield track def scan(self, path): print "Scanning path:", path @@ -23,10 +29,8 @@ class Collection: ins_index = self.index.insert ins_tree = self.tree. insert self.update(path) - self.tree.sort() self.save() - def reset(self): print "Resetting collection ... " from table import Table @@ -36,11 +40,17 @@ class Collection: self.index = Index() self.tree = Tree() - def save(self): from libsaria import data data.save((self.table, self.index, self.tree), "library", "") + def load(self): + from libsaria import data + objects = data.load("library", "") + if objects == None: + self.reset() + return + (self.table, self.index, self.tree) = objects def insert(self, file, filepath): @@ -63,7 +73,6 @@ class Collection: list.append(id) ins_tree(list, tags, audio_prop, filepath) - def update(self, path): global tag global audio diff --git a/libsaria/collection/tree.py b/libsaria/collection/tree.py index 9372db97..5d5f10c4 100644 --- a/libsaria/collection/tree.py +++ b/libsaria/collection/tree.py @@ -1,10 +1,11 @@ # Bryan Schumaker (8 / 12 / 2010) -get = dict.get +get = dict.get class TagNode(dict): - def __init__(self): + def __init__(self, id): dict.__init__(self) + self["id"] = id def insert(self, tag_list, tags, audio, filepath): self["artist"] = tags.artist @@ -15,30 +16,22 @@ class TagNode(dict): self["filepath"] = filepath self["playcount"] = 0 self["length"] = audio.length - #self.artist = tags.artist - #self.album = tags.album - #self.title = tags.title - #self.year = tags.year - #self.filepath = filepath - #self.playcount = 0 - #self.length = audio.length - - def sort(self, level): - pass + def walk(self): + yield self class Tree(dict): def __init__(self): dict.__init__(self) - self.sorted_keys = None - def sort(self, level=0): - self.sorted_keys = self.keys() - self.sorted_keys.sort() - space = ' '*level - for key in self.sorted_keys: - self[key].sort(level+1) + def walk(self): + keys = self.keys() + keys.sort() + get_item = self.__getitem__ + for key in keys: + for track in get_item(key).walk(): + yield track def insert(self, tag_list, tags, audio, filepath): if len(tag_list) == 0: @@ -49,7 +42,7 @@ class Tree(dict): node = get(self, tag, None) if node == None: if len(tag_list) == 1: - node = TagNode() + node = TagNode(tag) else: node = Tree() self[tag] = node diff --git a/libsaria/data.py b/libsaria/data.py index e43fcad0..cf69d888 100644 --- a/libsaria/data.py +++ b/libsaria/data.py @@ -6,8 +6,8 @@ plugin = None PROTO = pickle.HIGHEST_PROTOCOL -def save(item, file, ext="pickle"): - file = "%s.%s" % (path.join(path.saria_dir(),file), ext) +def save(item, file, ext=".pickle"): + file = "%s%s" % (path.join(path.saria_dir(),file), ext) savefile(item, file) @@ -18,8 +18,9 @@ def savefile(item, file): f.close() -def load(file, ext="pickle"): - file = "%s.%s" % (path.join(path.saria_dir(),file),ext) +def load(file, ext=".pickle"): + file = "%s%s" % (path.join(path.saria_dir(),file),ext) + print file return loadfile(file) diff --git a/libsaria/event.py b/libsaria/event.py index 2cdab671..fc5f63fa 100644 --- a/libsaria/event.py +++ b/libsaria/event.py @@ -1,26 +1,16 @@ # Bryan Schumaker (8/7/2010) -events = dict() -from threading import Thread +events = dict() +threads = None + +start_thread = None -class EventThread(Thread): - def __init__(self, func, action, arg): - Thread.__init__(self) - self.func = func - self.action = action - self.arg = arg - - def run(self): - res = call("BGTHREAD", self.func, self.arg) - start("POST%s" % self.action, res) - - -def invite(key, func): +def invite(key, func, bg=False): global events if key not in events: events[key] = set() - events[key].add(func) + events[key].add((func,bg)) def uninvite(key, func): @@ -30,50 +20,43 @@ def uninvite(key, func): events[key].discard(func) -def start(key, arg=None): +def start_thread_once(func, *args): + global threads + global start_thread + import threads + start_thread = threads.background + return threads.background(func, *args) +start_thread = start_thread_once + + +def start(key, *args): global events if key not in events: return - for func in events[key]: + for func, bg in events[key]: try: - if arg == None: - func() + if bg == False: + func(*args) else: - func(arg) + start_thread(func, *args) except Exception,e: print print "===== EVENT ERROR =====" print "Event:", key print "Function:", func - print "Args:", arg + print "Args:", args print e print "The function has been uninvited from this event" print "=====" -def start_thread(func, action, arg): - try: - th = EventThread(func, action, arg) - th.start() - return True - except Exception,e: - print e - return False - - -def call(action, func=None, arg=None, bg=False): - start("PRE%s" % action, arg) +def call(action, func=None, *args): + start("PRE%s" % action, *args) if func == None: return None - if bg == True: - res = start_thread(func, action, arg) - else: - if arg == None: - res = func() - else: - res = func(arg) - start("POST%s" % action, res) + res = func(*args) + start("POST%s" % action, res) return res diff --git a/libsaria/threads.py b/libsaria/threads.py new file mode 100644 index 00000000..e1ff1f1e --- /dev/null +++ b/libsaria/threads.py @@ -0,0 +1,41 @@ +# Bryan Schumaker (8/19/2010) + +import threading +Thread = threading.Thread +Mutex = threading.Lock + + +mutex_mutex = Mutex() +mutexes = dict() + + +class BG_Thread(Thread): + def __init__(self, func, *args): + Thread.__init__(self) + self.func = func + self.args = args + def run(self): + self.func(*self.args) + + +def background(func, *args): + try: + th = BG_Thread(func, *args) + th.start() + return th + except Exception,e: + print e + return None + + +def get_mutex(lock_name): + global mutexes + global Mutex + global mutex_mutex + mutex_mutex.acquire() + lock = mutexes.get(lock_name, None) + if lock == None: + lock = Mutex() + mutexes[lock_name] = lock + mutex_mutex.release() + return lock diff --git a/ocarina.py b/ocarina.py index 056667b4..137d94ab 100755 --- a/ocarina.py +++ b/ocarina.py @@ -1,20 +1,26 @@ #!/usr/bin/python # Bryan Schumaker (8/13/2010) +import datetime +now = datetime.datetime.now +before = now() + import libsaria import ocarina +from ocarina import collection + libsaria.init() libsaria.init_pref("window_size", (800,600)) prefs = libsaria.prefs -import gtk -label = gtk.Label("Ocarina 4.1") -label.show() - win = ocarina.get_window(prefs["window_size"]) ocarina.set_window_title("Ocarina 4.1") -ocarina.add_tab("Test", label) + +ocarina.add_tab("Library", collection.Library()) + +after = now() +print "Startup took:", after-before ocarina.startup() diff --git a/ocarina/__init__.py b/ocarina/__init__.py index 5b6d9959..30371f98 100644 --- a/ocarina/__init__.py +++ b/ocarina/__init__.py @@ -1,8 +1,11 @@ # Bryan Schumaker (8/13/2010) import gtk +import gobject import libsaria +gobject.threads_init() + # Lazy loaded modules window = None tabs = None diff --git a/ocarina/collection.py b/ocarina/collection.py new file mode 100644 index 00000000..f0e0e133 --- /dev/null +++ b/ocarina/collection.py @@ -0,0 +1,49 @@ +# Bryan Schumaker (8/16/2010) + +import ocarina +import list + + +libsaria = ocarina.libsaria +from libsaria import collection + +event = ocarina.libsaria.event +gtk = ocarina.gtk + + +class Collection(gtk.ScrolledWindow): + def __init__(self): + gtk.ScrolledWindow.__init__(self) + self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.list = list.List() + self.add(self.list) + self.show() + + def populate(self, func): + self.list.freeze() + append = self.list.append + for track in func(): + #print track + get = track.__getitem__ + append([get("id"), + get("title"), + get("length"), + get("artist"), + get("album"), + get("year"), + get("playcount")]) + self.list.thaw() + + + +class Library(Collection): + def __init__(self): + Collection.__init__(self) + libsaria.event.invite("POSTSTART", self.populate, bg=True) + + def populate(self): + import datetime + before = datetime.datetime.now() + Collection.populate(self, collection.walk_library) + after = datetime.datetime.now() + print "Populating took: %s" % (after-before) diff --git a/ocarina/list.py b/ocarina/list.py new file mode 100644 index 00000000..f9082794 --- /dev/null +++ b/ocarina/list.py @@ -0,0 +1,49 @@ +# Bryan Schumaker (8/15/2010) + +import ocarina +gtk = ocarina.gtk +gobject = ocarina.gobject + +#UNI = gobject.TYPE_UNICHAR + +class List(gtk.TreeView): + def __init__(self): + gtk.TreeView.__init__(self) + self.list = gtk.ListStore(int, str, str, str, str, int, int) + self.append = self.list.append + + cell = gtk.CellRendererText() + cell.set_fixed_height_from_font(1) + + cols = ["Id", "Title", "Length", "Artist", "Album", "Year", "Played"] + colw = [ 2, 300, 60, 125, 125, 50, 70] + for index, label in enumerate(cols): + col = gtk.TreeViewColumn(label, cell) + col.add_attribute(cell, 'text', index) + col.set_resizable(True) + col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + col.set_min_width(2) + col.set_max_width(700) + col.set_fixed_width(colw[index]) + if label!="Id" and label!="Played": + self.append_column(col) + + self.set_rules_hint(True) + self.set_tooltip_column(len(cols)-1) + self.filter_model = self.list.filter_new() + self.filter_model.set_visible_func(self.set_visible) + + self.set_model(self.filter_model) + self.show_all() + + def freeze(self): + self.set_model(None) + self.freeze_child_notify() + + def thaw(self): + self.set_model(self.filter_model) + self.thaw_child_notify() + + def set_visible(self, a, b): + return True + diff --git a/plugins/web_radio.py b/plugins/web_radio.py index 0b40503b..9c8d42df 100644 --- a/plugins/web_radio.py +++ b/plugins/web_radio.py @@ -1,6 +1,7 @@ # Bryan Schumaker (8/14/2010) import ocarina + music = ocarina.libsaria.music gtk = ocarina.gtk webkit = None diff --git a/tests/threads.py b/tests/threads.py new file mode 100644 index 00000000..5191a939 --- /dev/null +++ b/tests/threads.py @@ -0,0 +1,26 @@ +# Bryan Schumaker (8/19/2010) + +from libsaria import threads + +def test(a, b, c): + lock = threads.get_mutex("print") + lock.acquire() + print "a:", a + print "b:", b + print "c:", c + lock.release() + +def test2(): + lock = threads.get_mutex("print") + lock.acquire() + print "No args!" + lock.release() + + +a = threads.background(test, 1, 2, 3) +b = threads.background(test2) + +if a and a.is_alive(): + a.join() +if b.is_alive(): + b.join() diff --git a/tests/treewalk.py b/tests/treewalk.py new file mode 100644 index 00000000..99ec5359 --- /dev/null +++ b/tests/treewalk.py @@ -0,0 +1,18 @@ +# Bryan Schumaker (8/17/2010) + +from libsaria import collection + +count = 0 +list = [] +append = list.append + +for track in collection.walk_library(): + get = track.__getitem__ + append([get("id"), get("title"), get("length"), + get("artist"), + get("album"), + get("year"), + get("playcount")]) + count += 1 + +print "Counted: %s files" % count