diff --git a/examples/channel/basic-info.ts b/examples/channel/basic-info.ts index 06bfa18e..90f7cbd3 100644 --- a/examples/channel/basic-info.ts +++ b/examples/channel/basic-info.ts @@ -1,12 +1,14 @@ -import { Innertube, UniversalCache } from 'youtubei.js'; +import { Innertube, UniversalCache, YTNodes } from 'youtubei.js'; (async () => { const yt = await Innertube.create({ cache: new UniversalCache() }); const channel = await yt.getChannel('UCX6OQ3DkcsbYNE6H8uQQuVA'); - console.info('Viewing channel:', channel.header.author.name); - console.info('Family Safe:', channel.metadata.is_family_safe); + if (channel.header?.is(YTNodes.C4TabbedHeader)) { + console.info('Viewing channel:', channel?.header?.author.name); + console.info('Family Safe:', channel.metadata.is_family_safe); + } const about = await channel.getAbout(); diff --git a/scripts/build-parser-map.js b/scripts/build-parser-map.js index 5c12406c..571ddde6 100644 --- a/scripts/build-parser-map.js +++ b/scripts/build-parser-map.js @@ -24,11 +24,11 @@ import { YTNodeConstructor } from './helpers'; ${import_list.join('\n')} -const map: Record = { +export const YTNodes = { ${json.join(',\n ')} }; -export const YTNodes = map; +const map: Record = YTNodes; /** * @param name - Name of the node to be parsed diff --git a/src/parser/classes/CarouselHeader.ts b/src/parser/classes/CarouselHeader.ts new file mode 100644 index 00000000..e73362e9 --- /dev/null +++ b/src/parser/classes/CarouselHeader.ts @@ -0,0 +1,13 @@ +import Parser from '..'; +import { YTNode } from '../helpers'; + +export default class CarouselHeader extends YTNode { + static type = 'CarouselHeader'; + + contents: YTNode[]; + + constructor(data: any) { + super(); + this.contents = Parser.parseArray(data.contents); + } +} \ No newline at end of file diff --git a/src/parser/classes/CarouselItem.ts b/src/parser/classes/CarouselItem.ts new file mode 100644 index 00000000..417cff8f --- /dev/null +++ b/src/parser/classes/CarouselItem.ts @@ -0,0 +1,23 @@ +import Parser from '..'; +import { YTNode } from '../helpers'; + +import Thumbnail from './misc/Thumbnail'; + +export default class CarouselItem extends YTNode { + static type = 'CarouselItem'; + + items: YTNode[]; + background_color: string; + layout_style: string; + pagination_thumbnails: Thumbnail[]; + paginator_alignment: string; + + constructor (data: any) { + super(); + this.items = Parser.parseArray(data.carouselItems); + this.background_color = data.backgroundColor; + this.layout_style = data.layoutStyle; + this.pagination_thumbnails = Thumbnail.fromResponse(data.paginationThumbnails); + this.paginator_alignment = data.paginatorAlignment; + } +} \ No newline at end of file diff --git a/src/parser/classes/CompactStation.ts b/src/parser/classes/CompactStation.ts new file mode 100644 index 00000000..20afbe44 --- /dev/null +++ b/src/parser/classes/CompactStation.ts @@ -0,0 +1,25 @@ +import { YTNode } from '../helpers'; + +import Text from './misc/Text'; +import Thumbnail from './misc/Thumbnail'; +import NavigationEndpoint from './NavigationEndpoint'; + +export default class CompactStation extends YTNode { + static type = 'CompactStation'; + + title: Text; + description: Text; + video_count: Text; + endpoint: NavigationEndpoint; + thumbnail: Thumbnail[]; + + constructor(data: any) { + super(); + + this.title = new Text(data.title); + this.description = new Text(data.description); + this.video_count = new Text(data.videoCountText); + this.endpoint = new NavigationEndpoint(data.navigationEndpoint); + this.thumbnail = Thumbnail.fromResponse(data.thumbnail); + } +} \ No newline at end of file diff --git a/src/parser/classes/DefaultPromoPanel.ts b/src/parser/classes/DefaultPromoPanel.ts new file mode 100644 index 00000000..8e513dc4 --- /dev/null +++ b/src/parser/classes/DefaultPromoPanel.ts @@ -0,0 +1,36 @@ +import Parser from '..'; +import { YTNode } from '../helpers'; + +import Text from './misc/Text'; +import NavigationEndpoint from './NavigationEndpoint'; + +export default class DefaultPromoPanel extends YTNode { + static type = 'DefaultPromoPanel'; + + title: Text; + description: Text; + endpoint: NavigationEndpoint; + large_form_factor_background_thumbnail; + small_form_factor_background_thumbnail; + scrim_color_values: number[]; + min_panel_display_duration_ms: number; + min_video_play_duration_ms: number; + scrim_duration: number; + metadata_order: string; + panel_layout: string; + + constructor(data: any) { + super(); + this.title = new Text(data.title); + this.description = new Text(data.description); + this.endpoint = new NavigationEndpoint(data.navigationEndpoint); + this.large_form_factor_background_thumbnail = Parser.parseItem(data.largeFormFactorBackgroundThumbnail); + this.small_form_factor_background_thumbnail = Parser.parseItem(data.smallFormFactorBackgroundThumbnail); + this.scrim_color_values = data.scrimColorValues; + this.min_panel_display_duration_ms = data.minPanelDisplayDurationMs; + this.min_video_play_duration_ms = data.minVideoPlayDurationMs; + this.scrim_duration = data.scrimDuration; + this.metadata_order = data.metadataOrder; + this.panel_layout = data.panelLayout; + } +} \ No newline at end of file diff --git a/src/parser/classes/GameCard.ts b/src/parser/classes/GameCard.ts new file mode 100644 index 00000000..47b7778a --- /dev/null +++ b/src/parser/classes/GameCard.ts @@ -0,0 +1,13 @@ +import Parser from '..'; +import { YTNode } from '../helpers'; + +export default class GameCard extends YTNode { + static type = 'GameCard'; + + game; + + constructor(data: any) { + super(); + this.game = Parser.parseItem(data.game); + } +} \ No newline at end of file diff --git a/src/parser/classes/GameDetails.ts b/src/parser/classes/GameDetails.ts new file mode 100644 index 00000000..4ee4fe96 --- /dev/null +++ b/src/parser/classes/GameDetails.ts @@ -0,0 +1,24 @@ +import { YTNode } from '../helpers'; + +import Text from './misc/Text'; +import Thumbnail from './misc/Thumbnail'; +import NavigationEndpoint from './NavigationEndpoint'; + +export default class GameDetails extends YTNode { + static type = 'GameDetails'; + + title: Text; + box_art: Thumbnail[]; + box_art_overlay_text: Text; + endpoint: NavigationEndpoint; + is_official_box_art: boolean; + + constructor(data: any) { + super(); + this.title = new Text(data.title); + this.box_art = Thumbnail.fromResponse(data.boxArt); + this.box_art_overlay_text = new Text(data.boxArtOverlayText); + this.endpoint = new NavigationEndpoint(data.endpoint); + this.is_official_box_art = data.isOfficialBoxArt; + } +} \ No newline at end of file diff --git a/src/parser/classes/InteractiveTabbedHeader.ts b/src/parser/classes/InteractiveTabbedHeader.ts new file mode 100644 index 00000000..d7e6d11d --- /dev/null +++ b/src/parser/classes/InteractiveTabbedHeader.ts @@ -0,0 +1,36 @@ +import Parser from '..'; +import { ObservedArray, YTNode } from '../helpers'; + +import Text from './misc/Text'; +import Thumbnail from './misc/Thumbnail'; +import SubscribeButton from './SubscribeButton'; +import MetadataBadge from './MetadataBadge'; +import Button from './Button'; + +export default class InteractiveTabbedHeader extends YTNode { + static type = 'InteractiveTabbedHeader'; + + header_type: string; + title: Text; + description: Text; + metadata: Text; + badges: MetadataBadge[]; + box_art: Thumbnail[]; + banner: Thumbnail[]; + buttons: ObservedArray; + auto_generated: Text; + + constructor(data: any) { + super(); + + this.header_type = data.type; + this.title = new Text(data.title); + this.description = new Text(data.description); + this.metadata = new Text(data.metadata); + this.badges = Parser.parseArray(data.badges, MetadataBadge); + this.box_art = Thumbnail.fromResponse(data.boxArt); + this.banner = Thumbnail.fromResponse(data.banner); + this.buttons = Parser.parseArray(data.buttons, [ SubscribeButton, Button ]); + this.auto_generated = new Text(data.autoGenerated); + } +} \ No newline at end of file diff --git a/src/parser/classes/ItemSection.ts b/src/parser/classes/ItemSection.ts index 06415a43..d111057c 100644 --- a/src/parser/classes/ItemSection.ts +++ b/src/parser/classes/ItemSection.ts @@ -3,17 +3,18 @@ import ItemSectionHeader from './ItemSectionHeader'; import { YTNode } from '../helpers'; import ItemSectionTabbedHeader from './ItemSectionTabbedHeader'; +import CommentsHeader from './comments/CommentsHeader'; class ItemSection extends YTNode { static type = 'ItemSection'; - header: ItemSectionHeader | ItemSectionTabbedHeader | null; + header: CommentsHeader | ItemSectionHeader | ItemSectionTabbedHeader | null; contents; target_id; constructor(data: any) { super(); - this.header = Parser.parseItem(data.header, [ ItemSectionHeader, ItemSectionTabbedHeader ]); + this.header = Parser.parseItem(data.header); this.contents = Parser.parse(data.contents, true); if (data.targetId || data.sectionIdentifier) { diff --git a/src/parser/classes/PlaylistCustomThumbnail.ts b/src/parser/classes/PlaylistCustomThumbnail.ts new file mode 100644 index 00000000..6b3baee8 --- /dev/null +++ b/src/parser/classes/PlaylistCustomThumbnail.ts @@ -0,0 +1,13 @@ +import { YTNode } from '../helpers'; +import Thumbnail from './misc/Thumbnail'; + +export default class PlaylistCustomThumbnail extends YTNode { + static type = 'PlaylistCustomThumbnail'; + + thumbnail: Thumbnail[]; + + constructor(data: any) { + super(); + this.thumbnail = Thumbnail.fromResponse(data.thumbnail); + } +} \ No newline at end of file diff --git a/src/parser/classes/RecognitionShelf.ts b/src/parser/classes/RecognitionShelf.ts new file mode 100644 index 00000000..97539dfa --- /dev/null +++ b/src/parser/classes/RecognitionShelf.ts @@ -0,0 +1,26 @@ +import Parser from '..'; +import { YTNode } from '../helpers'; + +import Button from './Button'; +import Text from './misc/Text'; +import Thumbnail from './misc/Thumbnail'; + +export default class RecognitionShelf extends YTNode { + static type = 'RecognitionShelf'; + + title: Text; + subtitle: Text; + avatars: Thumbnail[]; + button: Button | null; + surface: string; + + constructor(data: any) { + super(); + + this.title = new Text(data.title); + this.subtitle = new Text(data.subtitle); + this.avatars = data.avatars.map((avatar: any) => new Thumbnail(avatar)); + this.button = Parser.parseItem