def debounced_now_playing_notify(self, track: Track): self._now_playing_notify_debouncer = None logger.debug( "Advanced-Scrobbler submitting now playing notification: %s", track.uri) correction: Optional[Correction] try: db = db_service.retrieve_service().get(timeout=10) correction = db.find_correction(track.uri).get(timeout=10) except ActorRetrievalFailure as exc: logger.exception( f"Database connection found to be unavailable: {exc}") correction = None db_service.request_service_restart(self._global_config) except Exception as exc: logger.exception( f"Error while finding scrobbler correction for track with URI '{track.uri}': {exc}" ) correction = None play = prepare_play(track, -1, correction) try: network = network_service.retrieve_service().get(timeout=10) network.send_now_playing_notification(play) except NetworkException as exc: logger.exception( f"Error while sending now playing notification: {exc}") except ActorRetrievalFailure as exc: logger.exception(f"Network service found to be unavailable: {exc}") network_service.request_service_restart(self.config)
def get(self): self.set_extra_headers() load_args = {} try: page_num = int(self.get_query_argument("page", "")) load_args["page_num"] = max(page_num, 1) except ValueError: pass try: page_size = int(self.get_query_argument("page_size", "")) load_args["page_size"] = max(min(page_size, 100), 1) except ValueError: pass try: db = db_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving database service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return try: corrections = db.load_corrections(**load_args).get() except ActorRetrievalFailure as exc: logger.exception( f"Error while retrieving corrections from database: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return overall_corrections_count = -1 try: overall_corrections_count = db.get_corrections_count().get() except Exception as exc: logger.exception( f"Error while retrieving play counts from database: {exc}") response = { "success": True, "corrections": correction_schema.dump(corrections, many=True), "counts": { "overall": overall_corrections_count, }, } self.write(response)
def get(self): self.set_extra_headers() track_future = self.core.playback.get_current_track() playback_state_future = self.core.playback.get_state() playback_time_pos_future = self.core.playback.get_time_position() track: Track = track_future.get() if track: try: db = db_service.retrieve_service().get(timeout=10) correction = db.find_correction(track.uri).get(timeout=10) except Exception as exc: logger.exception( f"Error while finding scrobbler correction for track with URI '{track.uri}': {exc}" ) correction = None play = prepare_play(track, -1, correction) playing = { "trackUri": play.track_uri, "title": play.title, "artist": play.artist, "album": play.album, "duration": play.duration, } else: playing = { "trackUri": "", "title": "", "artist": "", "album": "", "duration": 0, } playback_state: str = playback_state_future.get() playback_time_pos_msec: Optional[int] = playback_time_pos_future.get() if playback_time_pos_msec: playback_time_pos = int(playback_time_pos_msec / 1000) else: playback_time_pos = 0 response = { "success": True, "playback": { "state": playback_state, "position": playback_time_pos, }, "playing": playing, } self.write(response)
def _post(self, data): if "correction" not in data: self.set_status(400) self.write({ "success": False, "message": "Missing correction data." }) return try: correction_edit = correction_edit_schema.load(data["correction"]) except Exception: self.set_status(400) self.write({ "success": False, "message": "Invalid correction data." }) return try: db = db_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving database service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return try: db.edit_correction(correction_edit).get() except DbClientError as exc: self.set_status(400) self.write({"success": False, "message": str(exc)}) return except ActorRetrievalFailure as exc: logger.exception(f"Error while editing correction: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return self.write({"success": True})
def _post(self, data): if "playId" not in data: self.set_status(400) self.write({"success": False, "message": "Missing play ID."}) return try: play_id = int(data["playId"]) except Exception: self.set_status(400) self.write({"success": False, "message": "Invalid play ID."}) return try: db = db_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving database service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return try: db.approve_auto_correction(play_id).get() except DbClientError as exc: self.set_status(400) self.write({"success": False, "message": str(exc)}) return except ActorRetrievalFailure as exc: logger.exception(f"Error while approving auto-correction: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return self.write({"success": True})
def _post(self, data): if "trackUri" not in data: self.set_status(400) self.write({"success": False, "message": "Missing track URI."}) return try: track_uri = str(data["trackUri"]) except Exception: self.set_status(400) self.write({"success": False, "message": "Invalid track URI."}) return try: db = db_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving database service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return try: success = db.delete_correction(track_uri).get() except DbClientError as exc: self.set_status(400) self.write({"success": False, "message": str(exc)}) return except ActorRetrievalFailure as exc: logger.exception(f"Error while deleting correction: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return self.write({"success": success})
def _post(self, data): if "playIds" not in data: self.set_status(400) self.write({"success": False, "message": "Missing play IDs."}) return try: if not isinstance(data["playIds"], list): raise ValueError() play_ids = tuple(map(int, data["playIds"])) except Exception: self.set_status(400) self.write({"success": False, "message": "Invalid play IDs."}) return try: db = db_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving database service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return try: db.delete_plays(play_ids).get() except ActorRetrievalFailure as exc: logger.exception(f"Error while deleting plays: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return self.write({"success": True})
def track_playback_ended(self, tl_track: TlTrack, time_position): track = tl_track.track if not self.is_uri_allowed(track.uri): return time_position_sec = time_position / 1000 logger.debug( "Advanced-Scrobbler track playback ended after %s: %s", int(time_position_sec), track.uri, ) db_restart_future = None try: db = db_service.retrieve_service().get(timeout=10) correction = db.find_correction(track.uri).get(timeout=10) except ActorRetrievalFailure as exc: logger.exception( f"Database connection found to be unavailable: {exc}") correction = None db_restart_future = db_service.request_service_restart( self._global_config) except Exception as exc: logger.exception( f"Error while finding scrobbler correction for track with URI '{track.uri}': {exc}" ) correction = None play = prepare_play(track, int(time.time() - time_position_sec), correction) if play.duration < 30: logger.debug( "Advanced-Scrobbler track too short to scrobble (%d secs): %s", time_position_sec, track.uri, ) return threshold = self.config["scrobble_time_threshold"] / 100 threshold_duration = play.duration * threshold if time_position_sec < threshold_duration and time_position_sec < 240: logger.debug( "Advanced-Scrobbler track not played long enough to scrobble (%d/%d secs): %s", time_position_sec, play.duration, track.uri, ) return try: if db_restart_future: db_restart_future.get(timeout=10) db = db_service.retrieve_service().get(timeout=10) logger.debug("Advanced-Scrobbler recording finished playback: %s", track.uri) db.record_play(play) except ActorRetrievalFailure as exc: logger.exception( f"Database connection found to be unavailable: {exc}") except Exception as exc: logger.exception( f"Error while recording play for track with URI '{track.uri}: {exc}" ) raise
def _post(self, data): if "checkpoint" in data: try: checkpoint = int(data["checkpoint"]) except Exception: self.set_status(400) self.write({ "success": False, "message": "Invalid checkpoint." }) return else: checkpoint = None try: db = db_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving database service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return try: network = network_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving network service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Last.fm connection issue." }) return found_plays: List[int] = [] scrobbled_plays: List[int] = [] marked_plays: List[int] = [] err_msg: Optional[str] = None for _ in range(0, 5): try: unsubmitted_plays = db.load_unsubmitted_plays_batch( checkpoint=checkpoint).get() except ActorRetrievalFailure as exc: logger.exception( f"Error while retrieving unsubmitted plays: {exc}") err_msg = "Error while retrieving unsubmitted plays." break if not unsubmitted_plays: break unsubmitted_play_ids = tuple( map(lambda play: play.play_id, unsubmitted_plays)) found_plays.extend(unsubmitted_play_ids) try: network.submit_scrobbles(unsubmitted_plays).get() except NetworkException as exc: logger.exception( f"Network error while scrobbling plays: {exc}") err_msg = "Network error while scrobbling plays." break except ActorRetrievalFailure as exc: logger.exception(f"Error while scrobbling plays: {exc}") err_msg = "Error while scrobbling plays." break scrobbled_plays.extend(unsubmitted_play_ids) try: db.mark_plays_submitted(unsubmitted_play_ids).get() except DbClientError as exc: logger.exception(f"Error after successful scrobble: {exc}") err_msg = "Error after successful scrobble." break except ActorRetrievalFailure as exc: logger.exception( f"Error while marking plays as submitted: {exc}") err_msg = "Error while marking plays as submitted." break except Exception as exc: logger.exception( f"Error while marking plays as submitted: {exc}") err_msg = "Error while marking plays as submitted." break marked_plays.extend(unsubmitted_play_ids) # Vague rate-limiting of requests to the Network API sleep(1) self.write({ "success": True, "foundPlays": found_plays, "scrobbledPlays": scrobbled_plays, "markedPlays": marked_plays, "message": err_msg, })
def _post(self, data): if "playIds" not in data: self.set_status(400) self.write({"success": False, "message": "Missing play IDs."}) return try: if not isinstance(data["playIds"], list): raise ValueError() play_ids = tuple(map(int, data["playIds"])) except Exception: self.set_status(400) self.write({"success": False, "message": "Invalid play IDs."}) return try: db = db_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving database service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return try: network = network_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving network service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Last.fm connection issue." }) return found_plays: List[int] = [] scrobbled_plays: List[int] = [] marked_plays: List[int] = [] err_msg: Optional[str] = None for batch_start_id in range(0, 250, 50): play_ids_batch = play_ids[batch_start_id:batch_start_id + 50] try: unsubmitted_plays = db.find_plays(play_ids_batch, only_unsubmitted=True).get() except ActorRetrievalFailure as exc: logger.exception( f"Error while retrieving unsubmitted plays: {exc}") err_msg = "Error while retrieving unsubmitted plays." break if not unsubmitted_plays: break unsubmitted_play_ids = tuple( map(lambda play: play.play_id, unsubmitted_plays)) found_plays.extend(unsubmitted_play_ids) try: network.submit_scrobbles(unsubmitted_plays).get() except NetworkException as exc: logger.exception( f"Network error while scrobbling plays: {exc}") err_msg = "Network error while scrobbling plays." break except ActorRetrievalFailure as exc: logger.exception(f"Error while scrobbling plays: {exc}") err_msg = "Error while scrobbling plays." break scrobbled_plays.extend(unsubmitted_play_ids) try: db.mark_plays_submitted(unsubmitted_play_ids).get() except DbClientError as exc: logger.exception(f"Error after successful scrobble: {exc}") err_msg = "Error after successful scrobble." break except ActorRetrievalFailure as exc: logger.exception( f"Error while marking plays as submitted: {exc}") err_msg = "Error while marking plays as submitted." break except Exception as exc: logger.exception( f"Error while marking plays as submitted: {exc}") err_msg = "Error while marking plays as submitted." break marked_plays.extend(unsubmitted_play_ids) # Vague rate-limiting of requests to the Network API sleep(1) self.write({ "success": True, "foundPlays": found_plays, "scrobbledPlays": scrobbled_plays, "markedPlays": marked_plays, "message": err_msg, })
def _post(self, data): if "playId" not in data: self.set_status(400) self.write({"success": False, "message": "Missing play ID."}) return try: play_id = int(data["playId"]) except Exception: self.set_status(400) self.write({"success": False, "message": "Invalid play ID."}) return try: db = db_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving database service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return try: play: RecordedPlay = db.find_play(play_id).get() except ActorRetrievalFailure as exc: logger.exception(f"Error while finding play in database: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return if not play: self.set_status(400) self.write({"success": False, "message": "Play does not exist."}) return elif play.submitted_at: self.set_status(400) self.write({ "success": False, "message": "Play was already submitted." }) return try: network = network_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving network service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Last.fm connection issue." }) return try: network.submit_scrobble(play).get() except ActorRetrievalFailure as exc: logger.exception(f"Error while scrobbling play: {exc}") self.set_status(500) self.write({ "success": False, "message": "Last.fm connection issue." }) return try: success = db.mark_play_submitted(play.play_id).get() except DbClientError as exc: self.set_status(400) self.write({ "success": False, "message": f"Error after successful scrobble: {exc}" }) return except ActorRetrievalFailure as exc: logger.exception(f"Error while marking play as submitted: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue after ." }) return self.write({"success": success})
def get(self): self.set_extra_headers() load_args = {} try: sort_direction = SortDirectionEnum( self.get_query_argument("sort", "")) load_args["sort_direction"] = sort_direction except ValueError: pass try: page_num = int(self.get_query_argument("page", "")) load_args["page_num"] = max(page_num, 1) except ValueError: pass try: page_size = int(self.get_query_argument("page_size", "")) load_args["page_size"] = max(min(page_size, 100), 1) except ValueError: pass try: db = db_service.retrieve_service().get() except ActorRetrievalFailure as exc: logger.exception(f"Error while retrieving database service: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return try: plays = db.load_plays(**load_args).get() except ActorRetrievalFailure as exc: logger.exception( f"Error while retrieving plays from database: {exc}") self.set_status(500) self.write({ "success": False, "message": "Database connection issue." }) return overall_plays_count = -1 unsubmitted_plays_count = -1 try: overall_plays_count = db.get_plays_count().get() unsubmitted_plays_count = db.get_plays_count( only_unsubmitted=True).get() except Exception as exc: logger.exception( f"Error while retrieving play counts from database: {exc}") play_id_mapping = {} for idx, play in enumerate(plays): play_id_mapping[play.play_id] = idx response = { "success": True, "plays": recorded_play_schema.dump(plays, many=True), "playIdMapping": play_id_mapping, "counts": { "overall": overall_plays_count, "unsubmitted": unsubmitted_plays_count, }, } self.write(response)