mirror of
https://code.eliotberriot.com/funkwhale/funkwhale.git
synced 2025-10-06 02:09:55 +02:00
Updated playlist management API
This commit is contained in:
parent
a1865cf9d8
commit
f6458fd75a
10 changed files with 157 additions and 247 deletions
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue