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:
@ -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)
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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"]
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user