Compare commits

...

8 Commits
0.8.1 ... 0.8.4

Author SHA1 Message Date
5e7c8ff137 Bump version to 0.8.4 2023-02-07 00:52:11 -05:00
fae59849f8 Add mb lookups to TSV imports 2023-02-07 00:51:43 -05:00
837e1280bd Bump version to 0.8.3 2023-02-06 23:30:14 -05:00
8f9c825903 Fix user timezones in scrobbler files 2023-02-06 23:28:57 -05:00
541073aae3 Bump version to 0.8.2 2023-02-06 19:32:15 -05:00
b63ec6b15f Fix bug in export when artist does not exist 2023-02-06 19:31:25 -05:00
117157e3ae Fix audioscrobbler import bug
Issue was not having a user so we couldn't set a timezone. All fixed now
2023-02-06 19:30:58 -05:00
0c10e78d5e Fix bug in unified scrobbler for podcasts 2023-02-06 17:54:48 -05:00
7 changed files with 85 additions and 19 deletions

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "vrobbler" name = "vrobbler"
version = "0.8.1" version = "0.8.4"
description = "" description = ""
authors = ["Colin Powell <colin@unbl.ink>"] authors = ["Colin Powell <colin@unbl.ink>"]

View File

@ -1,3 +1,5 @@
import pytz
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import models from django.db import models
from django_extensions.db.models import TimeStampedModel from django_extensions.db.models import TimeStampedModel
@ -16,3 +18,7 @@ class UserProfile(TimeStampedModel):
def __str__(self): def __str__(self):
return f"User profile for {self.user}" return f"User profile for {self.user}"
@property
def tzinfo(self):
return pytz.timezone(self.timezone)

View File

@ -52,8 +52,9 @@ def export_scrobbles(start_date=None, end_date=None, format="AS"):
track = scrobble.track track = scrobble.track
track_number = 0 # TODO Add track number track_number = 0 # TODO Add track number
track_rating = "S" # TODO implement ratings? track_rating = "S" # TODO implement ratings?
track_artist = track.artist or track.album.primary_artist
row = [ row = [
track.album.primary_artist.name, track_artist,
track.album.name, track.album.name,
track.title, track.title,
track_number, track_number,

View File

@ -0,0 +1,26 @@
# Generated by Django 4.1.5 on 2023-02-07 00:07
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('scrobbles', '0016_audioscrobblertsvimport_process_count'),
]
operations = [
migrations.AddField(
model_name='audioscrobblertsvimport',
name='user',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to=settings.AUTH_USER_MODEL,
),
),
]

View File

@ -24,6 +24,7 @@ class AudioScrobblerTSVImport(TimeStampedModel):
uuid = instance.uuid uuid = instance.uuid
return f'audioscrobbler-uploads/{uuid}.{extension}' return f'audioscrobbler-uploads/{uuid}.{extension}'
user = models.ForeignKey(User, on_delete=models.DO_NOTHING, **BNULL)
uuid = models.UUIDField(editable=False, default=uuid4) uuid = models.UUIDField(editable=False, default=uuid4)
tsv_file = models.FileField(upload_to=get_path, **BNULL) tsv_file = models.FileField(upload_to=get_path, **BNULL)
processed_on = models.DateTimeField(**BNULL) processed_on = models.DateTimeField(**BNULL)
@ -48,7 +49,12 @@ class AudioScrobblerTSVImport(TimeStampedModel):
logger.info(f"{self} already processed on {self.processed_on}") logger.info(f"{self} already processed on {self.processed_on}")
return return
scrobbles = process_audioscrobbler_tsv_file(self.tsv_file.path) tz = None
if self.user:
tz = self.user.profile.tzinfo
scrobbles = process_audioscrobbler_tsv_file(
self.tsv_file.path, user_tz=tz
)
if scrobbles: if scrobbles:
self.process_log = f"Created {len(scrobbles)} scrobbles" self.process_log = f"Created {len(scrobbles)} scrobbles"
for scrobble in scrobbles: for scrobble in scrobbles:
@ -255,7 +261,7 @@ class Scrobble(TimeStampedModel):
scrobble_data['video_id'] = media.id scrobble_data['video_id'] = media.id
if media.__class__.__name__ == 'Episode': if media.__class__.__name__ == 'Episode':
media_query = models.Q(podcast_episode=media) media_query = models.Q(podcast_episode=media)
scrobble_data['podcast_id'] = media.id scrobble_data['podcast_episode_id'] = media.id
if media.__class__.__name__ == 'SportEvent': if media.__class__.__name__ == 'SportEvent':
media_query = models.Q(sport_event=media) media_query = models.Q(sport_event=media)
scrobble_data['sport_event_id'] = media.id scrobble_data['sport_event_id'] = media.id

