mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-04 23:29:29 +00:00
split video app
This commit is contained in:
@@ -6,36 +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(
|
||||
"video/",
|
||||
views.VideoApiListView.as_view(),
|
||||
name="api-video-list",
|
||||
),
|
||||
path(
|
||||
"video/<slug:video_id>/",
|
||||
views.VideoApiView.as_view(),
|
||||
name="api-video",
|
||||
),
|
||||
path(
|
||||
"video/<slug:video_id>/progress/",
|
||||
views.VideoProgressView.as_view(),
|
||||
name="api-video-progress",
|
||||
),
|
||||
path(
|
||||
"video/<slug:video_id>/comment/",
|
||||
views.VideoCommentView.as_view(),
|
||||
name="api-video-comment",
|
||||
),
|
||||
path(
|
||||
"video/<slug:video_id>/similar/",
|
||||
views.VideoSimilarView.as_view(),
|
||||
name="api-video-similar",
|
||||
),
|
||||
path(
|
||||
"video/<slug:video_id>/sponsor/",
|
||||
views.VideoSponsorView.as_view(),
|
||||
name="api-video-sponsor",
|
||||
),
|
||||
path(
|
||||
"channel/",
|
||||
views.ChannelApiListView.as_view(),
|
||||
|
||||
@@ -26,7 +26,6 @@ from home.src.index.channel import YoutubeChannel
|
||||
from home.src.index.generic import Pagination
|
||||
from home.src.index.playlist import YoutubePlaylist
|
||||
from home.src.index.reindex import ReindexProgress
|
||||
from home.src.index.video import SponsorBlock, YoutubeVideo
|
||||
from home.src.ta.config import AppConfig, ReleaseVersion
|
||||
from home.src.ta.notify import Notifications, get_all_notifications
|
||||
from home.src.ta.settings import EnvironmentSettings
|
||||
@@ -138,180 +137,6 @@ class ApiBaseView(APIView):
|
||||
self.response["paginate"] = self.pagination_handler.pagination
|
||||
|
||||
|
||||
class VideoApiView(ApiBaseView):
|
||||
"""resolves to /api/video/<video_id>/
|
||||
GET: returns metadata dict of video
|
||||
"""
|
||||
|
||||
search_base = "ta_video/_doc/"
|
||||
permission_classes = [AdminWriteOnly]
|
||||
|
||||
def get(self, request, video_id):
|
||||
# pylint: disable=unused-argument
|
||||
"""get request"""
|
||||
self.get_document(video_id)
|
||||
return Response(self.response, status=self.status_code)
|
||||
|
||||
def delete(self, request, video_id):
|
||||
# pylint: disable=unused-argument
|
||||
"""delete single video"""
|
||||
message = {"video": video_id}
|
||||
try:
|
||||
YoutubeVideo(video_id).delete_media_file()
|
||||
status_code = 200
|
||||
message.update({"state": "delete"})
|
||||
except FileNotFoundError:
|
||||
status_code = 404
|
||||
message.update({"state": "not found"})
|
||||
|
||||
return Response(message, status=status_code)
|
||||
|
||||
|
||||
class VideoApiListView(ApiBaseView):
|
||||
"""resolves to /api/video/
|
||||
GET: returns list of videos
|
||||
"""
|
||||
|
||||
search_base = "ta_video/_search/"
|
||||
|
||||
def get(self, request):
|
||||
"""get request"""
|
||||
self.data.update({"sort": [{"published": {"order": "desc"}}]})
|
||||
self.get_document_list(request)
|
||||
|
||||
return Response(self.response)
|
||||
|
||||
|
||||
class VideoProgressView(ApiBaseView):
|
||||
"""resolves to /api/video/<video_id>/progress/
|
||||
handle progress status for video
|
||||
"""
|
||||
|
||||
def get(self, request, video_id):
|
||||
"""get progress for a single video"""
|
||||
user_id = request.user.id
|
||||
key = f"{user_id}:progress:{video_id}"
|
||||
video_progress = RedisArchivist().get_message(key)
|
||||
position = video_progress.get("position", 0)
|
||||
|
||||
self.response = {
|
||||
"youtube_id": video_id,
|
||||
"user_id": user_id,
|
||||
"position": position,
|
||||
}
|
||||
return Response(self.response)
|
||||
|
||||
def post(self, request, video_id):
|
||||
"""set progress position in redis"""
|
||||
position = request.data.get("position", 0)
|
||||
key = f"{request.user.id}:progress:{video_id}"
|
||||
message = {"position": position, "youtube_id": video_id}
|
||||
RedisArchivist().set_message(key, message)
|
||||
self.response = request.data
|
||||
return Response(self.response)
|
||||
|
||||
def delete(self, request, video_id):
|
||||
"""delete progress position"""
|
||||
key = f"{request.user.id}:progress:{video_id}"
|
||||
RedisArchivist().del_message(key)
|
||||
self.response = {"progress-reset": video_id}
|
||||
|
||||
return Response(self.response)
|
||||
|
||||
|
||||
class VideoCommentView(ApiBaseView):
|
||||
"""resolves to /api/video/<video_id>/comment/
|
||||
handle video comments
|
||||
GET: return all comments from video with reply threads
|
||||
"""
|
||||
|
||||
search_base = "ta_comment/_doc/"
|
||||
|
||||
def get(self, request, video_id):
|
||||
"""get video comments"""
|
||||
# pylint: disable=unused-argument
|
||||
self.get_document(video_id)
|
||||
|
||||
return Response(self.response, status=self.status_code)
|
||||
|
||||
|
||||
class VideoSimilarView(ApiBaseView):
|
||||
"""resolves to /api/video/<video-id>/similar/
|
||||
GET: return max 6 videos similar to this
|
||||
"""
|
||||
|
||||
search_base = "ta_video/_search/"
|
||||
|
||||
def get(self, request, video_id):
|
||||
"""get similar videos"""
|
||||
self.data = {
|
||||
"size": 6,
|
||||
"query": {
|
||||
"more_like_this": {
|
||||
"fields": ["tags", "title"],
|
||||
"like": {"_id": video_id},
|
||||
"min_term_freq": 1,
|
||||
"max_query_terms": 25,
|
||||
}
|
||||
},
|
||||
}
|
||||
self.get_document_list(request, pagination=False)
|
||||
return Response(self.response, status=self.status_code)
|
||||
|
||||
|
||||
class VideoSponsorView(ApiBaseView):
|
||||
"""resolves to /api/video/<video_id>/sponsor/
|
||||
handle sponsor block integration
|
||||
"""
|
||||
|
||||
search_base = "ta_video/_doc/"
|
||||
|
||||
def get(self, request, video_id):
|
||||
"""get sponsor info"""
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
self.get_document(video_id)
|
||||
if not self.response.get("data"):
|
||||
message = {"message": "video not found"}
|
||||
return Response(message, status=404)
|
||||
|
||||
sponsorblock = self.response["data"].get("sponsorblock")
|
||||
|
||||
return Response(sponsorblock)
|
||||
|
||||
def post(self, request, video_id):
|
||||
"""post verification and timestamps"""
|
||||
if "segment" in request.data:
|
||||
response, status_code = self._create_segment(request, video_id)
|
||||
elif "vote" in request.data:
|
||||
response, status_code = self._vote_on_segment(request)
|
||||
|
||||
return Response(response, status=status_code)
|
||||
|
||||
@staticmethod
|
||||
def _create_segment(request, video_id):
|
||||
"""create segment in API"""
|
||||
start_time = request.data["segment"]["startTime"]
|
||||
end_time = request.data["segment"]["endTime"]
|
||||
response, status_code = SponsorBlock(request.user.id).post_timestamps(
|
||||
video_id, start_time, end_time
|
||||
)
|
||||
|
||||
return response, status_code
|
||||
|
||||
@staticmethod
|
||||
def _vote_on_segment(request):
|
||||
"""validate on existing segment"""
|
||||
user_id = request.user.id
|
||||
uuid = request.data["vote"]["uuid"]
|
||||
vote = request.data["vote"]["yourVote"]
|
||||
response, status_code = SponsorBlock(user_id).vote_on_segment(
|
||||
uuid, vote
|
||||
)
|
||||
|
||||
return response, status_code
|
||||
|
||||
|
||||
class ChannelApiView(ApiBaseView):
|
||||
"""resolves to /api/channel/<channel_id>/
|
||||
GET: returns metadata dict of channel
|
||||
|
||||
@@ -62,6 +62,7 @@ INSTALLED_APPS = [
|
||||
"rest_framework",
|
||||
"rest_framework.authtoken",
|
||||
"api",
|
||||
"video",
|
||||
"config",
|
||||
]
|
||||
|
||||
|
||||
@@ -20,5 +20,6 @@ from django.urls import include, path
|
||||
urlpatterns = [
|
||||
path("", include("home.urls")),
|
||||
path("api/", include("api.urls")),
|
||||
path("api/video/", include("video.urls")),
|
||||
path("admin/", admin.site.urls),
|
||||
]
|
||||
|
||||
@@ -12,9 +12,9 @@ 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.index.video_constants import VideoTypeEnum
|
||||
from home.src.ta.config import AppConfig
|
||||
from home.src.ta.helper import get_duration_str, is_shorts
|
||||
from video.src.constants import VideoTypeEnum
|
||||
|
||||
|
||||
class PendingIndex:
|
||||
|
||||
@@ -9,11 +9,11 @@ from home.src.download.yt_dlp_base import YtWrap
|
||||
from home.src.es.connect import IndexPaginate
|
||||
from home.src.index.channel import YoutubeChannel
|
||||
from home.src.index.playlist import YoutubePlaylist
|
||||
from home.src.index.video import YoutubeVideo
|
||||
from home.src.index.video_constants import VideoTypeEnum
|
||||
from home.src.ta.config import AppConfig
|
||||
from home.src.ta.helper import is_missing
|
||||
from home.src.ta.urlparser import Parser
|
||||
from video.src.constants import VideoTypeEnum
|
||||
from video.src.index import YoutubeVideo
|
||||
|
||||
|
||||
class ChannelSubscription:
|
||||
|
||||
@@ -15,14 +15,14 @@ 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.channel import YoutubeChannel
|
||||
from home.src.index.comments import CommentList
|
||||
from home.src.index.playlist import YoutubePlaylist
|
||||
from home.src.index.video import YoutubeVideo, index_new_video
|
||||
from home.src.index.video_constants import VideoTypeEnum
|
||||
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 video.src.comments import CommentList
|
||||
from video.src.constants import VideoTypeEnum
|
||||
from video.src.index import YoutubeVideo, index_new_video
|
||||
|
||||
|
||||
class DownloaderBase:
|
||||
|
||||
@@ -6,10 +6,10 @@ Functionality:
|
||||
import os
|
||||
|
||||
from home.src.es.connect import ElasticWrap, IndexPaginate
|
||||
from home.src.index.comments import CommentList
|
||||
from home.src.index.video import YoutubeVideo, index_new_video
|
||||
from home.src.ta.helper import ignore_filelist
|
||||
from home.src.ta.settings import EnvironmentSettings
|
||||
from video.src.comments import CommentList
|
||||
from video.src.index import YoutubeVideo, index_new_video
|
||||
|
||||
|
||||
class Scanner:
|
||||
|
||||
@@ -12,12 +12,12 @@ import shutil
|
||||
import subprocess
|
||||
|
||||
from home.src.download.thumbnails import ThumbManager
|
||||
from home.src.index.comments import CommentList
|
||||
from home.src.index.video import YoutubeVideo
|
||||
from home.src.ta.config import AppConfig
|
||||
from home.src.ta.helper import ignore_filelist
|
||||
from home.src.ta.settings import EnvironmentSettings
|
||||
from PIL import Image
|
||||
from video.src.comments import CommentList
|
||||
from video.src.index import YoutubeVideo
|
||||
from yt_dlp.utils import ISO639Utils
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from home.src.download.thumbnails import ThumbManager
|
||||
from home.src.es.connect import ElasticWrap, IndexPaginate
|
||||
from home.src.index import channel
|
||||
from home.src.index.generic import YouTubeItem
|
||||
from home.src.index.video import YoutubeVideo
|
||||
from video.src.index import YoutubeVideo
|
||||
|
||||
|
||||
class YoutubePlaylist(YouTubeItem):
|
||||
|
||||
@@ -16,12 +16,12 @@ 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.channel import YoutubeChannel
|
||||
from home.src.index.comments import Comments
|
||||
from home.src.index.playlist import YoutubePlaylist
|
||||
from home.src.index.video import YoutubeVideo
|
||||
from home.src.ta.config import AppConfig
|
||||
from home.src.ta.settings import EnvironmentSettings
|
||||
from home.src.ta.ta_redis import RedisQueue
|
||||
from video.src.comments import Comments
|
||||
from video.src.index import YoutubeVideo
|
||||
|
||||
|
||||
class ReindexConfigType(TypedDict):
|
||||
|
||||
@@ -7,7 +7,7 @@ Functionality:
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
from home.src.download.yt_dlp_base import YtWrap
|
||||
from home.src.index.video_constants import VideoTypeEnum
|
||||
from video.src.constants import VideoTypeEnum
|
||||
|
||||
|
||||
class Parser:
|
||||
|
||||
@@ -44,7 +44,6 @@ from home.src.index.channel import channel_overwrites
|
||||
from home.src.index.generic import Pagination
|
||||
from home.src.index.playlist import YoutubePlaylist
|
||||
from home.src.index.reindex import ReindexProgress
|
||||
from home.src.index.video_constants import VideoTypeEnum
|
||||
from home.src.ta.config import AppConfig, ReleaseVersion
|
||||
from home.src.ta.config_schedule import ScheduleBuilder
|
||||
from home.src.ta.helper import check_stylesheet, time_parser
|
||||
@@ -54,6 +53,7 @@ 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 rest_framework.authtoken.models import Token
|
||||
from video.src.constants import VideoTypeEnum
|
||||
|
||||
|
||||
class ArchivistViewConfig(View):
|
||||
|
||||
0
tubearchivist/video/__init__.py
Normal file
0
tubearchivist/video/__init__.py
Normal file
0
tubearchivist/video/migrations/__init__.py
Normal file
0
tubearchivist/video/migrations/__init__.py
Normal file
0
tubearchivist/video/src/__init__.py
Normal file
0
tubearchivist/video/src/__init__.py
Normal file
@@ -11,16 +11,16 @@ import requests
|
||||
from django.conf import settings
|
||||
from home.src.es.connect import ElasticWrap
|
||||
from home.src.index import channel as ta_channel
|
||||
from home.src.index import comments as ta_comments
|
||||
from home.src.index import playlist as ta_playlist
|
||||
from home.src.index.generic import YouTubeItem
|
||||
from home.src.index.subtitle import YoutubeSubtitle
|
||||
from home.src.index.video_constants import VideoTypeEnum
|
||||
from home.src.index.video_streams import MediaStreamExtractor
|
||||
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 ryd_client import ryd_client
|
||||
from video.src.comments import Comments
|
||||
from video.src.constants import VideoTypeEnum
|
||||
from video.src.media_streams import MediaStreamExtractor
|
||||
from video.src.subtitle import YoutubeSubtitle
|
||||
|
||||
|
||||
class SponsorBlock:
|
||||
@@ -323,7 +323,7 @@ class YoutubeVideo(YouTubeItem, YoutubeSubtitle):
|
||||
|
||||
def delete_comments(self):
|
||||
"""delete comments from es"""
|
||||
comments = ta_comments.Comments(self.youtube_id, config=self.config)
|
||||
comments = Comments(self.youtube_id, config=self.config)
|
||||
comments.check_config()
|
||||
if comments.is_activated:
|
||||
comments.delete_comments()
|
||||
28
tubearchivist/video/urls.py
Normal file
28
tubearchivist/video/urls.py
Normal file
@@ -0,0 +1,28 @@
|
||||
"""all video API urls"""
|
||||
|
||||
from django.urls import path
|
||||
from video import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.VideoApiListView.as_view(), name="api-video-list"),
|
||||
path(
|
||||
"<slug:video_id>/",
|
||||
views.VideoApiView.as_view(),
|
||||
name="api-video",
|
||||
),
|
||||
path(
|
||||
"<slug:video_id>/progress/",
|
||||
views.VideoProgressView.as_view(),
|
||||
name="api-video-progress",
|
||||
),
|
||||
path(
|
||||
"<slug:video_id>/comment/",
|
||||
views.VideoCommentView.as_view(),
|
||||
name="api-video-comment",
|
||||
),
|
||||
path(
|
||||
"<slug:video_id>/similar/",
|
||||
views.VideoSimilarView.as_view(),
|
||||
name="api-video-similar",
|
||||
),
|
||||
]
|
||||
127
tubearchivist/video/views.py
Normal file
127
tubearchivist/video/views.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""all API views for video endpoints"""
|
||||
|
||||
from api.views import AdminWriteOnly, ApiBaseView
|
||||
from home.src.ta.ta_redis import RedisArchivist
|
||||
from rest_framework.response import Response
|
||||
from video.src.index import YoutubeVideo
|
||||
|
||||
|
||||
class VideoApiView(ApiBaseView):
|
||||
"""resolves to /api/video/<video_id>/
|
||||
GET: returns metadata dict of video
|
||||
"""
|
||||
|
||||
search_base = "ta_video/_doc/"
|
||||
permission_classes = [AdminWriteOnly]
|
||||
|
||||
def get(self, request, video_id):
|
||||
# pylint: disable=unused-argument
|
||||
"""get request"""
|
||||
self.get_document(video_id)
|
||||
return Response(self.response, status=self.status_code)
|
||||
|
||||
def delete(self, request, video_id):
|
||||
# pylint: disable=unused-argument
|
||||
"""delete single video"""
|
||||
message = {"video": video_id}
|
||||
try:
|
||||
YoutubeVideo(video_id).delete_media_file()
|
||||
status_code = 200
|
||||
message.update({"state": "delete"})
|
||||
except FileNotFoundError:
|
||||
status_code = 404
|
||||
message.update({"state": "not found"})
|
||||
|
||||
return Response(message, status=status_code)
|
||||
|
||||
|
||||
class VideoApiListView(ApiBaseView):
|
||||
"""resolves to /api/video/
|
||||
GET: returns list of videos
|
||||
"""
|
||||
|
||||
search_base = "ta_video/_search/"
|
||||
|
||||
def get(self, request):
|
||||
"""get request"""
|
||||
self.data.update({"sort": [{"published": {"order": "desc"}}]})
|
||||
self.get_document_list(request)
|
||||
|
||||
return Response(self.response)
|
||||
|
||||
|
||||
class VideoProgressView(ApiBaseView):
|
||||
"""resolves to /api/video/<video_id>/progress/
|
||||
handle progress status for video
|
||||
"""
|
||||
|
||||
def get(self, request, video_id):
|
||||
"""get progress for a single video"""
|
||||
user_id = request.user.id
|
||||
key = f"{user_id}:progress:{video_id}"
|
||||
video_progress = RedisArchivist().get_message(key)
|
||||
position = video_progress.get("position", 0)
|
||||
|
||||
self.response = {
|
||||
"youtube_id": video_id,
|
||||
"user_id": user_id,
|
||||
"position": position,
|
||||
}
|
||||
return Response(self.response)
|
||||
|
||||
def post(self, request, video_id):
|
||||
"""set progress position in redis"""
|
||||
position = request.data.get("position", 0)
|
||||
key = f"{request.user.id}:progress:{video_id}"
|
||||
message = {"position": position, "youtube_id": video_id}
|
||||
RedisArchivist().set_message(key, message)
|
||||
self.response = request.data
|
||||
return Response(self.response)
|
||||
|
||||
def delete(self, request, video_id):
|
||||
"""delete progress position"""
|
||||
key = f"{request.user.id}:progress:{video_id}"
|
||||
RedisArchivist().del_message(key)
|
||||
self.response = {"progress-reset": video_id}
|
||||
|
||||
return Response(self.response)
|
||||
|
||||
|
||||
class VideoCommentView(ApiBaseView):
|
||||
"""resolves to /api/video/<video_id>/comment/
|
||||
handle video comments
|
||||
GET: return all comments from video with reply threads
|
||||
"""
|
||||
|
||||
search_base = "ta_comment/_doc/"
|
||||
|
||||
def get(self, request, video_id):
|
||||
"""get video comments"""
|
||||
# pylint: disable=unused-argument
|
||||
self.get_document(video_id)
|
||||
|
||||
return Response(self.response, status=self.status_code)
|
||||
|
||||
|
||||
class VideoSimilarView(ApiBaseView):
|
||||
"""resolves to /api/video/<video-id>/similar/
|
||||
GET: return max 6 videos similar to this
|
||||
"""
|
||||
|
||||
search_base = "ta_video/_search/"
|
||||
|
||||
def get(self, request, video_id):
|
||||
"""get similar videos"""
|
||||
self.data = {
|
||||
"size": 6,
|
||||
"query": {
|
||||
"more_like_this": {
|
||||
"fields": ["tags", "title"],
|
||||
"like": {"_id": video_id},
|
||||
"min_term_freq": 1,
|
||||
"max_query_terms": 25,
|
||||
}
|
||||
},
|
||||
}
|
||||
self.get_document_list(request, pagination=False)
|
||||
return Response(self.response, status=self.status_code)
|
||||
Reference in New Issue
Block a user