mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-27 00:29:16 +00:00
feat: migrate all playlist renderers to TypeScript
This commit is contained in:
@@ -59,7 +59,7 @@ class MusicResponsiveListItem extends YTNode {
|
||||
channel_id?: string
|
||||
endpoint?: NavigationEndpoint
|
||||
};
|
||||
item_count?: number;
|
||||
item_count?: string | undefined;
|
||||
year?: string;
|
||||
|
||||
constructor(data: any) {
|
||||
@@ -190,14 +190,21 @@ class MusicResponsiveListItem extends YTNode {
|
||||
#parsePlaylist() {
|
||||
this.id = this.endpoint?.browse?.id;
|
||||
this.title = this.#flex_columns[0].key('title').instanceof(Text).toString();
|
||||
this.item_count = parseInt(this.#flex_columns[1].key('title').instanceof(Text).runs?.find((run) => run.text.match(/\d+ (song|songs)/))?.text.match(/\d+/g));
|
||||
|
||||
const item_count_run = this.#flex_columns[1].key('title')
|
||||
.instanceof(Text).runs?.find((run) => run.text.match(/\d+ (song|songs)/));
|
||||
|
||||
this.item_count = item_count_run ? item_count_run.text : undefined;
|
||||
|
||||
const author = this.#flex_columns[1].key('title').instanceof(Text).runs?.find((run) => Reflect.get(run, 'endpoint')?.browse?.id.startsWith('UC')) as TextRun;
|
||||
author && (this.author = {
|
||||
name: author.text,
|
||||
channel_id: author.endpoint?.browse?.id,
|
||||
endpoint: author.endpoint
|
||||
});
|
||||
|
||||
if (author) {
|
||||
this.author = {
|
||||
name: author.text,
|
||||
channel_id: author.endpoint?.browse?.id,
|
||||
endpoint: author.endpoint
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,20 @@ import { YTNode } from '../helpers';
|
||||
class Playlist extends YTNode {
|
||||
static type = 'Playlist';
|
||||
|
||||
constructor(data) {
|
||||
id: string;
|
||||
title: Text;
|
||||
author: Text | PlaylistAuthor;
|
||||
thumbnails: Thumbnail[];
|
||||
video_count: Text;
|
||||
video_count_short: Text;
|
||||
first_videos;
|
||||
share_url: string | null;
|
||||
menu;
|
||||
badges;
|
||||
endpoint: NavigationEndpoint;
|
||||
thumbnail_overlays;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.id = data.playlistId;
|
||||
this.title = new Text(data.title);
|
||||
@@ -17,7 +30,7 @@ class Playlist extends YTNode {
|
||||
new Text(data.shortBylineText) :
|
||||
new PlaylistAuthor(data.longBylineText, data.ownerBadges, null);
|
||||
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail || { thumbnails: data.thumbnails.map((th) => th.thumbnails).flat(1) });
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail || { thumbnails: data.thumbnails.map((th: any) => th.thumbnails).flat(1) });
|
||||
this.video_count = new Text(data.thumbnailText);
|
||||
this.video_count_short = new Text(data.videoCountShortText);
|
||||
this.first_videos = Parser.parse(data.videos) || [];
|
||||
@@ -6,12 +6,28 @@ import { YTNode } from '../helpers';
|
||||
class PlaylistHeader extends YTNode {
|
||||
static type = 'PlaylistHeader';
|
||||
|
||||
constructor(data) {
|
||||
id: string;
|
||||
title: Text;
|
||||
stats: Text[];
|
||||
brief_stats: Text[];
|
||||
author: PlaylistAuthor;
|
||||
description: Text;
|
||||
num_videos: Text;
|
||||
view_count: Text;
|
||||
can_share: boolean;
|
||||
can_delete: boolean;
|
||||
is_editable: boolean;
|
||||
privacy: string;
|
||||
save_button;
|
||||
shuffle_play_button;
|
||||
menu;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.id = data.playlistId;
|
||||
this.title = new Text(data.title);
|
||||
this.stats = data.stats.map((stat) => new Text(stat));
|
||||
this.brief_stats = data.briefStats.map((stat) => new Text(stat));
|
||||
this.stats = data.stats.map((stat: any) => new Text(stat));
|
||||
this.brief_stats = data.briefStats.map((stat: any) => new Text(stat));
|
||||
this.author = new PlaylistAuthor({ ...data.ownerText, navigationEndpoint: data.ownerEndpoint }, data.ownerBadges, null);
|
||||
this.description = new Text(data.descriptionText);
|
||||
this.num_videos = new Text(data.numVideosText);
|
||||
@@ -6,7 +6,13 @@ import { YTNode } from '../helpers';
|
||||
class PlaylistInfoCardContent extends YTNode {
|
||||
static type = 'PlaylistInfoCardContent';
|
||||
|
||||
constructor(data) {
|
||||
title: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
video_count: Text;
|
||||
channel_name: Text;
|
||||
endpoint: NavigationEndpoint;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.title = new Text(data.playlistTitle);
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
@@ -3,7 +3,10 @@ import { YTNode } from '../helpers';
|
||||
class PlaylistMetadata extends YTNode {
|
||||
static type = 'PlaylistMetadata';
|
||||
|
||||
constructor(data) {
|
||||
title: string;
|
||||
description: string;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.title = data.title;
|
||||
this.description = data.description || null;
|
||||
@@ -5,7 +5,17 @@ import { YTNode } from '../helpers';
|
||||
class PlaylistPanel extends YTNode {
|
||||
static type = 'PlaylistPanel';
|
||||
|
||||
constructor(data) {
|
||||
title: string;
|
||||
title_text: Text;
|
||||
contents;
|
||||
playlist_id: string;
|
||||
is_infinite: boolean;
|
||||
continuation: string;
|
||||
is_editable: boolean;
|
||||
preview_description: string;
|
||||
num_items_to_show: string;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.title = data.title;
|
||||
this.title_text = new Text(data.titleText);
|
||||
@@ -1,52 +0,0 @@
|
||||
import Parser from '../index';
|
||||
import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import { timeToSeconds } from '../../utils/Utils';
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class PlaylistPanelVideo extends YTNode {
|
||||
static type = 'PlaylistPanelVideo';
|
||||
|
||||
constructor(data) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.selected = data.selected;
|
||||
this.video_id = data.videoId;
|
||||
|
||||
this.duration = {
|
||||
text: new Text(data.lengthText).toString(),
|
||||
seconds: timeToSeconds(new Text(data.lengthText).toString())
|
||||
};
|
||||
|
||||
const album = new Text(data.longBylineText).runs?.find((run) => run.endpoint?.browse?.id.startsWith('MPR'));
|
||||
const artists = new Text(data.longBylineText).runs?.filter((run) => run.endpoint?.browse?.id.startsWith('UC'));
|
||||
|
||||
this.author = new Text(data.shortBylineText).toString();
|
||||
|
||||
if (album) {
|
||||
this.album = {
|
||||
id: album.endpoint?.browse.id,
|
||||
name: album.text,
|
||||
year: new Text(data.longBylineText).runs.slice(-1)[0].text,
|
||||
endpoint: album.endpoint
|
||||
};
|
||||
}
|
||||
|
||||
if (artists) {
|
||||
this.artists = artists.map((artist) => ({
|
||||
name: artist.text,
|
||||
channel_id: artist.endpoint?.browse.id,
|
||||
endpoint: artist.endpoint
|
||||
}));
|
||||
}
|
||||
|
||||
this.badges = Parser.parse(data.badges);
|
||||
this.menu = Parser.parse(data.menu);
|
||||
this.set_video_id = data.playlistSetVideoId;
|
||||
}
|
||||
}
|
||||
|
||||
export default PlaylistPanelVideo;
|
||||
85
src/parser/classes/PlaylistPanelVideo.ts
Normal file
85
src/parser/classes/PlaylistPanelVideo.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import Parser from '../index';
|
||||
import Text from './misc/Text';
|
||||
import TextRun from './misc/TextRun';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import { timeToSeconds } from '../../utils/Utils';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class PlaylistPanelVideo extends YTNode {
|
||||
static type = 'PlaylistPanelVideo';
|
||||
|
||||
title: Text;
|
||||
thumbnail: Thumbnail[];
|
||||
endpoint: NavigationEndpoint;
|
||||
selected: boolean;
|
||||
video_id: string;
|
||||
|
||||
duration: {
|
||||
text: string;
|
||||
seconds: number
|
||||
};
|
||||
|
||||
author: string;
|
||||
|
||||
album?: {
|
||||
id: string | undefined;
|
||||
name: string;
|
||||
year: string | undefined;
|
||||
endpoint: NavigationEndpoint | undefined;
|
||||
};
|
||||
|
||||
artists?: {
|
||||
name: string;
|
||||
channel_id: string | undefined;
|
||||
endpoint: NavigationEndpoint | undefined;
|
||||
}[];
|
||||
|
||||
badges;
|
||||
menu;
|
||||
set_video_id: string | undefined;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
|
||||
this.title = new Text(data.title);
|
||||
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.selected = data.selected;
|
||||
this.video_id = data.videoId;
|
||||
|
||||
this.duration = {
|
||||
text: new Text(data.lengthText).toString(),
|
||||
seconds: timeToSeconds(new Text(data.lengthText).toString())
|
||||
};
|
||||
|
||||
const album = new Text(data.longBylineText).runs?.find((run: any) => run.endpoint?.browse?.id.startsWith('MPR'));
|
||||
const artists = new Text(data.longBylineText).runs?.filter((run: any) => run.endpoint?.browse?.id.startsWith('UC'));
|
||||
|
||||
this.author = new Text(data.shortBylineText).toString();
|
||||
|
||||
if (album) {
|
||||
this.album = {
|
||||
id: (album as TextRun).endpoint?.browse?.id,
|
||||
name: (album as TextRun).text,
|
||||
year: new Text(data.longBylineText).runs?.slice(-1)[0].text,
|
||||
endpoint: (album as TextRun).endpoint
|
||||
};
|
||||
}
|
||||
|
||||
if (artists) {
|
||||
this.artists = artists.map((artist) => ({
|
||||
name: (artist as TextRun).text,
|
||||
channel_id: (artist as TextRun).endpoint?.browse?.id,
|
||||
endpoint: (artist as TextRun).endpoint
|
||||
}));
|
||||
}
|
||||
|
||||
this.badges = Parser.parse(data.badges);
|
||||
this.menu = Parser.parse(data.menu);
|
||||
this.set_video_id = data.playlistSetVideoId;
|
||||
}
|
||||
}
|
||||
|
||||
export default PlaylistPanelVideo;
|
||||
@@ -4,7 +4,9 @@ import { YTNode } from '../helpers';
|
||||
class PlaylistSidebar extends YTNode {
|
||||
static type = 'PlaylistSidebar';
|
||||
|
||||
constructor(data) {
|
||||
items;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.items = Parser.parse(data.items);
|
||||
}
|
||||
@@ -1,14 +1,22 @@
|
||||
import Parser from '../index';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import Text from './misc/Text';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class PlaylistSidebarPrimaryInfo extends YTNode {
|
||||
static type = 'PlaylistSidebarPrimaryInfo';
|
||||
|
||||
constructor(data) {
|
||||
stats: Text[];
|
||||
thumbnail_renderer;
|
||||
title: Text;
|
||||
menu;
|
||||
endpoint: NavigationEndpoint;
|
||||
description: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.stats = data.stats.map((stat) => new Text(stat));
|
||||
this.stats = data.stats.map((stat: any) => new Text(stat));
|
||||
this.thumbnail_renderer = Parser.parse(data.thumbnailRenderer);
|
||||
this.title = new Text(data.title);
|
||||
this.menu = data.menu && Parser.parse(data.menu);
|
||||
@@ -4,7 +4,10 @@ import { YTNode } from '../helpers';
|
||||
class PlaylistSidebarSecondaryInfo extends YTNode {
|
||||
static type = 'PlaylistSidebarSecondaryInfo';
|
||||
|
||||
constructor(data) {
|
||||
owner;
|
||||
button;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.owner = Parser.parse(data.videoOwner) || null;
|
||||
this.button = Parser.parse(data.button) || null;
|
||||
@@ -8,7 +8,23 @@ import { YTNode } from '../helpers';
|
||||
class PlaylistVideo extends YTNode {
|
||||
static type = 'PlaylistVideo';
|
||||
|
||||
constructor(data) {
|
||||
id: string;
|
||||
index: Text;
|
||||
title: Text;
|
||||
author: PlaylistAuthor;
|
||||
thumbnails: Thumbnail[];
|
||||
thumbnail_overlays;
|
||||
set_video_id: string | undefined;
|
||||
endpoint: NavigationEndpoint;
|
||||
is_playable: boolean;
|
||||
menu;
|
||||
|
||||
duration: {
|
||||
text: string;
|
||||
seconds: number;
|
||||
};
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.id = data.videoId;
|
||||
this.index = new Text(data.index);
|
||||
@@ -4,7 +4,12 @@ import { YTNode } from '../helpers';
|
||||
class PlaylistVideoList extends YTNode {
|
||||
static type = 'PlaylistVideoList';
|
||||
|
||||
constructor(data) {
|
||||
id: string;
|
||||
is_editable: boolean;
|
||||
can_reorder: boolean;
|
||||
videos;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.id = data.playlistId;
|
||||
this.is_editable = data.isEditable;
|
||||
@@ -4,7 +4,9 @@ import { YTNode } from '../helpers';
|
||||
class PlaylistVideoThumbnail extends YTNode {
|
||||
static type = 'PlaylistVideoThumbnail';
|
||||
|
||||
constructor(data) {
|
||||
thumbnail: Thumbnail[];
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
|
||||
}
|
||||
@@ -1,16 +1,19 @@
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import { YTNode } from '../helpers';
|
||||
import Thumbnail from './Thumbnail';
|
||||
|
||||
class EmojiRun extends YTNode {
|
||||
static type = 'EmojiRun';
|
||||
|
||||
constructor(data) {
|
||||
super();
|
||||
class EmojiRun {
|
||||
text: string;
|
||||
emoji: {
|
||||
emoji_id: string;
|
||||
shortcuts: string[];
|
||||
search_terms: string[];
|
||||
image: Thumbnail[];
|
||||
};
|
||||
|
||||
constructor(data: any) {
|
||||
this.text =
|
||||
data.emoji?.emojiId ||
|
||||
data.emoji?.shortcuts?.[0] ||
|
||||
null;
|
||||
'';
|
||||
|
||||
this.emoji = {
|
||||
emoji_id: data.emoji.emojiId,
|
||||
@@ -1,8 +1,8 @@
|
||||
import Author from './Author';
|
||||
|
||||
class PlaylistAuthor extends Author {
|
||||
constructor(data) {
|
||||
super(data);
|
||||
constructor(item, badges, thumbs) {
|
||||
super(item, badges, thumbs);
|
||||
delete this.badges;
|
||||
delete this.is_verified;
|
||||
delete this.is_verified_artist;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import TextRun from './TextRun';
|
||||
import EmojiRun from '../EmojiRun';
|
||||
import EmojiRun from './EmojiRun';
|
||||
|
||||
class Text {
|
||||
text: string;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import NavigationEndpoint from '../NavigationEndpoint';
|
||||
|
||||
class TextRun {
|
||||
constructor(data) {
|
||||
text: string;
|
||||
endpoint: NavigationEndpoint | undefined;
|
||||
|
||||
constructor(data: any) {
|
||||
this.text = data.text;
|
||||
this.endpoint = data.navigationEndpoint ? new NavigationEndpoint(data.navigationEndpoint) : undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user