View File

@ -6,12 +6,19 @@ import pytz
from music.models import Album, Artist, Track from music.models import Album, Artist, Track
from scrobbles.models import Scrobble from scrobbles.models import Scrobble
from vrobbler.apps.scrobbles.musicbrainz import (
lookup_album_dict_from_mb,
lookup_artist_id_from_mb,
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def process_audioscrobbler_tsv_file(file_path): def process_audioscrobbler_tsv_file(file_path, user_tz=None):
"""Takes a path to a file of TSV data and imports it as past scrobbles""" """Takes a path to a file of TSV data and imports it as past scrobbles"""
new_scrobbles = [] new_scrobbles = []
if not user_tz:
user_tz = pytz.utc
with open(file_path) as infile: with open(file_path) as infile:
source = 'Audioscrobbler File' source = 'Audioscrobbler File'
@ -30,9 +37,8 @@ def process_audioscrobbler_tsv_file(file_path):
continue continue
artist, artist_created = Artist.objects.get_or_create(name=row[0]) artist, artist_created = Artist.objects.get_or_create(name=row[0])
if artist_created: if artist_created:
logger.debug(f"Created artist {artist}") artist.musicbrainz_id = lookup_artist_id_from_mb(artist.name)
else: artist.save(update_fields=["musicbrainz_id"])
logger.debug(f"Found artist {artist}")
album = None album = None
album_created = False album_created = False
@ -50,9 +56,22 @@ def process_audioscrobbler_tsv_file(file_path):
album.artists.add(artist) album.artists.add(artist)
if album_created: if album_created:
logger.debug(f"Created album {album}") album_dict = lookup_album_dict_from_mb(
else: album.name, artist_name=artist.name
logger.debug(f"Found album {album}") )
album.year = album_dict["year"]
album.musicbrainz_id = album_dict["mb_id"]
album.musicbrainz_releasegroup_id = album_dict["mb_group_id"]
album.musicbrainz_albumartist_id = artist.musicbrainz_id
album.save(
update_fields=[
"year",
"musicbrainz_id",
"musicbrainz_releasegroup_id",
"musicbrainz_albumartist_id",
]
)
album.artists.add(artist)
track, track_created = Track.objects.get_or_create( track, track_created = Track.objects.get_or_create(
title=row[2], title=row[2],
@ -60,17 +79,16 @@ def process_audioscrobbler_tsv_file(file_path):
album=album, album=album,
) )
if track_created:
logger.debug(f"Created track {track}")
else:
logger.debug(f"Found track {track}")
if track_created: if track_created:
track.musicbrainz_id = row[7] track.musicbrainz_id = row[7]
track.run_time = int(row[4])
track.run_time_ticks = int(row[4]) * 1000
track.save() track.save()
timestamp = datetime.utcfromtimestamp(int(row[6])).replace( timestamp = (
tzinfo=pytz.utc datetime.utcfromtimestamp(int(row[6]))
.replace(tzinfo=user_tz)
.astimezone(pytz.utc)
) )
source = 'Audioscrobbler File' source = 'Audioscrobbler File'

View File

@ -4,6 +4,7 @@ from datetime import datetime
import pytz import pytz
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models.fields import timezone from django.db.models.fields import timezone
from django.http import FileResponse, HttpResponseRedirect, JsonResponse from django.http import FileResponse, HttpResponseRedirect, JsonResponse
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
@ -160,12 +161,20 @@ class JsonableResponseMixin:
return JsonResponse(data) return JsonResponse(data)
class AudioScrobblerImportCreateView(JsonableResponseMixin, CreateView): class AudioScrobblerImportCreateView(
LoginRequiredMixin, JsonableResponseMixin, CreateView
):
model = AudioScrobblerTSVImport model = AudioScrobblerTSVImport
fields = ['tsv_file'] fields = ['tsv_file']
template_name = 'scrobbles/upload_form.html' template_name = 'scrobbles/upload_form.html'
success_url = reverse_lazy('vrobbler-home') success_url = reverse_lazy('vrobbler-home')
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return HttpResponseRedirect(self.get_success_url())
@csrf_exempt @csrf_exempt
@api_view(['GET']) @api_view(['GET'])