Files
MarietjeDjango/marietje/queues/api/v1/views.py
2023-11-24 22:35:16 +01:00

301 lines
11 KiB
Python

from django.db.models import Q
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, Queue
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 = 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()
queue.log_action(request.user, "next", "Skipped to next song.")
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()
for queue in Queue.objects.filter(
Q(playlist=playlist_song.playlist) | Q(random_playlist=playlist_song.playlist)
):
queue.log_action(
request.user,
"down",
'Moved song "{}" of playlist "{}" down.'.format(playlist_song.song, playlist_song.playlist),
)
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()
for queue in Queue.objects.filter(
Q(playlist=playlist_song.playlist) | Q(random_playlist=playlist_song.playlist)
):
queue.log_action(
request.user,
"cancel",
'Cancelled song "{}" of playlist "{}".'.format(playlist_song.song, playlist_song.playlist),
)
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(data={"success": False, "errorMessage": str(e)})
queue.log_action(request.user, "request_song", "Requested song {}.".format(song))
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")
queue.log_action(request.user, "volume_down", "Reduced the volume of {}.".format(queue))
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")
queue.log_action(request.user, "volume_up", "Increased the volume of {}.".format(queue))
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")
queue.log_action(request.user, "mute", "Muted the volume of {}.".format(queue))
return Response(status=200, data=self.serializer_class(queue).data)