class NowPlaying(Plugin): edit_toggle = False SONG_THRESHOLD = 1 snipr_cache = SniprSongLRU(20) inited = False def setup(self): self.links = [] self.timer = RepeatTimer(5, lambda: wx.CallAfter(self.on_timer)) self.song = (None, 0) self.status = None self.on_before_status_change(profile.status) def link(self): self.links.append(profile.prefs.link('plugins.nowplaying.format', self.format_string_change, callnow=False)) self.links.append(profile.prefs.link('plugins.nowplaying.backup_format', self.format_string_change, callnow=False)) def unlink(self): while self.links: link = self.links.pop(0) link.unlink() def format_string_change(self, value): log.info('Format string changed to %r', value) self.check_song(force=True) def on_before_status_change(self, status): ''' Invoked when the profile's status message changes. ''' log.info('on_status_change') is_music_status = isinstance(status, NowPlayingStatus) timer = getattr(self, 'timer', None) timer_alive = timer is not None and self.timer.isAlive() # If the status is a "music status" and our timer isn't running, start it. if is_music_status and not timer_alive: self.link() log.debug('starting now playing timer') if timer is None: self.timer = timer = RepeatTimer(5, lambda: wx.CallAfter(self.on_timer)) timer.start() # call it now to look responsive! yay # Otherwise if the status is not a "music status" and the timer IS running, stop it. elif not is_music_status and timer_alive: log.debug('stopping now playing timer') self.song = (None, 0) self.unlink() self.timer.stop() releaseAll() if is_music_status: s = status.status if pref(NOWPLAYING_STATUS_PREF, type = str, default = 'available') != s: profile.prefs.__setitem__(NOWPLAYING_STATUS_PREF, s.lower()) if not is_music_status: return status try: if status is not self.status: self.check_song(force=True, status=status) return self.status except Exception: traceback.print_exc() return status def on_timer(self): 'Invoked when the song check timer goes off. Which happens a lot.' status = profile.status if not isinstance(status, NowPlayingStatus): # early exit for when the status message is not a music status if hasattr(self, 'timer'): self.timer.stop() return self.check_song() try: new_message = self.status.message except Exception: traceback.print_exc() return if new_message != profile.status.message: log.info('current message: %r', profile.status.message) log.info('setting new now playing status: message=%r, cursong=%r', new_message, profile.status.message) profile.set_status(self.status) def check_song(self, force=False, status=None): cursong = currentSong() song = cursong.format_string % cursong.format_args new_message = ' '.join([NOTES_SYMBOL, song]) oldsong, songcount = self.song def got_new_song(): # on a new song, store the name, and the "count" of times we've # seen it self.snipr_cache[amazon_format(cursong.format_args)] self.song = (cursong, 0) def set_new_song(status = status): if status is None: status = profile.status self.status = status.copy(message = new_message, **cursong) if oldsong is None: got_new_song() set_new_song() elif cursong != oldsong: got_new_song() if force: set_new_song() else: # only set the profile status after a certain period of time passed # with the same song present songcount += 1 self.song = (cursong, songcount) # only set the song after (N * five) seconds of play time if force or (songcount >= self.SONG_THRESHOLD): set_new_song()