Update importing to include some logging

This commit is contained in:
2023-02-03 18:59:48 -05:00
parent c21d6a96fe
commit 64d9cac09c
10 changed files with 145 additions and 12 deletions

View File

@ -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",)

View File

@ -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),
),
]

View File

@ -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,
),
),
]

View File

@ -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),
),
]

View File

@ -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):

View File

@ -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):

View File

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

View File

@ -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'),
]

View File

@ -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'])

View File

@ -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>