mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-16 11:02:10 +00:00
feat(VideoSecondaryInfo): add support for attributed descriptions (#325)
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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<VideoOwner>(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<SubscribeButton | Button>(data.subscribeButton, [ SubscribeButton, Button ]);
|
||||
this.metadata = Parser.parseItem<MetadataRowContainer>(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;
|
||||
@@ -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) => `</${tag}>`).join('');
|
||||
const wrapped_text = `<span style="white-space: pre-wrap;">${styled_text}</span>`;
|
||||
|
||||
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 `<a href="${nav_url}" class="yt-ch-link" display: block; width: fit-content; font-size: small;><img src="${url}" style="vertical-align: middle; height: ${this.attachment.element.properties.layoutProperties.height.value}px; width: ${this.attachment.element.properties.layoutProperties.width.value}px;">${wrapped_text}</a>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.endpoint) {
|
||||
const url = this.endpoint.toURL();
|
||||
if (url) return `<a href="${url}">${wrapped_text}</a>`;
|
||||
}
|
||||
|
||||
return wrapped_text;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user