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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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})
Ejemplo n.º 5
0
    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})
Ejemplo n.º 6
0
    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})
Ejemplo n.º 7
0
    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
Ejemplo n.º 9
0
    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,
        })
Ejemplo n.º 10
0
    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,
        })
Ejemplo n.º 11
0
    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})
Ejemplo n.º 12
0
    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)