class Status(object): HEIGHT = 2 def __init__(self,curseslock,conf,audiobook,geom,interval): """Create the audiobook view. Arguments: conf The parsed program configuration. audiobook The audiobook object to create a view for. geom Geometry of the window. interval How often to show position while playing. """ self._lock = RLock() self._curseslock = curseslock self._audiobook = audiobook self._geom = geom with self._lock: self._audiobook.connect("position",self._on_position) self._audiobook.connect("notify::playing",self._on_playing) self._gst = self._audiobook.gst() self._window = geom.newwin() self._timer = Timer(interval*1000, self._on_timer, repeat=True) self.update() def getGeom(self): with self._lock: return self._geom def setGeom(self,geom): with self._lock: with self._curseslock: self._geom = geom if self._geom.is_sane(): self._window.resize(geom.h,geom.w) self._window.mvwin(geom.y,geom.x) self.update() def _on_playing(self,ab,prop): """Playing state updated. Arguments: ab The audiobook that this is a view for. prop The property that was updated. """ with self._lock: if ab.playing and (not self._timer.started()): self._timer.start() if (not ab.playing) and self._timer.started(): self._timer.stop() def _on_position(self,ab): """A hint has been received that it is a good idea to update the position information. Arguments: ab The audiobook that this is a view for. """ with self._lock: self.update() def _on_timer(self): with self._lock: self.update() def update(self): with self._lock: if self._geom.is_sane(): (filename,position,duration) = self._audiobook.position() if filename == None: filename = "" if self._audiobook.playing: state = "Playing" elif self._audiobook.eob: state = "End" else: state = "Paused" with self._curseslock: prefix = "File: " maxchars = self._geom.w - len(prefix) - 1 first = prefix + filename[-maxchars:] second = "{position} / {duration} [{state}]".format( position=ns_to_str(position), duration=ns_to_str(duration), state=state) self._window.erase() if self._geom.h>=1: self._window.addnstr(0,0,first,self._geom.w-1) if self._geom.h>=2: self._window.addnstr(1,0,second,self._geom.w-1) self._window.refresh()
class Log(gobject.GObject): @withdoc(gobject.property) def playlog(self): """The current playlog. """ with self._lock: return self._playlog def __init__(self,bus,player,directory,conf): """Create a new log handler. Arguments: bus Which gobject to send error events to. player Which object to ask the current position of. directory Directory of the audiobook. conf A configuration object like that from the result of the parser in pstorytime.coreparser. """ gobject.GObject.__gobject_init__(self) self._lock = threading.RLock() self._bus = bus self._player = player self._playlog_file = conf.playlog_file self._autolog_file = conf.playlog_file+".auto" self._playlog = self._load(self._playlog_file) self._pending = "" self._autologtimer = Timer(conf.autolog_interval*1000, self._autolognow, repeat=True) # Merge in old auto save (should only be there if the last session crashed # while playing.) if isfile(self._autolog_file): auto = self._load(self._autolog_file) if len(auto)==1: self._logentry(auto[0]) os.remove(self._autolog_file) def start(self): """Start autologging (or reset the timer.)""" with self._lock: self._autologtimer.start() self._autolognow() def stop(self): """Stop autologging and remove autolog file.""" with self._lock: self._autologtimer.stop() if isfile(self._autolog_file): os.remove(self._autolog_file) def lognow(self,event): """Log an event with the given event name at the current position and time. Arguments: event The event type to log. """ with self._lock: walltime = time.time() (filename,position,duration) = self._player.position() self._logentry(LogEntry(walltime,event,filename,position,duration)) def _load(self,logfile): """Load log from given file. Arguments: logfile File name to load log from. Return: The loaded log. """ with self._lock: try: with open(logfile,'rb') as f: lines = f.readlines() # Parse lines and remove invalid ones. return filter(lambda e: e!=None, map(LogEntry.parse, lines)) except IOError: return [] def _autolognow(self): """Save current position and such to autolog file now. """ with self._lock: # Retry writing pending entries to the play log. self._writelog() # Make sure the autolog timer is running. The autologging could have been stopped # while we were waiting at the lock. if self._autologtimer.started(): # Update autolog walltime = time.time() (filename,position,duration) = self._player.position() event = LogEntry(walltime, 'auto', filename, position, duration) line = str(event)+"\n" try: _write_file(self._autolog_file,'wb',line) except IOError: self._bus.emit("error","Failed to write to auto log: {0}".format(self._autolog_file)) def _logentry(self,entry): """Log the given entry to the playlog. Arguments: entry The entry to add. """ with self._lock: self._playlog.append(entry) self.notify("playlog") self._pending += str(entry)+"\n" self._writelog() def _writelog(self): """Write all pending log entries to file. """ with self._lock: if self._pending != "": try: _write_file(self._playlog_file,'ab',self._pending) self._pending = "" except IOError as e: self._bus.emit("error","Failed to write to play log, data will be included in next write: {0}".format(self._playlog_file))