Files
MarietjeDjango/marietje/queues/api/v1/views.py
Kees van Kempen 669e1e3b18 fix(queues): now really fix the request notifications
Instead of checking for success, now the correct HTTP response code is
sent by the request API. My previous fix broke the other responses, oops.

Closes #82

Relates #44

The refresh as introduced in my fix for #82 (!89) is reverted, as this
was already done on successful requests.
2024-03-05 10:48:21 +01:00

263 lines
10 KiB
Python

from rest_framework.generics import ListAPIView, RetrieveAPIView, get_object_or_404, CreateAPIView, DestroyAPIView
from rest_framework.views import APIView
from rest_framework.response import Response
from marietje.api.openapi import CustomAutoSchema
from marietje.api.permissions import IsAuthenticatedOrTokenHasScopeForMethod
from django.http import Http404
from queues.api.v1.serializers import PlaylistSerializer, QueueSerializer, PlaylistSongSerializer
from queues.exceptions import RequestException
from queues.models import Playlist, PlaylistSong, QueueCommand
from queues.services import get_user_or_default_queue
from songs.counters import request_counter
from songs.models import Song
class PlaylistListAPIView(ListAPIView):
serializer_class = PlaylistSerializer
queryset = Playlist.objects.all()
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {"GET": ["read"]}
class PlaylistRetrieveAPIView(RetrieveAPIView):
serializer_class = PlaylistSerializer
queryset = Playlist.objects.all()
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {"GET": ["read"]}
class QueueAPIView(APIView):
serializer_class = QueueSerializer
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {"GET": ["read"]}
schema = CustomAutoSchema(
response_schema={
"type": "object",
"properties": {
"id": {"type": "int", "example": 1},
"name": {"type": "string", "example": "string"},
"playlist": {"type": "int", "example": 1},
"random_playlist": {"type": "int", "example": 1},
"current_song": {"$ref": "#/components/schemas/PlaylistSong"},
"queue": {"type": "array", "items": {"$ref": "#/components/schemas/PlaylistSong"}},
"started_at": {"type": "string", "format": "date-time", "nullable": True},
},
}
)
def get(self, request, **kwargs):
queue = get_user_or_default_queue(request)
if queue is None:
raise Http404()
return Response(status=200, data=self.serializer_class(queue).data)
class QueueSkipAPIView(APIView):
serializer_class = QueueSerializer
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {
"POST": ["write"],
}
schema = CustomAutoSchema(
response_schema={
"type": "object",
"properties": {
"id": {"type": "int", "example": 1},
"name": {"type": "string", "example": "string"},
"playlist": {"type": "int", "example": 1},
"random_playlist": {"type": "int", "example": 1},
"current_song": {"$ref": "#/components/schemas/PlaylistSong"},
"queue": {"type": "array", "items": {"$ref": "#/components/schemas/PlaylistSong"}},
"started_at": {"type": "string", "format": "date-time", "nullable": True},
},
}
)
def post(self, request, **kwargs):
queue = get_user_or_default_queue(request)
if queue is None:
return Response(status=404)
playlist_song = request.user.queue.current_song()
if (
request.user is not None
and playlist_song.user != request.user
and not request.user.has_perm("queues.can_skip")
):
return Response(status=403)
playlist_song.state = 2
playlist_song.save()
return Response(status=200, data=QueueSerializer(queue).data)
class PlaylistSongMoveDownAPIView(APIView):
serializer_class = PlaylistSongSerializer
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {
"PATCH": ["write"],
}
schema = CustomAutoSchema(response_schema={"$ref": "#/components/schemas/PlaylistSong"})
def patch(self, request, **kwargs):
playlist_song_id = kwargs.get("id")
playlist_song = get_object_or_404(PlaylistSong, id=playlist_song_id)
if (
request.user is not None
and playlist_song.user != request.user
and not request.user.has_perm("queues.can_move")
):
return Response(status=403)
playlist_song.move_down()
return Response(status=200, data=self.serializer_class(playlist_song).data)
class PlaylistSongCancelAPIView(DestroyAPIView):
serializer_class = PlaylistSongSerializer
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {
"DELETE": ["write"],
}
def delete(self, request, **kwargs):
playlist_song_id = kwargs.get("id")
playlist_song = get_object_or_404(PlaylistSong, id=playlist_song_id)
if (
request.user is not None
and playlist_song.user != request.user
and not request.user.has_perm("queues.can_cancel")
):
return Response(status=403)
playlist_song.delete()
return Response(status=200, data=self.serializer_class(playlist_song).data)
class QueueRequestAPIView(CreateAPIView):
serializer_class = PlaylistSongSerializer
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {
"POST": ["write"],
}
schema = CustomAutoSchema(
request_schema={
"type": "object",
"properties": {
"song": {"type": "int", "example": 1},
},
}
)
def post(self, request, **kwargs):
queue = get_user_or_default_queue(request)
if queue is None:
return Response(status=404)
song_id = request.data.get("song", None)
if song_id is None:
return Response(status=400, data={"success": False, "errorMessage": "Song ID not set."})
song = get_object_or_404(Song, id=song_id, deleted=False)
try:
playlist_song = queue.request(song, request.user)
except RequestException as e:
return Response(status=403, data={"success": False, "errorMessage": str(e)})
request_counter.labels(queue=queue.name).inc()
return Response(status=200, data=self.serializer_class(playlist_song).data)
class QueueVolumeDownAPIView(APIView):
serializer_class = QueueSerializer
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {
"POST": ["write"],
}
schema = CustomAutoSchema(
response_schema={
"type": "object",
"properties": {
"id": {"type": "int", "example": 1},
"name": {"type": "string", "example": "string"},
"playlist": {"type": "int", "example": 1},
"random_playlist": {"type": "int", "example": 1},
"current_song": {"$ref": "#/components/schemas/PlaylistSong"},
"queue": {"type": "array", "items": {"$ref": "#/components/schemas/PlaylistSong"}},
"started_at": {"type": "string", "format": "date-time", "nullable": True},
},
}
)
def post(self, request, **kwargs):
queue = get_user_or_default_queue(request)
if queue is None:
return Response(status=404)
if request.user is not None and not request.user.has_perm("queues.can_control_volume"):
return Response(status=403)
QueueCommand.objects.create(queue=queue, command="volume_down")
return Response(status=200, data=self.serializer_class(queue).data)
class QueueVolumeUpAPIView(APIView):
serializer_class = QueueSerializer
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {
"POST": ["write"],
}
schema = CustomAutoSchema(
response_schema={
"type": "object",
"properties": {
"id": {"type": "int", "example": 1},
"name": {"type": "string", "example": "string"},
"playlist": {"type": "int", "example": 1},
"random_playlist": {"type": "int", "example": 1},
"current_song": {"$ref": "#/components/schemas/PlaylistSong"},
"queue": {"type": "array", "items": {"$ref": "#/components/schemas/PlaylistSong"}},
"started_at": {"type": "string", "format": "date-time", "nullable": True},
},
}
)
def post(self, request, **kwargs):
queue = get_user_or_default_queue(request)
if queue is None:
return Response(status=404)
if request.user is not None and not request.user.has_perm("queues.can_control_volume"):
return Response(status=403)
QueueCommand.objects.create(queue=queue, command="volume_up")
return Response(status=200, data=self.serializer_class(queue).data)
class QueueMuteAPIView(APIView):
serializer_class = QueueSerializer
permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
required_scopes_for_method = {
"POST": ["write"],
}
schema = CustomAutoSchema(
response_schema={
"type": "object",
"properties": {
"id": {"type": "int", "example": 1},
"name": {"type": "string", "example": "string"},
"playlist": {"type": "int", "example": 1},
"random_playlist": {"type": "int", "example": 1},
"current_song": {"$ref": "#/components/schemas/PlaylistSong"},
"queue": {"type": "array", "items": {"$ref": "#/components/schemas/PlaylistSong"}},
"started_at": {"type": "string", "format": "date-time", "nullable": True},
},
}
)
def post(self, request, **kwargs):
queue = get_user_or_default_queue(request)
if queue is None:
return Response(status=404)
if request.user is not None and not request.user.has_perm("queues.can_control_volume"):
return Response(status=403)
QueueCommand.objects.create(queue=queue, command="mute")
return Response(status=200, data=self.serializer_class(queue).data)