mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-23 23:09:28 +00:00
feat(Parser): just-in-time YTNode generation (#310)
* refactor: merge NavigatableText into Text * fix(Text): data might not be object * refactor: remove GetParserByName from map * feat(Parser): just-in-time YTNode generation * refactor: cleanup YTNodeGenerator * fix: YTNode map imports * feat(YTNodeGenerator): primative types Add support for inferring primatives types * fix(YTNodeGenerator): NavigationEndpoint detection * fix(YTNodeGenerator): fix generated typescript Correct types and linting for generated typescript class * chore: update parsers after merge * feat: add support for object type inference * fix: object type def * docs: basic YTNodeGenerator explanation * docs: tsdoc for YTNodeGenerator * docs: update parser updating guide * fix: apply suggested changes * docs: accessing generated nodes
This commit is contained in:
@@ -3,7 +3,6 @@ import Parser from '../index.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import PlaylistAuthor from './misc/PlaylistAuthor.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import NavigatableText from './misc/NavigatableText.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
class GridPlaylist extends YTNode {
|
||||
@@ -14,7 +13,7 @@ class GridPlaylist extends YTNode {
|
||||
author?: PlaylistAuthor;
|
||||
badges;
|
||||
endpoint: NavigationEndpoint;
|
||||
view_playlist: NavigatableText;
|
||||
view_playlist: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
thumbnail_renderer;
|
||||
sidebar_thumbnails: Thumbnail[] | null;
|
||||
@@ -32,7 +31,7 @@ class GridPlaylist extends YTNode {
|
||||
|
||||
this.badges = Parser.parse(data.ownerBadges);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.view_playlist = new NavigatableText(data.viewPlaylistText);
|
||||
this.view_playlist = new Text(data.viewPlaylistText);
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.thumbnail_renderer = Parser.parse(data.thumbnailRenderer);
|
||||
this.sidebar_thumbnails = [].concat(...data.sidebarThumbnails?.map((thumbnail: any) => Thumbnail.fromResponse(thumbnail)) || []) || null;
|
||||
|
||||
@@ -43,8 +43,8 @@ class Movie extends YTNode {
|
||||
this.author = new Author(data.longBylineText, data.ownerBadges, data.channelThumbnailSupportedRenderers?.channelThumbnailWithLinkRenderer?.thumbnail);
|
||||
|
||||
this.duration = {
|
||||
text: data.lengthText ? new Text(data.lengthText).text : new Text(overlay_time_status).text,
|
||||
seconds: timeToSeconds(data.lengthText ? new Text(data.lengthText).text : new Text(overlay_time_status).text)
|
||||
text: data.lengthText ? new Text(data.lengthText).toString() : new Text(overlay_time_status).toString(),
|
||||
seconds: timeToSeconds(data.lengthText ? new Text(data.lengthText).toString() : new Text(overlay_time_status).toString())
|
||||
};
|
||||
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
|
||||
@@ -14,7 +14,7 @@ class MusicSortFilterButton extends YTNode {
|
||||
constructor(data: any) {
|
||||
super();
|
||||
|
||||
this.title = new Text(data.title).text;
|
||||
this.title = new Text(data.title).toString();
|
||||
this.icon_type = data.icon?.icon_type || null;
|
||||
this.menu = Parser.parseItem(data.menu, MusicMultiSelectMenu);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import Thumbnail from './misc/Thumbnail.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import PlaylistAuthor from './misc/PlaylistAuthor.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import NavigatableText from './misc/NavigatableText.js';
|
||||
|
||||
class Playlist extends YTNode {
|
||||
static type = 'Playlist';
|
||||
@@ -21,7 +20,7 @@ class Playlist extends YTNode {
|
||||
badges;
|
||||
endpoint: NavigationEndpoint;
|
||||
thumbnail_overlays;
|
||||
view_playlist?: NavigatableText;
|
||||
view_playlist?: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
@@ -43,7 +42,7 @@ class Playlist extends YTNode {
|
||||
this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
|
||||
|
||||
if (data.viewPlaylistText) {
|
||||
this.view_playlist = new NavigatableText(data.viewPlaylistText);
|
||||
this.view_playlist = new Text(data.viewPlaylistText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class PlaylistVideo extends YTNode {
|
||||
}
|
||||
|
||||
this.duration = {
|
||||
text: new Text(data.lengthText).text,
|
||||
text: new Text(data.lengthText).toString(),
|
||||
seconds: parseInt(data.lengthSeconds)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ class Video extends YTNode {
|
||||
}
|
||||
|
||||
this.duration = {
|
||||
text: data.lengthText ? new Text(data.lengthText).text : new Text(overlay_time_status).text,
|
||||
seconds: timeToSeconds(data.lengthText ? new Text(data.lengthText).text : new Text(overlay_time_status).text)
|
||||
text: data.lengthText ? new Text(data.lengthText).toString() : new Text(overlay_time_status).toString(),
|
||||
seconds: timeToSeconds(data.lengthText ? new Text(data.lengthText).toString() : new Text(overlay_time_status).toString())
|
||||
};
|
||||
|
||||
this.show_action_menu = data.showActionMenu;
|
||||
|
||||
@@ -13,7 +13,7 @@ class MusicMultiSelectMenu extends YTNode {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
this.title = new Text(data.title.musicMenuTitleRenderer?.primaryText).text;
|
||||
this.title = new Text(data.title.musicMenuTitleRenderer?.primaryText).toString();
|
||||
this.options = Parser.parseArray(data.options, [ MusicMultiSelectMenuItem, MusicMenuItemDivider ]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class MusicMultiSelectMenuItem extends YTNode {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
this.title = new Text(data.title).text;
|
||||
this.title = new Text(data.title).toString();
|
||||
this.form_item_entity_key = data.formItemEntityKey;
|
||||
this.selected_icon_type = data.selectedIcon?.iconType || null;
|
||||
this.endpoint = data.selectedCommand ? new NavigationEndpoint(data.selectedCommand) : null;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import Parser from '../../index.js';
|
||||
import NavigatableText from './NavigatableText.js';
|
||||
import NavigationEndpoint from '../NavigationEndpoint.js';
|
||||
import TextRun from './TextRun.js';
|
||||
import Thumbnail from './Thumbnail.js';
|
||||
import Constants from '../../../utils/Constants.js';
|
||||
import Text from './Text.js';
|
||||
|
||||
class Author {
|
||||
#nav_text;
|
||||
@@ -11,14 +11,14 @@ class Author {
|
||||
id: string;
|
||||
name: string;
|
||||
thumbnails: Thumbnail[];
|
||||
endpoint: NavigationEndpoint | null;
|
||||
endpoint?: NavigationEndpoint;
|
||||
badges?: any;
|
||||
is_verified?: boolean | null;
|
||||
is_verified_artist?: boolean | null;
|
||||
url: string | null;
|
||||
|
||||
constructor(item: any, badges?: any, thumbs?: any) {
|
||||
this.#nav_text = new NavigatableText(item);
|
||||
this.#nav_text = new Text(item);
|
||||
|
||||
this.id =
|
||||
(this.#nav_text.runs?.[0] as TextRun)?.endpoint?.payload?.browseId ||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import Text from './Text.js';
|
||||
import NavigationEndpoint from '../NavigationEndpoint.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class NavigatableText extends Text {
|
||||
static type = 'NavigatableText';
|
||||
|
||||
endpoint: NavigationEndpoint | null;
|
||||
|
||||
constructor(node: RawNode) {
|
||||
super(node);
|
||||
// TODO: is this needed? Text now supports this itself
|
||||
this.endpoint =
|
||||
node?.runs?.[0]?.navigationEndpoint ?
|
||||
new NavigationEndpoint(node?.runs[0].navigationEndpoint) :
|
||||
node?.navigationEndpoint ?
|
||||
new NavigationEndpoint(node?.navigationEndpoint) :
|
||||
node?.titleNavigationEndpoint ?
|
||||
new NavigationEndpoint(node?.titleNavigationEndpoint) : null;
|
||||
}
|
||||
|
||||
toJSON(): NavigatableText {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default NavigatableText;
|
||||
@@ -1,5 +1,6 @@
|
||||
import TextRun from './TextRun.js';
|
||||
import EmojiRun from './EmojiRun.js';
|
||||
import NavigationEndpoint from '../NavigationEndpoint.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
export interface Run {
|
||||
@@ -18,8 +19,9 @@ export function escape(text: string) {
|
||||
}
|
||||
|
||||
class Text {
|
||||
text: string;
|
||||
text?: string;
|
||||
runs;
|
||||
endpoint?: NavigationEndpoint;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
if (data?.hasOwnProperty('runs') && Array.isArray(data.runs)) {
|
||||
@@ -29,16 +31,28 @@ class Text {
|
||||
);
|
||||
this.text = this.runs.map((run) => run.text).join('');
|
||||
} else {
|
||||
this.text = data?.simpleText || 'N/A';
|
||||
this.text = data?.simpleText;
|
||||
}
|
||||
if (typeof data === 'object' && data !== null && Reflect.has(data, 'navigationEndpoint')) {
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
}
|
||||
if (typeof data === 'object' && data !== null && Reflect.has(data, 'titleNavigationEndpoint')) {
|
||||
this.endpoint = new NavigationEndpoint(data.titleNavigationEndpoint);
|
||||
}
|
||||
if (!this.endpoint)
|
||||
this.endpoint = (this.runs?.[0] as TextRun)?.endpoint;
|
||||
}
|
||||
|
||||
toHTML() {
|
||||
return this.runs ? this.runs.map((run) => run.toHTML()).join('') : this.text;
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return this.text === undefined;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.text;
|
||||
return this.text || 'N/A';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user