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)