diff --git a/vrobbler/apps/scrobbles/models.py b/vrobbler/apps/scrobbles/models.py index a65397f..33af3ae 100644 --- a/vrobbler/apps/scrobbles/models.py +++ b/vrobbler/apps/scrobbles/models.py @@ -78,8 +78,7 @@ class Scrobble(TimeStampedModel): def __str__(self): return f"Scrobble of {self.media_obj} {self.timestamp.year}-{self.timestamp.month}" - @property - def resumable(self): + def resumable(self, playback_ticks): """Check if a scrobble is not finished or beyond the configured resume limit. The idea here is to check whether a scrobble should be resumed, or a new @@ -98,6 +97,10 @@ class Scrobble(TimeStampedModel): beyond_resume_limit = False now = timezone.now() + if self.playback_position_ticks == playback_ticks: + # shortcircut in the case where we've resumed a track at the same playback ticks + return True + if self.video: diff = timedelta(seconds=Video.RESUME_LIMIT) percent_for_completion = Video.COMPLETION_PERCENT @@ -140,7 +143,9 @@ class Scrobble(TimeStampedModel): .order_by('-modified') .first() ) - if scrobble and scrobble.resumable: + if scrobble and scrobble.resumable( + scrobble_data['playback_position_ticks'] + ): logger.info( f"Found existing scrobble for video {video}, updating", {"scrobble_data": scrobble_data}, @@ -169,7 +174,9 @@ class Scrobble(TimeStampedModel): .order_by('-modified') .first() ) - if scrobble and scrobble.resumable: + if scrobble and scrobble.resumable( + scrobble_data['playback_position_ticks'] + ): logger.debug( f"Found existing scrobble for track {track}, updating", {"scrobble_data": scrobble_data}, @@ -195,7 +202,9 @@ class Scrobble(TimeStampedModel): .order_by('-modified') .first() ) - if scrobble and scrobble.resumable: + if scrobble and scrobble.resumable( + scrobble_data['playback_position_ticks'] + ): logger.debug( f"Found existing scrobble for podcast {episode}, updating", {"scrobble_data": scrobble_data}, @@ -220,7 +229,9 @@ class Scrobble(TimeStampedModel): .order_by('-modified') .first() ) - if scrobble and scrobble.resumable: + if scrobble and scrobble.resumable( + scrobble_data['playback_position_ticks'] + ): logger.debug( f"Found existing scrobble for sport event {event}, updating", {"scrobble_data": scrobble_data}, @@ -236,30 +247,25 @@ class Scrobble(TimeStampedModel): return cls.create(scrobble_data) @classmethod - def update(cls, scrobble: "Scrobble", scrobble_data: dict): + def update(cls, scrobble: "Scrobble", scrobble_data: dict) -> "Scrobble": # Status is a field we get from Mopidy, which refuses to poll us scrobble_status = scrobble_data.pop('mopidy_status', None) if not scrobble_status: scrobble_status = scrobble_data.pop('jellyfin_status', None) if not scrobble_status: - logger.warning( - f"No status update found in message, not scrobbling" - ) - return + scrobble_status = 'resumed' logger.debug(f"Scrobbling to {scrobble} with status {scrobble_status}") scrobble.update_ticks(scrobble_data) # On stop, stop progress and send it to the check for completion if scrobble_status == "stopped": - return scrobble.stop() - + scrobble.stop() # On pause, set is_paused and stop scrobbling if scrobble_status == "paused": - return scrobble.pause() - + scrobble.pause() if scrobble_status == "resumed": - return scrobble.resume() + scrobble.resume() for key, value in scrobble_data.items(): setattr(scrobble, key, value) @@ -287,7 +293,7 @@ class Scrobble(TimeStampedModel): check_scrobble_for_finish(self) def pause(self) -> None: - if self.is_paused: + if self.is_paused and not self.played_to_completion: logger.warning("Scrobble already paused") return self.is_paused = True @@ -295,10 +301,15 @@ class Scrobble(TimeStampedModel): check_scrobble_for_finish(self) def resume(self) -> None: - if self.is_paused or not self.in_progress: + if self.is_paused or not self.played_to_completion: self.is_paused = False self.in_progress = True - return self.save(update_fields=["is_paused", "in_progress"]) + return self.save( + update_fields=[ + "is_paused", + "in_progress", + ] + ) def update_ticks(self, data) -> None: self.playback_position_ticks = data.get("playback_position_ticks") diff --git a/vrobbler/apps/scrobbles/scrobblers.py b/vrobbler/apps/scrobbles/scrobblers.py index b7d2830..24897bd 100644 --- a/vrobbler/apps/scrobbles/scrobblers.py +++ b/vrobbler/apps/scrobbles/scrobblers.py @@ -86,15 +86,12 @@ def mopidy_scrobble_track( "mopidy_status": data_dict.get("status"), } - scrobble = None + # Jellyfin MB ids suck, so always overwrite with Mopidy if they're offering + track.musicbrainz_id = data_dict.get("musicbrainz_track_id") + track.save() + + scrobble = Scrobble.create_or_update_for_track(track, user_id, mopidy_data) - if track: - # Jellyfin MB ids suck, so always overwrite with Mopidy if they're offering - track.musicbrainz_id = data_dict.get("musicbrainz_track_id") - track.save() - scrobble = Scrobble.create_or_update_for_track( - track, user_id, mopidy_data - ) return scrobble diff --git a/vrobbler/apps/scrobbles/utils.py b/vrobbler/apps/scrobbles/utils.py index a4c219d..251e1d8 100644 --- a/vrobbler/apps/scrobbles/utils.py +++ b/vrobbler/apps/scrobbles/utils.py @@ -76,6 +76,9 @@ def check_scrobble_for_finish(scrobble: "Scrobble") -> None: settings, "PODCAST_COMPLETION_PERCENT", 25 ) if scrobble.percent_played >= completion_percent: + logger.debug( + f"Beyond completion percent {completion_percent}, finishing scrobble" + ) scrobble.in_progress = False scrobble.is_paused = False scrobble.played_to_completion = True diff --git a/vrobbler/apps/scrobbles/views.py b/vrobbler/apps/scrobbles/views.py index fa0ca33..8dcb46e 100644 --- a/vrobbler/apps/scrobbles/views.py +++ b/vrobbler/apps/scrobbles/views.py @@ -148,9 +148,7 @@ def jellyfin_websocket(request): if not scrobble: return Response({}, status=status.HTTP_400_BAD_REQUEST) - return Response( - {'scrobble_id': scrobble.id}, status=status.HTTP_201_CREATED - ) + return Response({'scrobble_id': scrobble.id}, status=status.HTTP_200_OK) @csrf_exempt @@ -171,6 +169,4 @@ def mopidy_websocket(request): if not scrobble: return Response({}, status=status.HTTP_400_BAD_REQUEST) - return Response( - {'scrobble_id': scrobble.id}, status=status.HTTP_201_CREATED - ) + return Response({'scrobble_id': scrobble.id}, status=status.HTTP_200_OK)