mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-04 20:49:27 +00:00
serialize playlists, add custom playlist endpoints
This commit is contained in:
@@ -49,3 +49,43 @@ class PlaylistListQuerySerializer(serializers.Serializer):
|
|||||||
type = serializers.ChoiceField(
|
type = serializers.ChoiceField(
|
||||||
choices=["regular", "custom"], required=False
|
choices=["regular", "custom"], required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistSingleAddSerializer(serializers.Serializer):
|
||||||
|
"""single item to add"""
|
||||||
|
|
||||||
|
playlist_id = serializers.CharField()
|
||||||
|
playlist_subscribed = serializers.ChoiceField(choices=[True])
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistBulkAddSerializer(serializers.Serializer):
|
||||||
|
"""bulk add playlists serializers"""
|
||||||
|
|
||||||
|
data = PlaylistSingleAddSerializer(many=True)
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistSingleUpdate(serializers.Serializer):
|
||||||
|
"""update state of single playlist"""
|
||||||
|
|
||||||
|
playlist_subscribed = serializers.BooleanField()
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistListCustomPostSerializer(serializers.Serializer):
|
||||||
|
"""serialize list post custom playlist"""
|
||||||
|
|
||||||
|
playlist_name = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistCustomPostSerializer(serializers.Serializer):
|
||||||
|
"""serialize playlist custom action"""
|
||||||
|
|
||||||
|
action = serializers.ChoiceField(
|
||||||
|
choices=["create", "remove", "up", "down", "top", "bottom"]
|
||||||
|
)
|
||||||
|
video_id = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistDeleteQuerySerializer(serializers.Serializer):
|
||||||
|
"""serialize playlist delete query params"""
|
||||||
|
|
||||||
|
delete_videos = serializers.BooleanField(required=False)
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ class QueryBuilder:
|
|||||||
|
|
||||||
subscribed = self.request_params.get("subscribed")
|
subscribed = self.request_params.get("subscribed")
|
||||||
if subscribed:
|
if subscribed:
|
||||||
subed_bool = subscribed[0] == "true"
|
must_list.append({"match": {"playlist_subscribed": subscribed}})
|
||||||
must_list.append({"match": {"playlist_subscribed": subed_bool}})
|
|
||||||
|
|
||||||
playlist_type = self.request_params.get("type")
|
playlist_type = self.request_params.get("type")
|
||||||
if playlist_type:
|
if playlist_type:
|
||||||
|
|||||||
@@ -9,6 +9,16 @@ urlpatterns = [
|
|||||||
views.PlaylistApiListView.as_view(),
|
views.PlaylistApiListView.as_view(),
|
||||||
name="api-playlist-list",
|
name="api-playlist-list",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"custom/",
|
||||||
|
views.PlaylistCustomApiListView.as_view(),
|
||||||
|
name="api-custom-playlist-list",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"custom/<slug:playlist_id>/",
|
||||||
|
views.PlaylistCustomApiView.as_view(),
|
||||||
|
name="api-custom-playlist",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"<slug:playlist_id>/",
|
"<slug:playlist_id>/",
|
||||||
views.PlaylistApiView.as_view(),
|
views.PlaylistApiView.as_view(),
|
||||||
|
|||||||
@@ -2,18 +2,25 @@
|
|||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from common.serializers import ErrorResponseSerializer
|
from common.serializers import (
|
||||||
|
AsyncTaskResponseSerializer,
|
||||||
|
ErrorResponseSerializer,
|
||||||
|
)
|
||||||
from common.views_base import AdminWriteOnly, ApiBaseView
|
from common.views_base import AdminWriteOnly, ApiBaseView
|
||||||
from download.src.subscriptions import PlaylistSubscription
|
from download.src.subscriptions import PlaylistSubscription
|
||||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||||
from playlist.serializers import (
|
from playlist.serializers import (
|
||||||
|
PlaylistBulkAddSerializer,
|
||||||
|
PlaylistCustomPostSerializer,
|
||||||
|
PlaylistDeleteQuerySerializer,
|
||||||
|
PlaylistListCustomPostSerializer,
|
||||||
PlaylistListQuerySerializer,
|
PlaylistListQuerySerializer,
|
||||||
PlaylistListSerializer,
|
PlaylistListSerializer,
|
||||||
PlaylistSerializer,
|
PlaylistSerializer,
|
||||||
|
PlaylistSingleUpdate,
|
||||||
)
|
)
|
||||||
from playlist.src.index import YoutubePlaylist
|
from playlist.src.index import YoutubePlaylist
|
||||||
from playlist.src.query_building import QueryBuilder
|
from playlist.src.query_building import QueryBuilder
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from task.tasks import subscribe_to
|
from task.tasks import subscribe_to
|
||||||
from user.src.user_config import UserConfig
|
from user.src.user_config import UserConfig
|
||||||
@@ -61,46 +68,127 @@ class PlaylistApiListView(ApiBaseView):
|
|||||||
|
|
||||||
return Response(response_serializer.data)
|
return Response(response_serializer.data)
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
request=PlaylistBulkAddSerializer(),
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(AsyncTaskResponseSerializer()),
|
||||||
|
400: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="Bad request"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""subscribe/unsubscribe to list of playlists"""
|
"""async subscribe to list of playlists"""
|
||||||
data = request.data
|
data_serializer = PlaylistBulkAddSerializer(data=request.data)
|
||||||
try:
|
data_serializer.is_valid(raise_exception=True)
|
||||||
to_add = data["data"]
|
validated_data = data_serializer.validated_data
|
||||||
except KeyError:
|
|
||||||
message = "missing expected data key"
|
|
||||||
print(message)
|
|
||||||
return Response({"message": message}, status=400)
|
|
||||||
|
|
||||||
data = data["data"]
|
pending = [i["playlist_id"] for i in validated_data["data"]]
|
||||||
if isinstance(data, dict):
|
if not pending:
|
||||||
custom_name = data.get("create")
|
error = ErrorResponseSerializer({"error": "nothing to subscribe"})
|
||||||
if custom_name:
|
return Response(error.data, status=400)
|
||||||
playlist_id = f"TA_playlist_{uuid.uuid4()}"
|
|
||||||
custom_playlist = YoutubePlaylist(playlist_id)
|
|
||||||
custom_playlist.create(custom_name)
|
|
||||||
return Response(custom_playlist.json_data)
|
|
||||||
|
|
||||||
pending = []
|
url_str = " ".join(pending)
|
||||||
for playlist_item in to_add:
|
task = subscribe_to.delay(url_str, expected_type="playlist")
|
||||||
playlist_id = playlist_item["playlist_id"]
|
|
||||||
if playlist_item["playlist_subscribed"]:
|
|
||||||
pending.append(playlist_id)
|
|
||||||
else:
|
|
||||||
self._unsubscribe(playlist_id)
|
|
||||||
|
|
||||||
if pending:
|
message = {
|
||||||
url_str = " ".join(pending)
|
"message": "playlist subscribe task started",
|
||||||
subscribe_to.delay(url_str, expected_type="playlist")
|
"task_id": task.id,
|
||||||
|
}
|
||||||
|
serializer = AsyncTaskResponseSerializer(message)
|
||||||
|
|
||||||
return Response(data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _unsubscribe(playlist_id: str):
|
class PlaylistCustomApiListView(ApiBaseView):
|
||||||
"""unsubscribe"""
|
"""resolves to /api/playlist/custom/
|
||||||
print(f"[{playlist_id}] unsubscribe from playlist")
|
POST: Create new custom playlist
|
||||||
_ = PlaylistSubscription().change_subscribe(
|
"""
|
||||||
playlist_id, subscribe_status=False
|
|
||||||
)
|
search_base = "ta_playlist/_search/"
|
||||||
|
permission_classes = [AdminWriteOnly]
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
request=PlaylistListCustomPostSerializer(),
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(PlaylistSerializer()),
|
||||||
|
400: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="Bad request"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
def post(self, request):
|
||||||
|
"""create new custom playlist"""
|
||||||
|
serializer = PlaylistListCustomPostSerializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
validated_data = serializer.validated_data
|
||||||
|
|
||||||
|
custom_name = validated_data["playlist_name"]
|
||||||
|
playlist_id = f"TA_playlist_{uuid.uuid4()}"
|
||||||
|
custom_playlist = YoutubePlaylist(playlist_id)
|
||||||
|
custom_playlist.create(custom_name)
|
||||||
|
|
||||||
|
response_serializer = PlaylistSerializer(custom_playlist.json_data)
|
||||||
|
|
||||||
|
return Response(response_serializer.data)
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistCustomApiView(ApiBaseView):
|
||||||
|
"""resolves to /api/playlist/custom/<playlist_id>/
|
||||||
|
POST: modify custom playlist
|
||||||
|
"""
|
||||||
|
|
||||||
|
search_base = "ta_playlist/_doc/"
|
||||||
|
permission_classes = [AdminWriteOnly]
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
request=PlaylistCustomPostSerializer(),
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(PlaylistSerializer()),
|
||||||
|
400: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="bad request"
|
||||||
|
),
|
||||||
|
404: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="playlist not found"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
def post(self, request, playlist_id):
|
||||||
|
"""modify custom playlist"""
|
||||||
|
data_serializer = PlaylistCustomPostSerializer(data=request.data)
|
||||||
|
data_serializer.is_valid(raise_exception=True)
|
||||||
|
validated_data = data_serializer.validated_data
|
||||||
|
|
||||||
|
self.get_document(playlist_id)
|
||||||
|
if not self.response:
|
||||||
|
error = ErrorResponseSerializer({"error": "playlist not found"})
|
||||||
|
return Response(error.data, status=404)
|
||||||
|
|
||||||
|
if not self.response["playlist_type"] == "custom":
|
||||||
|
error = ErrorResponseSerializer(
|
||||||
|
{"error": f"playlist with ID {playlist_id} is not custom"}
|
||||||
|
)
|
||||||
|
return Response(error.data, status=400)
|
||||||
|
|
||||||
|
action = validated_data.get("action")
|
||||||
|
video_id = validated_data.get("video_id")
|
||||||
|
|
||||||
|
playlist = YoutubePlaylist(playlist_id)
|
||||||
|
if action == "create":
|
||||||
|
try:
|
||||||
|
playlist.add_video_to_playlist(video_id)
|
||||||
|
except TypeError:
|
||||||
|
error = ErrorResponseSerializer(
|
||||||
|
{"error": f"failed to add video {video_id} to playlist"}
|
||||||
|
)
|
||||||
|
return Response(error.data, status=400)
|
||||||
|
else:
|
||||||
|
hide = UserConfig(request.user.id).get_value("hide_watched")
|
||||||
|
playlist.move_video(video_id, action, hide_watched=hide)
|
||||||
|
|
||||||
|
response_serializer = PlaylistSerializer(playlist.json_data)
|
||||||
|
|
||||||
|
return Response(response_serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class PlaylistApiView(ApiBaseView):
|
class PlaylistApiView(ApiBaseView):
|
||||||
@@ -132,45 +220,54 @@ class PlaylistApiView(ApiBaseView):
|
|||||||
|
|
||||||
return Response(response_serializer.data)
|
return Response(response_serializer.data)
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
request=PlaylistSingleUpdate(),
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(PlaylistSerializer()),
|
||||||
|
404: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="playlist not found"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
def post(self, request, playlist_id):
|
def post(self, request, playlist_id):
|
||||||
"""post to custom playlist to add a video to list"""
|
"""update subscribed state of playlist"""
|
||||||
|
data_serializer = PlaylistSingleUpdate(data=request.data)
|
||||||
|
data_serializer.is_valid(raise_exception=True)
|
||||||
|
validated_data = data_serializer.validated_data
|
||||||
|
|
||||||
self.get_document(playlist_id)
|
self.get_document(playlist_id)
|
||||||
if not self.response["data"]:
|
if not self.response:
|
||||||
return Response({"error": "playlist not found"}, status=404)
|
error = ErrorResponseSerializer({"error": "playlist not found"})
|
||||||
|
return Response(error.data, status=404)
|
||||||
|
|
||||||
data = request.data
|
subscribed = validated_data["playlist_subscribed"]
|
||||||
subscribed = data.get("playlist_subscribed")
|
playlist_sub = PlaylistSubscription()
|
||||||
if subscribed is not None:
|
json_data = playlist_sub.change_subscribe(playlist_id, subscribed)
|
||||||
playlist_sub = PlaylistSubscription()
|
|
||||||
json_data = playlist_sub.change_subscribe(playlist_id, subscribed)
|
|
||||||
return Response(json_data, status=200)
|
|
||||||
|
|
||||||
if not self.response["data"]["playlist_type"] == "custom":
|
response_serializer = PlaylistSerializer(json_data)
|
||||||
message = f"playlist with ID {playlist_id} is not custom"
|
return Response(response_serializer.data)
|
||||||
return Response({"message": message}, status=400)
|
|
||||||
|
|
||||||
action = request.data.get("action")
|
|
||||||
if action not in self.valid_custom_actions:
|
|
||||||
message = f"invalid action: {action}"
|
|
||||||
return Response({"message": message}, status=400)
|
|
||||||
|
|
||||||
playlist = YoutubePlaylist(playlist_id)
|
|
||||||
video_id = request.data.get("video_id")
|
|
||||||
if action == "create":
|
|
||||||
playlist.add_video_to_playlist(video_id)
|
|
||||||
else:
|
|
||||||
hide = UserConfig(request.user.id).get_value("hide_watched")
|
|
||||||
playlist.move_video(video_id, action, hide_watched=hide)
|
|
||||||
|
|
||||||
return Response({"success": True}, status=status.HTTP_201_CREATED)
|
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
parameters=[PlaylistDeleteQuerySerializer],
|
||||||
|
responses={
|
||||||
|
204: OpenApiResponse(description="playlist deleted"),
|
||||||
|
},
|
||||||
|
)
|
||||||
def delete(self, request, playlist_id):
|
def delete(self, request, playlist_id):
|
||||||
"""delete playlist"""
|
"""delete playlist"""
|
||||||
print(f"{playlist_id}: delete playlist")
|
print(f"{playlist_id}: delete playlist")
|
||||||
delete_videos = request.GET.get("delete-videos", False)
|
|
||||||
|
query_serializer = PlaylistDeleteQuerySerializer(
|
||||||
|
data=request.query_params
|
||||||
|
)
|
||||||
|
query_serializer.is_valid(raise_exception=True)
|
||||||
|
validated_query = query_serializer.validated_data
|
||||||
|
|
||||||
|
delete_videos = validated_query.get("delete_videos", False)
|
||||||
|
|
||||||
if delete_videos:
|
if delete_videos:
|
||||||
YoutubePlaylist(playlist_id).delete_videos_playlist()
|
YoutubePlaylist(playlist_id).delete_videos_playlist()
|
||||||
else:
|
else:
|
||||||
YoutubePlaylist(playlist_id).delete_metadata()
|
YoutubePlaylist(playlist_id).delete_metadata()
|
||||||
|
|
||||||
return Response({"success": True})
|
return Response(status=204)
|
||||||
|
|||||||
Reference in New Issue
Block a user