From 7af9ba364b432059a04d221d0971e5448dc09fb1 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 18 Jul 2024 22:53:25 +0200 Subject: [PATCH] split playlist app --- tubearchivist/api/urls.py | 15 -- tubearchivist/api/views.py | 134 +---------------- tubearchivist/channel/src/index.py | 2 +- tubearchivist/config/settings.py | 1 + tubearchivist/config/urls.py | 1 + tubearchivist/home/src/download/queue.py | 2 +- .../home/src/download/subscriptions.py | 2 +- .../home/src/download/yt_dlp_handler.py | 2 +- tubearchivist/home/src/index/reindex.py | 2 +- tubearchivist/home/views.py | 2 +- tubearchivist/playlist/__init__.py | 0 tubearchivist/playlist/migrations/__init__.py | 0 tubearchivist/playlist/src/__init__.py | 0 .../playlist.py => playlist/src/index.py} | 0 tubearchivist/playlist/urls.py | 22 +++ tubearchivist/playlist/views.py | 138 ++++++++++++++++++ tubearchivist/video/src/index.py | 2 +- 17 files changed, 170 insertions(+), 155 deletions(-) create mode 100644 tubearchivist/playlist/__init__.py create mode 100644 tubearchivist/playlist/migrations/__init__.py create mode 100644 tubearchivist/playlist/src/__init__.py rename tubearchivist/{home/src/index/playlist.py => playlist/src/index.py} (100%) create mode 100644 tubearchivist/playlist/urls.py create mode 100644 tubearchivist/playlist/views.py diff --git a/tubearchivist/api/urls.py b/tubearchivist/api/urls.py index 0fe2e635..82c0b59d 100644 --- a/tubearchivist/api/urls.py +++ b/tubearchivist/api/urls.py @@ -6,21 +6,6 @@ from django.urls import path urlpatterns = [ path("ping/", views.PingView.as_view(), name="ping"), path("login/", views.LoginApiView.as_view(), name="api-login"), - path( - "playlist/", - views.PlaylistApiListView.as_view(), - name="api-playlist-list", - ), - path( - "playlist//", - views.PlaylistApiView.as_view(), - name="api-playlist", - ), - path( - "playlist//video/", - views.PlaylistApiVideoView.as_view(), - name="api-playlist-video", - ), path( "download/", views.DownloadApiListView.as_view(), diff --git a/tubearchivist/api/views.py b/tubearchivist/api/views.py index 58c9daa7..3f9c068c 100644 --- a/tubearchivist/api/views.py +++ b/tubearchivist/api/views.py @@ -12,7 +12,6 @@ from api.src.aggs import ( from api.src.search_processor import SearchProcess from home.models import CustomPeriodicTask from home.src.download.queue import PendingInteract -from home.src.download.subscriptions import PlaylistSubscription from home.src.download.yt_dlp_base import CookieHandler from home.src.es.backup import ElasticBackup from home.src.es.connect import ElasticWrap @@ -20,7 +19,6 @@ from home.src.es.snapshot import ElasticSnapshot from home.src.frontend.searching import SearchForm from home.src.frontend.watched import WatchState from home.src.index.generic import Pagination -from home.src.index.playlist import YoutubePlaylist from home.src.index.reindex import ReindexProgress from home.src.ta.config import AppConfig, ReleaseVersion from home.src.ta.notify import Notifications, get_all_notifications @@ -34,9 +32,8 @@ from home.tasks import ( download_pending, extrac_dl, run_restore_backup, - subscribe_to, ) -from rest_framework import permissions, status +from rest_framework import permissions from rest_framework.authentication import ( SessionAuthentication, TokenAuthentication, @@ -132,135 +129,6 @@ class ApiBaseView(APIView): self.response["paginate"] = self.pagination_handler.pagination -class PlaylistApiListView(ApiBaseView): - """resolves to /api/playlist/ - GET: returns list of indexed playlists - """ - - search_base = "ta_playlist/_search/" - permission_classes = [AdminWriteOnly] - valid_playlist_type = ["regular", "custom"] - - def get(self, request): - """handle get request""" - playlist_type = request.GET.get("playlist_type", None) - query = {"sort": [{"playlist_name.keyword": {"order": "asc"}}]} - if playlist_type is not None: - if playlist_type not in self.valid_playlist_type: - message = f"invalid playlist_type {playlist_type}" - return Response({"message": message}, status=400) - - query.update( - { - "query": { - "term": {"playlist_type": {"value": playlist_type}} - }, - } - ) - - self.data.update(query) - self.get_document_list(request) - return Response(self.response) - - def post(self, request): - """subscribe/unsubscribe to list of playlists""" - data = request.data - try: - to_add = data["data"] - except KeyError: - message = "missing expected data key" - print(message) - return Response({"message": message}, status=400) - - pending = [] - for playlist_item in to_add: - playlist_id = playlist_item["playlist_id"] - if playlist_item["playlist_subscribed"]: - pending.append(playlist_id) - else: - self._unsubscribe(playlist_id) - - if pending: - url_str = " ".join(pending) - subscribe_to.delay(url_str, expected_type="playlist") - - return Response(data) - - @staticmethod - def _unsubscribe(playlist_id: str): - """unsubscribe""" - print(f"[{playlist_id}] unsubscribe from playlist") - PlaylistSubscription().change_subscribe( - playlist_id, subscribe_status=False - ) - - -class PlaylistApiView(ApiBaseView): - """resolves to /api/playlist// - GET: returns metadata dict of playlist - """ - - search_base = "ta_playlist/_doc/" - permission_classes = [AdminWriteOnly] - valid_custom_actions = ["create", "remove", "up", "down", "top", "bottom"] - - def get(self, request, playlist_id): - # pylint: disable=unused-argument - """get request""" - self.get_document(playlist_id) - return Response(self.response, status=self.status_code) - - def post(self, request, playlist_id): - """post to custom playlist to add a video to list""" - playlist = YoutubePlaylist(playlist_id) - if not playlist.is_custom_playlist(): - message = f"playlist with ID {playlist_id} is not custom" - 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) - - 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) - - def delete(self, request, playlist_id): - """delete playlist""" - print(f"{playlist_id}: delete playlist") - delete_videos = request.GET.get("delete-videos", False) - if delete_videos: - YoutubePlaylist(playlist_id).delete_videos_playlist() - else: - YoutubePlaylist(playlist_id).delete_metadata() - - return Response({"success": True}) - - -class PlaylistApiVideoView(ApiBaseView): - """resolves to /api/playlist//video - GET: returns list of videos in playlist - """ - - search_base = "ta_video/_search/" - - def get(self, request, playlist_id): - """handle get request""" - self.data["query"] = { - "term": {"playlist.keyword": {"value": playlist_id}} - } - self.data.update({"sort": [{"published": {"order": "desc"}}]}) - - self.get_document_list(request) - return Response(self.response, status=self.status_code) - - class DownloadApiView(ApiBaseView): """resolves to /api/download// GET: returns metadata dict of an item in the download queue diff --git a/tubearchivist/channel/src/index.py b/tubearchivist/channel/src/index.py index d55e46c4..956ec7f4 100644 --- a/tubearchivist/channel/src/index.py +++ b/tubearchivist/channel/src/index.py @@ -12,8 +12,8 @@ from home.src.download.thumbnails import ThumbManager from home.src.download.yt_dlp_base import YtWrap from home.src.es.connect import ElasticWrap, IndexPaginate from home.src.index.generic import YouTubeItem -from home.src.index.playlist import YoutubePlaylist from home.src.ta.settings import EnvironmentSettings +from playlist.src.index import YoutubePlaylist class YoutubeChannel(YouTubeItem): diff --git a/tubearchivist/config/settings.py b/tubearchivist/config/settings.py index e2275a24..5321eacb 100644 --- a/tubearchivist/config/settings.py +++ b/tubearchivist/config/settings.py @@ -64,6 +64,7 @@ INSTALLED_APPS = [ "api", "video", "channel", + "playlist", "config", ] diff --git a/tubearchivist/config/urls.py b/tubearchivist/config/urls.py index 5d6bbd83..40563133 100644 --- a/tubearchivist/config/urls.py +++ b/tubearchivist/config/urls.py @@ -22,5 +22,6 @@ urlpatterns = [ path("api/", include("api.urls")), path("api/video/", include("video.urls")), path("api/channel/", include("channel.urls")), + path("api/playlist/", include("playlist.urls")), path("admin/", admin.site.urls), ] diff --git a/tubearchivist/home/src/download/queue.py b/tubearchivist/home/src/download/queue.py index bf303305..b91c4489 100644 --- a/tubearchivist/home/src/download/queue.py +++ b/tubearchivist/home/src/download/queue.py @@ -11,9 +11,9 @@ from home.src.download.subscriptions import ChannelSubscription from home.src.download.thumbnails import ThumbManager from home.src.download.yt_dlp_base import YtWrap from home.src.es.connect import ElasticWrap, IndexPaginate -from home.src.index.playlist import YoutubePlaylist from home.src.ta.config import AppConfig from home.src.ta.helper import get_duration_str, is_shorts +from playlist.src.index import YoutubePlaylist from video.src.constants import VideoTypeEnum diff --git a/tubearchivist/home/src/download/subscriptions.py b/tubearchivist/home/src/download/subscriptions.py index 70b75556..72cf029a 100644 --- a/tubearchivist/home/src/download/subscriptions.py +++ b/tubearchivist/home/src/download/subscriptions.py @@ -8,10 +8,10 @@ from channel.src.index import YoutubeChannel from home.src.download.thumbnails import ThumbManager from home.src.download.yt_dlp_base import YtWrap from home.src.es.connect import IndexPaginate -from home.src.index.playlist import YoutubePlaylist from home.src.ta.config import AppConfig from home.src.ta.helper import is_missing from home.src.ta.urlparser import Parser +from playlist.src.index import YoutubePlaylist from video.src.constants import VideoTypeEnum from video.src.index import YoutubeVideo diff --git a/tubearchivist/home/src/download/yt_dlp_handler.py b/tubearchivist/home/src/download/yt_dlp_handler.py index 0b5e751f..edfd17d0 100644 --- a/tubearchivist/home/src/download/yt_dlp_handler.py +++ b/tubearchivist/home/src/download/yt_dlp_handler.py @@ -15,11 +15,11 @@ from home.src.download.queue import PendingList from home.src.download.subscriptions import PlaylistSubscription from home.src.download.yt_dlp_base import YtWrap from home.src.es.connect import ElasticWrap, IndexPaginate -from home.src.index.playlist import YoutubePlaylist from home.src.ta.config import AppConfig from home.src.ta.helper import get_channel_overwrites, ignore_filelist from home.src.ta.settings import EnvironmentSettings from home.src.ta.ta_redis import RedisQueue +from playlist.src.index import YoutubePlaylist from video.src.comments import CommentList from video.src.constants import VideoTypeEnum from video.src.index import YoutubeVideo, index_new_video diff --git a/tubearchivist/home/src/index/reindex.py b/tubearchivist/home/src/index/reindex.py index 9c2ebdf3..8947459c 100644 --- a/tubearchivist/home/src/index/reindex.py +++ b/tubearchivist/home/src/index/reindex.py @@ -16,10 +16,10 @@ from home.src.download.subscriptions import ChannelSubscription from home.src.download.thumbnails import ThumbManager from home.src.download.yt_dlp_base import CookieHandler from home.src.es.connect import ElasticWrap, IndexPaginate -from home.src.index.playlist import YoutubePlaylist from home.src.ta.config import AppConfig from home.src.ta.settings import EnvironmentSettings from home.src.ta.ta_redis import RedisQueue +from playlist.src.index import YoutubePlaylist from video.src.comments import Comments from video.src.index import YoutubeVideo diff --git a/tubearchivist/home/views.py b/tubearchivist/home/views.py index b9a5466f..049b8d6e 100644 --- a/tubearchivist/home/views.py +++ b/tubearchivist/home/views.py @@ -42,7 +42,6 @@ from home.src.frontend.forms_schedule import ( SchedulerSettingsForm, ) from home.src.index.generic import Pagination -from home.src.index.playlist import YoutubePlaylist from home.src.index.reindex import ReindexProgress from home.src.ta.config import AppConfig, ReleaseVersion from home.src.ta.config_schedule import ScheduleBuilder @@ -52,6 +51,7 @@ from home.src.ta.settings import EnvironmentSettings from home.src.ta.ta_redis import RedisArchivist from home.src.ta.users import UserConfig from home.tasks import index_channel_playlists, subscribe_to +from playlist.src.index import YoutubePlaylist from rest_framework.authtoken.models import Token from video.src.constants import VideoTypeEnum diff --git a/tubearchivist/playlist/__init__.py b/tubearchivist/playlist/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tubearchivist/playlist/migrations/__init__.py b/tubearchivist/playlist/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tubearchivist/playlist/src/__init__.py b/tubearchivist/playlist/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tubearchivist/home/src/index/playlist.py b/tubearchivist/playlist/src/index.py similarity index 100% rename from tubearchivist/home/src/index/playlist.py rename to tubearchivist/playlist/src/index.py diff --git a/tubearchivist/playlist/urls.py b/tubearchivist/playlist/urls.py new file mode 100644 index 00000000..d1dc6fe4 --- /dev/null +++ b/tubearchivist/playlist/urls.py @@ -0,0 +1,22 @@ +"""all playlist API urls""" + +from django.urls import path +from playlist import views + +urlpatterns = [ + path( + "", + views.PlaylistApiListView.as_view(), + name="api-playlist-list", + ), + path( + "/", + views.PlaylistApiView.as_view(), + name="api-playlist", + ), + path( + "/video/", + views.PlaylistApiVideoView.as_view(), + name="api-playlist-video", + ), +] diff --git a/tubearchivist/playlist/views.py b/tubearchivist/playlist/views.py new file mode 100644 index 00000000..66e4edb8 --- /dev/null +++ b/tubearchivist/playlist/views.py @@ -0,0 +1,138 @@ +"""all playlist API views""" + +from api.views import AdminWriteOnly, ApiBaseView +from home.src.download.subscriptions import PlaylistSubscription +from home.src.ta.users import UserConfig +from home.tasks import subscribe_to +from playlist.src.index import YoutubePlaylist +from rest_framework import status +from rest_framework.response import Response + + +class PlaylistApiListView(ApiBaseView): + """resolves to /api/playlist/ + GET: returns list of indexed playlists + """ + + search_base = "ta_playlist/_search/" + permission_classes = [AdminWriteOnly] + valid_playlist_type = ["regular", "custom"] + + def get(self, request): + """handle get request""" + playlist_type = request.GET.get("playlist_type", None) + query = {"sort": [{"playlist_name.keyword": {"order": "asc"}}]} + if playlist_type is not None: + if playlist_type not in self.valid_playlist_type: + message = f"invalid playlist_type {playlist_type}" + return Response({"message": message}, status=400) + + query.update( + { + "query": { + "term": {"playlist_type": {"value": playlist_type}} + }, + } + ) + + self.data.update(query) + self.get_document_list(request) + return Response(self.response) + + def post(self, request): + """subscribe/unsubscribe to list of playlists""" + data = request.data + try: + to_add = data["data"] + except KeyError: + message = "missing expected data key" + print(message) + return Response({"message": message}, status=400) + + pending = [] + for playlist_item in to_add: + playlist_id = playlist_item["playlist_id"] + if playlist_item["playlist_subscribed"]: + pending.append(playlist_id) + else: + self._unsubscribe(playlist_id) + + if pending: + url_str = " ".join(pending) + subscribe_to.delay(url_str, expected_type="playlist") + + return Response(data) + + @staticmethod + def _unsubscribe(playlist_id: str): + """unsubscribe""" + print(f"[{playlist_id}] unsubscribe from playlist") + PlaylistSubscription().change_subscribe( + playlist_id, subscribe_status=False + ) + + +class PlaylistApiView(ApiBaseView): + """resolves to /api/playlist// + GET: returns metadata dict of playlist + """ + + search_base = "ta_playlist/_doc/" + permission_classes = [AdminWriteOnly] + valid_custom_actions = ["create", "remove", "up", "down", "top", "bottom"] + + def get(self, request, playlist_id): + # pylint: disable=unused-argument + """get request""" + self.get_document(playlist_id) + return Response(self.response, status=self.status_code) + + def post(self, request, playlist_id): + """post to custom playlist to add a video to list""" + playlist = YoutubePlaylist(playlist_id) + if not playlist.is_custom_playlist(): + message = f"playlist with ID {playlist_id} is not custom" + 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) + + 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) + + def delete(self, request, playlist_id): + """delete playlist""" + print(f"{playlist_id}: delete playlist") + delete_videos = request.GET.get("delete-videos", False) + if delete_videos: + YoutubePlaylist(playlist_id).delete_videos_playlist() + else: + YoutubePlaylist(playlist_id).delete_metadata() + + return Response({"success": True}) + + +class PlaylistApiVideoView(ApiBaseView): + """resolves to /api/playlist//video + GET: returns list of videos in playlist + """ + + search_base = "ta_video/_search/" + + def get(self, request, playlist_id): + """handle get request""" + self.data["query"] = { + "term": {"playlist.keyword": {"value": playlist_id}} + } + self.data.update({"sort": [{"published": {"order": "desc"}}]}) + + self.get_document_list(request) + return Response(self.response, status=self.status_code) diff --git a/tubearchivist/video/src/index.py b/tubearchivist/video/src/index.py index 8b171fd5..408fae32 100644 --- a/tubearchivist/video/src/index.py +++ b/tubearchivist/video/src/index.py @@ -11,11 +11,11 @@ import requests from channel.src import index as ta_channel from django.conf import settings from home.src.es.connect import ElasticWrap -from home.src.index import playlist as ta_playlist from home.src.index.generic import YouTubeItem from home.src.ta.helper import get_duration_sec, get_duration_str, randomizor from home.src.ta.settings import EnvironmentSettings from home.src.ta.users import UserConfig +from playlist.src import index as ta_playlist from ryd_client import ryd_client from video.src.comments import Comments from video.src.constants import VideoTypeEnum