mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-07 22:39:28 +00:00
split playlist app
This commit is contained in:
@@ -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/<slug:playlist_id>/",
|
||||
views.PlaylistApiView.as_view(),
|
||||
name="api-playlist",
|
||||
),
|
||||
path(
|
||||
"playlist/<slug:playlist_id>/video/",
|
||||
views.PlaylistApiVideoView.as_view(),
|
||||
name="api-playlist-video",
|
||||
),
|
||||
path(
|
||||
"download/",
|
||||
views.DownloadApiListView.as_view(),
|
||||
|
||||
@@ -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/<playlist_id>/
|
||||
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/<playlist_id>/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/<video_id>/
|
||||
GET: returns metadata dict of an item in the download queue
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -64,6 +64,7 @@ INSTALLED_APPS = [
|
||||
"api",
|
||||
"video",
|
||||
"channel",
|
||||
"playlist",
|
||||
"config",
|
||||
]
|
||||
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
0
tubearchivist/playlist/__init__.py
Normal file
0
tubearchivist/playlist/__init__.py
Normal file
0
tubearchivist/playlist/migrations/__init__.py
Normal file
0
tubearchivist/playlist/migrations/__init__.py
Normal file
0
tubearchivist/playlist/src/__init__.py
Normal file
0
tubearchivist/playlist/src/__init__.py
Normal file
22
tubearchivist/playlist/urls.py
Normal file
22
tubearchivist/playlist/urls.py
Normal file
@@ -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(
|
||||
"<slug:playlist_id>/",
|
||||
views.PlaylistApiView.as_view(),
|
||||
name="api-playlist",
|
||||
),
|
||||
path(
|
||||
"<slug:playlist_id>/video/",
|
||||
views.PlaylistApiVideoView.as_view(),
|
||||
name="api-playlist-video",
|
||||
),
|
||||
]
|
||||
138
tubearchivist/playlist/views.py
Normal file
138
tubearchivist/playlist/views.py
Normal file
@@ -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/<playlist_id>/
|
||||
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/<playlist_id>/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)
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user