Another attempt to fix the Jellyfin issue

This time, we simplify the progress updates, aggressively mark tracks as
100 played if they are marked played_to_completion, and implement a hack
for Jellyfin spamming us with progress updates less than a second apart.
This commit is contained in:
2023-01-31 10:28:54 -05:00
parent 6fc51d9296
commit ca57eabf87
3 changed files with 19 additions and 19 deletions

View File

@ -12,6 +12,7 @@ from podcasts.models import Episode
from scrobbles.utils import check_scrobble_for_finish from scrobbles.utils import check_scrobble_for_finish
from sports.models import SportEvent from sports.models import SportEvent
from videos.models import Series, Video from videos.models import Series, Video
from vrobbler.apps.profiles.utils import now_user_timezone
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
User = get_user_model() User = get_user_model()
@ -146,7 +147,7 @@ class Scrobble(TimeStampedModel):
playback_ticks = (timezone.now() - self.timestamp).seconds * 1000 playback_ticks = (timezone.now() - self.timestamp).seconds * 1000
if self.played_to_completion: if self.played_to_completion:
playback_ticks = self.media_obj.run_time_ticks return 100
percent = int((playback_ticks / self.media_obj.run_time_ticks) * 100) percent = int((playback_ticks / self.media_obj.run_time_ticks) * 100)
return percent return percent
@ -210,17 +211,26 @@ class Scrobble(TimeStampedModel):
cls.objects.filter( cls.objects.filter(
track=track, track=track,
user_id=user_id, user_id=user_id,
played_to_completion=False,
) )
.order_by('-modified') .order_by('-modified')
.first() .first()
) )
if scrobble and scrobble.percent_played <= 100: if scrobble:
logger.debug( logger.debug(
f"Found existing scrobble for track {track}, updating", f"Found existing scrobble for track {track}, updating",
{"scrobble_data": scrobble_data}, {"scrobble_data": scrobble_data},
) )
return cls.update(scrobble, scrobble_data) return cls.update(scrobble, scrobble_data)
if 'jellyfin_status' in scrobble_data.keys():
last_scrobble = Scrobble.objects.last()
if (
scrobble_data['timestamp'] - last_scrobble.timestamp
).seconds <= 1:
logger.warning('Jellyfin spammed us with duplicate updates')
return last_scrobble
logger.debug( logger.debug(
f"No existing scrobble for track {track}, creating", f"No existing scrobble for track {track}, creating",
{"scrobble_data": scrobble_data}, {"scrobble_data": scrobble_data},
@ -311,7 +321,6 @@ class Scrobble(TimeStampedModel):
for key, value in scrobble_data.items(): for key, value in scrobble_data.items():
setattr(scrobble, key, value) setattr(scrobble, key, value)
scrobble.save() scrobble.save()
check_scrobble_for_finish(scrobble)
return scrobble return scrobble
@classmethod @classmethod
@ -334,6 +343,7 @@ class Scrobble(TimeStampedModel):
check_scrobble_for_finish(self, force_finish) check_scrobble_for_finish(self, force_finish)
def pause(self) -> None: def pause(self) -> None:
print('Trying to pause it')
if self.is_paused: if self.is_paused:
logger.warning("Scrobble already paused") logger.warning("Scrobble already paused")
return return
@ -346,6 +356,7 @@ class Scrobble(TimeStampedModel):
self.is_paused = False self.is_paused = False
self.in_progress = True self.in_progress = True
return self.save(update_fields=["is_paused", "in_progress"]) return self.save(update_fields=["is_paused", "in_progress"])
logger.warning("Resume called but in progress or not paused")
def cancel(self) -> None: def cancel(self) -> None:
check_scrobble_for_finish(self, force_finish=True) check_scrobble_for_finish(self, force_finish=True)

View File

@ -99,26 +99,15 @@ def create_jellyfin_scrobble_dict(data_dict: dict, user_id: int) -> dict:
jellyfin_status = "resumed" jellyfin_status = "resumed"
if data_dict.get("IsPaused"): if data_dict.get("IsPaused"):
jellyfin_status = "paused" jellyfin_status = "paused"
if data_dict.get("PlayedToCompletion"): if data_dict.get("NotificationType") == 'PlaybackStop':
jellyfin_status = "stopped" jellyfin_status = "stopped"
playback_position_ticks = None
if data_dict.get("PlaybackPositionTicks"):
playback_position_ticks = (
data_dict.get("PlaybackPositionTicks") // 10000
)
if playback_position_ticks <= 0:
playback_position_ticks = None
playback_position = data_dict.get("PlaybackPosition")
if playback_position:
playback_position = convert_to_seconds(playback_position)
return { return {
"user_id": user_id, "user_id": user_id,
"timestamp": parse(data_dict.get("UtcTimestamp")), "timestamp": parse(data_dict.get("UtcTimestamp")),
"playback_position_ticks": playback_position_ticks, "playback_position_ticks": data_dict.get("PlaybackPositionTicks", "")
"playback_position": playback_position, // 10000,
"playback_position": data_dict.get("PlaybackPosition", ""),
"source": data_dict.get("ClientName", "Vrobbler"), "source": data_dict.get("ClientName", "Vrobbler"),
"source_id": data_dict.get('MediaSourceId'), "source_id": data_dict.get('MediaSourceId'),
"jellyfin_status": jellyfin_status, "jellyfin_status": jellyfin_status,
@ -128,6 +117,7 @@ def create_jellyfin_scrobble_dict(data_dict: dict, user_id: int) -> dict:
def jellyfin_scrobble_track( def jellyfin_scrobble_track(
data_dict: dict, user_id: Optional[int] data_dict: dict, user_id: Optional[int]
) -> Optional[Scrobble]: ) -> Optional[Scrobble]:
if not data_dict.get("Provider_musicbrainztrack", None): if not data_dict.get("Provider_musicbrainztrack", None):
logger.error( logger.error(
"No MBrainz Track ID received. This is likely because all metadata is bad, not scrobbling" "No MBrainz Track ID received. This is likely because all metadata is bad, not scrobbling"

View File

@ -77,7 +77,6 @@ def check_scrobble_for_finish(
scrobble.in_progress = False scrobble.in_progress = False
scrobble.is_paused = False scrobble.is_paused = False
scrobble.played_to_completion = True scrobble.played_to_completion = True
scrobble.playback_position_ticks = scrobble.media_obj.run_time_ticks
scrobble.save( scrobble.save(
update_fields=["in_progress", "is_paused", "played_to_completion"] update_fields=["in_progress", "is_paused", "played_to_completion"]
) )