diff --git a/src/parser/classes/NavigationEndpoint.ts b/src/parser/classes/NavigationEndpoint.ts index addbe8df6..0f28216e7 100644 --- a/src/parser/classes/NavigationEndpoint.ts +++ b/src/parser/classes/NavigationEndpoint.ts @@ -21,7 +21,6 @@ class NavigationEndpoint extends YTNode { constructor(data: any) { super(); - // This is only present in Android nav endpoints if (Reflect.has(data || {}, 'innertubeCommand')) data = data.innertubeCommand; diff --git a/src/parser/classes/VideoSecondaryInfo.ts b/src/parser/classes/VideoSecondaryInfo.ts index 77e5fb271..b719bdf01 100644 --- a/src/parser/classes/VideoSecondaryInfo.ts +++ b/src/parser/classes/VideoSecondaryInfo.ts @@ -1,4 +1,4 @@ -import Parser from '../index.js'; +import Parser, { RawNode } from '../index.js'; import Text from './misc/Text.js'; import Button from './Button.js'; import VideoOwner from './VideoOwner.js'; @@ -9,19 +9,24 @@ import { YTNode } from '../helpers.js'; class VideoSecondaryInfo extends YTNode { static type = 'VideoSecondaryInfo'; - owner: VideoOwner | null;// TODO: VideoOwner? + owner: VideoOwner | null; description: Text; - subscribe_button; + subscribe_button: SubscribeButton | Button | null; metadata: MetadataRowContainer | null; show_more_text: string; show_less_text: string; default_expanded: string; description_collapsed_lines: string; - constructor(data: any) { + constructor(data: RawNode) { super(); this.owner = Parser.parseItem(data.owner); this.description = new Text(data.description); + + if (Reflect.has(data, 'attributedDescription')) { + this.description = new Text(this.#convertAttributedDescriptionToRuns(data.attributedDescription)); + } + this.subscribe_button = Parser.parseItem(data.subscribeButton, [ SubscribeButton, Button ]); this.metadata = Parser.parseItem(data.metadataRowContainer, MetadataRowContainer); this.show_more_text = data.showMoreText; @@ -29,6 +34,74 @@ class VideoSecondaryInfo extends YTNode { this.default_expanded = data.defaultExpanded; this.description_collapsed_lines = data.descriptionCollapsedLines; } + + #convertAttributedDescriptionToRuns(description: RawNode) { + const runs: { + text: string, + navigationEndpoint?: RawNode, + attachment?: RawNode + }[] = []; + + const content = description.content; + const command_runs = description.commandRuns; + + let last_end_index = 0; + + if (command_runs) { + for (const item of command_runs) { + const length: number = item.length; + const start_index: number = item.startIndex; + + if (start_index > last_end_index) { + runs.push({ + text: content.slice(last_end_index, start_index) + }); + } + + if (Reflect.has(item, 'onTap')) { + let attachment = null; + + if (Reflect.has(description, 'attachmentRuns')) { + const attachment_runs = description.attachmentRuns; + + for (const attatchment_run of attachment_runs) { + if ((attatchment_run.startIndex - 2) == start_index) { + attachment = attatchment_run; + break; + } + } + } + + if (attachment) { + runs.push({ + text: content.slice(start_index, start_index + length), + navigationEndpoint: item.onTap, + attachment + }); + } else { + runs.push({ + text: content.slice(start_index, start_index + length), + navigationEndpoint: item.onTap + }); + } + } + + last_end_index = start_index + length; + } + + if (last_end_index < content.length) { + runs.push({ + text: content.slice(last_end_index) + }); + } + } else { + runs.push({ + text: content + }); + } + + return { runs }; + } } export default VideoSecondaryInfo; \ No newline at end of file diff --git a/src/parser/classes/misc/TextRun.ts b/src/parser/classes/misc/TextRun.ts index 8bec9b929..7c3d53cbd 100644 --- a/src/parser/classes/misc/TextRun.ts +++ b/src/parser/classes/misc/TextRun.ts @@ -1,5 +1,6 @@ import NavigationEndpoint from '../NavigationEndpoint.js'; import { escape, Run } from './Text.js'; +import type { RawNode } from '../../index.js'; class TextRun implements Run { text: string; @@ -7,13 +8,15 @@ class TextRun implements Run { bold: boolean; italics: boolean; strikethrough: boolean; + attachment; - constructor(data: any) { + constructor(data: RawNode) { this.text = data.text; this.bold = Boolean(data.bold); this.italics = Boolean(data.italics); this.strikethrough = Boolean(data.strikethrough); this.endpoint = data.navigationEndpoint ? new NavigationEndpoint(data.navigationEndpoint) : undefined; + this.attachment = data.attachment; } toString() { @@ -22,16 +25,30 @@ class TextRun implements Run { toHTML(): string { const tags: string[] = []; + if (this.bold) tags.push('b'); if (this.italics) tags.push('i'); if (this.strikethrough) tags.push('s'); + const escaped_text = escape(this.text); const styled_text = tags.map((tag) => `<${tag}>`).join('') + escaped_text + tags.map((tag) => ``).join(''); const wrapped_text = `${styled_text}`; + + if (this.attachment) { + if (this.attachment.element.type.imageType.image.sources.length) { + const { url } = this.attachment.element.type.imageType.image.sources[0]; + if (this.endpoint) { + const nav_url = this.endpoint.toURL(); + if (nav_url) return `${wrapped_text}`; + } + } + } + if (this.endpoint) { const url = this.endpoint.toURL(); if (url) return `${wrapped_text}`; } + return wrapped_text; } }