def volchange(self, val): if self.volumecontrol is not None: self.volumecontrol.change_volume_percent(val) report_usage("audiocontrol_powercontroller_volume", 1) else: logging.info( "no volume control, ignoring powercontroller feedback")
def notify(self, metadata): """ Scrobble metadata of last song, store meta data of the current song """ if metadata is not None and metadata.sameSong(self.current_metadata): self.current_metadata = metadata logging.debug( "updated metadata for current song, not scrobbling now") return # Check if the last song was played at least 30 seconds, otherwise # don't scrobble it' now = time.time() listening_time = (now - self.starttime) lastsong_md = None if listening_time > 30: lastsong_md = self.current_metadata else: logging.debug("not yet logging %s, not listened for at least 30s", lastsong_md) self.starttime = now logging.info("new song: %s", metadata) self.current_metadata = metadata if (lastsong_md is not None) and not (lastsong_md.is_unknown()): sender = ScrobbleSender(self.get_network(), lastsong_md) sender.start() report_usage("audiocontrol_lastfm_scrobble", 1) else: logging.info("no track data, not scrobbling %s", lastsong_md)
def keyboard_hook(self, e): import keyboard if e.event_type == keyboard.KEY_DOWN: try: command = self.codetable[e.scan_code] except: logging.error("%s unknown", e.scan_code) return try: command_run = False if command == "volume_up": if self.volumecontrol is not None: self.volumecontrol.change_volume_percent(5) command_run = True else: logging.info("ignoring %s, no volume control", command) elif command == "volume_down": if self.volumecontrol is not None: self.volumecontrol.change_volume_percent(-5) command_run = True else: logging.info("ignoring %s, no volume control", command) elif command == "previous": if self.playercontrol is not None: self.playercontrol.previous() command_run = True else: logging.info("ignoring %s, no playback control", command) elif command == "next": if self.playercontrol is not None: self.playercontrol.next() command_run = True else: logging.info("ignoring %s, no playback control", command) elif command == "playpause": if self.playercontrol is not None: self.playercontrol.playpause() command_run = True else: logging.info("ignoring %s, no playback control", command) if command_run: report_usage("audiocontrol_keyboard_key", 1) logging.debug("processed %s", command) except Exception as e: logging.warning("problem handling %s (%s)", command, e)
def set_ips(self, ip_list): if isinstance(ip_list, str): ips = [] for ip in ip_list.split(","): ips.append(ip.strip()) ip_list = ips self.urls = [] for ip in ip_list: if len(ip) > 0: url = "https://{}:4343/api/v1/dev/widget/update/com.lametric.b647e225d0b81484c19ff25030915e58".format( ip) self.urls.append(url) report_usage("audiocontrol_lametric_discovered", 1)
def love(self, love): try: track = self.get_network().get_track(self.current_metadata.artist, self.current_metadata.title) if love: logging.info("sending love to Last.FM") track.love() report_usage("audiocontrol_lastfm_love", 1) else: logging.info("sending unlove to Last.FM") track.unlove() report_usage("audiocontrol_lastfm_love", 1) except Exception as e: logging.warning("got exception %s while love/unlove", e) return False return True
def notify(self, metadata): if metadata.artist is None or metadata.title is None: logging.debug("ignoring undefined metatdata") return data = { "frames": [{ "text": metadata.artist + "-" + metadata.title, "icon": "a22046", "duration": 10000, }] } headers = { "X-Access-Token": ACCESS_TOKEN, "Accept": "application/json", "Cache-Control": "no-cache" } for url in self.urls: logging.info("sending update to LaMetric at %s", url) report_usage("audiocontrol_lametric_metadata", 1) post_data(url, json.dumps(data), headers=headers, verify=False)
def playpause(self): if self.playercontrol is not None: self.playercontrol.playpause() report_usage("audiocontrol_powercontroller_button", 1) else: logging.info("no player control, ignoring press")
def main_loop(self): """ Main loop: - monitors state of all players - pauses players if a new player starts playback """ finished = False md = Metadata() active_players = [] MAX_FAIL = 3 # Workaround for spotifyd problems # spotify_stopped = 0 # Workaround for squeezelite mute squeezelite_active = 0 previous_state = "" ts = datetime.datetime.now() while not (finished): additional_delay = 0 new_player_started = None metadata_notified = False playing = False new_song = False state = "unknown" last_ts = ts ts = datetime.datetime.now() duration = (ts - last_ts).total_seconds() for p in self.all_players(): if self.playername(p) in self.ignore_players: continue if p not in self.state_table: ps = PlayerState() ps.supported_commands = self.get_supported_commands(p) logging.debug("Player %s supports %s", p, ps.supported_commands) self.state_table[p] = ps thisplayer_state = "unknown" try: thisplayer_state = self.get_player_state(p).lower() self.state_table[p].failed = 0 except: logging.info("Got no state from " + p) state = "unknown" self.state_table[p].failed = \ self.state_table[p].failed + 1 if self.state_table[p].failed >= MAX_FAIL: playername = self.playername(p) logging.warning("%s failed, trying to restart", playername) watchdog.restart_service(playername) self.state_table[p].failed = 0 self.state_table[p].state = thisplayer_state # Check if playback started on a player that wasn't # playing before if thisplayer_state == STATE_PLAYING: playing = True state = "playing" # if self.playername(p) == SPOTIFY_NAME: # spotify_stopped = 0 if self.playername(p) == LMS_NAME: squeezelite_active = 2 report_usage( "audiocontrol_playing_{}".format(self.playername(p)), duration) md = self.get_meta(p) if (p not in active_players): new_player_started = p active_players.insert(0, p) md.playerState = thisplayer_state # MPRIS delivers only very few metadata, these will be # enriched with external sources if (md.sameSong(self.metadata)): md.fill_undefined(self.metadata) else: new_song = True self.state_table[p].metadata = md if not (md.sameSong(self.metadata)): logging.debug("updated metadata: \nold %s\nnew %s", self.metadata, md) # Store this as "current" with self.metadata_lock: self.metadata = md self.metadata_notify(md) logging.debug("notifications about new metadata sent") elif state != previous_state: logging.debug("changed state to playing") self.metadata_notify(md) # Some players deliver artwork after initial metadata if md.artUrl != self.metadata.artUrl: logging.debug("artwork changes from %s to %s", self.metadata.artUrl, md.artUrl) self.metadata_notify(md) # Add metadata if this is a new song if new_song: enrich_metadata_bg(md, callback=self) logging.debug("metadata updater thread started") # Even if we din't send metadata, this is still # flagged metadata_notified = True else: # always keep one player in the active_players # list if len(active_players) > 1: if p in active_players: active_players.remove(p) # update metadata for stopped players from time to time i = randint(0, 600) if (i == 0): md = self.get_meta(p) md.playerState = thisplayer_state self.state_table[p].metadata = md self.playing = playing # Find active (or last paused) player if len(active_players) > 0: self.active_player = active_players[0] else: self.active_player = None # # Workaround for wrong state messages by Spotify # # Assume Spotify is still playing for 10 seconds if it's the # # active (or last stopped) player # if self.playername(self.active_player) == SPOTIFY_NAME: # # Less aggressive metadata polling on Spotify as each polling will # # result in an API request # additional_delay = 4 # if not(playing): # spotify_stopped += 1 + additional_delay # if spotify_stopped < 26: # if (spotify_stopped % 5) == 0: # logging.debug("spotify workaround %s", spotify_stopped) # playing = True # # Workaround for LMS muting the output after stopping the # player if self.volume_control is not None: if self.playername(self.active_player) != LMS_NAME: if squeezelite_active > 0: squeezelite_active = squeezelite_active - 1 logging.debug( "squeezelite was active before, unmuting") self.volume_control.set_mute(False) if not (playing) and squeezelite_active > 0: squeezelite_active = squeezelite_active - 1 logging.debug("squeezelite was active before, unmuting") self.volume_control.set_mute(False) # There might be no active player, but one that is paused # or stopped if not (playing) and len(active_players) > 0: p = active_players[0] md = self.get_meta(p) md.playerState = self.state_table[p].state state = md.playerState if state != previous_state: logging.debug("state transition %s -> %s", previous_state, state) if not metadata_notified: self.metadata_notify(md) for sd in self.state_displays: sd.update_playback_state(state) previous_state = state if new_player_started is not None: if self.auto_pause: logging.info( "new player %s started, pausing other active players", self.playername(active_players[0])) self.pause_inactive(new_player_started) else: logging.debug("auto-pause disabled") self.last_update = datetime.datetime.now() time.sleep(self.loop_delay + additional_delay)
def decrease(self,val): if self.volumecontrol is not None: self.volumecontrol.change_volume_percent(-self.step) report_usage("audiocontrol_rotary_volume", 1) else: logging.info("no volume control, ignoring rotary control")