From 644f8bc042cb8829fc051a846e0d431b9334f373 Mon Sep 17 00:00:00 2001 From: Daniel Wykerd Date: Tue, 31 Jan 2023 18:32:47 +0200 Subject: [PATCH] feat(parser): Text#toHTML Added support to render Text nodes as HTML for use in web applications. --- src/parser/classes/NavigationEndpoint.ts | 11 +++++++++++ src/parser/classes/misc/EmojiRun.ts | 12 +++++++++++- src/parser/classes/misc/Text.ts | 19 +++++++++++++++++++ src/parser/classes/misc/TextRun.ts | 22 +++++++++++++++++++++- 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/parser/classes/NavigationEndpoint.ts b/src/parser/classes/NavigationEndpoint.ts index 400632064..79755edc4 100644 --- a/src/parser/classes/NavigationEndpoint.ts +++ b/src/parser/classes/NavigationEndpoint.ts @@ -94,6 +94,17 @@ class NavigationEndpoint extends YTNode { throw new Error('Expected an api_url, but none was found, this is a bug.'); return actions.execute(this.metadata.api_url, { ...this.payload, ...args }); } + + toURL(): string | undefined { + if (!this.metadata.url) + return undefined; + if (!this.metadata.page_type) + return undefined; + return ( + this.metadata.page_type === 'WEB_PAGE_TYPE_UNKNOWN' ? + this.metadata.url : `https://www.youtube.com${this.metadata.url}` + ); + } } export default NavigationEndpoint; \ No newline at end of file diff --git a/src/parser/classes/misc/EmojiRun.ts b/src/parser/classes/misc/EmojiRun.ts index c3a6f7ca6..47093ebb3 100644 --- a/src/parser/classes/misc/EmojiRun.ts +++ b/src/parser/classes/misc/EmojiRun.ts @@ -1,6 +1,7 @@ +import { escape, Run } from './Text'; import Thumbnail from './Thumbnail'; -class EmojiRun { +class EmojiRun implements Run { text: string; emoji: { emoji_id: string; @@ -24,6 +25,15 @@ class EmojiRun { is_custom: !!data.emoji?.isCustomEmoji }; } + + toString() { + return this.text; + } + + toHTML(): string { + const escaped_text = escape(this.text); + return `${escaped_text}`; + } } export default EmojiRun; \ No newline at end of file diff --git a/src/parser/classes/misc/Text.ts b/src/parser/classes/misc/Text.ts index 205efd412..fc08f8385 100644 --- a/src/parser/classes/misc/Text.ts +++ b/src/parser/classes/misc/Text.ts @@ -1,6 +1,21 @@ import TextRun from './TextRun'; import EmojiRun from './EmojiRun'; +export interface Run { + text: string; + toString(): string; + toHTML(): string; +} + +export function escape(text: string) { + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + class Text { text: string; runs; @@ -17,6 +32,10 @@ class Text { } } + toHTML() { + return this.runs ? this.runs.map((run) => run.toHTML()).join('') : this.text; + } + toString() { return this.text; } diff --git a/src/parser/classes/misc/TextRun.ts b/src/parser/classes/misc/TextRun.ts index 4edb46b8a..3e35929df 100644 --- a/src/parser/classes/misc/TextRun.ts +++ b/src/parser/classes/misc/TextRun.ts @@ -1,6 +1,7 @@ import NavigationEndpoint from '../NavigationEndpoint'; +import { escape, Run } from './Text'; -class TextRun { +class TextRun implements Run { text: string; endpoint: NavigationEndpoint | undefined; bold: boolean; @@ -14,6 +15,25 @@ class TextRun { this.strikethrough = Boolean(data.strikethrough); this.endpoint = data.navigationEndpoint ? new NavigationEndpoint(data.navigationEndpoint) : undefined; } + + toString() { + return this.text; + } + + 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.endpoint) { + const url = this.endpoint.toURL(); + if (url) return `${wrapped_text}`; + } + return wrapped_text; + } } export default TextRun; \ No newline at end of file