Fix resume bug, stop trying to avoid resuming

Turns out trying to not resume in-progress Scrobbles is super painful.
Maybe I'll come up with a better idea later, but for now, I'd rather
just resurrect old paused scrobbles of past tracks, rather than
completely mess up all other aspects of scrobbling.
This commit is contained in:
2023-01-16 23:44:49 -05:00
parent fe53b68714
commit 0f562b7c58
5 changed files with 24 additions and 76 deletions

View File

@ -94,7 +94,6 @@ class Album(TimeStampedModel):
class Track(ScrobblableMixin):
RESUME_LIMIT = getattr(settings, 'MUSIC_RESUME_LIMIT', 60 * 60)
COMPLETION_PERCENT = getattr(settings, 'MUSIC_COMPLETION_PERCENT', 90)
class Opinion(models.IntegerChoices):

View File

@ -35,7 +35,6 @@ class Podcast(TimeStampedModel):
class Episode(ScrobblableMixin):
RESUME_LIMIT = getattr(settings, 'PODCAST_RESUME_LIMIT', 180 * 60)
COMPLETION_PERCENT = getattr(settings, 'PODCAST_COMPLETION_PERCENT', 90)
podcast = models.ForeignKey(Podcast, on_delete=models.DO_NOTHING)

View File

@ -72,60 +72,6 @@ class Scrobble(TimeStampedModel):
def __str__(self):
return f"Scrobble of {self.media_obj} {self.timestamp.year}-{self.timestamp.month}"
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
one created. If this method returns true, we should update an existing
scrobble, suggesting the user just paused their scrobble. This limit
should be different for different media. We are more likely to pause a video
or sports event for a while, and expect to resume it than an audio track or
a podcast.
"""
diff = None
# Default finish expectation
percent_for_completion = 100
# By default, assume we're not beyond resume limits
# This is to avoid spam scrobbles if webhooks go crazy
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
if self.track:
diff = timedelta(seconds=Track.RESUME_LIMIT)
percent_for_completion = Track.COMPLETION_PERCENT
if self.podcast_episode:
diff = timedelta(seconds=Episode.RESUME_LIMIT)
percent_for_completion = Episode.COMPLETION_PERCENT
if self.sport_event:
diff = timedelta(seconds=SportEvent.RESUME_LIMIT)
percent_for_completion = SportEvent.COMPLETION_PERCENT
if diff and self.timestamp:
beyond_resume_limit = self.timestamp + diff <= now
finished = self.percent_played >= percent_for_completion
resumable = not finished or not beyond_resume_limit
if not finished:
logger.debug(
f"{self} resumable, percent played {self.percent_played} is less than {percent_for_completion}"
)
if not beyond_resume_limit:
logger.debug(
f"{self} resumable, started less than {diff.seconds} seconds ago"
)
return not finished and not beyond_resume_limit
@classmethod
def create_or_update_for_video(
cls, video: "Video", user_id: int, scrobble_data: dict
@ -133,13 +79,15 @@ class Scrobble(TimeStampedModel):
scrobble_data['video_id'] = video.id
scrobble = (
cls.objects.filter(video=video, user_id=user_id)
cls.objects.filter(
video=video,
user_id=user_id,
played_to_completion=False,
)
.order_by('-modified')
.first()
)
if scrobble and scrobble.resumable(
scrobble_data['playback_position_ticks']
):
if scrobble:
logger.info(
f"Found existing scrobble for video {video}, updating",
{"scrobble_data": scrobble_data},
@ -164,13 +112,15 @@ class Scrobble(TimeStampedModel):
scrobble_data['track_id'] = track.id
scrobble = (
cls.objects.filter(track=track, user_id=user_id)
cls.objects.filter(
track=track,
user_id=user_id,
played_to_completion=False,
)
.order_by('-modified')
.first()
)
if scrobble and scrobble.resumable(
scrobble_data['playback_position_ticks']
):
if scrobble:
logger.debug(
f"Found existing scrobble for track {track}, updating",
{"scrobble_data": scrobble_data},
@ -192,13 +142,15 @@ class Scrobble(TimeStampedModel):
scrobble_data['podcast_episode_id'] = episode.id
scrobble = (
cls.objects.filter(podcast_episode=episode, user_id=user_id)
cls.objects.filter(
podcast_episode=episode,
user_id=user_id,
played_to_completion=False,
)
.order_by('-modified')
.first()
)
if scrobble and scrobble.resumable(
scrobble_data['playback_position_ticks']
):
if scrobble:
logger.debug(
f"Found existing scrobble for podcast {episode}, updating",
{"scrobble_data": scrobble_data},
@ -219,13 +171,15 @@ class Scrobble(TimeStampedModel):
) -> "Scrobble":
scrobble_data['sport_event_id'] = event.id
scrobble = (
cls.objects.filter(sport_event=event, user_id=user_id)
cls.objects.filter(
sport_event=event,
user_id=user_id,
played_to_completion=False,
)
.order_by('-modified')
.first()
)
if scrobble and scrobble.resumable(
scrobble_data['playback_position_ticks']
):
if scrobble:
logger.debug(
f"Found existing scrobble for sport event {event}, updating",
{"scrobble_data": scrobble_data},
@ -246,8 +200,6 @@ class Scrobble(TimeStampedModel):
scrobble_status = scrobble_data.pop('mopidy_status', None)
if not scrobble_status:
scrobble_status = scrobble_data.pop('jellyfin_status', None)
if not scrobble_status:
scrobble_status = "resumed"
logger.debug(f"Scrobbling to {scrobble} with status {scrobble_status}")
scrobble.update_ticks(scrobble_data)

View File

@ -39,7 +39,6 @@ class Team(TimeStampedModel):
class SportEvent(ScrobblableMixin):
RESUME_LIMIT = getattr(settings, 'SPORT_RESUME_LIMIT', (12 * 60) * 60)
COMPLETION_PERCENT = getattr(settings, 'SPORT_COMPLETION_PERCENT', 90)
class Type(models.TextChoices):

View File

@ -33,7 +33,6 @@ class Series(TimeStampedModel):
class Video(ScrobblableMixin):
RESUME_LIMIT = getattr(settings, 'VIDEO_RESUME_LIMIT', (12 * 60) * 60)
COMPLETION_PERCENT = getattr(settings, 'VIDEO_COMPLETION_PERCENT', 90)
class VideoType(models.TextChoices):