mirror of
https://github.com/tubearchivist/tubearchivist.git
synced 2025-03-14 01:40:12 +00:00
200 lines
6.0 KiB
Python
200 lines
6.0 KiB
Python
"""all API views for video endpoints"""
|
|
|
|
from common.src.helper import calc_is_watched
|
|
from common.src.ta_redis import RedisArchivist
|
|
from common.src.watched import WatchState
|
|
from common.views_base import AdminWriteOnly, ApiBaseView
|
|
from playlist.src.index import YoutubePlaylist
|
|
from rest_framework.response import Response
|
|
from video.src.index import YoutubeVideo
|
|
from video.src.query_building import QueryBuilder
|
|
|
|
|
|
class VideoApiListView(ApiBaseView):
|
|
"""resolves to /api/video/
|
|
GET: returns list of videos
|
|
params:
|
|
- playlist:str=<playlist-id>
|
|
- channel:str=<channel-id>
|
|
- watch:enum=watched|unwatched|continue
|
|
- sort:enum=published|downloaded|views|likes|duration|filesize
|
|
- order:enum=asc|desc
|
|
- type:enum=videos|streams|shorts
|
|
"""
|
|
|
|
search_base = "ta_video/_search/"
|
|
|
|
def get(self, request):
|
|
"""get request"""
|
|
try:
|
|
data = QueryBuilder(request.user.id, **request.GET).build_data()
|
|
except ValueError as err:
|
|
return Response({"error": str(err)}, status=400)
|
|
|
|
if data == {"query": {"bool": {"must": [None]}}}:
|
|
# skip empty lookup
|
|
return Response([])
|
|
|
|
self.data = data
|
|
self.get_document_list(request, progress_match=request.user.id)
|
|
|
|
return Response(self.response)
|
|
|
|
|
|
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, progress_match=request.user.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 VideoApiNavView(ApiBaseView):
|
|
"""resolves to /api/video/<video-id>/nav/
|
|
GET: returns playlist nav
|
|
"""
|
|
|
|
search_base = "ta_video/_doc/"
|
|
|
|
def get(self, request, video_id):
|
|
# pylint: disable=unused-argument
|
|
"""get request"""
|
|
self.get_document(video_id)
|
|
if self.status_code != 200:
|
|
return Response(status=self.status_code)
|
|
|
|
playlist_nav = []
|
|
|
|
if not self.response["data"].get("playlist"):
|
|
return Response(playlist_nav)
|
|
|
|
for playlist_id in self.response["data"]["playlist"]:
|
|
playlist = YoutubePlaylist(playlist_id)
|
|
playlist.get_from_es()
|
|
playlist.build_nav(video_id)
|
|
if playlist.nav:
|
|
playlist_nav.append(playlist.nav)
|
|
|
|
return Response(playlist_nav, status=self.status_code)
|
|
|
|
|
|
class VideoProgressView(ApiBaseView):
|
|
"""resolves to /api/video/<video_id>/progress/
|
|
handle progress status for video
|
|
"""
|
|
|
|
search_base = "ta_video/_doc/"
|
|
|
|
@staticmethod
|
|
def _get_key(user_id: int, video_id: str) -> str:
|
|
"""redis key"""
|
|
return f"{user_id}:progress:{video_id}"
|
|
|
|
def post(self, request, video_id):
|
|
"""set progress position in redis"""
|
|
position = request.data.get("position", 0)
|
|
key = self._get_key(request.user.id, video_id)
|
|
redis_con = RedisArchivist()
|
|
current_progress = redis_con.get_message_dict(key)
|
|
|
|
if not current_progress:
|
|
self.get_document(video_id)
|
|
if self.status_code != 200:
|
|
return Response(status=self.status_code)
|
|
|
|
current_progress = self.response["data"]["player"]
|
|
|
|
current_progress.update({"position": position, "youtube_id": video_id})
|
|
watched = self._check_watched(request, video_id, current_progress)
|
|
if watched:
|
|
expire = 60
|
|
else:
|
|
expire = False
|
|
|
|
current_progress.update({"watched": watched})
|
|
redis_con.set_message(key, current_progress, expire=expire)
|
|
|
|
return Response(current_progress)
|
|
|
|
def _check_watched(self, request, video_id, current_progress) -> bool:
|
|
"""check watched state"""
|
|
if current_progress["watched"]:
|
|
return True
|
|
|
|
watched = calc_is_watched(
|
|
current_progress["duration"], current_progress["position"]
|
|
)
|
|
if watched:
|
|
WatchState(video_id, watched, request.user.id).change()
|
|
|
|
return watched
|
|
|
|
def delete(self, request, video_id):
|
|
"""delete progress position"""
|
|
key = self._get_key(request.user.id, 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=200)
|
|
|
|
|
|
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=200)
|