Преглед изворни кода

Fix proper scrobbling of podcasts

Colin Powell пре 2 година
родитељ
комит
685c99d023

+ 2 - 1
vrobbler/apps/scrobbles/admin.py

@@ -13,8 +13,9 @@ class ScrobbleAdmin(admin.ModelAdmin):
         "source",
         "playback_position",
         "in_progress",
+        "is_paused",
     )
-    list_filter = ("in_progress", "source", "track__artist")
+    list_filter = ("is_paused", "in_progress", "source", "track__artist")
     ordering = ("-timestamp",)
 
     def media_name(self, obj):

+ 40 - 12
vrobbler/apps/scrobbles/models.py

@@ -10,6 +10,7 @@ from django.utils import timezone
 from django_extensions.db.models import TimeStampedModel
 from music.models import Track
 from podcasts.models import Episode
+from scrobbles.utils import check_scrobble_for_finish
 from videos.models import Video
 
 logger = logging.getLogger(__name__)
@@ -180,6 +181,13 @@ class Scrobble(TimeStampedModel):
         mopidy_status = scrobble_data.pop('status', None)
         scrobble_is_stale = False
 
+        if scrobble:
+            logger.debug(f"Updating scrobble ticks")
+            scrobble.playback_position_ticks = scrobble_data.get(
+                "playback_position_ticks"
+            )
+            scrobble.save(update_fields=['playback_position_ticks'])
+
         if mopidy_status == "stopped":
             logger.info(f"Mopidy sent a message to stop {scrobble}")
             if not scrobble:
@@ -190,7 +198,37 @@ class Scrobble(TimeStampedModel):
 
             # Mopidy finished a play, scrobble away
             scrobble.in_progress = False
-            scrobble.save(update_fields=['in_progress'])
+            scrobble.played_to_completion = True
+            scrobble.save(
+                update_fields=['in_progress', 'played_to_completion']
+            )
+            return scrobble
+
+        if mopidy_status == "paused":
+            logger.info(f"Mopidy sent a message to pause {scrobble}")
+            if not scrobble:
+                logger.info("Message to pause while not started, ignoring")
+                return
+            if scrobble.is_paused:
+                logger.info("Message to pause while paused, ignoring")
+                return
+
+            # Mopidy finished a play, scrobble away
+            scrobble.is_paused = True
+            scrobble.save(update_fields=["is_paused"])
+            scrobble = check_scrobble_for_finish(scrobble)
+            return scrobble
+
+        if mopidy_status == "resumed":
+            logger.info(f"Mopidy sent a message to resume {scrobble}")
+            if not scrobble:
+                logger.info("Message to resume while not started, ignoring")
+                return
+            if not scrobble.is_paused:
+                logger.info("Message to resume while not paused, resuming")
+            # Mopidy finished a play, scrobble away
+            scrobble.is_paused = False
+            scrobble.save(update_fields=["is_paused"])
             return scrobble
 
         if scrobble and not mopidy_status:
@@ -218,16 +256,6 @@ class Scrobble(TimeStampedModel):
 
         # If we hit our completion threshold, save it and get ready
         # to scrobble again if we re-watch this.
-        if scrobble.percent_played >= getattr(
-            settings, "PERCENT_FOR_COMPLETION", 95
-        ):
-            scrobble.in_progress = False
-            scrobble.playback_position_ticks = scrobble.media_run_time_ticks
-            scrobble.save()
-
-        if scrobble.percent_played % 5 == 0:
-            if getattr(settings, "KEEP_DETAILED_SCROBBLE_LOGS", False):
-                scrobble.scrobble_log += f"\n{str(scrobble.timestamp)} - {scrobble.playback_position} - {str(scrobble.playback_position_ticks)} - {str(scrobble.percent_played)}%"
-                scrobble.save(update_fields=['scrobble_log'])
+        scrobble = check_scrobble_for_finish(scrobble)
 
         return scrobble

+ 6 - 2
vrobbler/apps/scrobbles/scrobblers.py

@@ -10,7 +10,9 @@ from scrobbles.utils import parse_mopidy_uri
 logger = logging.getLogger(__name__)
 
 
-def scrobble_podcast(data_dict: dict, user_id: Optional[int]) -> Scrobble:
+def mopidy_scrobble_podcast(
+    data_dict: dict, user_id: Optional[int]
+) -> Scrobble:
     mopidy_uri = data_dict.get("mopidy_uri", "")
     parsed_data = parse_mopidy_uri(mopidy_uri)
 
