Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 837e1280bd | |||
| 8f9c825903 | |||
| 541073aae3 | |||
| b63ec6b15f | |||
| 117157e3ae | |||
| 0c10e78d5e |
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "vrobbler"
|
name = "vrobbler"
|
||||||
version = "0.8.1"
|
version = "0.8.3"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Colin Powell <colin@unbl.ink>"]
|
authors = ["Colin Powell <colin@unbl.ink>"]
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -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
|
||||||
|
|||||||
@ -9,9 +9,11 @@ from scrobbles.models import Scrobble
|
|||||||
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'
|
||||||
@ -69,8 +71,10 @@ def process_audioscrobbler_tsv_file(file_path):
|
|||||||
track.musicbrainz_id = row[7]
|
track.musicbrainz_id = row[7]
|
||||||
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'
|
||||||
|
|
||||||
|
|||||||
@ -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'])
|
||||||
|
|||||||
Reference in New Issue
Block a user