From ce06b1688146123c0e39d352b395232853eebf57 Mon Sep 17 00:00:00 2001 From: Mark Hoekveen Date: Thu, 22 May 2025 19:41:31 +0200 Subject: [PATCH] track nomination moet nog wel een hoop oude code weg, maar dat is een probleem voor later --- playlist/models/profile.py | 12 +++--- playlist/models/track.py | 82 ++++++++++++++++++++++++++++++++++++- playlist/views/api/track.py | 20 ++++++++- 3 files changed, 106 insertions(+), 8 deletions(-) diff --git a/playlist/models/profile.py b/playlist/models/profile.py index a4f0457..11db46c 100644 --- a/playlist/models/profile.py +++ b/playlist/models/profile.py @@ -49,20 +49,20 @@ class Profile(models.Model): self.quota = min(self.quota, MAX_QUOTA) print(f"{self.user.username}: Er zijn {elapsed.total_seconds()}s voorbij, je nieuwe quota is {self.quota}") self.quota_last_updated = timezone.now() - + @property def can_nominate(self): """Is this user allowed to nominate at this point in time?""" end_date = os.getenv('DATE_NOM_END') if self.user.is_superuser: - return (True,) + return (True, None) if end_date: end_date = datetime.fromisoformat(end_date) if end_date <= datetime.now(tz=UTC): return (False, "over") if self.quota < 1: return (False, "quota") - return (True,) + return (True, None) @property def can_vote(self): @@ -86,7 +86,7 @@ class Profile(models.Model): def init_spt(self, code): """Initial setting of spotify tokens from user accept code""" data = spt.get_token_json( - grant_type='authorization_code', + grant_type='authorization_code', code=code ) self.save_spt(data) @@ -109,7 +109,7 @@ class Profile(models.Model): self.spt_refresh_token = refresh self.spt_expires = timezone.now() + timedelta(seconds=expires - 300) self.save() - + def unlink_spt(self): self.spt_access_token = None self.spt_refresh_token = None @@ -122,7 +122,7 @@ class Profile(models.Model): self.spt_expires and (timezone.now() < self.spt_expires) ) - + def get_or_update_spt(self): if self.spt_refresh_token: if not self.can_spt: diff --git a/playlist/models/track.py b/playlist/models/track.py index a5b105a..8f0bc7a 100644 --- a/playlist/models/track.py +++ b/playlist/models/track.py @@ -4,12 +4,92 @@ from random import choice from .album import Album from .artist import Artist from .vote import Vote +from playlist.spotify import spt +#from playlist.utils import track_from_json import re class TrackManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(old=False) + def from_json(self, cls, json): + try: + return cls.objects.get(pk=json["id"]) + except cls.DoesNotExist: + keys = [f.name for f in cls._meta.get_fields()] + subset = {key:json[key] for key in set(keys) & set(json.keys())} + return cls.objects.create(**subset) + + def track_from_json(self, json): + """Creates the track, album and artist from spotify response.""" + # Album artists + album_artists = [] + for artist in json["album"]["artists"]: + album_artists.append(self.from_json(Artist, artist)) + del json["album"]["artists"] + + # Album + album = self.from_json(Album, json["album"]) + album.image_url = json["album"]["images"][0]["url"] + album.save() + del json["album"] + for artist in album_artists: + album.artists.add(artist) + + # Track artists + track_artists = [] + for artist in json["artists"]: + track_artists.append(self.from_json(Artist, artist)) + del json["artists"] + + # Track + track = self.from_json(Track, json) + for artist in track_artists: + track.artists.add(artist) + track.album = album + track.save() + return track + + def create_from_spotify(self, spotify_link, profile): + if re.match(r"^https:\/\/spotify.link.*", spotify_link): + spotify_link = spt.unshort(spotify_link) + + m = re.match(r"^https:\/\/open.spotify.com\/track\/([a-zA-Z0-9]+)\??", spotify_link) + if m is None: + return None, Exception("Invalid Spotify track URL") + + spotify_id = m.group(1) + + try: + existing_track = self.model.all_tracks.get(pk=spotify_id) + if existing_track.old: + existing_track.old = False + existing_track.user = profile.user + existing_track.save() + return (existing_track, "Updated old track") + else: + return (None, "Track already exists") + except: + pass + try: + json = spt.get_song_info(spotify_id) + except Exception as e: + print(e) + return (None, "Error fetching track") + #return TemplateResponse(request, "Nominate.html", {"message_type": "error", "message": "Fout bij het ophalen van het liedje."}) + + track = self.track_from_json(json) + track.nominated_by = profile.user + track.save() + dup = track.is_duplicate_of() + if dup: + track.delete() + return (None, "Track already exists") + #get_banter.delay(track.id) + profile.quota -= 1 + profile.save() + return (track, None) + class Track(models.Model): def __str__(self): s = f"'{self.name}'" @@ -23,7 +103,7 @@ class Track(models.Model): if self.artists.count() > 0: artists = list(map(lambda a:a.name, self.artists.all())) return " & ".join(artists) - + @property def name_sanitized(self): return re.match("[^\-\(]*\w", self.name)[0] diff --git a/playlist/views/api/track.py b/playlist/views/api/track.py index d1be233..d4d534f 100644 --- a/playlist/views/api/track.py +++ b/playlist/views/api/track.py @@ -2,8 +2,10 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated -from playlist.models import Track +from playlist.models import Track, Profile from playlist.serializers import TrackSerializer +from playlist.spotify import spt +from playlist.tasks import get_banter, get_and_dither_image class TrackListView(APIView): authentication_classes = [SessionAuthentication] @@ -13,6 +15,22 @@ class TrackListView(APIView): serializer = TrackSerializer(tracks, many=True, context={'request': request}) return Response(serializer.data) + def post(self, request): + (profile, _) = Profile.objects.get_or_create(user=request.user) + (can_nom, reason) = profile.can_nominate + if not can_nom: + return Response({'error': reason}, status=403) + (track, message) = Track.objects.create_from_spotify(request.data['spotify_link'], profile) + serializer = TrackSerializer(track, context={'request': request}) + return Response(serializer.data) + + def delete(self, request): + if request.user.is_superuser: + Track.objects.all().delete() + return Response(status=204) + else: + return Response({'error': 'Only superusers can delete all tracks'}, status=403) + class TrackDetailView(APIView): authentication_classes = [SessionAuthentication] permission_classes = [IsAuthenticated]