UNPKG

@kevinwatt/yt-dlp-mcp

Version:

An MCP server implementation that integrates with yt-dlp, providing video and audio content download capabilities (e.g. YouTube, Facebook, Tiktok, etc.) for LLMs.

149 lines 5.36 kB
export function prepareComments(rawComments) { const comments = normalizeComments(rawComments); const commentById = new Map(); for (const comment of comments) { commentById.set(comment.id, comment); } const childrenByParent = new Map(); const rootIds = []; for (const comment of comments) { const originalParent = comment.parent; const parentExists = originalParent === "root" || commentById.has(originalParent); if (originalParent === comment.id || !parentExists) { comment.parent = "root"; comment.is_orphan = originalParent !== "root"; } if (comment.parent === "root") { rootIds.push(comment.id); continue; } const children = childrenByParent.get(comment.parent) ?? []; children.push(comment.id); childrenByParent.set(comment.parent, children); } const assigned = new Set(); const threadedComments = []; for (const rootId of rootIds) { const thread = buildThread(rootId, 0, commentById, childrenByParent, assigned, new Set()); if (thread) { threadedComments.push(thread); } } for (const comment of comments) { if (assigned.has(comment.id)) { continue; } if (comment.parent !== "root") { comment.parent = "root"; comment.is_orphan = true; } const thread = buildThread(comment.id, 0, commentById, childrenByParent, assigned, new Set()); if (thread) { threadedComments.push(thread); } } return { detectedCount: comments.length, hasThreading: threadedComments.some((thread) => thread.reply_count > 0), flatComments: comments.map(stripInternalComment), threadedComments: threadedComments.map(stripInternalThread), orphanCommentIds: comments .filter((comment) => comment.is_orphan) .map((comment) => comment.id), }; } function normalizeComments(rawComments) { if (!Array.isArray(rawComments)) { return []; } const seenIds = new Set(); const comments = []; for (const item of rawComments) { if (!item || typeof item !== "object" || Array.isArray(item)) { continue; } const rawComment = item; const id = readOptionalIdentifier(rawComment.id); if (!id || seenIds.has(id)) { continue; } seenIds.add(id); comments.push({ id, text: readOptionalString(rawComment.text, true), author: readOptionalString(rawComment.author), author_id: readOptionalString(rawComment.author_id), author_url: readOptionalString(rawComment.author_url), author_is_uploader: readOptionalBoolean(rawComment.author_is_uploader), author_is_verified: readOptionalBoolean(rawComment.author_is_verified), like_count: readOptionalNumber(rawComment.like_count), is_pinned: readOptionalBoolean(rawComment.is_pinned), is_favorited: readOptionalBoolean(rawComment.is_favorited), parent: readOptionalIdentifier(rawComment.parent) ?? "root", timestamp: readOptionalNumber(rawComment.timestamp), time_text: readOptionalString(rawComment.time_text) ?? readOptionalString(rawComment._time_text) ?? null, depth: 0, reply_count: 0, is_orphan: false, }); } return comments; } function buildThread(commentId, depth, commentById, childrenByParent, assigned, stack) { const comment = commentById.get(commentId); if (!comment || stack.has(commentId) || assigned.has(commentId)) { return null; } assigned.add(commentId); comment.depth = depth; const nextStack = new Set(stack); nextStack.add(commentId); const replies = []; for (const childId of childrenByParent.get(commentId) ?? []) { const child = buildThread(childId, depth + 1, commentById, childrenByParent, assigned, nextStack); if (child) { replies.push(child); } } comment.reply_count = replies.length; return { ...comment, replies, }; } function stripInternalComment(comment) { const { is_orphan: _isOrphan, ...publicComment } = comment; return publicComment; } function stripInternalThread(comment) { const { is_orphan: _isOrphan, replies, ...publicComment } = comment; return { ...publicComment, replies: replies.map(stripInternalThread), }; } function readOptionalString(value, allowEmpty = false) { if (typeof value !== "string") { return undefined; } if (allowEmpty || value.length > 0) { return value; } return undefined; } function readOptionalIdentifier(value) { if (typeof value === "string") { return value.length > 0 ? value : undefined; } if (typeof value === "number" && Number.isFinite(value)) { return String(value); } return undefined; } function readOptionalNumber(value) { return typeof value === "number" && Number.isFinite(value) ? value : undefined; } function readOptionalBoolean(value) { return typeof value === "boolean" ? value : undefined; } //# sourceMappingURL=comments-prepare.js.map