2018-03-19 14:28:33 +00:00
|
|
|
from django.db import transaction
|
2017-06-23 21:00:42 +00:00
|
|
|
from rest_framework import serializers
|
|
|
|
|
2018-04-28 03:30:23 +00:00
|
|
|
from funkwhale_api.common import preferences
|
2019-09-09 09:10:25 +00:00
|
|
|
from funkwhale_api.federation import serializers as federation_serializers
|
2018-03-20 18:56:42 +00:00
|
|
|
from funkwhale_api.music.models import Track
|
2018-05-22 19:10:48 +00:00
|
|
|
from funkwhale_api.music.serializers import TrackSerializer
|
2018-03-20 18:56:42 +00:00
|
|
|
from funkwhale_api.users.serializers import UserBasicSerializer
|
2018-06-10 08:55:16 +00:00
|
|
|
|
2017-06-23 21:00:42 +00:00
|
|
|
from . import models
|
|
|
|
|
|
|
|
|
|
|
|
class PlaylistTrackSerializer(serializers.ModelSerializer):
|
2018-09-28 18:46:18 +00:00
|
|
|
# track = TrackSerializer()
|
|
|
|
track = serializers.SerializerMethodField()
|
2017-06-23 21:00:42 +00:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = models.PlaylistTrack
|
2018-09-28 18:46:18 +00:00
|
|
|
fields = ("id", "track", "playlist", "index", "creation_date")
|
2018-09-28 14:45:28 +00:00
|
|
|
|
2018-09-28 18:46:18 +00:00
|
|
|
def get_track(self, o):
|
|
|
|
track = o._prefetched_track if hasattr(o, "_prefetched_track") else o.track
|
|
|
|
return TrackSerializer(track).data
|
2017-06-23 21:00:42 +00:00
|
|
|
|
|
|
|
|
2018-03-19 14:28:33 +00:00
|
|
|
class PlaylistTrackWriteSerializer(serializers.ModelSerializer):
|
2018-06-09 13:36:16 +00:00
|
|
|
index = serializers.IntegerField(required=False, min_value=0, allow_null=True)
|
2019-04-24 09:31:46 +00:00
|
|
|
allow_duplicates = serializers.BooleanField(required=False)
|
2017-06-23 21:00:42 +00:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = models.PlaylistTrack
|
2019-04-24 09:31:46 +00:00
|
|
|
fields = ("id", "track", "playlist", "index", "allow_duplicates")
|
2017-06-23 21:00:42 +00:00
|
|
|
|
2018-03-19 11:36:15 +00:00
|
|
|
def validate_playlist(self, value):
|
2018-06-09 13:36:16 +00:00
|
|
|
if self.context.get("request"):
|
2018-03-19 14:28:33 +00:00
|
|
|
# validate proper ownership on the playlist
|
2018-06-09 13:36:16 +00:00
|
|
|
if self.context["request"].user != value.user:
|
2018-03-19 14:28:33 +00:00
|
|
|
raise serializers.ValidationError(
|
2018-06-09 13:36:16 +00:00
|
|
|
"You do not have the permission to edit this playlist"
|
|
|
|
)
|
2018-03-19 11:36:15 +00:00
|
|
|
existing = value.playlist_tracks.count()
|
2018-06-09 13:36:16 +00:00
|
|
|
max_tracks = preferences.get("playlists__max_tracks")
|
2018-04-28 03:30:23 +00:00
|
|
|
if existing >= max_tracks:
|
2018-03-19 11:36:15 +00:00
|
|
|
raise serializers.ValidationError(
|
2018-06-09 13:36:16 +00:00
|
|
|
"Playlist has reached the maximum of {} tracks".format(max_tracks)
|
|
|
|
)
|
2018-03-19 11:36:15 +00:00
|
|
|
return value
|
|
|
|
|
2018-03-19 14:28:33 +00:00
|
|
|
@transaction.atomic
|
|
|
|
def create(self, validated_data):
|
2018-06-09 13:36:16 +00:00
|
|
|
index = validated_data.pop("index", None)
|
2019-04-24 09:31:46 +00:00
|
|
|
allow_duplicates = validated_data.pop("allow_duplicates", True)
|
2018-03-19 14:28:33 +00:00
|
|
|
instance = super().create(validated_data)
|
2019-04-24 09:31:46 +00:00
|
|
|
|
|
|
|
instance.playlist.insert(instance, index, allow_duplicates)
|
2018-03-19 14:28:33 +00:00
|
|
|
return instance
|
|
|
|
|
|
|
|
@transaction.atomic
|
|
|
|
def update(self, instance, validated_data):
|
2018-06-09 13:36:16 +00:00
|
|
|
update_index = "index" in validated_data
|
|
|
|
index = validated_data.pop("index", None)
|
2019-04-24 09:31:46 +00:00
|
|
|
allow_duplicates = validated_data.pop("allow_duplicates", True)
|
2018-03-19 14:28:33 +00:00
|
|
|
super().update(instance, validated_data)
|
|
|
|
if update_index:
|
2019-04-24 09:31:46 +00:00
|
|
|
instance.playlist.insert(instance, index, allow_duplicates)
|
|
|
|
|
2018-03-19 14:28:33 +00:00
|
|
|
return instance
|
|
|
|
|
|
|
|
def get_unique_together_validators(self):
|
|
|
|
"""
|
|
|
|
We explicitely disable unique together validation here
|
|
|
|
because it collides with our internal logic
|
|
|
|
"""
|
|
|
|
return []
|
|
|
|
|
2017-06-23 21:00:42 +00:00
|
|
|
|
|
|
|
class PlaylistSerializer(serializers.ModelSerializer):
|
2018-03-21 18:03:57 +00:00
|
|
|
tracks_count = serializers.SerializerMethodField(read_only=True)
|
2018-07-17 11:09:13 +00:00
|
|
|
duration = serializers.SerializerMethodField(read_only=True)
|
|
|
|
album_covers = serializers.SerializerMethodField(read_only=True)
|
2018-03-21 18:03:57 +00:00
|
|
|
user = UserBasicSerializer(read_only=True)
|
2018-09-28 14:45:28 +00:00
|
|
|
is_playable = serializers.SerializerMethodField()
|
2019-09-09 09:10:25 +00:00
|
|
|
actor = serializers.SerializerMethodField()
|
2017-06-23 21:00:42 +00:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = models.Playlist
|
2018-03-19 18:07:45 +00:00
|
|
|
fields = (
|
2018-06-09 13:36:16 +00:00
|
|
|
"id",
|
|
|
|
"name",
|
|
|
|
"user",
|
|
|
|
"modification_date",
|
|
|
|
"creation_date",
|
|
|
|
"privacy_level",
|
2018-07-17 11:09:13 +00:00
|
|
|
"tracks_count",
|
|
|
|
"album_covers",
|
|
|
|
"duration",
|
2018-09-28 14:45:28 +00:00
|
|
|
"is_playable",
|
2019-09-09 09:10:25 +00:00
|
|
|
"actor",
|
2018-06-09 13:36:16 +00:00
|
|
|
)
|
|
|
|
read_only_fields = ["id", "modification_date", "creation_date"]
|
2018-03-19 18:07:45 +00:00
|
|
|
|
2019-09-09 09:10:25 +00:00
|
|
|
def get_actor(self, obj):
|
|
|
|
actor = obj.user.actor
|
|
|
|
if actor:
|
|
|
|
return federation_serializers.APIActorSerializer(actor).data
|
|
|
|
|
2018-09-28 14:45:28 +00:00
|
|
|
def get_is_playable(self, obj):
|
|
|
|
try:
|
2018-10-26 12:18:50 +00:00
|
|
|
return bool(obj.playable_plts)
|
2018-09-28 14:45:28 +00:00
|
|
|
except AttributeError:
|
|
|
|
return None
|
|
|
|
|
2018-03-19 18:07:45 +00:00
|
|
|
def get_tracks_count(self, obj):
|
|
|
|
try:
|
|
|
|
return obj.tracks_count
|
|
|
|
except AttributeError:
|
|
|
|
# no annotation?
|
|
|
|
return obj.playlist_tracks.count()
|
2018-03-20 18:56:42 +00:00
|
|
|
|
2018-07-17 11:09:13 +00:00
|
|
|
def get_duration(self, obj):
|
|
|
|
try:
|
|
|
|
return obj.duration
|
|
|
|
except AttributeError:
|
|
|
|
# no annotation?
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def get_album_covers(self, obj):
|
|
|
|
try:
|
|
|
|
plts = obj.plts_for_cover
|
|
|
|
except AttributeError:
|
|
|
|
return []
|
|
|
|
|
2019-02-25 09:36:02 +00:00
|
|
|
excluded_artists = []
|
2019-02-14 09:49:06 +00:00
|
|
|
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)
|
|
|
|
)
|
|
|
|
|
2018-07-17 11:09:13 +00:00
|
|
|
covers = []
|
|
|
|
max_covers = 5
|
|
|
|
for plt in plts:
|
2019-02-14 09:49:06 +00:00
|
|
|
if plt.track.album.artist_id in excluded_artists:
|
|
|
|
continue
|
2018-07-18 13:37:07 +00:00
|
|
|
url = plt.track.album.cover.crop["200x200"].url
|
2018-07-17 11:09:13 +00:00
|
|
|
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
|
|
|
|
|
2018-03-20 18:56:42 +00:00
|
|
|
|
|
|
|
class PlaylistAddManySerializer(serializers.Serializer):
|
|
|
|
tracks = serializers.PrimaryKeyRelatedField(
|
2018-06-09 13:36:16 +00:00
|
|
|
many=True, queryset=Track.objects.for_nested_serialization()
|
|
|
|
)
|
2019-04-24 09:31:46 +00:00
|
|
|
allow_duplicates = serializers.BooleanField(required=False)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
fields = "allow_duplicates"
|