Skip to content

Commit

Permalink
feat(VideoSecondaryInfo): add support for attributed descriptions (#325)
Browse files Browse the repository at this point in the history
  • Loading branch information
LuanRT authored Feb 26, 2023
1 parent a0e6cef commit f933cb4
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 6 deletions.
1 change: 0 additions & 1 deletion src/parser/classes/NavigationEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
81 changes: 77 additions & 4 deletions src/parser/classes/VideoSecondaryInfo.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -9,26 +9,99 @@ 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;
this.show_less_text = data.showLessText;
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;
19 changes: 18 additions & 1 deletion src/parser/classes/misc/TextRun.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import NavigationEndpoint from '../NavigationEndpoint.js';
import { escape, Run } from './Text.js';
import type { RawNode } from '../../index.js';

class TextRun implements Run {
text: string;
endpoint: NavigationEndpoint | undefined;
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() {
Expand All @@ -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;
}
}
Expand Down

0 comments on commit f933cb4

Please sign in to comment.