implement comment tree, #1102

This commit is contained in:
Simon 2025-12-31 12:42:11 +07:00
parent 7950dbb729
commit dcfbfb0b6a
No known key found for this signature in database
GPG Key ID: 2C15AA5E89985DD4
5 changed files with 39 additions and 50 deletions

View File

@ -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"""

View File

@ -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

View File

@ -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):

View File

@ -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) => {
<div className="comments-replies" style={{ display: 'block' }}>
{showSubComments &&
comment.comment_replies?.map(comment => {
comment.comment_replies.map(comment => {
return (
<Fragment key={comment.comment_id}>
<CommentBox comment={comment} onTimestampClick={onTimestampClick} />
<CommentBox
comment={comment}
onTimestampClick={onTimestampClick}
showThread={showSubComments}
/>
</Fragment>
);
})}

View File

@ -633,17 +633,17 @@ const SettingsApplication = () => {
Download and index comments. Browsable on the video detail page. Example:
<ul>
<li>
<span className="settings-current">all,100,all,30</span>: Get 100
max-parents and 30 max-replies-per-thread.
<span className="settings-current">all,100,all,30,all</span>: Get 100
max-parents and 30 max-replies-per-thread at any depth.
</li>
<li>
<span className="settings-current">1000,all,all,50</span>: Get a total of
1000 comments over all, 50 replies per thread.
<span className="settings-current">1000,all,all,50,2</span>: Get a total
of 1000 comments over all, 50 replies per thread, only 2 levels of depth.
</li>
<li>
Values are in the format:{' '}
<span className="settings-current">
max-comments,max-parents,max-replies,max-replies-per-thread
max-comments,max-parents,max-replies,max-replies-per-thread,max-depth
</span>
.
</li>