mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-24 07:11:48 +00:00
feat: add support for topic/auto-generated channels and fix minor parsing errors (#233)
* dev: add support for topic channels * dev(parser): do not try to parse empty nodes * dev: add support for auto-generated game channels
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ import { YTNodeConstructor } from './helpers';
|
||||
|
||||
${import_list.join('\n')}
|
||||
|
||||
const map: Record<string, YTNodeConstructor> = {
|
||||
export const YTNodes = {
|
||||
${json.join(',\n ')}
|
||||
};
|
||||
|
||||
export const YTNodes = map;
|
||||
const map: Record<string, YTNodeConstructor> = YTNodes;
|
||||
|
||||
/**
|
||||
* @param name - Name of the node to be parsed
|
||||
|
||||
13
src/parser/classes/CarouselHeader.ts
Normal file
13
src/parser/classes/CarouselHeader.ts
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
23
src/parser/classes/CarouselItem.ts
Normal file
23
src/parser/classes/CarouselItem.ts
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
25
src/parser/classes/CompactStation.ts
Normal file
25
src/parser/classes/CompactStation.ts
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
36
src/parser/classes/DefaultPromoPanel.ts
Normal file
36
src/parser/classes/DefaultPromoPanel.ts
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
13
src/parser/classes/GameCard.ts
Normal file
13
src/parser/classes/GameCard.ts
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
24
src/parser/classes/GameDetails.ts
Normal file
24
src/parser/classes/GameDetails.ts
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
36
src/parser/classes/InteractiveTabbedHeader.ts
Normal file
36
src/parser/classes/InteractiveTabbedHeader.ts
Normal file
@@ -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<SubscribeButton | Button>;
|
||||
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<MetadataBadge>(data.badges, MetadataBadge);
|
||||
this.box_art = Thumbnail.fromResponse(data.boxArt);
|
||||
this.banner = Thumbnail.fromResponse(data.banner);
|
||||
this.buttons = Parser.parseArray<SubscribeButton | Button>(data.buttons, [ SubscribeButton, Button ]);
|
||||
this.auto_generated = new Text(data.autoGenerated);
|
||||
}
|
||||
}
|
||||
@@ -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<ItemSectionHeader | ItemSectionTabbedHeader>(data.header, [ ItemSectionHeader, ItemSectionTabbedHeader ]);
|
||||
this.header = Parser.parseItem<CommentsHeader | ItemSectionHeader | ItemSectionTabbedHeader>(data.header);
|
||||
this.contents = Parser.parse(data.contents, true);
|
||||
|
||||
if (data.targetId || data.sectionIdentifier) {
|
||||
|
||||
13
src/parser/classes/PlaylistCustomThumbnail.ts
Normal file
13
src/parser/classes/PlaylistCustomThumbnail.ts
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
26
src/parser/classes/RecognitionShelf.ts
Normal file
26
src/parser/classes/RecognitionShelf.ts
Normal file
@@ -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<Button>(data.button, Button);
|
||||
this.surface = data.surface;
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,16 @@ class RichListHeader extends YTNode {
|
||||
static type = 'RichListHeader';
|
||||
|
||||
title: Text;
|
||||
subtitle: Text;
|
||||
title_style: string | undefined;
|
||||
icon_type: string;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
this.icon_type = data.icon.iconType;
|
||||
this.subtitle = new Text(data.subtitle);
|
||||
this.title_style = data?.titleStyle?.style;
|
||||
this.icon_type = data?.icon?.iconType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
src/parser/classes/ThumbnailLandscapePortrait.ts
Normal file
15
src/parser/classes/ThumbnailLandscapePortrait.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { YTNode } from '../helpers';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
|
||||
export default class ThumbnailLandscapePortrait extends YTNode {
|
||||
static type = 'ThumbnailLandscapePortrait';
|
||||
|
||||
landscape: Thumbnail[];
|
||||
portrait: Thumbnail[];
|
||||
|
||||
constructor (data: any) {
|
||||
super();
|
||||
this.landscape = Thumbnail.fromResponse(data.landscape);
|
||||
this.portrait = Thumbnail.fromResponse(data.portrait);
|
||||
}
|
||||
}
|
||||
27
src/parser/classes/TopicChannelDetails.ts
Normal file
27
src/parser/classes/TopicChannelDetails.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import Parser from '..';
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import SubscribeButton from './SubscribeButton';
|
||||
|
||||
export default class TopicChannelDetails extends YTNode {
|
||||
static type = 'TopicChannelDetails';
|
||||
|
||||
title: Text;
|
||||
avatar: Thumbnail[];
|
||||
subtitle: Text;
|
||||
subscribe_button: SubscribeButton | null;
|
||||
endpoint: NavigationEndpoint;
|
||||
|
||||
constructor (data: any) {
|
||||
super();
|
||||
|
||||
this.title = new Text(data.title);
|
||||
this.avatar = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.subtitle = new Text(data.title);
|
||||
this.subscribe_button = Parser.parseItem<SubscribeButton>(data.subscribeButton, SubscribeButton);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
}
|
||||
}
|
||||
9
src/parser/classes/VideoCard.ts
Normal file
9
src/parser/classes/VideoCard.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import Video from './Video';
|
||||
|
||||
export default class VideoCard extends Video {
|
||||
static type = 'VideoCard';
|
||||
|
||||
constructor(data: any) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,12 @@ class NavigatableText extends Text {
|
||||
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;
|
||||
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 {
|
||||
|
||||
@@ -357,9 +357,13 @@ export default class Parser {
|
||||
}
|
||||
|
||||
static parseItem<T extends YTNode = YTNode>(data: any, validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]) {
|
||||
if (!data) return null;
|
||||
if (!data ) return null;
|
||||
|
||||
const keys = Object.keys(data);
|
||||
|
||||
if (!keys.length)
|
||||
return null;
|
||||
|
||||
const classname = this.sanitizeClassName(keys[0]);
|
||||
|
||||
if (!this.shouldIgnore(classname)) {
|
||||
|
||||
@@ -28,6 +28,8 @@ import { default as C4TabbedHeader } from './classes/C4TabbedHeader';
|
||||
import { default as CallToActionButton } from './classes/CallToActionButton';
|
||||
import { default as Card } from './classes/Card';
|
||||
import { default as CardCollection } from './classes/CardCollection';
|
||||
import { default as CarouselHeader } from './classes/CarouselHeader';
|
||||
import { default as CarouselItem } from './classes/CarouselItem';
|
||||
import { default as Channel } from './classes/Channel';
|
||||
import { default as ChannelAboutFullMetadata } from './classes/ChannelAboutFullMetadata';
|
||||
import { default as ChannelFeaturedContent } from './classes/ChannelFeaturedContent';
|
||||
@@ -54,11 +56,13 @@ import { default as CommentThread } from './classes/comments/CommentThread';
|
||||
import { default as CompactLink } from './classes/CompactLink';
|
||||
import { default as CompactMix } from './classes/CompactMix';
|
||||
import { default as CompactPlaylist } from './classes/CompactPlaylist';
|
||||
import { default as CompactStation } from './classes/CompactStation';
|
||||
import { default as CompactVideo } from './classes/CompactVideo';
|
||||
import { default as ConfirmDialog } from './classes/ConfirmDialog';
|
||||
import { default as ContinuationItem } from './classes/ContinuationItem';
|
||||
import { default as CopyLink } from './classes/CopyLink';
|
||||
import { default as CreatePlaylistDialog } from './classes/CreatePlaylistDialog';
|
||||
import { default as DefaultPromoPanel } from './classes/DefaultPromoPanel';
|
||||
import { default as DidYouMean } from './classes/DidYouMean';
|
||||
import { default as DownloadButton } from './classes/DownloadButton';
|
||||
import { default as Dropdown } from './classes/Dropdown';
|
||||
@@ -73,6 +77,8 @@ import { default as ExpandableTab } from './classes/ExpandableTab';
|
||||
import { default as ExpandedShelfContents } from './classes/ExpandedShelfContents';
|
||||
import { default as FeedFilterChipBar } from './classes/FeedFilterChipBar';
|
||||
import { default as FeedTabbedHeader } from './classes/FeedTabbedHeader';
|
||||
import { default as GameCard } from './classes/GameCard';
|
||||
import { default as GameDetails } from './classes/GameDetails';
|
||||
import { default as Grid } from './classes/Grid';
|
||||
import { default as GridChannel } from './classes/GridChannel';
|
||||
import { default as GridHeader } from './classes/GridHeader';
|
||||
@@ -83,6 +89,7 @@ import { default as HistorySuggestion } from './classes/HistorySuggestion';
|
||||
import { default as HorizontalCardList } from './classes/HorizontalCardList';
|
||||
import { default as HorizontalList } from './classes/HorizontalList';
|
||||
import { default as IconLink } from './classes/IconLink';
|
||||
import { default as InteractiveTabbedHeader } from './classes/InteractiveTabbedHeader';
|
||||
import { default as ItemSection } from './classes/ItemSection';
|
||||
import { default as ItemSectionHeader } from './classes/ItemSectionHeader';
|
||||
import { default as ItemSectionTab } from './classes/ItemSectionTab';
|
||||
@@ -189,6 +196,7 @@ import { default as PlayerOverlay } from './classes/PlayerOverlay';
|
||||
import { default as PlayerOverlayAutoplay } from './classes/PlayerOverlayAutoplay';
|
||||
import { default as PlayerStoryboardSpec } from './classes/PlayerStoryboardSpec';
|
||||
import { default as Playlist } from './classes/Playlist';
|
||||
import { default as PlaylistCustomThumbnail } from './classes/PlaylistCustomThumbnail';
|
||||
import { default as PlaylistHeader } from './classes/PlaylistHeader';
|
||||
import { default as PlaylistInfoCardContent } from './classes/PlaylistInfoCardContent';
|
||||
import { default as PlaylistMetadata } from './classes/PlaylistMetadata';
|
||||
@@ -207,6 +215,7 @@ import { default as ProfileColumn } from './classes/ProfileColumn';
|
||||
import { default as ProfileColumnStats } from './classes/ProfileColumnStats';
|
||||
import { default as ProfileColumnStatsEntry } from './classes/ProfileColumnStatsEntry';
|
||||
import { default as ProfileColumnUserInfo } from './classes/ProfileColumnUserInfo';
|
||||
import { default as RecognitionShelf } from './classes/RecognitionShelf';
|
||||
import { default as ReelItem } from './classes/ReelItem';
|
||||
import { default as ReelShelf } from './classes/ReelShelf';
|
||||
import { default as RelatedChipCloud } from './classes/RelatedChipCloud';
|
||||
@@ -245,6 +254,7 @@ import { default as Tab } from './classes/Tab';
|
||||
import { default as Tabbed } from './classes/Tabbed';
|
||||
import { default as TabbedSearchResults } from './classes/TabbedSearchResults';
|
||||
import { default as TextHeader } from './classes/TextHeader';
|
||||
import { default as ThumbnailLandscapePortrait } from './classes/ThumbnailLandscapePortrait';
|
||||
import { default as ThumbnailOverlayBottomPanel } from './classes/ThumbnailOverlayBottomPanel';
|
||||
import { default as ThumbnailOverlayEndorsement } from './classes/ThumbnailOverlayEndorsement';
|
||||
import { default as ThumbnailOverlayHoverText } from './classes/ThumbnailOverlayHoverText';
|
||||
@@ -261,6 +271,7 @@ import { default as TitleAndButtonListHeader } from './classes/TitleAndButtonLis
|
||||
import { default as ToggleButton } from './classes/ToggleButton';
|
||||
import { default as ToggleMenuServiceItem } from './classes/ToggleMenuServiceItem';
|
||||
import { default as Tooltip } from './classes/Tooltip';
|
||||
import { default as TopicChannelDetails } from './classes/TopicChannelDetails';
|
||||
import { default as TwoColumnBrowseResults } from './classes/TwoColumnBrowseResults';
|
||||
import { default as TwoColumnSearchResults } from './classes/TwoColumnSearchResults';
|
||||
import { default as TwoColumnWatchNextResults } from './classes/TwoColumnWatchNextResults';
|
||||
@@ -268,6 +279,7 @@ import { default as UniversalWatchCard } from './classes/UniversalWatchCard';
|
||||
import { default as VerticalList } from './classes/VerticalList';
|
||||
import { default as VerticalWatchCardList } from './classes/VerticalWatchCardList';
|
||||
import { default as Video } from './classes/Video';
|
||||
import { default as VideoCard } from './classes/VideoCard';
|
||||
import { default as VideoInfoCardContent } from './classes/VideoInfoCardContent';
|
||||
import { default as VideoOwner } from './classes/VideoOwner';
|
||||
import { default as VideoPrimaryInfo } from './classes/VideoPrimaryInfo';
|
||||
@@ -279,7 +291,7 @@ import { default as WatchCardSectionSequence } from './classes/WatchCardSectionS
|
||||
import { default as WatchNextEndScreen } from './classes/WatchNextEndScreen';
|
||||
import { default as WatchNextTabbedResults } from './classes/WatchNextTabbedResults';
|
||||
|
||||
const map: Record<string, YTNodeConstructor> = {
|
||||
export const YTNodes = {
|
||||
AccountChannel,
|
||||
AccountItemSection,
|
||||
AccountItemSectionHeader,
|
||||
@@ -306,6 +318,8 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
CallToActionButton,
|
||||
Card,
|
||||
CardCollection,
|
||||
CarouselHeader,
|
||||
CarouselItem,
|
||||
Channel,
|
||||
ChannelAboutFullMetadata,
|
||||
ChannelFeaturedContent,
|
||||
@@ -332,11 +346,13 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
CompactLink,
|
||||
CompactMix,
|
||||
CompactPlaylist,
|
||||
CompactStation,
|
||||
CompactVideo,
|
||||
ConfirmDialog,
|
||||
ContinuationItem,
|
||||
CopyLink,
|
||||
CreatePlaylistDialog,
|
||||
DefaultPromoPanel,
|
||||
DidYouMean,
|
||||
DownloadButton,
|
||||
Dropdown,
|
||||
@@ -351,6 +367,8 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
ExpandedShelfContents,
|
||||
FeedFilterChipBar,
|
||||
FeedTabbedHeader,
|
||||
GameCard,
|
||||
GameDetails,
|
||||
Grid,
|
||||
GridChannel,
|
||||
GridHeader,
|
||||
@@ -361,6 +379,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
HorizontalCardList,
|
||||
HorizontalList,
|
||||
IconLink,
|
||||
InteractiveTabbedHeader,
|
||||
ItemSection,
|
||||
ItemSectionHeader,
|
||||
ItemSectionTab,
|
||||
@@ -467,6 +486,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
PlayerOverlayAutoplay,
|
||||
PlayerStoryboardSpec,
|
||||
Playlist,
|
||||
PlaylistCustomThumbnail,
|
||||
PlaylistHeader,
|
||||
PlaylistInfoCardContent,
|
||||
PlaylistMetadata,
|
||||
@@ -485,6 +505,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
ProfileColumnStats,
|
||||
ProfileColumnStatsEntry,
|
||||
ProfileColumnUserInfo,
|
||||
RecognitionShelf,
|
||||
ReelItem,
|
||||
ReelShelf,
|
||||
RelatedChipCloud,
|
||||
@@ -523,6 +544,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
Tabbed,
|
||||
TabbedSearchResults,
|
||||
TextHeader,
|
||||
ThumbnailLandscapePortrait,
|
||||
ThumbnailOverlayBottomPanel,
|
||||
ThumbnailOverlayEndorsement,
|
||||
ThumbnailOverlayHoverText,
|
||||
@@ -539,6 +561,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
ToggleButton,
|
||||
ToggleMenuServiceItem,
|
||||
Tooltip,
|
||||
TopicChannelDetails,
|
||||
TwoColumnBrowseResults,
|
||||
TwoColumnSearchResults,
|
||||
TwoColumnWatchNextResults,
|
||||
@@ -546,6 +569,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
VerticalList,
|
||||
VerticalWatchCardList,
|
||||
Video,
|
||||
VideoCard,
|
||||
VideoInfoCardContent,
|
||||
VideoOwner,
|
||||
VideoPrimaryInfo,
|
||||
@@ -558,7 +582,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
WatchNextTabbedResults
|
||||
};
|
||||
|
||||
export const YTNodes = map;
|
||||
const map: Record<string, YTNodeConstructor> = YTNodes;
|
||||
|
||||
/**
|
||||
* @param name - Name of the node to be parsed
|
||||
|
||||
@@ -1,28 +1,36 @@
|
||||
import Actions from '../../core/Actions';
|
||||
import TabbedFeed from '../../core/TabbedFeed';
|
||||
import C4TabbedHeader from '../classes/C4TabbedHeader';
|
||||
import CarouselHeader from '../classes/CarouselHeader';
|
||||
import InteractiveTabbedHeader from '../classes/InteractiveTabbedHeader';
|
||||
import ChannelAboutFullMetadata from '../classes/ChannelAboutFullMetadata';
|
||||
import ChannelMetadata from '../classes/ChannelMetadata';
|
||||
import MicroformatData from '../classes/MicroformatData';
|
||||
import SubscribeButton from '../classes/SubscribeButton';
|
||||
import Tab from '../classes/Tab';
|
||||
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
|
||||
class Channel extends TabbedFeed {
|
||||
header;
|
||||
metadata;
|
||||
sponsor_button;
|
||||
subscribe_button;
|
||||
current_tab;
|
||||
|
||||
constructor(actions: Actions, data: any, already_parsed = false) {
|
||||
super(actions, data, already_parsed);
|
||||
|
||||
this.header = this.page.header?.item().as(C4TabbedHeader);
|
||||
const metadata = this.page.metadata.item().as(ChannelMetadata);
|
||||
this.header = this.page.header?.item()?.as(C4TabbedHeader, CarouselHeader, InteractiveTabbedHeader);
|
||||
|
||||
const metadata = this.page.metadata?.item().as(ChannelMetadata);
|
||||
const microformat = this.page.microformat?.as(MicroformatData);
|
||||
|
||||
if (!metadata && !this.page.contents)
|
||||
throw new InnertubeError('Invalid channel', this);
|
||||
|
||||
this.metadata = { ...metadata, ...(microformat || {}) };
|
||||
this.sponsor_button = this.header?.sponsor_button;
|
||||
this.subscribe_button = this.header?.subscribe_button;
|
||||
|
||||
this.subscribe_button = this.page.header_memo.getType(SubscribeButton)?.[0];
|
||||
|
||||
const tab = this.page.contents.item().key('tabs').parsed().array().filterType(Tab).get({ selected: true });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user