Updated playlist management API

This commit is contained in:
Agate 2020-07-27 15:31:49 +02:00
parent a1865cf9d8
commit f6458fd75a
10 changed files with 157 additions and 247 deletions

View file

@ -203,6 +203,15 @@ class PlaylistTrackQuerySet(models.QuerySet):
else:
return self.exclude(track__pk__in=tracks).distinct()
def by_index(self, index):
plts = self.order_by("index").values_list("id", flat=True)
try:
plt_id = plts[index]
except IndexError:
raise PlaylistTrack.DoesNotExist
return PlaylistTrack.objects.get(pk=plt_id)
class PlaylistTrack(models.Model):
track = models.ForeignKey(

View file

@ -1,7 +1,5 @@
from django.db import transaction
from rest_framework import serializers
from funkwhale_api.common import preferences
from funkwhale_api.federation import serializers as federation_serializers
from funkwhale_api.music.models import Track
from funkwhale_api.music.serializers import TrackSerializer
@ -16,64 +14,13 @@ class PlaylistTrackSerializer(serializers.ModelSerializer):
class Meta:
model = models.PlaylistTrack
fields = ("id", "track", "playlist", "index", "creation_date")
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 PlaylistTrackWriteSerializer(serializers.ModelSerializer):
index = serializers.IntegerField(required=False, min_value=0, allow_null=True)
allow_duplicates = serializers.BooleanField(required=False)
class Meta:
model = models.PlaylistTrack
fields = ("id", "track", "playlist", "index", "allow_duplicates")
def validate_playlist(self, value):
if self.context.get("request"):
# validate proper ownership on the playlist
if self.context["request"].user != value.user:
raise serializers.ValidationError(
"You do not have the permission to edit this playlist"
)
existing = value.playlist_tracks.count()
max_tracks = preferences.get("playlists__max_tracks")
if existing >= max_tracks:
raise serializers.ValidationError(
"Playlist has reached the maximum of {} tracks".format(max_tracks)
)
return value
@transaction.atomic
def create(self, validated_data):
index = validated_data.pop("index", None)
allow_duplicates = validated_data.pop("allow_duplicates", True)
instance = super().create(validated_data)
instance.playlist.insert(instance, index, allow_duplicates)
return instance
@transaction.atomic
def update(self, instance, validated_data):
update_index = "index" in validated_data
index = validated_data.pop("index", None)
allow_duplicates = validated_data.pop("allow_duplicates", True)
super().update(instance, validated_data)
if update_index:
instance.playlist.insert(instance, index, allow_duplicates)
return instance
def get_unique_together_validators(self):
"""
We explicitely disable unique together validation here
because it collides with our internal logic
"""
return []
class PlaylistSerializer(serializers.ModelSerializer):
tracks_count = serializers.SerializerMethodField(read_only=True)
duration = serializers.SerializerMethodField(read_only=True)

View file

@ -93,40 +93,43 @@ class PlaylistViewSet(
),
)
@action(methods=["post", "delete"], detail=True)
@transaction.atomic
def remove(self, request, *args, **kwargs):
playlist = self.get_object()
try:
index = int(request.data["index"])
assert index >= 0
except (KeyError, ValueError, AssertionError, TypeError):
return Response(status=400)
class PlaylistTrackViewSet(
mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
try:
plt = playlist.playlist_tracks.by_index(index)
except models.PlaylistTrack.DoesNotExist:
return Response(status=404)
plt.delete(update_indexes=True)
serializer_class = serializers.PlaylistTrackSerializer
queryset = models.PlaylistTrack.objects.all()
permission_classes = [
oauth_permissions.ScopePermission,
permissions.OwnerPermission,
]
required_scope = "playlists"
anonymous_policy = "setting"
owner_field = "playlist.user"
owner_checks = ["write"]
return Response(status=204)
def get_serializer_class(self):
if self.request.method in ["PUT", "PATCH", "DELETE", "POST"]:
return serializers.PlaylistTrackWriteSerializer
return self.serializer_class
@action(methods=["post"], detail=True)
@transaction.atomic
def move(self, request, *args, **kwargs):
playlist = self.get_object()
try:
from_index = int(request.data["from"])
assert from_index >= 0
except (KeyError, ValueError, AssertionError, TypeError):
return Response({"detail": "invalid from index"}, status=400)
def get_queryset(self):
return self.queryset.filter(
fields.privacy_level_query(
self.request.user,
lookup_field="playlist__privacy_level",
user_field="playlist__user",
)
).for_nested_serialization(music_utils.get_actor_from_request(self.request))
try:
to_index = int(request.data["to"])
assert to_index >= 0
except (KeyError, ValueError, AssertionError, TypeError):
return Response({"detail": "invalid to index"}, status=400)
def perform_destroy(self, instance):
instance.delete(update_indexes=True)
try:
plt = playlist.playlist_tracks.by_index(from_index)
except models.PlaylistTrack.DoesNotExist:
return Response(status=404)
playlist.insert(plt, to_index)
return Response(status=204)