diff --git a/backend/common/src/search_processor.py b/backend/common/src/search_processor.py index e5c0cac4..e0ceaefc 100644 --- a/backend/common/src/search_processor.py +++ b/backend/common/src/search_processor.py @@ -182,17 +182,23 @@ class SearchProcess: def _process_comment(self, comment_dict): """run on all comments, create reply thread""" - all_comments = comment_dict["comment_comments"] - processed_comments = [] + comment_tree = [] + lookup = {} + for comment in comment_dict["comment_comments"]: + comment = comment.copy() + comment["comment_replies"] = [] + lookup[comment["comment_id"]] = comment - for comment in all_comments: - if comment["comment_parent"] == "root": - comment.update({"comment_replies": []}) - processed_comments.append(comment) + for comment in lookup.values(): + parent = comment.get("comment_parent") + if parent == "root": + comment_tree.append(comment) else: - processed_comments[-1]["comment_replies"].append(comment) + parent_node = lookup.get(parent) + if parent_node: + parent_node["comment_replies"].append(comment) - return processed_comments + return comment_tree def _process_subtitle(self, result): """take complete result dict to extract highlight""" diff --git a/backend/requirements.txt b/backend/requirements.txt index 136e49af..a310c1f5 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -11,4 +11,4 @@ redis==7.1.0 requests==2.32.5 ryd-client==0.0.6 uvicorn==0.38.0 -yt-dlp[default]==2025.12.8 +yt-dlp[default] @ git+https://github.com/yt-dlp/yt-dlp@468aa6a9b431194949ede9eaad8f33e314a288d6 diff --git a/backend/video/serializers.py b/backend/video/serializers.py index 393681ee..1cdf167c 100644 --- a/backend/video/serializers.py +++ b/backend/video/serializers.py @@ -4,6 +4,7 @@ from channel.serializers import ChannelSerializer from common.serializers import PaginationSerializer +from drf_spectacular.utils import extend_schema_field from rest_framework import serializers from video.src.constants import OrderEnum, SortEnum, VideoTypeEnum, WatchedEnum @@ -142,22 +143,6 @@ class VideoListQuerySerializer(serializers.Serializer): height = serializers.IntegerField(required=False) -class CommentThreadItemSerializer(serializers.Serializer): - """serialize comment thread item""" - - comment_id = serializers.CharField() - comment_text = serializers.CharField() - comment_timestamp = serializers.IntegerField() - comment_time_text = serializers.CharField() - comment_likecount = serializers.IntegerField() - comment_is_favorited = serializers.BooleanField() - comment_author = serializers.CharField() - comment_author_id = serializers.CharField() - comment_author_thumbnail = serializers.URLField() - comment_author_is_uploader = serializers.BooleanField() - comment_parent = serializers.CharField() - - class CommentItemSerializer(serializers.Serializer): """serialize comment item""" @@ -172,7 +157,14 @@ class CommentItemSerializer(serializers.Serializer): comment_author_thumbnail = serializers.URLField() comment_author_is_uploader = serializers.BooleanField() comment_parent = serializers.CharField() - comment_replies = CommentThreadItemSerializer(many=True, required=False) + comment_replies = serializers.SerializerMethodField() + + @extend_schema_field(serializers.ListField()) + def get_comment_replies(self, obj): + """recursive replies""" + return CommentItemSerializer( + obj.get("comment_replies", []), many=True + ).data class CommentsSerializer(serializers.Serializer): diff --git a/frontend/src/components/CommentBox.tsx b/frontend/src/components/CommentBox.tsx index f7f71379..f2df2b9b 100644 --- a/frontend/src/components/CommentBox.tsx +++ b/frontend/src/components/CommentBox.tsx @@ -6,20 +6,6 @@ import Linkify from './Linkify'; import formatNumbers from '../functions/formatNumbers'; import Button from './Button'; -export type CommentReplyType = { - comment_id: string; - comment_text: string; - comment_timestamp: number; - comment_time_text: string; - comment_likecount: number; - comment_is_favorited: false; - comment_author: string; - comment_author_id: string; - comment_author_thumbnail: string; - comment_author_is_uploader: boolean; - comment_parent: string; -}; - export type CommentsType = { comment_id: string; comment_text: string; @@ -32,16 +18,17 @@ export type CommentsType = { comment_author_thumbnail: string; comment_author_is_uploader: boolean; comment_parent: string; - comment_replies?: CommentReplyType[]; + comment_replies: CommentsType[]; }; type CommentBoxProps = { comment: CommentsType; + showThread?: boolean; onTimestampClick?: (seconds: number) => void; }; -const CommentBox = ({ comment, onTimestampClick }: CommentBoxProps) => { - const [showSubComments, setShowSubComments] = useState(false); +const CommentBox = ({ comment, showThread = false, onTimestampClick }: CommentBoxProps) => { + const [showSubComments, setShowSubComments] = useState(showThread); const hasSubComments = comment.comment_replies !== undefined && comment.comment_replies.length > 0; @@ -93,10 +80,14 @@ const CommentBox = ({ comment, onTimestampClick }: CommentBoxProps) => {
{showSubComments && - comment.comment_replies?.map(comment => { + comment.comment_replies.map(comment => { return ( - + ); })} diff --git a/frontend/src/pages/SettingsApplication.tsx b/frontend/src/pages/SettingsApplication.tsx index 5cfacf6a..755e0eaa 100644 --- a/frontend/src/pages/SettingsApplication.tsx +++ b/frontend/src/pages/SettingsApplication.tsx @@ -633,17 +633,17 @@ const SettingsApplication = () => { Download and index comments. Browsable on the video detail page. Example: