瀏覽代碼

Update importing to include some logging

Colin Powell 2 年之前
父節點
當前提交
64d9cac09c

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

@@ -13,7 +13,7 @@ class ScrobbleInline(admin.TabularInline):
 @admin.register(AudioScrobblerTSVImport)
 class AudioScrobblerTSVImportAdmin(admin.ModelAdmin):
     date_hierarchy = "created"
-    list_display = ("id", "tsv_file", "created")
+    list_display = ("uuid", "created", "process_count", "tsv_file")
     ordering = ("-created",)
 
 

+ 18 - 0
vrobbler/apps/scrobbles/migrations/0014_audioscrobblertsvimport_process_log.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.5 on 2023-02-03 22:53
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scrobbles', '0013_audioscrobblertsvimport_processed_on'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='audioscrobblertsvimport',
+            name='process_log',
+            field=models.TextField(blank=True, null=True),
+        ),
+    ]

+ 29 - 0
vrobbler/apps/scrobbles/migrations/0015_audioscrobblertsvimport_uuid_and_more.py

@@ -0,0 +1,29 @@
+# Generated by Django 4.1.5 on 2023-02-03 23:36
+
+from django.db import migrations, models
+import scrobbles.models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scrobbles', '0014_audioscrobblertsvimport_process_log'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='audioscrobblertsvimport',
+            name='uuid',
+            field=models.UUIDField(default=uuid.uuid4, editable=False),
+        ),
+        migrations.AlterField(
+            model_name='audioscrobblertsvimport',
+            name='tsv_file',
+            field=models.FileField(
+                blank=True,
+                null=True,
+                upload_to=scrobbles.models.AudioScrobblerTSVImport.get_path,
+            ),
+        ),
+    ]

+ 18 - 0
vrobbler/apps/scrobbles/migrations/0016_audioscrobblertsvimport_process_count.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.5 on 2023-02-03 23:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scrobbles', '0015_audioscrobblertsvimport_uuid_and_more'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='audioscrobblertsvimport',
+            name='process_count',
+            field=models.IntegerField(blank=True, null=True),
+        ),
+    ]

+ 29 - 8
vrobbler/apps/scrobbles/models.py

@@ -18,18 +18,27 @@ BNULL = {"blank": True, "null": True}
 
 
 class AudioScrobblerTSVImport(TimeStampedModel):
-    tsv_file = models.FileField(
-        upload_to="audioscrobbler-uploads/%Y/%m-%d/", **BNULL
-    )
+    def get_path(instance, filename):
+        extension = filename.split('.')[-1]
+        uuid = instance.uuid
+        return f'audioscrobbler-uploads/{uuid}.{extension}'
+
+    uuid = models.UUIDField(editable=False, default=uuid4)
+    tsv_file = models.FileField(upload_to=get_path, **BNULL)
     processed_on = models.DateTimeField(**BNULL)
+    process_log = models.TextField(**BNULL)
+    process_count = models.IntegerField(**BNULL)
 
     def __str__(self):
-        return f"Audioscrobbler TSV upload: {self.tsv_file.path}"
+        if self.tsv_file:
+            return f"Audioscrobbler TSV upload: {self.tsv_file.path}"
+        return f"Audioscrobbler TSV upload {self.id}"
 
     def save(self, **kwargs):
         """On save, attempt to import the TSV file"""
-
-        return super().save(**kwargs)
+        super().save(**kwargs)
+        self.process()
+        return
 
     def process(self, force=False):
         from scrobbles.tsv import process_audioscrobbler_tsv_file
@@ -38,9 +47,21 @@ class AudioScrobblerTSVImport(TimeStampedModel):
             logger.info(f"{self} already processed on {self.processed_on}")
             return
 
-        process_audioscrobbler_tsv_file(self.tsv_file.path)
+        scrobbles = process_audioscrobbler_tsv_file(self.tsv_file.path)
+        if scrobbles:
+            self.process_log = f"Created {len(scrobbles)} scrobbles"
+            for scrobble in scrobbles:
+                scrobble_str = f"{scrobble.id}\t{scrobble.timestamp}\t{scrobble.track.title}\t"
+                self.process_log += f"\n{scrobble_str}"
+            self.process_count = len(scrobbles)
+        else:
+            self.process_log = f"Created no new scrobbles"
+            self.process_count = 0
+
         self.processed_on = timezone.now()
