From e82e23dfbb24dff3ddf45754c7319d783990e254 Mon Sep 17 00:00:00 2001
From: Daniel Wykerd <45672955+Wykerd@users.noreply.github.com>
Date: Wed, 1 Feb 2023 21:27:59 +0200
Subject: [PATCH] feat(parser): Text#toHTML (#300)
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 40063206..79755edc 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 c3a6f7ca..47093ebb 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 `
`;
+ }
}
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 205efd41..fc08f838 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 4edb46b8..3e35929d 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) => `${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