Fix Mopidy resuming messing things up
This commit is contained in:
@ -78,8 +78,7 @@ class Scrobble(TimeStampedModel):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Scrobble of {self.media_obj} {self.timestamp.year}-{self.timestamp.month}"
|
return f"Scrobble of {self.media_obj} {self.timestamp.year}-{self.timestamp.month}"
|
||||||
|
|
||||||
@property
|
def resumable(self, playback_ticks):
|
||||||
def resumable(self):
|
|
||||||
"""Check if a scrobble is not finished or beyond the configured resume limit.
|
"""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
|
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
|
beyond_resume_limit = False
|
||||||
now = timezone.now()
|
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:
|
if self.video:
|
||||||
diff = timedelta(seconds=Video.RESUME_LIMIT)
|
diff = timedelta(seconds=Video.RESUME_LIMIT)
|
||||||
percent_for_completion = Video.COMPLETION_PERCENT
|
percent_for_completion = Video.COMPLETION_PERCENT
|
||||||
@ -140,7 +143,9 @@ class Scrobble(TimeStampedModel):
|
|||||||
.order_by('-modified')
|
.order_by('-modified')
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
if scrobble and scrobble.resumable:
|
if scrobble and scrobble.resumable(
|
||||||
|
scrobble_data['playback_position_ticks']
|
||||||
|
):
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Found existing scrobble for video {video}, updating",
|
f"Found existing scrobble for video {video}, updating",
|
||||||
{"scrobble_data": scrobble_data},
|
{"scrobble_data": scrobble_data},
|
||||||
@ -169,7 +174,9 @@ class Scrobble(TimeStampedModel):
|
|||||||
.order_by('-modified')
|
.order_by('-modified')
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
if scrobble and scrobble.resumable:
|
if scrobble and scrobble.resumable(
|
||||||
|
scrobble_data['playback_position_ticks']
|
||||||
|
):
|
||||||
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},
|
||||||
@ -195,7 +202,9 @@ class Scrobble(TimeStampedModel):
|
|||||||
.order_by('-modified')
|
.order_by('-modified')
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
if scrobble and scrobble.resumable:
|
if scrobble and scrobble.resumable(
|
||||||
|
scrobble_data['playback_position_ticks']
|
||||||
|
):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Found existing scrobble for podcast {episode}, updating",
|
f"Found existing scrobble for podcast {episode}, updating",
|
||||||
{"scrobble_data": scrobble_data},
|
{"scrobble_data": scrobble_data},
|
||||||
@ -220,7 +229,9 @@ class Scrobble(TimeStampedModel):
|
|||||||
.order_by('-modified')
|
.order_by('-modified')
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
if scrobble and scrobble.resumable:
|
if scrobble and scrobble.resumable(
|
||||||
|
scrobble_data['playback_position_ticks']
|
||||||
|
):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Found existing scrobble for sport event {event}, updating",
|
f"Found existing scrobble for sport event {event}, updating",
|
||||||
{"scrobble_data": scrobble_data},
|
{"scrobble_data": scrobble_data},
|
||||||
@ -236,30 +247,25 @@ class Scrobble(TimeStampedModel):
|
|||||||
return cls.create(scrobble_data)
|
return cls.create(scrobble_data)
|
||||||
|
|
||||||
@classmethod
|
@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
|
# Status is a field we get from Mopidy, which refuses to poll us
|
||||||
scrobble_status = scrobble_data.pop('mopidy_status', None)
|
scrobble_status = scrobble_data.pop('mopidy_status', None)
|
||||||
if not scrobble_status:
|
if not scrobble_status:
|
||||||
scrobble_status = scrobble_data.pop('jellyfin_status', None)
|
scrobble_status = scrobble_data.pop('jellyfin_status', None)
|
||||||
if not scrobble_status:
|
if not scrobble_status:
|
||||||
logger.warning(
|
scrobble_status = 'resumed'
|
||||||
f"No status update found in message, not scrobbling"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.debug(f"Scrobbling to {scrobble} with status {scrobble_status}")
|
logger.debug(f"Scrobbling to {scrobble} with status {scrobble_status}")
|
||||||
scrobble.update_ticks(scrobble_data)
|
scrobble.update_ticks(scrobble_data)
|
||||||
|
|
||||||
# On stop, stop progress and send it to the check for completion
|
# On stop, stop progress and send it to the check for completion
|
||||||
if scrobble_status == "stopped":
|
if scrobble_status == "stopped":
|
||||||
return scrobble.stop()
|
scrobble.stop()
|
||||||
|
|
||||||
# On pause, set is_paused and stop scrobbling
|
# On pause, set is_paused and stop scrobbling
|
||||||
if scrobble_status == "paused":
|
if scrobble_status == "paused":
|
||||||
return scrobble.pause()
|
scrobble.pause()
|
||||||
|
|
||||||
if scrobble_status == "resumed":
|
if scrobble_status == "resumed":
|
||||||
return scrobble.resume()
|
scrobble.resume()
|
||||||
|
|
||||||
for key, value in scrobble_data.items():
|
for key, value in scrobble_data.items():
|
||||||
setattr(scrobble, key, value)
|
setattr(scrobble, key, value)
|
||||||
@ -287,7 +293,7 @@ class Scrobble(TimeStampedModel):
|
|||||||
check_scrobble_for_finish(self)
|
check_scrobble_for_finish(self)
|
||||||
|
|
||||||
def pause(self) -> None:
|
def pause(self) -> None:
|
||||||
if self.is_paused:
|
if self.is_paused and not self.played_to_completion:
|
||||||
logger.warning("Scrobble already paused")
|
logger.warning("Scrobble already paused")
|
||||||
return
|
return
|
||||||
self.is_paused = True
|
self.is_paused = True
|
||||||
@ -295,10 +301,15 @@ class Scrobble(TimeStampedModel):
|
|||||||
check_scrobble_for_finish(self)
|
check_scrobble_for_finish(self)
|
||||||
|
|
||||||
def resume(self) -> None:
|
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.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",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def update_ticks(self, data) -> None:
|
def update_ticks(self, data) -> None:
|
||||||
self.playback_position_ticks = data.get("playback_position_ticks")
|
self.playback_position_ticks = data.get("playback_position_ticks")
|
||||||
|
|||||||
@ -86,15 +86,12 @@ def mopidy_scrobble_track(
|
|||||||
"mopidy_status": data_dict.get("status"),
|
"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
|
return scrobble
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -76,6 +76,9 @@ def check_scrobble_for_finish(scrobble: "Scrobble") -> None:
|
|||||||
settings, "PODCAST_COMPLETION_PERCENT", 25
|
settings, "PODCAST_COMPLETION_PERCENT", 25
|
||||||
)
|
)
|
||||||
if scrobble.percent_played >= completion_percent:
|
if scrobble.percent_played >= completion_percent:
|
||||||
|
logger.debug(
|
||||||
|
f"Beyond completion percent {completion_percent}, finishing scrobble"
|
||||||
|
)
|
||||||
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
|
||||||
|
|||||||
@ -148,9 +148,7 @@ def jellyfin_websocket(request):
|
|||||||
if not scrobble:
|
if not scrobble:
|
||||||
return Response({}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
return Response(
|
return Response({'scrobble_id': scrobble.id}, status=status.HTTP_200_OK)
|
||||||
{'scrobble_id': scrobble.id}, status=status.HTTP_201_CREATED
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@ -171,6 +169,4 @@ def mopidy_websocket(request):
|
|||||||
if not scrobble:
|
if not scrobble:
|
||||||
return Response({}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
return Response(
|
return Response({'scrobble_id': scrobble.id}, status=status.HTTP_200_OK)
|
||||||
{'scrobble_id': scrobble.id}, status=status.HTTP_201_CREATED
|
|
||||||
)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user