-        self.save(update_fields=['processed_on'])
+        self.save(
+            update_fields=['processed_on', 'process_count', 'process_log']
+        )
 
 
 class ChartRecord(TimeStampedModel):

+ 7 - 1
vrobbler/apps/scrobbles/serializers.py

@@ -1,5 +1,11 @@
 from rest_framework import serializers
-from scrobbles.models import Scrobble
+from scrobbles.models import Scrobble, AudioScrobblerTSVImport
+
+
+class AudioScrobblerTSVImportSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = AudioScrobblerTSVImport
+        fields = ('tsv_file',)
 
 
 class ScrobbleSerializer(serializers.ModelSerializer):

+ 1 - 0
vrobbler/apps/scrobbles/tsv.py

@@ -96,3 +96,4 @@ def process_audioscrobbler_tsv_file(file_path):
             f"Created {len(created)} scrobbles",
             extra={'created_scrobbles': created},
         )
+        return created

+ 5 - 0
vrobbler/apps/scrobbles/urls.py

@@ -7,6 +7,11 @@ urlpatterns = [
     path('', views.scrobble_endpoint, name='api-list'),
     path('finish/<slug:uuid>', views.scrobble_finish, name='finish'),
     path('cancel/<slug:uuid>', views.scrobble_cancel, name='cancel'),
+    path(
+        'audioscrobbler-file-upload/',
+        views.import_audioscrobbler_file,
+        name='audioscrobbler-file-upload',
+    ),
     path('jellyfin/', views.jellyfin_websocket, name='jellyfin-websocket'),
     path('mopidy/', views.mopidy_websocket, name='mopidy-websocket'),
 ]

+ 33 - 2
vrobbler/apps/scrobbles/views.py

@@ -11,7 +11,12 @@ from django.views.decorators.csrf import csrf_exempt
 from django.views.generic import FormView
 from django.views.generic.list import ListView
 from rest_framework import status
-from rest_framework.decorators import api_view, permission_classes
+from rest_framework.decorators import (
+    api_view,
+    parser_classes,
+    permission_classes,
+)
+from rest_framework.parsers import MultiPartParser
 from rest_framework.permissions import IsAuthenticated
 from rest_framework.response import Response
 from scrobbles.constants import (
@@ -29,7 +34,10 @@ from scrobbles.scrobblers import (
     mopidy_scrobble_podcast,
     mopidy_scrobble_track,
 )
-from scrobbles.serializers import ScrobbleSerializer
+from scrobbles.serializers import (
+    AudioScrobblerTSVImportSerializer,
+    ScrobbleSerializer,
+)
 from scrobbles.thesportsdb import lookup_event_from_thesportsdb
 
 from vrobbler.apps.music.aggregators import (
@@ -187,6 +195,29 @@ def mopidy_websocket(request):
     return Response({'scrobble_id': scrobble.id}, status=status.HTTP_200_OK)
 
 
+@csrf_exempt
+@permission_classes([IsAuthenticated])
+@api_view(['POST'])
+@parser_classes([MultiPartParser])
+def import_audioscrobbler_file(request):
+    """Takes a TSV file in the Audioscrobbler format, saves it and processes the
+    scrobbles.
+    """
+    scrobbles_created = []
+    # tsv_file = request.FILES[0]
+
+    file_serializer = AudioScrobblerTSVImportSerializer(data=request.data)
+    if file_serializer.is_valid():
+        import_file = file_serializer.save()
+        return Response(
+            {'scrobble_ids': scrobbles_created}, status=status.HTTP_200_OK
+        )
+    else:
+        return Response(
+            file_serializer.errors, status=status.HTTP_400_BAD_REQUEST
+        )
+
+
 @csrf_exempt
 @permission_classes([IsAuthenticated])
 @api_view(['GET'])

+ 4 - 0
vrobbler/templates/scrobbles/scrobble_list.html

@@ -179,7 +179,11 @@
                             {% for scrobble in object_list %}
                             <tr>
                                 <td>{{scrobble.timestamp|naturaltime}}</td>
+                                {% if scrobble.track.album.cover_image %}
                                 <td><img src="{{scrobble.track.album.cover_image.url}}" width=50 height=50 style="border:1px solid black;" /></td>
+                                {% else %}
+                                <td>{{scrobble.track.album.name}}</td>
+                                {% endif %}
                                 <td>{{scrobble.track.title}}</td>
                                 <td>{{scrobble.track.artist.name}}</td>
                             </tr>