diff --git a/State.py b/State.py index 539cf5bd..b2010499 100644 --- a/State.py +++ b/State.py @@ -21,13 +21,20 @@ def songs(): class RecentlyplayedList: Limit = 10 - def __init__(self, list=[], previous=None): + def __init__(self, list=[], previous=None, index=0): + self.index = index self.list = deque(list) self.previous = None def append(self, song): + if not song: return self.list.append(song) - if len(self.list) >= self.Limit: - newList = RecentlyplayedList(list=self.list, previous=self.previous) + if len(self.list) >= self.Limit: + newList = PersistentObject(RecentlyplayedList, "recentlyplayed-%i.dat" % self.index, persistentRepr=True) + newList.index = self.index + newList.list = self.list + newList.previous = self.previous + newList.save() + self.index += 1 self.previous = newList self.list = deque() def __repr__(self): @@ -38,10 +45,35 @@ def __repr__(self): def loadRecentlyplayedList(state): return PersistentObject(RecentlyplayedList, "recentlyplayed.dat") -class State: +class PlayerEventCallbacks: + onSongChange = None + onSongFinished = None + onPlayingStateChange = None + +def loadPlayer(state): + import ffmpeg + player = ffmpeg.createPlayer() + for e in [m for m in dir(PlayerEventCallbacks) if not m.startswith("_")]: + cb = EventCallback(targetQueue=state.updates, name=e) + setattr(PlayerEventCallbacks, e, cb) + setattr(player, e, cb) + player.queue = state.queue + return player + +def playerMain(): + state.player.playing = True + for ev,args,kwargs in state.updates.read(): + if ev is PlayerEventCallbacks.onSongChange: + state.curSong = kwargs["newSong"] + state.curSong.save() + state.recentlyPlayedList.append(kwargs["oldSong"]) + state.recentlyPlayedList.save() + pass # onPlayingStateChange + +class State(object): queue = initBy(loadQueue) recentlyPlayedList = initBy(loadRecentlyplayedList) - curSong = None + curSong = initBy(lambda self: PersistentObject(Song, "cursong.dat")) playState = oneOf( "playing", @@ -49,7 +81,6 @@ class State: ) updates = initBy(lambda self: OnRequestQueue()) - player = None + player = initBy(loadPlayer) state = State() - \ No newline at end of file diff --git a/main.py b/main.py index 17928f01..950bff72 100755 --- a/main.py +++ b/main.py @@ -6,23 +6,7 @@ from utils import * from pprint import pprint -from State import state - -class PlayerEventCallbacks: - onSongChange = None - onSongFinished = None - onPlayingStateChange = None - -def playerMain(): - import ffmpeg - state.player = ffmpeg.createPlayer() - for e in [m for m in dir(PlayerEventCallbacks) if not m.startswith("_")]: - cb = EventCallback(targetQueue=state.updates, name=e) - setattr(PlayerEventCallbacks, e, cb) - setattr(state.player, e, cb) - state.player.queue = state.queue - state.player.playing = True - for ev in state.updates.read(): pass # wait for exit +from State import state, playerMain, PlayerEventCallbacks import lastfm diff --git a/utils.py b/utils.py index 256fd07d..a9c4ed02 100644 --- a/utils.py +++ b/utils.py @@ -50,24 +50,31 @@ def __call__(self, *args, **kwargs): def __repr__(self): return "" % self.name -class initBy(property): +class initBy(object): def __init__(self, initFunc): - property.__init__(self, fget = self.fget) self.initFunc = initFunc - def fget(self, inst): - if hasattr(self, "value"): return self.value - self.value = self.initFunc(inst) + def load(self, inst): + if not hasattr(self, "value"): + self.value = self.initFunc(inst) + def __get__(self, inst, type=None): + self.load(inst) + if hasattr(self.value, "__get__"): + return self.value.__get__(inst, type) return self.value - -class oneOf(property): + def __set__(self, inst, value): + self.load(inst) + if hasattr(self.value, "__set__"): + return self.value.__set__(inst, value) + self.value = value + +class oneOf(object): def __init__(self, *consts): - property.__init__(self, fget = self.fget, fset = self.fset) assert len(consts) > 0 self.consts = consts self.value = consts[0] - def fget(self, inst): + def __get__(self, inst, type=None): return self - def fset(self, inst, value): + def __set__(self, inst, value): assert value in self.consts self.value = value @@ -108,7 +115,9 @@ def betterRepr(o): # the main difference: this one is deterministic # the orig dict.__repr__ has the order undefined. if isinstance(o, list): - return "[" + ", ".join(map(betterRepr, o)) + "]" + return "[\n" + "".join(map(lambda v: betterRepr(v) + ",\n", o)) + "]" + if isinstance(o, deque): + return "deque([\n" + "".join(map(lambda v: betterRepr(v) + ",\n", o)) + "])" if isinstance(o, tuple): return "(" + ", ".join(map(betterRepr, o)) + ")" if isinstance(o, dict): @@ -116,7 +125,7 @@ def betterRepr(o): # fallback return repr(o) -def ObjectProxy(lazyLoader, custom_attribs={}, baseType=object, delHook=None, getattrHook=getattr, setattrHook=setattr): +def ObjectProxy(lazyLoader, custom_attribs={}, baseType=object): class Value: pass obj = Value() def load(): @@ -124,22 +133,28 @@ def load(): obj.value = lazyLoader() def obj_getattr(self, key): load() - if key in custom_attribs: - value = custom_attribs[key] - if hasattr(value, "__get__"): - value = value.__get__(self, type(self)) - return value return getattr(obj.value, key) def obj_setattr(self, key, value): load() return setattr(obj.value, key, value) - def obj_del(self): - if delHook: delHook(self) - attribs = {"__getattr__": obj_getattr, "__setattr__": obj_setattr, "__del__": obj_del} - LazyObject = type("LazyObject", (baseType,), attribs) + def obj_desc_get(self, inst, type=None): + if inst is None: + load() + return obj.value + return self + def obj_desc_set(self, inst, value): + obj.value = value + attribs = custom_attribs.copy() + attribs.update({ + "__getattr__": obj_getattr, + "__setattr__": obj_setattr, + "__get__": obj_desc_get, + "__set__": obj_desc_set, + }) + LazyObject = type("LazyObject", (object,baseType), attribs) return LazyObject() -def PersistentObject(baseType, filename): +def PersistentObject(baseType, filename, persistentRepr = False): def load(): import appinfo try: @@ -147,15 +162,32 @@ def load(): except IOError: # e.g. file-not-found. that's ok return baseType() - obj = eval(f.read()) + # some common types + g = {baseType.__name__: baseType} # the baseType itself + g.update(globals()) # all what we have here + if baseType.__module__: + # the module of the basetype + import sys + m = sys.modules[baseType.__module__] + g.update([(varname,getattr(m,varname)) for varname in dir(m)]) + obj = eval(f.read(), g) assert isinstance(obj, baseType) return obj def save(obj): + s = betterRepr(obj) import appinfo - f = open(appinfo.userdir + "/" + filename) - f.write(betterRepr(obj)) + f = open(appinfo.userdir + "/" + filename, "w") + f.write(s) f.write("\n") def obj_repr(obj): - return "PersistentObject(%r, %r)" % (baseType, filename) - return ObjectProxy(load, delHook=save, baseType=baseType, - custom_attribs={"save": save, "__repr__": obj_repr}) + if persistentRepr: + return "PersistentObject(%r, %r)" % (baseType, filename) + return betterRepr(obj.__get__(None)) + def obj_del(obj): + save(obj) + return ObjectProxy(load, baseType=baseType, + custom_attribs={ + "save": save, + "__repr__": obj_repr, + "__del__": obj_del, + })