173 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			173 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
| import logging
 | |
| 
 | |
| from django.core.exceptions import ObjectDoesNotExist, ValidationError
 | |
| from drf_spectacular.types import OpenApiTypes
 | |
| from drf_spectacular.utils import extend_schema_field
 | |
| from rest_framework import serializers
 | |
| 
 | |
| from funkwhale_api.federation.serializers import APIActorSerializer
 | |
| from funkwhale_api.music import tasks
 | |
| from funkwhale_api.music.models import Album, Artist, Track
 | |
| from funkwhale_api.music.serializers import TrackSerializer
 | |
| 
 | |
| from . import models
 | |
| 
 | |
| logger = logging.getLogger(__name__)
 | |
| 
 | |
| 
 | |
| class PlaylistTrackSerializer(serializers.ModelSerializer):
 | |
|     # track = TrackSerializer()
 | |
|     track = serializers.SerializerMethodField()
 | |
| 
 | |
|     class Meta:
 | |
|         model = models.PlaylistTrack
 | |
|         fields = ("track", "index", "creation_date")
 | |
| 
 | |
|     def get_track(self, o):
 | |
|         track = o._prefetched_track if hasattr(o, "_prefetched_track") else o.track
 | |
|         return TrackSerializer(track).data
 | |
| 
 | |
| 
 | |
| class PlaylistSerializer(serializers.ModelSerializer):
 | |
|     tracks_count = serializers.SerializerMethodField(read_only=True)
 | |
|     duration = serializers.SerializerMethodField(read_only=True)
 | |
|     album_covers = serializers.SerializerMethodField(read_only=True)
 | |
|     is_playable = serializers.SerializerMethodField()
 | |
|     actor = APIActorSerializer(read_only=True)
 | |
| 
 | |
|     class Meta:
 | |
|         model = models.Playlist
 | |
|         fields = (
 | |
|             "id",
 | |
|             "name",
 | |
|             "actor",
 | |
|             "modification_date",
 | |
|             "creation_date",
 | |
|             "privacy_level",
 | |
|             "tracks_count",
 | |
|             "album_covers",
 | |
|             "duration",
 | |
|             "is_playable",
 | |
|             "actor",
 | |
|         )
 | |
|         read_only_fields = ["id", "modification_date", "creation_date"]
 | |
| 
 | |
|     @extend_schema_field(OpenApiTypes.BOOL)
 | |
|     def get_is_playable(self, obj):
 | |
|         return getattr(obj, "is_playable_by_actor", False)
 | |
| 
 | |
|     def get_tracks_count(self, obj) -> int:
 | |
|         return getattr(obj, "tracks_count", obj.playlist_tracks.count())
 | |
| 
 | |
|     def get_duration(self, obj) -> int:
 | |
|         try:
 | |
|             return obj.duration
 | |
|         except AttributeError:
 | |
|             # no annotation?
 | |
|             return 0
 | |
| 
 | |
|     @extend_schema_field({"type": "array", "items": {"type": "string"}})
 | |
|     def get_album_covers(self, obj):
 | |
|         try:
 | |
|             plts = obj.plts_for_cover
 | |
|         except AttributeError:
 | |
|             return []
 | |
| 
 | |
|         excluded_artists = []
 | |
|         try:
 | |
|             user = self.context["request"].user
 | |
|         except (KeyError, AttributeError):
 | |
|             user = None
 | |
|         if user and user.is_authenticated:
 | |
|             excluded_artists = list(
 | |
|                 user.content_filters.values_list("target_artist", flat=True)
 | |
|             )
 | |
| 
 | |
|         covers = []
 | |
|         max_covers = 5
 | |
|         for plt in plts:
 | |
|             if [
 | |
|                 ac.artist.pk for ac in plt.track.album.artist_credit.all()
 | |
|             ] in excluded_artists:
 | |
|                 continue
 | |
|             url = plt.track.album.attachment_cover.download_url_medium_square_crop
 | |
|             if url in covers:
 | |
|                 continue
 | |
|             covers.append(url)
 | |
|             if len(covers) >= max_covers:
 | |
|                 break
 | |
| 
 | |
|         full_urls = []
 | |
|         for url in covers:
 | |
|             if "request" in self.context:
 | |
|                 url = self.context["request"].build_absolute_uri(url)
 | |
|             full_urls.append(url)
 | |
|         return full_urls
 | |
| 
 | |
| 
 | |
| class PlaylistAddManySerializer(serializers.Serializer):
 | |
|     tracks = serializers.PrimaryKeyRelatedField(
 | |
|         many=True, queryset=Track.objects.for_nested_serialization()
 | |
|     )
 | |
|     allow_duplicates = serializers.BooleanField(required=False)
 | |
| 
 | |
|     class Meta:
 | |
|         fields = "allow_duplicates"
 | |
| 
 | |
| 
 | |
| class XspfTrackSerializer(serializers.Serializer):
 | |
|     location = serializers.CharField(allow_blank=True, required=False)
 | |
|     title = serializers.CharField()
 | |
|     creator = serializers.CharField()
 | |
|     album = serializers.CharField(allow_blank=True, required=False)
 | |
|     duration = serializers.CharField(allow_blank=True, required=False)
 | |
| 
 | |
|     def validate(self, data):
 | |
|         title = data["title"]
 | |
|         album = data.get("album", None)
 | |
|         acs_tuples = tasks.parse_credits(data["creator"], "", 0)
 | |
|         try:
 | |
|             artist_id = Artist.objects.get(name=acs_tuples[0][0])
 | |
|         except ObjectDoesNotExist:
 | |
|             raise ValidationError("Couldn't find artist in the database")
 | |
|         if album:
 | |
|             try:
 | |
|                 album_id = Album.objects.get(title=album)
 | |
|                 fw_track = Track.objects.get(
 | |
|                     title=title, artist_credit__artist=artist_id, album=album_id
 | |
|                 )
 | |
|             except ObjectDoesNotExist:
 | |
|                 pass
 | |
|         try:
 | |
|             fw_track = Track.objects.get(title=title, artist_credit__artist=artist_id)
 | |
|         except ObjectDoesNotExist as e:
 | |
|             raise ValidationError(f"Couldn't find track in the database : {e!r}")
 | |
| 
 | |
|         super().validate(data)
 | |
|         return fw_track
 | |
| 
 | |
| 
 | |
| class XspfSerializer(serializers.Serializer):
 | |
|     title = serializers.CharField()
 | |
|     creator = serializers.CharField(allow_blank=True, required=False)
 | |
|     creation_date = serializers.DateTimeField(required=False)
 | |
|     version = serializers.IntegerField(required=False)
 | |
|     tracks = XspfTrackSerializer(many=True, required=False)
 | |
| 
 | |
|     def create(self, validated_data):
 | |
|         pl = models.Playlist.objects.create(
 | |
|             name=validated_data["title"],
 | |
|             privacy_level="private",
 | |
|             actor=validated_data["request"].user.actor,
 | |
|         )
 | |
|         pl.insert_many(validated_data["tracks"])
 | |
| 
 | |
|         return pl
 | |
| 
 | |
|     def update(self, instance, validated_data):
 | |
|         instance.name = validated_data["title"]
 | |
|         instance.playlist_tracks.all().delete()
 | |
|         instance.insert_many(validated_data["tracks"])
 | |
|         instance.save()
 | |
|         return instance
 |