mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-15 02:22:11 +00:00
* tests: improve coverage * refactor: clean up nodes * chore: lint * feat(parser): ignore `BrandVideoShelf` Seems to be used for ads. * feat(parser): ignore `BrandVideoSingleton` too
106 lines
3.5 KiB
TypeScript
106 lines
3.5 KiB
TypeScript
import Parser from '../../index.js';
|
|
import Button from '../Button.js';
|
|
import ContinuationItem from '../ContinuationItem.js';
|
|
import Comment from './Comment.js';
|
|
import CommentReplies from './CommentReplies.js';
|
|
|
|
import { InnertubeError } from '../../../utils/Utils.js';
|
|
import { observe, YTNode } from '../../helpers.js';
|
|
|
|
import type Actions from '../../../core/Actions.js';
|
|
import type { ObservedArray } from '../../helpers.js';
|
|
import type { RawNode } from '../../index.js';
|
|
|
|
export default class CommentThread extends YTNode {
|
|
static type = 'CommentThread';
|
|
|
|
#actions?: Actions;
|
|
#continuation?: ContinuationItem;
|
|
|
|
comment: Comment | null;
|
|
replies?: ObservedArray<Comment>;
|
|
comment_replies_data: CommentReplies | null;
|
|
is_moderated_elq_comment: boolean;
|
|
has_replies: boolean;
|
|
|
|
constructor(data: RawNode) {
|
|
super();
|
|
this.comment = Parser.parseItem(data.comment, Comment);
|
|
this.comment_replies_data = Parser.parseItem(data.replies, CommentReplies);
|
|
this.is_moderated_elq_comment = data.isModeratedElqComment;
|
|
this.has_replies = !!this.comment_replies_data;
|
|
}
|
|
|
|
/**
|
|
* Retrieves replies to this comment thread.
|
|
*/
|
|
async getReplies(): Promise<CommentThread> {
|
|
if (!this.#actions)
|
|
throw new InnertubeError('Actions instance not set for this thread.');
|
|
|
|
if (!this.comment_replies_data)
|
|
throw new InnertubeError('This comment has no replies.', this);
|
|
|
|
const continuation = this.comment_replies_data.contents?.firstOfType(ContinuationItem);
|
|
|
|
if (!continuation)
|
|
throw new InnertubeError('Replies continuation not found.');
|
|
|
|
const response = await continuation.endpoint.call(this.#actions, { parse: true });
|
|
|
|
if (!response.on_response_received_endpoints_memo)
|
|
throw new InnertubeError('Unexpected response.', response);
|
|
|
|
this.replies = observe(response.on_response_received_endpoints_memo.getType(Comment).map((comment) => {
|
|
comment.setActions(this.#actions);
|
|
return comment;
|
|
}));
|
|
|
|
this.#continuation = response?.on_response_received_endpoints_memo.getType(ContinuationItem).first();
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Retrieves next batch of replies.
|
|
*/
|
|
async getContinuation(): Promise<CommentThread> {
|
|
if (!this.replies)
|
|
throw new InnertubeError('Cannot retrieve continuation because this thread\'s replies have not been loaded.');
|
|
|
|
if (!this.#continuation)
|
|
throw new InnertubeError('Continuation not found.');
|
|
|
|
if (!this.#actions)
|
|
throw new InnertubeError('Actions instance not set for this thread.');
|
|
|
|
const load_more_button = this.#continuation.button?.as(Button);
|
|
|
|
if (!load_more_button)
|
|
throw new InnertubeError('"Load more" button not found.');
|
|
|
|
const response = await load_more_button.endpoint.call(this.#actions, { parse: true });
|
|
|
|
if (!response.on_response_received_endpoints_memo)
|
|
throw new InnertubeError('Unexpected response.', response);
|
|
|
|
this.replies = observe(response.on_response_received_endpoints_memo.getType(Comment).map((comment) => {
|
|
comment.setActions(this.#actions);
|
|
return comment;
|
|
}));
|
|
|
|
this.#continuation = response.on_response_received_endpoints_memo.getType(ContinuationItem).first();
|
|
|
|
return this;
|
|
}
|
|
|
|
get has_continuation(): boolean {
|
|
if (!this.replies)
|
|
throw new InnertubeError('Cannot determine if there is a continuation because this thread\'s replies have not been loaded.');
|
|
return !!this.#continuation;
|
|
}
|
|
|
|
setActions(actions: Actions) {
|
|
this.#actions = actions;
|
|
}
|
|
} |