Browse Source

Add scrobble log option and CLI tool

Colin Powell 2 years ago
parent
commit
41a74f46c8

+ 1 - 3
pyproject.toml

@@ -62,9 +62,7 @@ combine_as_imports = true
 exclude_dirs = ["*/tests/*", "*/migrations/*"]
 
 [tool.poetry.scripts]
-server = 'scripts:server'
-migrate = 'scripts:migrate'
-shell = 'scripts:shell'
+vrobbler = "vrobbler.cli:main"
 
 [build-system]
 requires = ["poetry-core>=1.0.0"]

+ 18 - 0
scrobbles/migrations/0004_scrobble_scrobble_log.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.5 on 2023-01-05 17:50
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scrobbles', '0003_remove_scrobble_counted_scrobble_in_progress'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='scrobble',
+            name='scrobble_log',
+            field=models.TextField(blank=True, null=True),
+        ),
+    ]

+ 1 - 0
scrobbles/models.py

@@ -21,6 +21,7 @@ class Scrobble(TimeStampedModel):
     source = models.CharField(max_length=255, **BNULL)
     source_id = models.TextField(**BNULL)
     in_progress = models.BooleanField(default=True)
+    scrobble_log = models.TextField(**BNULL)
 
     @property
     def percent_played(self) -> int:

+ 40 - 6
scrobbles/views.py

@@ -34,7 +34,9 @@ class RecentScrobbleList(ListView):
     model = Scrobble
 
     def get_queryset(self):
-        return Scrobble.objects.filter(in_progress=False)
+        return Scrobble.objects.filter(in_progress=False).order_by(
+            '-timestamp'
+        )
 
 
 @csrf_exempt
@@ -93,8 +95,16 @@ def jellyfin_websocket(request):
         .order_by('-modified')
         .first()
     )
+    existing_in_progress_scrobble = (
+        Scrobble.objects.filter(
+            video=video, user_id=request.user.id, in_progress=True
+        )
+        .order_by('-modified')
+        .first()
+    )
 
     minutes_from_now = timezone.now() + timedelta(minutes=15)
+    a_day_from_now = timezone.now() + timedelta(days=1)
 
     if (
         existing_finished_scrobble
@@ -105,14 +115,28 @@ def jellyfin_websocket(request):
         )
         return Response(video_dict, status=status.HTTP_204_NO_CONTENT)
 
-    scrobble, scrobble_created = Scrobble.objects.get_or_create(
-        **scrobble_dict
-    )
+    # Check if found in progress scrobble is more than a day old
+    if not (
+        existing_in_progress_scrobble
+        and existing_in_progress_scrobble.modified < a_day_from_now
+    ):
+        logger.info(
+            'Found a scrobble for this video more than a day old, creating a new scrobble'
+        )
+        scrobble = existing_in_progress_scrobble
+        scrobble_created = False
+    else:
+        if getattr(settings, "DELETE_STALE_SCROBBLES", True):
+            existing_in_progress_scrobble.delete()
+        scrobble, scrobble_created = Scrobble.objects.get_or_create(
+            **scrobble_dict
+        )
 
     if scrobble_created:
         # If we newly created this, capture the client we're watching from
         scrobble.source = data_dict['ClientName']
         scrobble.source_id = data_dict['MediaSourceId']
+        scrobble.scrobble_log = ""
     else:
         last_tick = scrobble.playback_position_ticks
 
@@ -121,7 +145,14 @@ def jellyfin_websocket(request):
     scrobble.playback_position = data_dict["PlaybackPosition"]
     scrobble.timestamp = parse(data_dict["UtcTimestamp"])
     scrobble.is_paused = data_dict["IsPaused"] in TRUTHY_VALUES
-    scrobble.save()
+    scrobble.save(
+        update_fields=[
+            'playback_position_ticks',
+            'playback_position',
+            'timestamp',
+            'is_paused',
+        ]
+    )
 
     # If we hit our completion threshold, save it and get ready
     # to scrobble again if we re-watch this.
@@ -133,6 +164,9 @@ def jellyfin_websocket(request):
         scrobble.save()
 
     if scrobble.percent_played % 5 == 0:
-        logger.info(f"You are {scrobble.percent_played}% through {video}")
+        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'])
+        logger.debug(f"You are {scrobble.percent_played}% through {video}")
 
     return Response(video_dict, status=status.HTTP_201_CREATED)

+ 34 - 0
vrobbler/cli.py

@@ -0,0 +1,34 @@
+# cli.py
+import logging
+import sys
+from os import environ as env
+
+
+if not 'DJANGO_SETTINGS_MODULE' in env:
+    from vrobbler import settings
+    env.setdefault('DJANGO_SETTINGS_MODULE', settings.__name__)
+
+
+import django
+django.setup()
+
+# this line must be after django.setup() for logging configure
+logger = logging.getLogger('vrobbler')
+
+def main():
+    # to get configured settings
+    from django.conf import settings
+
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+    main()

+ 6 - 1
vrobbler/settings.py

@@ -32,6 +32,12 @@ DEBUG = os.getenv("VROBBLER_DEBUG", False)
 
 TESTING = len(sys.argv) > 1 and sys.argv[1] == "test"
 
+KEEP_DETAILED_SCROBBLE_LOGS = os.getenv(
+    "VROBBLER_KEEP_DETAILED_SCROBBLE_LOGS", False
+)
+
+DELETE_STALE_SCROBBLES = os.getenv("VROBBLER_DELETE_STALE_SCROBBLES", True)
+
 TMDB_API_KEY = os.getenv("VROBBLER_TMDB_API_KEY", "")
 
 DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
@@ -61,7 +67,6 @@ INSTALLED_APPS = [
     "django_filters",
     "django_extensions",
     'rest_framework.authtoken',
-    "vrobbler",
     "scrobbles",
     "videos",
     "rest_framework",