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:
LuanRT
2022-11-11 00:38:44 -03:00
committed by GitHub
parent 4c00f15f55
commit 3cbcd71a3a
20 changed files with 325 additions and 22 deletions

View 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);
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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);
}
}

View File

@@ -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) {

View 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);
}
}

View 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;
}
}

View File

@@ -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;
}
}

View 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);
}
}

View 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);
}
}

View File

@@ -0,0 +1,9 @@
import Video from './Video';
export default class VideoCard extends Video {
static type = 'VideoCard';
constructor(data: any) {
super(data);
}
}

View File

@@ -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 {