@@ -37,6 +39,7 @@ def scrobble_podcast(data_dict: dict, user_id: Optional[int]) -> Scrobble:
     mopidy_data = {
         "user_id": user_id,
         "timestamp": timezone.now(),
+        "playback_position_ticks": data_dict.get("playback_time_ticks"),
         "source": "Mopidy",
         "status": data_dict.get("status"),
     }
@@ -49,7 +52,7 @@ def scrobble_podcast(data_dict: dict, user_id: Optional[int]) -> Scrobble:
     return scrobble
 
 
-def scrobble_track(
+def mopidy_scrobble_track(
     data_dict: dict, user_id: Optional[int]
 ) -> Optional[Scrobble]:
     artist_dict = {
@@ -74,6 +77,7 @@ def scrobble_track(
     mopidy_data = {
         "user_id": user_id,
         "timestamp": timezone.now(),
+        "playback_position_ticks": data_dict.get("playback_time_ticks"),
         "source": "Mopidy",
         "status": data_dict.get("status"),
     }

+ 32 - 0
vrobbler/apps/scrobbles/utils.py

@@ -3,6 +3,7 @@ from typing import Any
 from urllib.parse import unquote
 
 from dateutil.parser import ParserError, parse
+from django.conf import settings
 
 logger = logging.getLogger(__name__)
 
@@ -57,3 +58,34 @@ def parse_mopidy_uri(uri: str) -> dict:
         'podcast_name': podcast_str,
         'pub_date': pub_date,
     }
+
+
+def check_scrobble_for_finish(scrobble: "Scrobble") -> None:
+    completion_percent = getattr(settings, "MUSIC_COMPLETION_PERCENT", 90)
+    if scrobble.podcast_episode:
+        completion_percent = getattr(
+            settings, "PODCAST_COMPLETION_PERCENT", 25
+        )
+    logger.debug(f"Completion set to {completion_percent}")
+
+    if scrobble.percent_played >= completion_percent:
+        logger.debug(
+            f"{scrobble} meets completion goal of {completion_percent}, finishing"
+        )
+        scrobble.in_progress = False
+        scrobble.is_paused = False
+        scrobble.playback_position_ticks = scrobble.media_obj.run_time_ticks
+        scrobble.save(
+            update_fields=[
+                "in_progress",
+                "is_paused",
+                "playback_position_ticks",
+            ]
+        )
+
+    if scrobble.percent_played % 5 == 0:
+        if getattr(settings, "KEEP_DETAILED_SCROBBLE_LOGS", False):
+            scrobble.scrobble_log += f"\n{str(scrobble.timestamp)} - {scrobble.playback_position} - {str(scrobble.playback_position_ticks)} - {str(scrobble.percent_played)}%"
+            scrobble.save(update_fields=['scrobble_log'])
+
+    return scrobble

+ 3 - 3
vrobbler/apps/scrobbles/views.py

@@ -27,7 +27,7 @@ from vrobbler.apps.music.aggregators import (
     top_tracks,
     week_of_scrobbles,
 )
-from scrobbles.scrobblers import scrobble_podcast, scrobble_track
+from scrobbles.scrobblers import mopidy_scrobble_podcast, mopidy_scrobble_track
 
 logger = logging.getLogger(__name__)
 
@@ -191,9 +191,9 @@ def mopidy_websocket(request):
         logger.debug(f"{json_data}")
 
     if 'podcast' in data_dict.get('mopidy_uri'):
-        scrobble = scrobble_podcast(data_dict, request.user.id)
+        scrobble = mopidy_scrobble_podcast(data_dict, request.user.id)
     else:
-        scrobble = scrobble_track(data_dict, request.user.id)
+        scrobble = mopidy_scrobble_track(data_dict, request.user.id)
 
     if not scrobble:
         return Response({}, status=status.HTTP_400_BAD_REQUEST)

+ 5 - 0
vrobbler/settings.py

@@ -37,6 +37,11 @@ KEEP_DETAILED_SCROBBLE_LOGS = os.getenv(
     "VROBBLER_KEEP_DETAILED_SCROBBLE_LOGS", False
 )
 
+PODCAST_COMPLETION_PERCENT = os.getenv(
+    "VROBBLER_PODCAST_COMPLETION_PERCENT", 25
+)
+MUSIC_COMPLETION_PERCENT = os.getenv("VROBBLER_MUSIC_COMPLETION_PERCENT", 90)
+
 # Should we cull old in-progress scrobbles that are beyond the wait period for resuming?
 DELETE_STALE_SCROBBLES = os.getenv("VROBBLER_DELETE_STALE_SCROBBLES", True)