From 87b65caf8a3589da554f7dd0c684f9e6c69c972b Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 30 Jan 2025 22:02:48 +0700 Subject: [PATCH] handle is_watched state in backend --- backend/common/src/helper.py | 16 ++++++++ backend/common/src/search_processor.py | 6 ++- backend/video/src/query_building.py | 6 ++- backend/video/views.py | 52 +++++++++++++++++++++++--- 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/backend/common/src/helper.py b/backend/common/src/helper.py index 90b9e068..5f001f8d 100644 --- a/backend/common/src/helper.py +++ b/backend/common/src/helper.py @@ -275,3 +275,19 @@ def get_channel_overwrites() -> dict[str, dict[str, Any]]: overwrites = {i["channel_id"]: i["channel_overwrites"] for i in result} return overwrites + + +def calc_is_watched(duration: float, position: float) -> bool: + """considered watched based on duration position""" + + if not duration or duration <= 0: + return False + + if duration < 60: + threshold = 0.5 + elif duration > 900: + threshold = 1 - (180 / duration) + else: + threshold = 0.9 + + return position >= duration * threshold diff --git a/backend/common/src/search_processor.py b/backend/common/src/search_processor.py index 9fc1f2b1..ad83b0a3 100644 --- a/backend/common/src/search_processor.py +++ b/backend/common/src/search_processor.py @@ -45,7 +45,11 @@ class SearchProcess: if not all_positions: return None - pos_index = {i["youtube_id"]: i["position"] for i in all_positions} + pos_index = { + i["youtube_id"]: i["position"] + for i in all_positions + if not i.get("watched") + } return pos_index def _process_result(self, result): diff --git a/backend/video/src/query_building.py b/backend/video/src/query_building.py index da98cd34..c214bbcc 100644 --- a/backend/video/src/query_building.py +++ b/backend/video/src/query_building.py @@ -63,7 +63,11 @@ class QueryBuilder: if not results: return None - ids = [{"match": {"youtube_id": i.get("youtube_id")}} for i in results] + ids = [ + {"match": {"youtube_id": i.get("youtube_id")}} + for i in results + if not i.get("watched") + ] continue_ids = {"bool": {"should": ids}} return continue_ids diff --git a/backend/video/views.py b/backend/video/views.py index 08f484ab..d601282d 100644 --- a/backend/video/views.py +++ b/backend/video/views.py @@ -1,6 +1,8 @@ """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 @@ -102,18 +104,56 @@ class VideoProgressView(ApiBaseView): 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 = 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) + 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_state(video_id, current_progress) + if watched: + redis_con.del_message(key) + expire = 360 + else: + expire = False + + current_progress.update({"watched": watched}) + redis_con.set_message(key, current_progress, expire=expire) + + return Response(current_progress) + + def _check_watched_state(self, 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).change() + + return watched def delete(self, request, video_id): """delete progress position""" - key = f"{request.user.id}:progress:{video_id}" + key = self._get_key(request.user.id, video_id) RedisArchivist().del_message(key) self.response = {"progress-reset": video_id}