diff --git a/README.md b/README.md
index 52897504..634ffc07 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@
Special thanks to:
-
+
diff --git a/deno/package.json b/deno/package.json
index de758ec0..215a0805 100644
--- a/deno/package.json
+++ b/deno/package.json
@@ -1,6 +1,6 @@
{
"name": "youtubei.js",
- "version": "4.2.0",
+ "version": "4.3.0",
"description": "A wrapper around YouTube's private API. Supports YouTube, YouTube Music, YouTube Kids and YouTube Studio (WIP).",
"type": "module",
"types": "./dist/src/platform/lib.d.ts",
diff --git a/deno/src/parser/classes/Button.ts b/deno/src/parser/classes/Button.ts
index aed8b2aa..8c3fc848 100644
--- a/deno/src/parser/classes/Button.ts
+++ b/deno/src/parser/classes/Button.ts
@@ -2,8 +2,9 @@ import Text from './misc/Text.ts';
import NavigationEndpoint from './NavigationEndpoint.ts';
import { YTNode } from '../helpers.ts';
+import type { RawNode } from '../index.ts';
-class Button extends YTNode {
+export default class Button extends YTNode {
static type = 'Button';
text?: string;
@@ -15,7 +16,7 @@ class Button extends YTNode {
endpoint: NavigationEndpoint;
- constructor(data: any) {
+ constructor(data: RawNode) {
super();
if (data.text) {
@@ -40,6 +41,4 @@ class Button extends YTNode {
this.endpoint = new NavigationEndpoint(data.navigationEndpoint || data.serviceEndpoint || data.command);
}
-}
-
-export default Button;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/deno/src/parser/classes/GridVideo.ts b/deno/src/parser/classes/GridVideo.ts
index 0e8d1b7f..d6d1f04f 100644
--- a/deno/src/parser/classes/GridVideo.ts
+++ b/deno/src/parser/classes/GridVideo.ts
@@ -8,7 +8,7 @@ import Menu from './menus/Menu.ts';
import { YTNode } from '../helpers.ts';
-class GridVideo extends YTNode {
+export default class GridVideo extends YTNode {
static type = 'GridVideo';
id: string;
@@ -23,10 +23,15 @@ class GridVideo extends YTNode {
short_view_count: Text;
endpoint: NavigationEndpoint;
menu: Menu | null;
+ buttons?;
+ upcoming?: Date;
+ upcoming_text?: Text;
+ is_reminder_set?: boolean;
constructor(data: RawNode) {
super();
const length_alt = data.thumbnailOverlays.find((overlay: any) => overlay.hasOwnProperty('thumbnailOverlayTimeStatusRenderer'))?.thumbnailOverlayTimeStatusRenderer;
+
this.id = data.videoId;
this.title = new Text(data.title);
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
@@ -39,7 +44,19 @@ class GridVideo extends YTNode {
this.short_view_count = new Text(data.shortViewCountText);
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
this.menu = Parser.parseItem(data.menu, Menu);
- }
-}
-export default GridVideo;
\ No newline at end of file
+ if (Reflect.has(data, 'buttons')) {
+ this.buttons = Parser.parseArray(data.buttons);
+ }
+
+ if (Reflect.has(data, 'upcomingEventData')) {
+ this.upcoming = new Date(Number(`${data.upcomingEventData.startTime}000`));
+ this.upcoming_text = new Text(data.upcomingEventData.upcomingEventText);
+ this.is_reminder_set = !!data.upcomingEventData?.isReminderSet;
+ }
+ }
+
+ get is_upcoming(): boolean {
+ return Boolean(this.upcoming && this.upcoming > new Date());
+ }
+}
\ No newline at end of file
diff --git a/deno/src/parser/classes/GuideCollapsibleEntry.ts b/deno/src/parser/classes/GuideCollapsibleEntry.ts
index 28cd98ac..921fa40c 100644
--- a/deno/src/parser/classes/GuideCollapsibleEntry.ts
+++ b/deno/src/parser/classes/GuideCollapsibleEntry.ts
@@ -1,35 +1,19 @@
-import Text from './misc/Text.ts';
-import { YTNode } from '../helpers.ts';
import Parser from '../parser.ts';
+import GuideEntry from './GuideEntry.ts';
+import type { RawNode } from '../index.ts';
+import { YTNode } from '../helpers.ts';
-class GuideCollapsibleEntry extends YTNode {
+export default class GuideCollapsibleEntry extends YTNode {
static type = 'GuideCollapsibleEntry';
- expander_item: {
- title: string,
- icon_type: string
- };
- collapser_item: {
- title: string,
- icon_type: string
- };
+ expander_item: GuideEntry | null;
+ collapser_item: GuideEntry | null;
expandable_items;
- constructor(data: any) {
+ constructor(data: RawNode) {
super();
-
- this.expander_item = {
- title: new Text(data.expanderItem.guideEntryRenderer.formattedTitle).toString(),
- icon_type: data.expanderItem.guideEntryRenderer.icon.iconType
- };
-
- this.collapser_item = {
- title: new Text(data.collapserItem.guideEntryRenderer.formattedTitle).toString(),
- icon_type: data.collapserItem.guideEntryRenderer.icon.iconType
- };
-
+ this.expander_item = Parser.parseItem(data.expanderItem, GuideEntry);
+ this.collapser_item = Parser.parseItem(data.collapserItem, GuideEntry);
this.expandable_items = Parser.parseArray(data.expandableItems);
}
-}
-
-export default GuideCollapsibleEntry;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/deno/src/parser/classes/GuideCollapsibleSectionEntry.ts b/deno/src/parser/classes/GuideCollapsibleSectionEntry.ts
index 75eb0d73..767eea3f 100644
--- a/deno/src/parser/classes/GuideCollapsibleSectionEntry.ts
+++ b/deno/src/parser/classes/GuideCollapsibleSectionEntry.ts
@@ -1,7 +1,8 @@
-import { YTNode } from '../helpers.ts';
import Parser from '../parser.ts';
+import type { RawNode } from '../index.ts';
+import { YTNode } from '../helpers.ts';
-class GuideCollapsibleSectionEntry extends YTNode {
+export default class GuideCollapsibleSectionEntry extends YTNode {
static type = 'GuideCollapsibleSectionEntry';
header_entry;
@@ -9,15 +10,11 @@ class GuideCollapsibleSectionEntry extends YTNode {
collapser_icon: string;
section_items;
- constructor(data: any) {
+ constructor(data: RawNode) {
super();
-
this.header_entry = Parser.parseItem(data.headerEntry);
this.expander_icon = data.expanderIcon.iconType;
this.collapser_icon = data.collapserIcon.iconType;
this.section_items = Parser.parseArray(data.sectionItems);
-
}
-}
-
-export default GuideCollapsibleSectionEntry;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/deno/src/parser/classes/GuideDownloadsEntry.ts b/deno/src/parser/classes/GuideDownloadsEntry.ts
index 2c27f921..07884970 100644
--- a/deno/src/parser/classes/GuideDownloadsEntry.ts
+++ b/deno/src/parser/classes/GuideDownloadsEntry.ts
@@ -1,14 +1,13 @@
import GuideEntry from './GuideEntry.ts';
+import type { RawNode } from '../index.ts';
-class GuideDownloadsEntry extends GuideEntry {
+export default class GuideDownloadsEntry extends GuideEntry {
static type = 'GuideDownloadsEntry';
always_show: boolean;
- constructor(data: any) {
+ constructor(data: RawNode) {
super(data.entryRenderer.guideEntryRenderer);
this.always_show = !!data.alwaysShow;
}
-}
-
-export default GuideDownloadsEntry;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/deno/src/parser/classes/GuideEntry.ts b/deno/src/parser/classes/GuideEntry.ts
index 76a232fc..529a4d5f 100644
--- a/deno/src/parser/classes/GuideEntry.ts
+++ b/deno/src/parser/classes/GuideEntry.ts
@@ -1,9 +1,11 @@
-import Text from './misc/Text.ts';
import NavigationEndpoint from './NavigationEndpoint.ts';
-import { YTNode } from '../helpers.ts';
+import Text from './misc/Text.ts';
import Thumbnail from './misc/Thumbnail.ts';
-class GuideEntry extends YTNode {
+import { YTNode } from '../helpers.ts';
+import type { RawNode } from '../index.ts';
+
+export default class GuideEntry extends YTNode {
static type = 'GuideEntry';
title: Text;
@@ -13,21 +15,24 @@ class GuideEntry extends YTNode {
badges?: any;
is_primary: boolean;
- constructor(data: any) {
+ constructor(data: RawNode) {
super();
this.title = new Text(data.formattedTitle);
this.endpoint = new NavigationEndpoint(data.navigationEndpoint || data.serviceEndpoint);
- if (data.icon?.iconType) {
+
+ if (Reflect.has(data, 'icon') && Reflect.has(data.icon, 'iconType')) {
this.icon_type = data.icon.iconType;
}
- if (data.thumbnail) {
+
+ if (Reflect.has(data, 'thumbnail')) {
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
}
- if (data.badges) {
+
+ // (LuanRT) XXX: Check this property's data and parse it.
+ if (Reflect.has(data, 'badges')) {
this.badges = data.badges;
}
+
this.is_primary = !!data.isPrimary;
}
-}
-
-export default GuideEntry;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/deno/src/parser/classes/GuideSection.ts b/deno/src/parser/classes/GuideSection.ts
index 0361b5fd..ef1fca23 100644
--- a/deno/src/parser/classes/GuideSection.ts
+++ b/deno/src/parser/classes/GuideSection.ts
@@ -1,20 +1,20 @@
import Text from './misc/Text.ts';
-import { YTNode } from '../helpers.ts';
import Parser from '../parser.ts';
+import { YTNode } from '../helpers.ts';
+import type { RawNode } from '../index.ts';
-class GuideSection extends YTNode {
+export default class GuideSection extends YTNode {
static type = 'GuideSection';
title?: Text;
items;
- constructor(data: any) {
+ constructor(data: RawNode) {
super();
- if (data.formattedTitle) {
+ if (Reflect.has(data, 'formattedTitle')) {
this.title = new Text(data.formattedTitle);
}
+
this.items = Parser.parseArray(data.items);
}
-}
-
-export default GuideSection;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/deno/src/parser/classes/GuideSubscriptionsSection.ts b/deno/src/parser/classes/GuideSubscriptionsSection.ts
index e6a2864f..6af8e45a 100644
--- a/deno/src/parser/classes/GuideSubscriptionsSection.ts
+++ b/deno/src/parser/classes/GuideSubscriptionsSection.ts
@@ -1,7 +1,5 @@
import GuideSection from './GuideSection.ts';
-class GuideSubscriptionsSection extends GuideSection {
+export default class GuideSubscriptionsSection extends GuideSection {
static type = 'GuideSubscriptionsSection';
-}
-
-export default GuideSubscriptionsSection;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/deno/src/parser/classes/MusicResponsiveListItem.ts b/deno/src/parser/classes/MusicResponsiveListItem.ts
index fc48ad02..80bf0f4e 100644
--- a/deno/src/parser/classes/MusicResponsiveListItem.ts
+++ b/deno/src/parser/classes/MusicResponsiveListItem.ts
@@ -1,33 +1,33 @@
-// TODO: this needs a refactor
-// Seems like a mess to use
+// TODO: Clean up and refactor this.
import Parser from '../index.ts';
-import Text from './misc/Text.ts';
-import TextRun from './misc/TextRun.ts';
-import Thumbnail from './misc/Thumbnail.ts';
-import NavigationEndpoint from './NavigationEndpoint.ts';
import MusicItemThumbnailOverlay from './MusicItemThumbnailOverlay.ts';
-import MusicResponsiveListItemFlexColumn from './MusicResponsiveListItemFlexColumn.ts';
import MusicResponsiveListItemFixedColumn from './MusicResponsiveListItemFixedColumn.ts';
+import MusicResponsiveListItemFlexColumn from './MusicResponsiveListItemFlexColumn.ts';
+import MusicThumbnail from './MusicThumbnail.ts';
+import NavigationEndpoint from './NavigationEndpoint.ts';
import Menu from './menus/Menu.ts';
+import Text from './misc/Text.ts';
-import { timeToSeconds } from '../../utils/Utils.ts';
+import { isTextRun, timeToSeconds } from '../../utils/Utils.ts';
+import type { ObservedArray } from '../helpers.ts';
import { YTNode } from '../helpers.ts';
+import type { RawNode } from '../index.ts';
-class MusicResponsiveListItem extends YTNode {
+export default class MusicResponsiveListItem extends YTNode {
static type = 'MusicResponsiveListItem';
- #flex_columns;
- #fixed_columns;
+ flex_columns: ObservedArray;
+ fixed_columns: ObservedArray;
#playlist_item_data;
- endpoint;
- item_type;
- index;
- thumbnails;
+ endpoint: NavigationEndpoint | null;
+ item_type: 'album' | 'playlist' | 'artist' | 'library_artist' | 'video' | 'song' | 'endpoint' | 'unknown' | undefined;
+ index?: Text;
+ thumbnail?: MusicThumbnail | null;
badges;
- menu;
- overlay;
+ menu?: Menu | null;
+ overlay?: MusicItemThumbnailOverlay | null;
id?: string;
title?: string;
@@ -59,19 +59,20 @@ class MusicResponsiveListItem extends YTNode {
subtitle?: Text;
subscribers?: string;
song_count?: string;
+
// TODO: these might be replaceable with Author class
author?: {
name: string,
channel_id?: string
endpoint?: NavigationEndpoint
};
- item_count?: string | undefined;
+ item_count?: string;
year?: string;
- constructor(data: any) {
+ constructor(data: RawNode) {
super();
- this.#flex_columns = Parser.parseArray(data.flexColumns, MusicResponsiveListItemFlexColumn);
- this.#fixed_columns = Parser.parseArray(data.fixedColumns, MusicResponsiveListItemFixedColumn);
+ this.flex_columns = Parser.parseArray(data.flexColumns, MusicResponsiveListItemFlexColumn);
+ this.fixed_columns = Parser.parseArray(data.fixedColumns, MusicResponsiveListItemFixedColumn);
this.#playlist_item_data = {
video_id: data?.playlistItemData?.videoId || null,
@@ -101,7 +102,7 @@ class MusicResponsiveListItem extends YTNode {
this.#parseLibraryArtist();
break;
default:
- if (this.#flex_columns[1]) {
+ if (this.flex_columns[1]) {
this.#parseVideoOrSong();
} else {
this.#parseOther();
@@ -113,14 +114,14 @@ class MusicResponsiveListItem extends YTNode {
this.index = new Text(data.index);
}
- this.thumbnails = data.thumbnail ? Thumbnail.fromResponse(data.thumbnail.musicThumbnailRenderer?.thumbnail) : [];
+ this.thumbnail = Parser.parseItem(data.thumbnail, MusicThumbnail);
this.badges = Parser.parseArray(data.badges);
- this.menu = Parser.parseItem