mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-18 20:12:12 +00:00
refactor(Parser)!: general refactoring of parsers (#344)
* refactor: move common info into MediaInfo * refactor: better inference on Memo * refactor: improved typesafety in parser methods * refactor: remove PlaylistAuthor in favor of Author * refactor: cleanup live chat parsers - Replace non standard author type with Author class - Remove redundant code * fix: new errors due to changes * fix: pass actions to FormatUtils#toDash * refactor!: merge NavigatableText and Text into single class
This commit is contained in:
@@ -39,7 +39,7 @@ class AccountItemSection extends YTNode {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.contents = data.contents.map((ac: any) => new AccountItem(ac.accountItem));
|
||||
this.header = Parser.parseItem<AccountItemSectionHeader>(data.header, AccountItemSectionHeader);
|
||||
this.header = Parser.parseItem(data.header, AccountItemSectionHeader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ class AccountSectionList extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.contents = Parser.parseItem<AccountItemSection>(data.contents[0], AccountItemSection);
|
||||
this.footers = Parser.parseItem<AccountChannel>(data.footers[0], AccountChannel);
|
||||
this.contents = Parser.parseItem(data.contents[0], AccountItemSection);
|
||||
this.footers = Parser.parseItem(data.footers[0], AccountChannel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import Parser from '../index.js';
|
||||
import Author from './misc/Author.js';
|
||||
import Text from './misc/Text.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import type CommentActionButtons from './comments/CommentActionButtons.js';
|
||||
import type Menu from './menus/Menu.js';
|
||||
import CommentActionButtons from './comments/CommentActionButtons.js';
|
||||
import Menu from './menus/Menu.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
@@ -49,15 +49,15 @@ class BackstagePost extends YTNode {
|
||||
}
|
||||
|
||||
if (data.actionMenu) {
|
||||
this.menu = Parser.parseItem<Menu>(data.actionMenu);
|
||||
this.menu = Parser.parseItem(data.actionMenu, Menu);
|
||||
}
|
||||
|
||||
if (data.actionButtons) {
|
||||
this.action_buttons = Parser.parseItem<CommentActionButtons>(data.actionButtons);
|
||||
this.action_buttons = Parser.parseItem(data.actionButtons, CommentActionButtons);
|
||||
}
|
||||
|
||||
if (data.voteButton) {
|
||||
this.vote_button = Parser.parseItem(data.voteButton);
|
||||
this.vote_button = Parser.parseItem(data.voteButton, CommentActionButtons);
|
||||
}
|
||||
|
||||
if (data.navigationEndpoint) {
|
||||
|
||||
@@ -3,9 +3,9 @@ import Author from './misc/Author.js';
|
||||
import Text from './misc/Text.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
|
||||
import type Button from './Button.js';
|
||||
import type ChannelHeaderLinks from './ChannelHeaderLinks.js';
|
||||
import type SubscribeButton from './SubscribeButton.js';
|
||||
import Button from './Button.js';
|
||||
import ChannelHeaderLinks from './ChannelHeaderLinks.js';
|
||||
import SubscribeButton from './SubscribeButton.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
@@ -19,7 +19,7 @@ class C4TabbedHeader extends YTNode {
|
||||
subscribers?: Text;
|
||||
videos_count?: Text;
|
||||
sponsor_button?: Button | null;
|
||||
subscribe_button?: SubscribeButton | null;
|
||||
subscribe_button?: SubscribeButton | Button | null;
|
||||
header_links?: ChannelHeaderLinks | null;
|
||||
channel_handle?: Text;
|
||||
channel_id?: string;
|
||||
@@ -52,15 +52,15 @@ class C4TabbedHeader extends YTNode {
|
||||
}
|
||||
|
||||
if (data.sponsorButton) {
|
||||
this.sponsor_button = Parser.parseItem<Button>(data.sponsorButton);
|
||||
this.sponsor_button = Parser.parseItem(data.sponsorButton, Button);
|
||||
}
|
||||
|
||||
if (data.subscribeButton) {
|
||||
this.subscribe_button = Parser.parseItem<SubscribeButton>(data.subscribeButton);
|
||||
this.subscribe_button = Parser.parseItem(data.subscribeButton, [ SubscribeButton, Button ]);
|
||||
}
|
||||
|
||||
if (data.headerLinks) {
|
||||
this.header_links = Parser.parseItem<ChannelHeaderLinks>(data.headerLinks);
|
||||
this.header_links = Parser.parseItem(data.headerLinks, ChannelHeaderLinks);
|
||||
}
|
||||
|
||||
if (data.channelHandleText) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import Text from './misc/Text.js';
|
||||
import Author from './misc/Author.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
|
||||
import type SubscribeButton from './SubscribeButton.js';
|
||||
import SubscribeButton from './SubscribeButton.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
@@ -36,7 +36,7 @@ class Channel extends YTNode {
|
||||
this.long_byline = new Text(data.longBylineText);
|
||||
this.short_byline = new Text(data.shortBylineText);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.subscribe_button = Parser.parseItem<SubscribeButton>(data.subscribeButton);
|
||||
this.subscribe_button = Parser.parseItem(data.subscribeButton, SubscribeButton);
|
||||
this.description_snippet = new Text(data.descriptionSnippet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Text from './misc/Text.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
|
||||
import type Button from './Button.js';
|
||||
import Button from './Button.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
@@ -49,7 +49,7 @@ class ChannelAboutFullMetadata extends YTNode {
|
||||
this.email_reveal = new NavigationEndpoint(data.onBusinessEmailRevealClickCommand);
|
||||
this.can_reveal_email = !data.signInForBusinessEmail;
|
||||
this.country = new Text(data.country);
|
||||
this.buttons = Parser.parseArray<Button>(data.actionButtons);
|
||||
this.buttons = Parser.parseArray(data.actionButtons, Button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class ChannelAgeGate extends YTNode {
|
||||
this.avatar = Thumbnail.fromResponse(data.avatar);
|
||||
this.header = new Text(data.header);
|
||||
this.main_text = new Text(data.mainText);
|
||||
this.sign_in_button = Parser.parseItem<Button>(data.signInButton, Button);
|
||||
this.sign_in_button = Parser.parseItem(data.signInButton, Button);
|
||||
this.secondary_text = new Text(data.secondaryText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ class ChipCloud extends YTNode {
|
||||
constructor(data: any) {
|
||||
super();
|
||||
// TODO: check this assumption that chipcloudchip is always returned
|
||||
this.chips = Parser.parseArray<ChipCloudChip>(data.chips, ChipCloudChip);
|
||||
this.next_button = Parser.parseItem<Button>(data.nextButton, Button);
|
||||
this.previous_button = Parser.parseItem<Button>(data.previousButton, Button);
|
||||
this.chips = Parser.parseArray(data.chips, ChipCloudChip);
|
||||
this.next_button = Parser.parseItem(data.nextButton, Button);
|
||||
this.previous_button = Parser.parseItem(data.previousButton, Button);
|
||||
this.horizontal_scrollable = data.horizontalScrollable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import Parser from '../index.js';
|
||||
import Text from './misc/Text.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import type Menu from './menus/Menu.js';
|
||||
import Menu from './menus/Menu.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
class CompactChannel extends YTNode {
|
||||
@@ -28,7 +28,7 @@ class CompactChannel extends YTNode {
|
||||
this.subscriber_count = new Text(data.subscriberCountText);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.tv_banner = Thumbnail.fromResponse(data.tvBanner);
|
||||
this.menu = Parser.parseItem<Menu>(data.menu);
|
||||
this.menu = Parser.parseItem(data.menu, Menu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ class ConfirmDialog extends YTNode {
|
||||
constructor (data: any) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
this.confirm_button = Parser.parseItem<Button>(data.confirmButton, Button);
|
||||
this.cancel_button = Parser.parseItem<Button>(data.cancelButton, Button);
|
||||
this.confirm_button = Parser.parseItem(data.confirmButton, Button);
|
||||
this.cancel_button = Parser.parseItem(data.cancelButton, Button);
|
||||
this.dialog_messages = data.dialogMessages.map((txt: any) => new Text(txt));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class ConversationBar extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.availability_message = Parser.parseItem<Message>(data.availabilityMessage, Message);
|
||||
this.availability_message = Parser.parseItem(data.availabilityMessage, Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class CopyLink extends YTNode {
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.copy_button = Parser.parseItem<Button>(data.copyButton, Button);
|
||||
this.copy_button = Parser.parseItem(data.copyButton, Button);
|
||||
this.short_url = data.shortUrl;
|
||||
this.style = data.style;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ class CreatePlaylistDialog extends YTNode {
|
||||
this.title = new Text(data.dialogTitle).toString();
|
||||
this.title_placeholder = data.titlePlaceholder || '';
|
||||
this.privacy_option = Parser.parseItem(data.privacyOption, Dropdown)?.entries || null;
|
||||
this.create_button = Parser.parseItem(data.cancelButton);
|
||||
this.cancel_button = Parser.parseItem(data.cancelButton);
|
||||
this.create_button = Parser.parseItem(data.cancelButton, Button);
|
||||
this.cancel_button = Parser.parseItem(data.cancelButton, Button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Parser from '../index.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type Button from './Button.js';
|
||||
import type MultiMarkersPlayerBar from './MultiMarkersPlayerBar.js';
|
||||
import Button from './Button.js';
|
||||
import MultiMarkersPlayerBar from './MultiMarkersPlayerBar.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
|
||||
class DecoratedPlayerBar extends YTNode {
|
||||
@@ -12,8 +12,8 @@ class DecoratedPlayerBar extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.player_bar = Parser.parseItem<MultiMarkersPlayerBar>(data.playerBar);
|
||||
this.player_bar_action_button = Parser.parseItem<Button>(data.playerBarActionButton);
|
||||
this.player_bar = Parser.parseItem(data.playerBar, MultiMarkersPlayerBar);
|
||||
this.player_bar_action_button = Parser.parseItem(data.playerBarActionButton, Button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,9 @@ class ExpandableMetadata extends YTNode {
|
||||
expanded_title: new Text(data.header.expandedTitle)
|
||||
};
|
||||
|
||||
this.expanded_content = Parser.parseItem<HorizontalCardList>(data.expandedContent);
|
||||
this.expand_button = Parser.parseItem<Button>(data.expandButton);
|
||||
this.collapse_button = Parser.parseItem<Button>(data.collapseButton);
|
||||
this.expanded_content = Parser.parseItem(data.expandedContent, HorizontalCardList);
|
||||
this.expand_button = Parser.parseItem(data.expandButton, Button);
|
||||
this.collapse_button = Parser.parseItem(data.collapseButton, Button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Parser, { RawNode } from '../index.js';
|
||||
import PlaylistAuthor from './misc/PlaylistAuthor.js';
|
||||
import Author from './misc/Author.js';
|
||||
import Text from './misc/Text.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
@@ -10,7 +10,7 @@ class GridPlaylist extends YTNode {
|
||||
|
||||
id: string;
|
||||
title: Text;
|
||||
author?: PlaylistAuthor;
|
||||
author?: Author;
|
||||
badges;
|
||||
endpoint: NavigationEndpoint;
|
||||
view_playlist: Text;
|
||||
@@ -26,7 +26,7 @@ class GridPlaylist extends YTNode {
|
||||
this.title = new Text(data.title);
|
||||
|
||||
if (data.shortBylineText) {
|
||||
this.author = new PlaylistAuthor(data.shortBylineText, data.ownerBadges);
|
||||
this.author = new Author(data.shortBylineText, data.ownerBadges);
|
||||
}
|
||||
|
||||
this.badges = Parser.parseArray(data.ownerBadges);
|
||||
|
||||
@@ -4,7 +4,7 @@ import Thumbnail from './misc/Thumbnail.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import Author from './misc/Author.js';
|
||||
|
||||
import type Menu from './menus/Menu.js';
|
||||
import Menu from './menus/Menu.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
@@ -38,7 +38,7 @@ class GridVideo extends YTNode {
|
||||
this.views = new Text(data.viewCountText);
|
||||
this.short_view_count = new Text(data.shortViewCountText);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.menu = Parser.parseItem<Menu>(data.menu);
|
||||
this.menu = Parser.parseItem(data.menu, Menu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Parser from '../index.js';
|
||||
import type HeatMarker from './HeatMarker.js';
|
||||
import HeatMarker from './HeatMarker.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
@@ -17,7 +17,7 @@ class Heatmap extends YTNode {
|
||||
this.max_height_dp = data.maxHeightDp;
|
||||
this.min_height_dp = data.minHeightDp;
|
||||
this.show_hide_animation_duration_millis = data.showHideAnimationDurationMillis;
|
||||
this.heat_markers = Parser.parseArray<HeatMarker>(data.heatMarkers);
|
||||
this.heat_markers = Parser.parseArray(data.heatMarkers, HeatMarker);
|
||||
this.heat_markers_decorations = Parser.parseArray(data.heatMarkersDecorations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { YTNode } from '../helpers.js';
|
||||
import SearchRefinementCard from './SearchRefinementCard.js';
|
||||
import Button from './Button.js';
|
||||
import MacroMarkersListItem from './MacroMarkersListItem.js';
|
||||
import GameCard from './GameCard.js';
|
||||
import VideoCard from './VideoCard.js';
|
||||
|
||||
class HorizontalCardList extends YTNode {
|
||||
static type = 'HorizontalCardList';
|
||||
@@ -14,10 +16,10 @@ class HorizontalCardList extends YTNode {
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.cards = Parser.parseArray<SearchRefinementCard | MacroMarkersListItem>(data.cards);
|
||||
this.cards = Parser.parseArray(data.cards, [ SearchRefinementCard, MacroMarkersListItem, GameCard, VideoCard ]);
|
||||
this.header = Parser.parseItem(data.header);
|
||||
this.previous_button = Parser.parseItem<Button>(data.previousButton, Button);
|
||||
this.next_button = Parser.parseItem<Button>(data.nextButton, Button);
|
||||
this.previous_button = Parser.parseItem(data.previousButton, Button);
|
||||
this.next_button = Parser.parseItem(data.nextButton, Button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class InteractiveTabbedHeader extends YTNode {
|
||||
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.buttons = Parser.parseArray(data.buttons, [ SubscribeButton, Button ]);
|
||||
this.auto_generated = new Text(data.autoGenerated);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class ItemSection extends YTNode {
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.header = Parser.parseItem<CommentsHeader | ItemSectionHeader | ItemSectionTabbedHeader>(data.header);
|
||||
this.header = Parser.parseItem(data.header, [ CommentsHeader, ItemSectionHeader, ItemSectionTabbedHeader ]);
|
||||
this.contents = Parser.parse(data.contents, true);
|
||||
|
||||
if (data.targetId || data.sectionIdentifier) {
|
||||
|
||||
@@ -13,7 +13,7 @@ class ItemSectionTabbedHeader extends YTNode {
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
this.tabs = Parser.parseArray<ItemSectionTab>(data.tabs, ItemSectionTab);
|
||||
this.tabs = Parser.parseArray(data.tabs, ItemSectionTab);
|
||||
if (data.endItems) {
|
||||
this.end_items = Parser.parseArray(data.endItems);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Parser from '../index.js';
|
||||
import type Menu from './menus/Menu.js';
|
||||
import type Button from './Button.js';
|
||||
import type SortFilterSubMenu from './SortFilterSubMenu.js';
|
||||
import Menu from './menus/Menu.js';
|
||||
import Button from './Button.js';
|
||||
import SortFilterSubMenu from './SortFilterSubMenu.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
class LiveChatHeader extends YTNode {
|
||||
@@ -13,9 +13,9 @@ class LiveChatHeader extends YTNode {
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.overflow_menu = Parser.parseItem<Menu>(data.overflowMenu);
|
||||
this.collapse_button = Parser.parseItem<Button>(data.collapseButton);
|
||||
this.view_selector = Parser.parseItem<SortFilterSubMenu>(data.viewSelector);
|
||||
this.overflow_menu = Parser.parseItem(data.overflowMenu, Menu);
|
||||
this.collapse_button = Parser.parseItem(data.collapseButton, Button);
|
||||
this.view_selector = Parser.parseItem(data.viewSelector, SortFilterSubMenu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Parser from '../index.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type Button from './Button.js';
|
||||
import Button from './Button.js';
|
||||
|
||||
class LiveChatItemList extends YTNode {
|
||||
static type = 'LiveChatItemList';
|
||||
@@ -11,7 +11,7 @@ class LiveChatItemList extends YTNode {
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.max_items_to_display = data.maxItemsToDisplay;
|
||||
this.more_comments_below_button = Parser.parseItem<Button>(data.moreCommentsBelowButton);
|
||||
this.more_comments_below_button = Parser.parseItem(data.moreCommentsBelowButton, Button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Text from './misc/Text.js';
|
||||
import Parser from '../index.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import type Button from './Button.js';
|
||||
import Button from './Button.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
class LiveChatMessageInput extends YTNode {
|
||||
@@ -16,7 +16,7 @@ class LiveChatMessageInput extends YTNode {
|
||||
super();
|
||||
this.author_name = new Text(data.authorName);
|
||||
this.author_photo = Thumbnail.fromResponse(data.authorPhoto);
|
||||
this.send_button = Parser.parseItem<Button>(data.sendButton);
|
||||
this.send_button = Parser.parseItem(data.sendButton, Button);
|
||||
this.target_id = data.targetId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Parser from '../index.js';
|
||||
import Text from './misc/Text.js';
|
||||
import { ObservedArray, YTNode } from '../helpers.js';
|
||||
import type LiveChatParticipant from './LiveChatParticipant.js';
|
||||
import LiveChatParticipant from './LiveChatParticipant.js';
|
||||
|
||||
class LiveChatParticipantsList extends YTNode {
|
||||
static type = 'LiveChatParticipantsList';
|
||||
@@ -12,7 +12,7 @@ class LiveChatParticipantsList extends YTNode {
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
this.participants = Parser.parseArray<LiveChatParticipant>(data.participants);
|
||||
this.participants = Parser.parseArray(data.participants, LiveChatParticipant);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Parser from '../index.js';
|
||||
import type Chapter from './Chapter.js';
|
||||
import type Heatmap from './Heatmap.js';
|
||||
import Chapter from './Chapter.js';
|
||||
import Heatmap from './Heatmap.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
|
||||
import { observe, ObservedArray, YTNode } from '../helpers.js';
|
||||
@@ -21,11 +21,11 @@ class Marker extends YTNode {
|
||||
this.value = {};
|
||||
|
||||
if (data.value.heatmap) {
|
||||
this.value.heatmap = Parser.parseItem<Heatmap>(data.value.heatmap);
|
||||
this.value.heatmap = Parser.parseItem(data.value.heatmap, Heatmap);
|
||||
}
|
||||
|
||||
if (data.value.chapters) {
|
||||
this.value.chapters = Parser.parseArray<Chapter>(data.value.chapters);
|
||||
this.value.chapters = Parser.parseArray(data.value.chapters, Chapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,19 +5,19 @@ import MusicResponsiveListItem from './MusicResponsiveListItem.js';
|
||||
import MusicCarouselShelfBasicHeader from './MusicCarouselShelfBasicHeader.js';
|
||||
import MusicNavigationButton from './MusicNavigationButton.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
import { ObservedArray, YTNode } from '../helpers.js';
|
||||
|
||||
class MusicCarouselShelf extends YTNode {
|
||||
static type = 'MusicCarouselShelf';
|
||||
|
||||
header: MusicCarouselShelfBasicHeader | null;
|
||||
contents: Array<MusicTwoRowItem | MusicResponsiveListItem | MusicNavigationButton>;
|
||||
contents: ObservedArray<MusicTwoRowItem | MusicResponsiveListItem | MusicNavigationButton>;
|
||||
num_items_per_column: number | null;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.header = Parser.parseItem<MusicCarouselShelfBasicHeader>(data.header, MusicCarouselShelfBasicHeader);
|
||||
this.contents = Parser.parseArray<MusicTwoRowItem | MusicResponsiveListItem | MusicNavigationButton>(data.contents, [ MusicTwoRowItem, MusicResponsiveListItem, MusicNavigationButton ]);
|
||||
this.contents = Parser.parseArray(data.contents, [ MusicTwoRowItem, MusicResponsiveListItem, MusicNavigationButton ]);
|
||||
this.num_items_per_column = Reflect.has(data, 'numItemsPerColumn') ? parseInt(data.numItemsPerColumn) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class MusicShelf extends YTNode {
|
||||
}
|
||||
|
||||
if (data.bottomButton) {
|
||||
this.bottom_button = Parser.parseItem<Button>(data.bottomButton);
|
||||
this.bottom_button = Parser.parseItem(data.bottomButton, Button);
|
||||
}
|
||||
|
||||
if (data.subheaders) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import Text from './misc/Text.js';
|
||||
import Parser from '../index.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import PlaylistAuthor from './misc/PlaylistAuthor.js';
|
||||
import Author from './misc/Author.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
class Playlist extends YTNode {
|
||||
@@ -10,7 +10,7 @@ class Playlist extends YTNode {
|
||||
|
||||
id: string;
|
||||
title: Text;
|
||||
author: Text | PlaylistAuthor;
|
||||
author: Text | Author;
|
||||
thumbnails: Thumbnail[];
|
||||
video_count: Text;
|
||||
video_count_short: Text;
|
||||
@@ -29,7 +29,7 @@ class Playlist extends YTNode {
|
||||
|
||||
this.author = data.shortBylineText?.simpleText ?
|
||||
new Text(data.shortBylineText) :
|
||||
new PlaylistAuthor(data.longBylineText, data.ownerBadges, null);
|
||||
new Author(data.longBylineText, data.ownerBadges, null);
|
||||
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail || { thumbnails: data.thumbnails.map((th: any) => th.thumbnails).flat(1) });
|
||||
this.video_count = new Text(data.thumbnailText);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Text from './misc/Text.js';
|
||||
import PlaylistAuthor from './misc/PlaylistAuthor.js';
|
||||
import Author from './misc/Author.js';
|
||||
import Parser, { RawNode } from '../index.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
@@ -10,7 +10,7 @@ class PlaylistHeader extends YTNode {
|
||||
title: Text;
|
||||
stats: Text[];
|
||||
brief_stats: Text[];
|
||||
author: PlaylistAuthor;
|
||||
author: Author;
|
||||
description: Text;
|
||||
num_videos: Text;
|
||||
view_count: Text;
|
||||
@@ -29,7 +29,7 @@ class PlaylistHeader extends YTNode {
|
||||
this.title = new Text(data.title);
|
||||
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.author = new Author({ ...data.ownerText, navigationEndpoint: data.ownerEndpoint }, data.ownerBadges, null);
|
||||
this.description = new Text(data.descriptionText);
|
||||
this.num_videos = new Text(data.numVideosText);
|
||||
this.view_count = new Text(data.viewCountText);
|
||||
|
||||
@@ -23,7 +23,7 @@ class PlaylistPanel extends YTNode {
|
||||
super();
|
||||
this.title = data.title;
|
||||
this.title_text = new Text(data.titleText);
|
||||
this.contents = Parser.parseArray<PlaylistPanelVideoWrapper | PlaylistPanelVideo | AutomixPreviewVideo>(data.contents);
|
||||
this.contents = Parser.parseArray(data.contents, [ PlaylistPanelVideoWrapper, PlaylistPanelVideo, AutomixPreviewVideo ]);
|
||||
this.playlist_id = data.playlistId;
|
||||
this.is_infinite = data.isInfinite;
|
||||
this.continuation = data.continuations?.[0]?.nextRadioContinuationData?.continuation || data.continuations?.[0]?.nextContinuationData?.continuation;
|
||||
|
||||
@@ -10,8 +10,8 @@ class PlaylistPanelVideoWrapper extends YTNode {
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.primary = Parser.parseItem<PlaylistPanelVideo>(data.primaryRenderer);
|
||||
this.counterpart = data.counterpart?.map((item: any) => Parser.parseItem<PlaylistPanelVideo>(item.counterpartRenderer)) || [];
|
||||
this.primary = Parser.parseItem(data.primaryRenderer, PlaylistPanelVideo);
|
||||
this.counterpart = data.counterpart?.map((item: any) => Parser.parseItem(item.counterpartRenderer, PlaylistPanelVideo)) || [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Text from './misc/Text.js';
|
||||
import Parser from '../index.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import PlaylistAuthor from './misc/PlaylistAuthor.js';
|
||||
import Author from './misc/Author.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import ThumbnailOverlayTimeStatus from './ThumbnailOverlayTimeStatus.js';
|
||||
import type Menu from './menus/Menu.js';
|
||||
import Menu from './menus/Menu.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
@@ -14,7 +14,7 @@ class PlaylistVideo extends YTNode {
|
||||
id: string;
|
||||
index: Text;
|
||||
title: Text;
|
||||
author: PlaylistAuthor;
|
||||
author: Author;
|
||||
thumbnails: Thumbnail[];
|
||||
thumbnail_overlays;
|
||||
set_video_id: string | undefined;
|
||||
@@ -33,13 +33,13 @@ class PlaylistVideo extends YTNode {
|
||||
this.id = data.videoId;
|
||||
this.index = new Text(data.index);
|
||||
this.title = new Text(data.title);
|
||||
this.author = new PlaylistAuthor(data.shortBylineText);
|
||||
this.author = new Author(data.shortBylineText);
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
|
||||
this.set_video_id = data?.setVideoId;
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.is_playable = data.isPlayable;
|
||||
this.menu = Parser.parseItem<Menu>(data.menu);
|
||||
this.menu = Parser.parseItem(data.menu, Menu);
|
||||
|
||||
const upcoming = data.upcomingEventData && Number(`${data.upcomingEventData.startTime}000`);
|
||||
if (upcoming) {
|
||||
|
||||
@@ -12,8 +12,8 @@ class SegmentedLikeDislikeButton extends YTNode {
|
||||
|
||||
constructor (data: RawNode) {
|
||||
super();
|
||||
this.like_button = Parser.parseItem<ToggleButton | Button>(data.likeButton, [ ToggleButton, Button ]);
|
||||
this.dislike_button = Parser.parseItem<ToggleButton | Button>(data.dislikeButton, [ ToggleButton, Button ]);
|
||||
this.like_button = Parser.parseItem(data.likeButton, [ ToggleButton, Button ]);
|
||||
this.dislike_button = Parser.parseItem(data.dislikeButton, [ ToggleButton, Button ]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class SettingsOptions extends YTNode {
|
||||
}
|
||||
|
||||
if (Reflect.has(data, 'options')) {
|
||||
this.options = Parser.parseArray<SettingsSwitch | Dropdown | CopyLink | SettingsCheckbox | ChannelOptions>(data.options, [
|
||||
this.options = Parser.parseArray(data.options, [
|
||||
SettingsSwitch, Dropdown, CopyLink,
|
||||
SettingsCheckbox, ChannelOptions
|
||||
]);
|
||||
|
||||
@@ -33,7 +33,7 @@ class Shelf extends YTNode {
|
||||
}
|
||||
|
||||
if (data.playAllButton) {
|
||||
this.play_all_button = Parser.parseItem<Button>(data.playAllButton);
|
||||
this.play_all_button = Parser.parseItem(data.playAllButton, Button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class Tab extends YTNode {
|
||||
this.title = data.title || 'N/A';
|
||||
this.selected = data.selected || false;
|
||||
this.endpoint = new NavigationEndpoint(data.endpoint);
|
||||
this.content = Parser.parseItem<SectionList | MusicQueue | RichGrid>(data.content, [ SectionList, MusicQueue, RichGrid ]);
|
||||
this.content = Parser.parseItem(data.content, [ SectionList, MusicQueue, RichGrid ]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Parser from '../index.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Text from './misc/Text.js';
|
||||
import PlaylistAuthor from './misc/PlaylistAuthor.js';
|
||||
import Author from './misc/Author.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
|
||||
import type Menu from './menus/Menu.js';
|
||||
import Menu from './menus/Menu.js';
|
||||
|
||||
type AutoplaySet = {
|
||||
autoplay_video: NavigationEndpoint,
|
||||
@@ -20,7 +20,7 @@ class TwoColumnWatchNextResults extends YTNode {
|
||||
playlist?: {
|
||||
id: string,
|
||||
title: string,
|
||||
author: Text | PlaylistAuthor,
|
||||
author: Text | Author,
|
||||
contents: YTNode[],
|
||||
current_index: number,
|
||||
is_infinite: boolean,
|
||||
@@ -45,11 +45,11 @@ class TwoColumnWatchNextResults extends YTNode {
|
||||
title: playlistData.title,
|
||||
author: playlistData.shortBylineText?.simpleText ?
|
||||
new Text(playlistData.shortBylineText) :
|
||||
new PlaylistAuthor(playlistData.longBylineText),
|
||||
new Author(playlistData.longBylineText),
|
||||
contents: Parser.parseArray(playlistData.contents),
|
||||
current_index: playlistData.currentIndex,
|
||||
is_infinite: !!playlistData.isInfinite,
|
||||
menu: Parser.parseItem<Menu>(playlistData.menu)
|
||||
menu: Parser.parseItem(playlistData.menu, Menu)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Parser from '../index.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type Button from './Button.js';
|
||||
import Button from './Button.js';
|
||||
import Text from './misc/Text.js';
|
||||
|
||||
class UpsellDialog extends YTNode {
|
||||
@@ -16,8 +16,8 @@ class UpsellDialog extends YTNode {
|
||||
super();
|
||||
this.message_title = new Text(data.dialogMessageTitle);
|
||||
this.message_text = new Text(data.dialogMessageText);
|
||||
this.action_button = Parser.parseItem<Button>(data.actionButton);
|
||||
this.dismiss_button = Parser.parseItem<Button>(data.dismissButton);
|
||||
this.action_button = Parser.parseItem(data.actionButton, Button);
|
||||
this.dismiss_button = Parser.parseItem(data.dismissButton, Button);
|
||||
this.is_visible = data.isVisible;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class Video extends YTNode {
|
||||
hover_text: new Text(snippet.snippetHoverText)
|
||||
})) || [];
|
||||
|
||||
this.expandable_metadata = Parser.parseItem<ExpandableMetadata>(data.expandableMetadata);
|
||||
this.expandable_metadata = Parser.parseItem(data.expandableMetadata, ExpandableMetadata);
|
||||
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
|
||||
|
||||
@@ -20,14 +20,14 @@ class VideoSecondaryInfo extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.owner = Parser.parseItem<VideoOwner>(data.owner);
|
||||
this.owner = Parser.parseItem(data.owner, VideoOwner);
|
||||
this.description = new Text(data.description);
|
||||
|
||||
if (Reflect.has(data, 'attributedDescription')) {
|
||||
this.description = new Text(this.#convertAttributedDescriptionToRuns(data.attributedDescription));
|
||||
}
|
||||
|
||||
this.subscribe_button = Parser.parseItem<SubscribeButton | Button>(data.subscribeButton, [ SubscribeButton, Button ]);
|
||||
this.subscribe_button = Parser.parseItem(data.subscribeButton, [ SubscribeButton, Button ]);
|
||||
this.metadata = Parser.parseItem<MetadataRowContainer>(data.metadataRowContainer, MetadataRowContainer);
|
||||
this.show_more_text = data.showMoreText;
|
||||
this.show_less_text = data.showLessText;
|
||||
|
||||
@@ -12,7 +12,7 @@ class WatchNextEndScreen extends YTNode {
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.results = Parser.parseArray<EndScreenVideo | EndScreenPlaylist>(data.results, [ EndScreenVideo, EndScreenPlaylist ]);
|
||||
this.results = Parser.parseArray(data.results, [ EndScreenVideo, EndScreenPlaylist ]);
|
||||
this.title = new Text(data.title).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import CommentReplyDialog from './CommentReplyDialog.js';
|
||||
import AuthorCommentBadge from './AuthorCommentBadge.js';
|
||||
import Author from '../misc/Author.js';
|
||||
|
||||
import type Menu from '../menus/Menu.js';
|
||||
import type CommentActionButtons from './CommentActionButtons.js';
|
||||
import type SponsorCommentBadge from './SponsorCommentBadge.js';
|
||||
import type PdgCommentChip from './PdgCommentChip.js';
|
||||
import Menu from '../menus/Menu.js';
|
||||
import CommentActionButtons from './CommentActionButtons.js';
|
||||
import SponsorCommentBadge from './SponsorCommentBadge.js';
|
||||
import PdgCommentChip from './PdgCommentChip.js';
|
||||
import type { ApiResponse } from '../../../core/Actions.js';
|
||||
import type Actions from '../../../core/Actions.js';
|
||||
|
||||
@@ -51,9 +51,9 @@ class Comment extends YTNode {
|
||||
this.published = new Text(data.publishedTimeText);
|
||||
this.author_is_channel_owner = data.authorIsChannelOwner;
|
||||
this.current_user_reply_thumbnail = Thumbnail.fromResponse(data.currentUserReplyThumbnail);
|
||||
this.sponsor_comment_badge = Parser.parseItem<SponsorCommentBadge>(data.sponsorCommentBadge);
|
||||
this.paid_comment_chip = Parser.parseItem<PdgCommentChip>(data.paidCommentChipRenderer);
|
||||
this.author_badge = Parser.parseItem<AuthorCommentBadge>(data.authorCommentBadge, AuthorCommentBadge);
|
||||
this.sponsor_comment_badge = Parser.parseItem(data.sponsorCommentBadge, SponsorCommentBadge);
|
||||
this.paid_comment_chip = Parser.parseItem(data.paidCommentChipRenderer, PdgCommentChip);
|
||||
this.author_badge = Parser.parseItem(data.authorCommentBadge, AuthorCommentBadge);
|
||||
|
||||
this.author = new Author({
|
||||
...data.authorText,
|
||||
@@ -62,8 +62,8 @@ class Comment extends YTNode {
|
||||
metadataBadgeRenderer: this.author_badge?.orig_badge
|
||||
} ] : null, data.authorThumbnail);
|
||||
|
||||
this.action_menu = Parser.parseItem<Menu>(data.actionMenu);
|
||||
this.action_buttons = Parser.parseItem<CommentActionButtons>(data.actionButtons);
|
||||
this.action_menu = Parser.parseItem(data.actionMenu, Menu);
|
||||
this.action_buttons = Parser.parseItem(data.actionButtons, CommentActionButtons);
|
||||
this.comment_id = data.commentId;
|
||||
this.vote_status = data.voteStatus;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Parser from '../../index.js';
|
||||
import type Button from '../Button.js';
|
||||
import type ToggleButton from '../ToggleButton.js';
|
||||
import type CreatorHeart from './CreatorHeart.js';
|
||||
import Button from '../Button.js';
|
||||
import ToggleButton from '../ToggleButton.js';
|
||||
import CreatorHeart from './CreatorHeart.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
@@ -15,10 +15,10 @@ class CommentActionButtons extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.like_button = Parser.parseItem<ToggleButton>(data.likeButton);
|
||||
this.dislike_button = Parser.parseItem<ToggleButton>(data.dislikeButton);
|
||||
this.reply_button = Parser.parseItem<Button>(data.replyButton);
|
||||
this.creator_heart = Parser.parseItem<CreatorHeart>(data.creatorHeart);
|
||||
this.like_button = Parser.parseItem(data.likeButton, ToggleButton);
|
||||
this.dislike_button = Parser.parseItem(data.dislikeButton, ToggleButton);
|
||||
this.reply_button = Parser.parseItem(data.replyButton, Button);
|
||||
this.creator_heart = Parser.parseItem(data.creatorHeart, CreatorHeart);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Parser from '../../index.js';
|
||||
import Text from '../misc/Text.js';
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import type Button from '../Button.js';
|
||||
import type EmojiPicker from './EmojiPicker.js';
|
||||
import Button from '../Button.js';
|
||||
import EmojiPicker from './EmojiPicker.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
@@ -21,11 +21,11 @@ class CommentDialog extends YTNode {
|
||||
super();
|
||||
this.editable_text = new Text(data.editableText);
|
||||
this.author_thumbnail = Thumbnail.fromResponse(data.authorThumbnail);
|
||||
this.submit_button = Parser.parseItem<Button>(data.submitButton);
|
||||
this.cancel_button = Parser.parseItem<Button>(data.cancelButton);
|
||||
this.submit_button = Parser.parseItem(data.submitButton, Button);
|
||||
this.cancel_button = Parser.parseItem(data.cancelButton, Button);
|
||||
this.placeholder = new Text(data.placeholderText);
|
||||
this.emoji_button = Parser.parseItem<Button>(data.emojiButton);
|
||||
this.emoji_picker = Parser.parseItem<EmojiPicker>(data.emojiPicker);
|
||||
this.emoji_button = Parser.parseItem(data.emojiButton, Button);
|
||||
this.emoji_picker = Parser.parseItem(data.emojiPicker, EmojiPicker);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Parser from '../../index.js';
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import type Button from '../Button.js';
|
||||
import Button from '../Button.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
class CommentReplies extends YTNode {
|
||||
@@ -15,8 +15,8 @@ class CommentReplies extends YTNode {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.contents = Parser.parseArray(data.contents);
|
||||
this.view_replies = Parser.parseItem<Button>(data.viewReplies);
|
||||
this.hide_replies = Parser.parseItem<Button>(data.hideReplies);
|
||||
this.view_replies = Parser.parseItem(data.viewReplies, Button);
|
||||
this.hide_replies = Parser.parseItem(data.hideReplies, Button);
|
||||
this.view_replies_creator_thumbnail = Thumbnail.fromResponse(data.viewRepliesCreatorThumbnail);
|
||||
this.has_channel_owner_replied = !!data.viewRepliesCreatorThumbnail;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Parser from '../../index.js';
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import Text from '../misc/Text.js';
|
||||
import type Button from '../Button.js';
|
||||
import Button from '../Button.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
@@ -16,8 +16,8 @@ class CommentReplyDialog extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.reply_button = Parser.parseItem<Button>(data.replyButton);
|
||||
this.cancel_button = Parser.parseItem<Button>(data.cancelButton);
|
||||
this.reply_button = Parser.parseItem(data.replyButton, Button);
|
||||
this.cancel_button = Parser.parseItem(data.cancelButton, Button);
|
||||
this.author_thumbnail = Thumbnail.fromResponse(data.authorThumbnail);
|
||||
this.placeholder = new Text(data.placeholderText);
|
||||
this.error_message = new Text(data.errorMessage);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Parser from '../../index.js';
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import Text from '../misc/Text.js';
|
||||
import type Button from '../Button.js';
|
||||
import Button from '../Button.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
@@ -16,8 +16,8 @@ class CommentSimplebox extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.submit_button = Parser.parseItem<Button>(data.submitButton);
|
||||
this.cancel_button = Parser.parseItem<Button>(data.cancelButton);
|
||||
this.submit_button = Parser.parseItem(data.submitButton, Button);
|
||||
this.cancel_button = Parser.parseItem(data.cancelButton, Button);
|
||||
this.author_thumbnails = Thumbnail.fromResponse(data.authorThumbnail);
|
||||
this.placeholder = new Text(data.placeholderText);
|
||||
this.avatar_size = data.avatarSize;
|
||||
|
||||
@@ -24,7 +24,7 @@ class CommentThread extends YTNode {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.comment = Parser.parseItem<Comment>(data.comment, Comment);
|
||||
this.comment_replies_data = Parser.parseItem<CommentReplies>(data.replies);
|
||||
this.comment_replies_data = Parser.parseItem(data.replies, CommentReplies);
|
||||
this.is_moderated_elq_comment = data.isModeratedElqComment;
|
||||
this.has_replies = !!this.comment_replies_data;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Parser from '../../index.js';
|
||||
import Text from '../misc/Text.js';
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import type SortFilterSubMenu from '../SortFilterSubMenu.js';
|
||||
import SortFilterSubMenu from '../SortFilterSubMenu.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
@@ -28,7 +28,7 @@ class CommentsHeader extends YTNode {
|
||||
this.count = new Text(data.countText);
|
||||
this.comments_count = new Text(data.commentsCount);
|
||||
this.create_renderer = Parser.parseItem(data.createRenderer);
|
||||
this.sort_menu = Parser.parseItem<SortFilterSubMenu>(data.sortMenu);
|
||||
this.sort_menu = Parser.parseItem(data.sortMenu, SortFilterSubMenu);
|
||||
|
||||
this.custom_emojis = data.customEmojis?.map((emoji: any) => ({
|
||||
emoji_id: emoji.emojiId,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type LiveChatBanner from './items/LiveChatBanner.js';
|
||||
import LiveChatBanner from './items/LiveChatBanner.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AddBannerToLiveChatCommand extends YTNode {
|
||||
@@ -10,7 +10,7 @@ class AddBannerToLiveChatCommand extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.banner = Parser.parseItem<LiveChatBanner>(data.bannerRenderer);
|
||||
this.banner = Parser.parseItem(data.bannerRenderer, LiveChatBanner);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class LiveChatAutoModMessage extends YTNode {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.menu_endpoint = new NavigationEndpoint(data.contextMenuEndpoint);
|
||||
this.moderation_buttons = Parser.parseArray<Button>(data.moderationButtons, [ Button ]);
|
||||
this.moderation_buttons = Parser.parseArray(data.moderationButtons, [ Button ]);
|
||||
this.auto_moderated_item = Parser.parseItem(data.autoModeratedItem);
|
||||
this.header_text = new Text(data.headerText);
|
||||
this.timestamp = Math.floor(parseInt(data.timestampUsec) / 1000);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { YTNode } from '../../../helpers.js';
|
||||
import Parser from '../../../index.js';
|
||||
import type LiveChatBannerHeader from './LiveChatBannerHeader.js';
|
||||
import LiveChatBannerHeader from './LiveChatBannerHeader.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
|
||||
class LiveChatBanner extends YTNode {
|
||||
@@ -16,7 +16,7 @@ class LiveChatBanner extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.header = Parser.parseItem<LiveChatBannerHeader>(data.header);
|
||||
this.header = Parser.parseItem(data.header, LiveChatBannerHeader);
|
||||
this.contents = Parser.parseItem(data.contents);
|
||||
this.action_id = data.actionId;
|
||||
this.viewer_is_creator = data.viewerIsCreator;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { YTNode } from '../../../helpers.js';
|
||||
import Parser from '../../../index.js';
|
||||
import type Button from '../../Button.js';
|
||||
import Button from '../../Button.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
|
||||
@@ -15,7 +15,7 @@ class LiveChatBannerHeader extends YTNode {
|
||||
super();
|
||||
this.text = new Text(data.text).toString();
|
||||
this.icon_type = data.icon?.iconType;
|
||||
this.context_menu_button = Parser.parseItem<Button>(data.contextMenuButton);
|
||||
this.context_menu_button = Parser.parseItem(data.contextMenuButton, Button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { observe, ObservedArray, YTNode } from '../../../helpers.js';
|
||||
import Parser from '../../../index.js';
|
||||
import LiveChatAuthorBadge from '../../LiveChatAuthorBadge.js';
|
||||
import MetadataBadge from '../../MetadataBadge.js';
|
||||
import { YTNode } from '../../../helpers.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import Thumbnail from '../../misc/Thumbnail.js';
|
||||
import NavigationEndpoint from '../../NavigationEndpoint.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
import Author from '../../misc/Author.js';
|
||||
class LiveChatMembershipItem extends YTNode {
|
||||
static type = 'LiveChatMembershipItem';
|
||||
|
||||
@@ -13,15 +10,7 @@ class LiveChatMembershipItem extends YTNode {
|
||||
timestamp: number;
|
||||
header_subtext: Text;
|
||||
|
||||
author: {
|
||||
id: string;
|
||||
name: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
badges: ObservedArray<LiveChatAuthorBadge | MetadataBadge>;
|
||||
is_moderator: boolean | null;
|
||||
is_verified: boolean | null;
|
||||
is_verified_artist: boolean | null;
|
||||
};
|
||||
author: Author;
|
||||
|
||||
menu_endpoint: NavigationEndpoint;
|
||||
|
||||
@@ -31,22 +20,7 @@ class LiveChatMembershipItem extends YTNode {
|
||||
this.timestamp = Math.floor(parseInt(data.timestampUsec) / 1000);
|
||||
this.header_subtext = new Text(data.headerSubtext);
|
||||
|
||||
this.author = {
|
||||
id: data.authorExternalChannelId,
|
||||
name: new Text(data?.authorName),
|
||||
thumbnails: Thumbnail.fromResponse(data.authorPhoto),
|
||||
badges: observe([]).as(LiveChatAuthorBadge, MetadataBadge),
|
||||
is_moderator: null,
|
||||
is_verified: null,
|
||||
is_verified_artist: null
|
||||
};
|
||||
|
||||
const badges = Parser.parseArray<LiveChatAuthorBadge | MetadataBadge>(data.authorBadges);
|
||||
|
||||
this.author.badges = badges;
|
||||
this.author.is_moderator = badges ? badges.some((badge) => badge.icon_type == 'MODERATOR') : null;
|
||||
this.author.is_verified = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED') : null;
|
||||
this.author.is_verified_artist = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') : null;
|
||||
this.author = new Author(data.authorName, data.authorBadges, data.authorPhoto, data.authorExternalChannelId);
|
||||
|
||||
this.menu_endpoint = new NavigationEndpoint(data.contextMenuEndpoint);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
import { observe, ObservedArray, YTNode } from '../../../helpers.js';
|
||||
import Parser from '../../../index.js';
|
||||
import LiveChatAuthorBadge from '../../LiveChatAuthorBadge.js';
|
||||
import MetadataBadge from '../../MetadataBadge.js';
|
||||
import { YTNode } from '../../../helpers.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import Thumbnail from '../../misc/Thumbnail.js';
|
||||
import NavigationEndpoint from '../../NavigationEndpoint.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
import Author from '../../misc/Author.js';
|
||||
|
||||
class LiveChatPaidMessage extends YTNode {
|
||||
static type = 'LiveChatPaidMessage';
|
||||
|
||||
message: Text;
|
||||
|
||||
author: {
|
||||
id: string;
|
||||
name: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
badges: ObservedArray<LiveChatAuthorBadge | MetadataBadge>;
|
||||
is_moderator: boolean | null;
|
||||
is_verified: boolean | null;
|
||||
is_verified_artist: boolean | null;
|
||||
};
|
||||
author: Author;
|
||||
|
||||
header_background_color: number;
|
||||
header_text_color: number;
|
||||
@@ -35,22 +25,7 @@ class LiveChatPaidMessage extends YTNode {
|
||||
super();
|
||||
this.message = new Text(data.message);
|
||||
|
||||
this.author = {
|
||||
id: data.authorExternalChannelId,
|
||||
name: new Text(data.authorName),
|
||||
thumbnails: Thumbnail.fromResponse(data.authorPhoto),
|
||||
badges: observe([]).as(LiveChatAuthorBadge, MetadataBadge),
|
||||
is_moderator: null,
|
||||
is_verified: null,
|
||||
is_verified_artist: null
|
||||
};
|
||||
|
||||
const badges = Parser.parseArray<LiveChatAuthorBadge | MetadataBadge>(data.authorBadges, [ MetadataBadge, LiveChatAuthorBadge ]);
|
||||
|
||||
this.author.badges = badges;
|
||||
this.author.is_moderator = badges ? badges.some((badge) => badge.icon_type == 'MODERATOR') : null;
|
||||
this.author.is_verified = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED') : null;
|
||||
this.author.is_verified_artist = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') : null;
|
||||
this.author = new Author(data.authorName, data.authorBadges, data.authorPhoto, data.authorExternalChannelId);
|
||||
|
||||
this.header_background_color = data.headerBackgroundColor;
|
||||
this.header_text_color = data.headerTextColor;
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
import { observe, ObservedArray, YTNode } from '../../../helpers.js';
|
||||
import Parser from '../../../index.js';
|
||||
import LiveChatAuthorBadge from '../../LiveChatAuthorBadge.js';
|
||||
import MetadataBadge from '../../MetadataBadge.js';
|
||||
import { YTNode } from '../../../helpers.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import Thumbnail from '../../misc/Thumbnail.js';
|
||||
import NavigationEndpoint from '../../NavigationEndpoint.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
import Author from '../../misc/Author.js';
|
||||
|
||||
class LiveChatPaidSticker extends YTNode {
|
||||
static type = 'LiveChatPaidSticker';
|
||||
|
||||
id: string;
|
||||
|
||||
author: {
|
||||
id: string;
|
||||
name: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
badges: ObservedArray<LiveChatAuthorBadge | MetadataBadge>;
|
||||
is_moderator: boolean | null;
|
||||
is_verified: boolean | null;
|
||||
is_verified_artist: boolean | null;
|
||||
};
|
||||
author: Author;
|
||||
|
||||
money_chip_background_color: number;
|
||||
money_chip_text_color: number;
|
||||
@@ -36,22 +26,7 @@ class LiveChatPaidSticker extends YTNode {
|
||||
super();
|
||||
this.id = data.id;
|
||||
|
||||
this.author = {
|
||||
id: data.authorExternalChannelId,
|
||||
name: new Text(data.authorName),
|
||||
thumbnails: Thumbnail.fromResponse(data.authorPhoto),
|
||||
badges: observe([]).as(LiveChatAuthorBadge, MetadataBadge),
|
||||
is_moderator: null,
|
||||
is_verified: null,
|
||||
is_verified_artist: null
|
||||
};
|
||||
|
||||
const badges = Parser.parseArray<LiveChatAuthorBadge | MetadataBadge>(data.authorBadges, [ MetadataBadge, LiveChatAuthorBadge ]);
|
||||
|
||||
this.author.badges = badges;
|
||||
this.author.is_moderator = badges ? badges.some((badge) => badge.icon_type == 'MODERATOR') : null;
|
||||
this.author.is_verified = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED') : null;
|
||||
this.author.is_verified_artist = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') : null;
|
||||
this.author = new Author(data.authorName, data.authorBadges, data.authorPhoto, data.authorExternalChannelId);
|
||||
|
||||
this.money_chip_background_color = data.moneyChipBackgroundColor;
|
||||
this.money_chip_text_color = data.moneyChipTextColor;
|
||||
|
||||
@@ -1,28 +1,16 @@
|
||||
import { observe, ObservedArray, YTNode } from '../../../helpers.js';
|
||||
import { ObservedArray, YTNode } from '../../../helpers.js';
|
||||
import Parser from '../../../index.js';
|
||||
import Button from '../../Button.js';
|
||||
import LiveChatAuthorBadge from '../../LiveChatAuthorBadge.js';
|
||||
import MetadataBadge from '../../MetadataBadge.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import Thumbnail from '../../misc/Thumbnail.js';
|
||||
import NavigationEndpoint from '../../NavigationEndpoint.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
import Author from '../../misc/Author.js';
|
||||
|
||||
class LiveChatTextMessage extends YTNode {
|
||||
static type = 'LiveChatTextMessage';
|
||||
export class LiveChatMessageBase extends YTNode {
|
||||
static type = 'LiveChatMessageBase';
|
||||
|
||||
message: Text;
|
||||
author?: {
|
||||
id: string;
|
||||
name: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
badges: ObservedArray<LiveChatAuthorBadge | MetadataBadge>;
|
||||
is_moderator: boolean | null;
|
||||
is_verified: boolean | null;
|
||||
is_verified_artist: boolean | null;
|
||||
};
|
||||
|
||||
menu_endpoint?: NavigationEndpoint;
|
||||
inline_action_buttons: ObservedArray<Button>;
|
||||
timestamp: number;
|
||||
id: string;
|
||||
@@ -31,28 +19,26 @@ class LiveChatTextMessage extends YTNode {
|
||||
super();
|
||||
this.message = new Text(data.message);
|
||||
|
||||
this.author = {
|
||||
id: data.authorExternalChannelId,
|
||||
name: new Text(data.authorName),
|
||||
thumbnails: Thumbnail.fromResponse(data.authorPhoto),
|
||||
badges: observe([]).as(LiveChatAuthorBadge, MetadataBadge),
|
||||
is_moderator: null,
|
||||
is_verified: null,
|
||||
is_verified_artist: null
|
||||
};
|
||||
|
||||
const badges = Parser.parseArray<LiveChatAuthorBadge | MetadataBadge>(data.authorBadges, [ MetadataBadge, LiveChatAuthorBadge ]);
|
||||
|
||||
this.author.badges = badges;
|
||||
this.author.is_moderator = badges ? badges.some((badge) => badge.icon_type == 'MODERATOR') : null;
|
||||
this.author.is_verified = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED') : null;
|
||||
this.author.is_verified_artist = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') : null;
|
||||
|
||||
this.menu_endpoint = new NavigationEndpoint(data.contextMenuEndpoint);
|
||||
this.inline_action_buttons = Parser.parseArray<Button>(data.inlineActionButtons, [ Button ]);
|
||||
this.inline_action_buttons = Parser.parseArray(data.inlineActionButtons, [ Button ]);
|
||||
this.timestamp = Math.floor(parseInt(data.timestampUsec) / 1000);
|
||||
this.id = data.id;
|
||||
}
|
||||
}
|
||||
|
||||
class LiveChatTextMessage extends LiveChatMessageBase {
|
||||
static type = 'LiveChatTextMessage';
|
||||
|
||||
author: Author;
|
||||
|
||||
menu_endpoint?: NavigationEndpoint;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super(data);
|
||||
|
||||
this.author = new Author(data.authorName, data.authorBadges, data.authorPhoto, data.authorExternalChannelId);
|
||||
|
||||
this.menu_endpoint = new NavigationEndpoint(data.contextMenuEndpoint);
|
||||
}
|
||||
}
|
||||
|
||||
export default LiveChatTextMessage;
|
||||
@@ -1,25 +1,15 @@
|
||||
import Parser from '../../../index.js';
|
||||
import LiveChatAuthorBadge from '../../LiveChatAuthorBadge.js';
|
||||
import MetadataBadge from '../../MetadataBadge.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import Thumbnail from '../../misc/Thumbnail.js';
|
||||
import NavigationEndpoint from '../../NavigationEndpoint.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
|
||||
import { observe, ObservedArray, YTNode } from '../../../helpers.js';
|
||||
import { YTNode } from '../../../helpers.js';
|
||||
import Author from '../../misc/Author.js';
|
||||
|
||||
class LiveChatTickerPaidMessageItem extends YTNode {
|
||||
static type = 'LiveChatTickerPaidMessageItem';
|
||||
|
||||
author: {
|
||||
id: string;
|
||||
name: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
badges: ObservedArray<LiveChatAuthorBadge | MetadataBadge>;
|
||||
is_moderator: boolean | null;
|
||||
is_verified: boolean | null;
|
||||
is_verified_artist: boolean | null;
|
||||
};
|
||||
author: Author;
|
||||
|
||||
amount: Text;
|
||||
duration_sec: string; // Or number?
|
||||
@@ -31,27 +21,12 @@ class LiveChatTickerPaidMessageItem extends YTNode {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
this.author = {
|
||||
id: data.authorExternalChannelId,
|
||||
name: new Text(data?.authorName),
|
||||
thumbnails: Thumbnail.fromResponse(data.authorPhoto),
|
||||
badges: observe([]).as(LiveChatAuthorBadge, MetadataBadge),
|
||||
is_moderator: null,
|
||||
is_verified: null,
|
||||
is_verified_artist: null
|
||||
};
|
||||
|
||||
const badges = Parser.parseArray<LiveChatAuthorBadge | MetadataBadge>(data.authorBadges, [ MetadataBadge, LiveChatAuthorBadge ]);
|
||||
|
||||
this.author.badges = badges;
|
||||
this.author.is_moderator = badges ? badges.some((badge) => badge.icon_type == 'MODERATOR') : null;
|
||||
this.author.is_verified = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED') : null;
|
||||
this.author.is_verified_artist = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') : null;
|
||||
this.author = new Author(data.authorName, data.authorBadges, data.authorPhoto, data.authorExternalChannelId);
|
||||
|
||||
this.amount = new Text(data.amount);
|
||||
this.duration_sec = data.durationSec;
|
||||
this.full_duration_sec = data.fullDurationSec;
|
||||
this.show_item = Parser.parse(data.showItemEndpoint?.showLiveChatItemEndpoint?.renderer);
|
||||
this.show_item = Parser.parseItem(data.showItemEndpoint?.showLiveChatItemEndpoint?.renderer);
|
||||
this.show_item_endpoint = new NavigationEndpoint(data.showItemEndpoint);
|
||||
this.id = data.id;
|
||||
}
|
||||
|
||||
@@ -1,60 +1,7 @@
|
||||
import Parser from '../../../index.js';
|
||||
import LiveChatAuthorBadge from '../../LiveChatAuthorBadge.js';
|
||||
import MetadataBadge from '../../MetadataBadge.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import Thumbnail from '../../misc/Thumbnail.js';
|
||||
import NavigationEndpoint from '../../NavigationEndpoint.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
import LiveChatTickerPaidMessageItem from './LiveChatTickerPaidMessageItem.js';
|
||||
|
||||
import { observe, ObservedArray, YTNode } from '../../../helpers.js';
|
||||
|
||||
class LiveChatTickerPaidStickerItem extends YTNode {
|
||||
class LiveChatTickerPaidStickerItem extends LiveChatTickerPaidMessageItem {
|
||||
static type = 'LiveChatTickerPaidStickerItem';
|
||||
|
||||
author: {
|
||||
id: string;
|
||||
name: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
badges: ObservedArray<LiveChatAuthorBadge | MetadataBadge>;
|
||||
is_moderator: boolean | null;
|
||||
is_verified: boolean | null;
|
||||
is_verified_artist: boolean | null;
|
||||
};
|
||||
|
||||
amount: Text;
|
||||
duration_sec: string; // Or number?
|
||||
full_duration_sec: string;
|
||||
show_item;
|
||||
show_item_endpoint: NavigationEndpoint;
|
||||
id: string;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
this.author = {
|
||||
id: data.authorExternalChannelId,
|
||||
name: new Text(data?.authorName),
|
||||
thumbnails: Thumbnail.fromResponse(data.authorPhoto),
|
||||
badges: observe([]).as(LiveChatAuthorBadge, MetadataBadge),
|
||||
is_moderator: null,
|
||||
is_verified: null,
|
||||
is_verified_artist: null
|
||||
};
|
||||
|
||||
const badges = Parser.parseArray<LiveChatAuthorBadge | MetadataBadge>(data.authorBadges, [ MetadataBadge, LiveChatAuthorBadge ]);
|
||||
|
||||
this.author.badges = badges;
|
||||
this.author.is_moderator = badges ? badges.some((badge) => badge.icon_type == 'MODERATOR') : null;
|
||||
this.author.is_verified = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED') : null;
|
||||
this.author.is_verified_artist = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') : null;
|
||||
|
||||
this.amount = new Text(data.amount);
|
||||
this.duration_sec = data.durationSec;
|
||||
this.full_duration_sec = data.fullDurationSec;
|
||||
this.show_item = Parser.parseItem(data.showItemEndpoint?.showLiveChatItemEndpoint?.renderer);
|
||||
this.show_item_endpoint = new NavigationEndpoint(data.showItemEndpoint);
|
||||
this.id = data.id;
|
||||
}
|
||||
}
|
||||
|
||||
export default LiveChatTickerPaidStickerItem;
|
||||
@@ -1,25 +1,14 @@
|
||||
import Parser from '../../../index.js';
|
||||
import { observe, ObservedArray, YTNode } from '../../../helpers.js';
|
||||
import LiveChatAuthorBadge from '../../LiveChatAuthorBadge.js';
|
||||
import MetadataBadge from '../../MetadataBadge.js';
|
||||
import { YTNode } from '../../../helpers.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import Thumbnail from '../../misc/Thumbnail.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
import Author from '../../misc/Author.js';
|
||||
|
||||
class LiveChatTickerSponsorItem extends YTNode {
|
||||
static type = 'LiveChatTickerSponsorItem';
|
||||
|
||||
id: string;
|
||||
detail: Text;
|
||||
author: {
|
||||
id: string;
|
||||
name: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
badges: ObservedArray<LiveChatAuthorBadge | MetadataBadge>;
|
||||
is_moderator: boolean | null;
|
||||
is_verified: boolean | null;
|
||||
is_verified_artist: boolean | null;
|
||||
};
|
||||
author: Author;
|
||||
|
||||
duration_sec: string;
|
||||
|
||||
@@ -28,22 +17,7 @@ class LiveChatTickerSponsorItem extends YTNode {
|
||||
this.id = data.id;
|
||||
this.detail = new Text(data.detailText);
|
||||
|
||||
this.author = {
|
||||
id: data.authorExternalChannelId,
|
||||
name: new Text(data?.authorName),
|
||||
thumbnails: Thumbnail.fromResponse(data.sponsorPhoto),
|
||||
badges: observe([]).as(LiveChatAuthorBadge, MetadataBadge),
|
||||
is_moderator: null,
|
||||
is_verified: null,
|
||||
is_verified_artist: null
|
||||
};
|
||||
|
||||
const badges = Parser.parseArray<LiveChatAuthorBadge | MetadataBadge>(data.authorBadges, [ MetadataBadge, LiveChatAuthorBadge ]);
|
||||
|
||||
this.author.badges = badges;
|
||||
this.author.is_moderator = badges ? badges.some((badge) => badge.icon_type == 'MODERATOR') : null;
|
||||
this.author.is_verified = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED') : null;
|
||||
this.author.is_verified_artist = badges ? badges.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') : null;
|
||||
this.author = new Author(data.authorName, data.authorBadges, data.sponsorPhoto, data.authorExternalChannelId);
|
||||
|
||||
this.duration_sec = data.durationSec;
|
||||
// TODO: finish this
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import Parser from '../../../index.js';
|
||||
import LiveChatTextMessage from './LiveChatTextMessage.js';
|
||||
import { LiveChatMessageBase } from './LiveChatTextMessage.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
|
||||
class LiveChatViewerEngagementMessage extends LiveChatTextMessage {
|
||||
|
||||
class LiveChatViewerEngagementMessage extends LiveChatMessageBase {
|
||||
static type = 'LiveChatViewerEngagementMessage';
|
||||
|
||||
icon_type: string;
|
||||
@@ -10,8 +11,6 @@ class LiveChatViewerEngagementMessage extends LiveChatTextMessage {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super(data);
|
||||
delete this.author;
|
||||
delete this.menu_endpoint;
|
||||
this.icon_type = data.icon.iconType;
|
||||
this.action_button = Parser.parseItem(data.actionButton);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import Parser from '../../index.js';
|
||||
import Parser, { RawNode } from '../../index.js';
|
||||
import Text from './Text.js';
|
||||
import NavigationEndpoint from '../NavigationEndpoint.js';
|
||||
import TextRun from './TextRun.js';
|
||||
import Thumbnail from './Thumbnail.js';
|
||||
import Constants from '../../../utils/Constants.js';
|
||||
import Text from './Text.js';
|
||||
import { observe, ObservedArray, YTNode } from '../../helpers.js';
|
||||
|
||||
class Author {
|
||||
#nav_text;
|
||||
@@ -11,23 +12,26 @@ class Author {
|
||||
id: string;
|
||||
name: string;
|
||||
thumbnails: Thumbnail[];
|
||||
endpoint?: NavigationEndpoint;
|
||||
badges?: any;
|
||||
endpoint: NavigationEndpoint | null;
|
||||
badges: ObservedArray<YTNode>;
|
||||
is_moderator?: boolean | null;
|
||||
is_verified?: boolean | null;
|
||||
is_verified_artist?: boolean | null;
|
||||
url: string | null;
|
||||
|
||||
constructor(item: any, badges?: any, thumbs?: any) {
|
||||
constructor(item: RawNode, badges?: any, thumbs?: any, id?: string) {
|
||||
this.#nav_text = new Text(item);
|
||||
|
||||
this.id =
|
||||
(this.#nav_text.runs?.[0] as TextRun)?.endpoint?.payload?.browseId ||
|
||||
id ||
|
||||
(this.#nav_text?.runs?.[0] as TextRun)?.endpoint?.payload?.browseId ||
|
||||
this.#nav_text?.endpoint?.payload?.browseId || 'N/A';
|
||||
|
||||
this.name = this.#nav_text.text || 'N/A';
|
||||
this.name = this.#nav_text?.text || 'N/A';
|
||||
this.thumbnails = thumbs ? Thumbnail.fromResponse(thumbs) : [];
|
||||
this.endpoint = ((this.#nav_text.runs?.[0] as TextRun) as TextRun)?.endpoint || this.#nav_text.endpoint;
|
||||
this.badges = Array.isArray(badges) ? Parser.parseArray(badges) : [];
|
||||
this.endpoint = ((this.#nav_text?.runs?.[0] as TextRun) as TextRun)?.endpoint || this.#nav_text?.endpoint || null;
|
||||
this.badges = Array.isArray(badges) ? Parser.parseArray(badges) : observe([] as YTNode[]);
|
||||
this.is_moderator = this.badges?.some((badge: any) => badge.icon_type == 'MODERATOR') || null;
|
||||
this.is_verified = this.badges?.some((badge: any) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED') || null;
|
||||
this.is_verified_artist = this.badges?.some((badge: any) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') || null;
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import Author from './Author.js';
|
||||
|
||||
class PlaylistAuthor extends Author {
|
||||
constructor(item: any, badges?: any, thumbs?: any) {
|
||||
super(item, badges, thumbs);
|
||||
delete this.badges;
|
||||
delete this.is_verified;
|
||||
delete this.is_verified_artist;
|
||||
}
|
||||
}
|
||||
|
||||
export default PlaylistAuthor;
|
||||
@@ -1,6 +1,6 @@
|
||||
import Parser from '../../index.js';
|
||||
import NavigationEndpoint from '../NavigationEndpoint.js';
|
||||
import type SectionList from '../SectionList.js';
|
||||
import SectionList from '../SectionList.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
@@ -19,7 +19,7 @@ class AnchoredSection extends YTNode {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = data.title;
|
||||
this.content = Parser.parseItem<SectionList>(data.content);
|
||||
this.content = Parser.parseItem(data.content, SectionList);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.category_assets = {
|
||||
asset_key: data.categoryAssets?.assetKey,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Parser from '../../index.js';
|
||||
import type Button from '../Button.js';
|
||||
import type KidsCategoryTab from './KidsCategoryTab.js';
|
||||
import Button from '../Button.js';
|
||||
import KidsCategoryTab from './KidsCategoryTab.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
@@ -12,8 +12,8 @@ class KidsCategoriesHeader extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.category_tabs = Parser.parseArray<KidsCategoryTab>(data.categoryTabs);
|
||||
this.privacy_button = Parser.parseItem<Button>(data.privacyButtonRenderer);
|
||||
this.category_tabs = Parser.parseArray(data.categoryTabs, KidsCategoryTab);
|
||||
this.privacy_button = Parser.parseItem(data.privacyButtonRenderer, Button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Parser from '../../index.js';
|
||||
import type AnchoredSection from './AnchoredSection.js';
|
||||
import AnchoredSection from './AnchoredSection.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
@@ -10,7 +10,7 @@ class KidsHomeScreen extends YTNode {
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.anchors = Parser.parseArray<AnchoredSection>(data.anchors);
|
||||
this.anchors = Parser.parseArray(data.anchors, AnchoredSection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -474,9 +474,10 @@ export function observe<T extends YTNode>(obj: Array<T>): ObservedArray<T> {
|
||||
}
|
||||
|
||||
export class Memo extends Map<string, YTNode[]> {
|
||||
getType<T extends YTNode>(type: YTNodeConstructor<T> | YTNodeConstructor<T>[]) {
|
||||
if (Array.isArray(type))
|
||||
return observe(type.flatMap((type) => (this.get(type.type) || []) as T[]));
|
||||
return observe((this.get(type.type) || []) as T[]);
|
||||
getType<T extends YTNode, K extends YTNodeConstructor<T>[]>(types: K): ObservedArray<InstanceType<K[number]>>;
|
||||
getType<T extends YTNode, K extends YTNodeConstructor<T>[]>(...types: K): ObservedArray<InstanceType<K[number]>>
|
||||
getType(...types: YTNodeConstructor<YTNode>[] | YTNodeConstructor<YTNode>[][]) {
|
||||
types = types.flat();
|
||||
return observe(types.flatMap((type) => (this.get(type.type) || []) as YTNode[]));
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ export { default as Author } from './classes/misc/Author.js';
|
||||
export { default as ChildElement } from './classes/misc/ChildElement.js';
|
||||
export { default as EmojiRun } from './classes/misc/EmojiRun.js';
|
||||
export { default as Format } from './classes/misc/Format.js';
|
||||
export { default as PlaylistAuthor } from './classes/misc/PlaylistAuthor.js';
|
||||
export { default as Text } from './classes/misc/Text.js';
|
||||
export { default as TextRun } from './classes/misc/TextRun.js';
|
||||
export { default as Thumbnail } from './classes/misc/Thumbnail.js';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import type AudioOnlyPlayability from './classes/AudioOnlyPlayability.js';
|
||||
import type CardCollection from './classes/CardCollection.js';
|
||||
import type Endscreen from './classes/Endscreen.js';
|
||||
import type PlayerAnnotationsExpanded from './classes/PlayerAnnotationsExpanded.js';
|
||||
import type PlayerCaptionsTracklist from './classes/PlayerCaptionsTracklist.js';
|
||||
import type PlayerLiveStoryboardSpec from './classes/PlayerLiveStoryboardSpec.js';
|
||||
import type PlayerStoryboardSpec from './classes/PlayerStoryboardSpec.js';
|
||||
import type Message from './classes/Message.js';
|
||||
import type LiveChatParticipantsList from './classes/LiveChatParticipantsList.js';
|
||||
import type LiveChatHeader from './classes/LiveChatHeader.js';
|
||||
import type LiveChatItemList from './classes/LiveChatItemList.js';
|
||||
import type Alert from './classes/Alert.js';
|
||||
import AudioOnlyPlayability from './classes/AudioOnlyPlayability.js';
|
||||
import CardCollection from './classes/CardCollection.js';
|
||||
import Endscreen from './classes/Endscreen.js';
|
||||
import PlayerAnnotationsExpanded from './classes/PlayerAnnotationsExpanded.js';
|
||||
import PlayerCaptionsTracklist from './classes/PlayerCaptionsTracklist.js';
|
||||
import PlayerLiveStoryboardSpec from './classes/PlayerLiveStoryboardSpec.js';
|
||||
import PlayerStoryboardSpec from './classes/PlayerStoryboardSpec.js';
|
||||
import Message from './classes/Message.js';
|
||||
import LiveChatParticipantsList from './classes/LiveChatParticipantsList.js';
|
||||
import LiveChatHeader from './classes/LiveChatHeader.js';
|
||||
import LiveChatItemList from './classes/LiveChatItemList.js';
|
||||
import Alert from './classes/Alert.js';
|
||||
|
||||
import type { IParsedResponse, IRawResponse, RawData, RawNode } from './types/index.js';
|
||||
|
||||
@@ -170,7 +170,7 @@ export default class Parser {
|
||||
parsed_data.overlay = overlay;
|
||||
}
|
||||
|
||||
const alerts = this.parseArray<Alert>(data.alerts);
|
||||
const alerts = this.parseArray(data.alerts, Alert);
|
||||
if (alerts.length) {
|
||||
parsed_data.alerts = alerts;
|
||||
}
|
||||
@@ -203,7 +203,7 @@ export default class Parser {
|
||||
status: data.playabilityStatus.status,
|
||||
reason: data.playabilityStatus.reason || '',
|
||||
embeddable: !!data.playabilityStatus.playableInEmbed || false,
|
||||
audio_only_playablility: this.parseItem<AudioOnlyPlayability>(data.playabilityStatus.audioOnlyPlayability),
|
||||
audio_only_playablility: this.parseItem(data.playabilityStatus.audioOnlyPlayability, AudioOnlyPlayability),
|
||||
error_screen: this.parseItem(data.playabilityStatus.errorScreen)
|
||||
} : null;
|
||||
|
||||
@@ -233,7 +233,7 @@ export default class Parser {
|
||||
parsed_data.endpoint = endpoint;
|
||||
}
|
||||
|
||||
const captions = this.parseItem<PlayerCaptionsTracklist>(data.captions);
|
||||
const captions = this.parseItem(data.captions, PlayerCaptionsTracklist);
|
||||
if (captions) {
|
||||
parsed_data.captions = captions;
|
||||
}
|
||||
@@ -243,22 +243,22 @@ export default class Parser {
|
||||
parsed_data.video_details = video_details;
|
||||
}
|
||||
|
||||
const annotations = this.parseArray<PlayerAnnotationsExpanded>(data.annotations);
|
||||
const annotations = this.parseArray(data.annotations, PlayerAnnotationsExpanded);
|
||||
if (annotations.length) {
|
||||
parsed_data.annotations = annotations;
|
||||
}
|
||||
|
||||
const storyboards = this.parseItem<PlayerStoryboardSpec | PlayerLiveStoryboardSpec>(data.storyboards);
|
||||
const storyboards = this.parseItem(data.storyboards, [ PlayerStoryboardSpec, PlayerLiveStoryboardSpec ]);
|
||||
if (storyboards) {
|
||||
parsed_data.storyboards = storyboards;
|
||||
}
|
||||
|
||||
const endscreen = this.parseItem<Endscreen>(data.endscreen);
|
||||
const endscreen = this.parseItem(data.endscreen, Endscreen);
|
||||
if (endscreen) {
|
||||
parsed_data.endscreen = endscreen;
|
||||
}
|
||||
|
||||
const cards = this.parseItem<CardCollection>(data.cards);
|
||||
const cards = this.parseItem(data.cards, CardCollection);
|
||||
if (cards) {
|
||||
parsed_data.cards = cards;
|
||||
}
|
||||
@@ -279,7 +279,10 @@ export default class Parser {
|
||||
* @param data - The data to parse.
|
||||
* @param validTypes - YTNode types that are allowed to be parsed.
|
||||
*/
|
||||
static parseItem<T extends YTNode = YTNode>(data?: RawNode, validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]) {
|
||||
static parseItem<T extends YTNode, K extends YTNodeConstructor<T>[]>(data: RawNode | undefined, validTypes: K): InstanceType<K[number]> | null;
|
||||
static parseItem<T extends YTNode>(data: RawNode | undefined, validTypes: YTNodeConstructor<T>): T | null;
|
||||
static parseItem(data?: RawNode) : YTNode;
|
||||
static parseItem(data?: RawNode, validTypes?: YTNodeConstructor | YTNodeConstructor[]) {
|
||||
if (!data) return null;
|
||||
|
||||
const keys = Object.keys(data);
|
||||
@@ -306,7 +309,7 @@ export default class Parser {
|
||||
const result = new TargetClass(data[keys[0]]);
|
||||
this.#addToMemo(classname, result);
|
||||
|
||||
return result as T;
|
||||
return result;
|
||||
} catch (err) {
|
||||
this.#errorHandler({ classname, classdata: data[keys[0]], err });
|
||||
return null;
|
||||
@@ -316,17 +319,24 @@ export default class Parser {
|
||||
return null;
|
||||
}
|
||||
|
||||
extra() {
|
||||
Parser.parseItem({}, [ MusicMultiSelectMenuItem, MusicMultiSelectMenuItem ]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array of items.
|
||||
* @param data - The data to parse.
|
||||
* @param validTypes - YTNode types that are allowed to be parsed.
|
||||
*/
|
||||
static parseArray<T extends YTNode = YTNode>(data?: RawNode[], validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]) {
|
||||
static parseArray<T extends YTNode, K extends YTNodeConstructor<T>[]>(data: RawNode[] | undefined, validTypes: K): ObservedArray<InstanceType<K[number]>>;
|
||||
static parseArray<T extends YTNode = YTNode>(data: RawNode[] | undefined, validType: YTNodeConstructor<T>): ObservedArray<T>;
|
||||
static parseArray(data: RawNode[] | undefined): ObservedArray<YTNode>;
|
||||
static parseArray(data?: RawNode[], validTypes?: YTNodeConstructor | YTNodeConstructor[]) {
|
||||
if (Array.isArray(data)) {
|
||||
const results: T[] = [];
|
||||
const results: YTNode[] = [];
|
||||
|
||||
for (const item of data) {
|
||||
const result = this.parseItem(item, validTypes);
|
||||
const result = this.parseItem(item, validTypes as YTNodeConstructor);
|
||||
if (result) {
|
||||
results.push(result);
|
||||
}
|
||||
@@ -334,7 +344,7 @@ export default class Parser {
|
||||
|
||||
return observe(results);
|
||||
} else if (!data) {
|
||||
return observe([] as T[]);
|
||||
return observe([] as YTNode[]);
|
||||
}
|
||||
throw new ParsingError('Expected array but got a single item');
|
||||
}
|
||||
@@ -345,7 +355,7 @@ export default class Parser {
|
||||
* @param requireArray - Whether the data should be parsed as an array.
|
||||
* @param validTypes - YTNode types that are allowed to be parsed.
|
||||
*/
|
||||
static parse<T extends YTNode = YTNode>(data: RawData, requireArray: true, validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]): ObservedArray<T> | null;
|
||||
static parse<T extends YTNode, K extends YTNodeConstructor<T>[]>(data: RawData, requireArray: true, validTypes?: K): ObservedArray<InstanceType<K[number]>> | null;
|
||||
static parse<T extends YTNode = YTNode>(data?: RawData, requireArray?: false | undefined, validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]): SuperParsedResult<T>;
|
||||
static parse<T extends YTNode = YTNode>(data?: RawData, requireArray?: boolean, validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]) {
|
||||
if (!data) return null;
|
||||
@@ -354,7 +364,7 @@ export default class Parser {
|
||||
const results: T[] = [];
|
||||
|
||||
for (const item of data) {
|
||||
const result = this.parseItem(item, validTypes);
|
||||
const result = this.parseItem(item, validTypes as YTNodeConstructor<T>);
|
||||
if (result) {
|
||||
results.push(result);
|
||||
}
|
||||
@@ -367,7 +377,7 @@ export default class Parser {
|
||||
throw new ParsingError('Expected array but got a single item');
|
||||
}
|
||||
|
||||
return new SuperParsedResult(this.parseItem(data, validTypes));
|
||||
return new SuperParsedResult(this.parseItem(data, validTypes as YTNodeConstructor<T>));
|
||||
}
|
||||
|
||||
static parseC(data: RawNode) {
|
||||
@@ -699,10 +709,10 @@ export class LiveChatContinuation extends YTNode {
|
||||
}), true) || observe<YTNode>([]);
|
||||
|
||||
this.action_panel = Parser.parseItem(data.actionPanel);
|
||||
this.item_list = Parser.parseItem<LiveChatItemList>(data.itemList);
|
||||
this.header = Parser.parseItem<LiveChatHeader>(data.header);
|
||||
this.participants_list = Parser.parseItem<LiveChatParticipantsList>(data.participantsList);
|
||||
this.popout_message = Parser.parseItem<Message>(data.popoutMessage);
|
||||
this.item_list = Parser.parseItem(data.itemList, LiveChatItemList);
|
||||
this.header = Parser.parseItem(data.header, LiveChatHeader);
|
||||
this.participants_list = Parser.parseItem(data.participantsList, LiveChatParticipantsList);
|
||||
this.popout_message = Parser.parseItem(data.popoutMessage, Message);
|
||||
|
||||
this.emojis = data.emojis?.map((emoji: any) => ({
|
||||
emoji_id: emoji.emojiId,
|
||||
|
||||
@@ -36,6 +36,7 @@ import ItemMenu from './ItemMenu.js';
|
||||
|
||||
import type Actions from '../../core/Actions.js';
|
||||
import type { IParsedResponse, IUpdatedMetadataResponse } from '../types/ParsedResponse.js';
|
||||
import { NavigationEndpoint } from '../nodes.js';
|
||||
|
||||
export type ChatAction =
|
||||
AddChatItemAction | AddBannerToLiveChatCommand | AddLiveChatTickerItemAction |
|
||||
@@ -288,10 +289,10 @@ class LiveChat extends EventEmitter {
|
||||
* Retrieves given chat item's menu.
|
||||
*/
|
||||
async getItemMenu(item: ChatItemWithMenu): Promise<ItemMenu> {
|
||||
if (!item.menu_endpoint)
|
||||
if (!item.hasKey('menu_endpoint') || !item.key('menu_endpoint').isInstanceof(NavigationEndpoint))
|
||||
throw new InnertubeError('This item does not have a menu.', item);
|
||||
|
||||
const response = await item.menu_endpoint.call(this.#actions, { parse: true });
|
||||
const response = await item.key('menu_endpoint').instanceof(NavigationEndpoint).call(this.#actions, { parse: true });
|
||||
|
||||
if (!response)
|
||||
throw new InnertubeError('Could not retrieve item menu.', item);
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import Constants from '../../utils/Constants.js';
|
||||
import Parser from '../index.js';
|
||||
|
||||
import ChipCloud from '../classes/ChipCloud.js';
|
||||
import ChipCloudChip from '../classes/ChipCloudChip.js';
|
||||
import CommentsEntryPointHeader from '../classes/comments/CommentsEntryPointHeader.js';
|
||||
@@ -25,7 +22,6 @@ import PlayerLegacyDesktopYpcTrailer from '../classes/PlayerLegacyDesktopYpcTrai
|
||||
|
||||
import type CardCollection from '../classes/CardCollection.js';
|
||||
import type Endscreen from '../classes/Endscreen.js';
|
||||
import type Format from '../classes/misc/Format.js';
|
||||
import type PlayerAnnotationsExpanded from '../classes/PlayerAnnotationsExpanded.js';
|
||||
import type PlayerCaptionsTracklist from '../classes/PlayerCaptionsTracklist.js';
|
||||
import type PlayerLiveStoryboardSpec from '../classes/PlayerLiveStoryboardSpec.js';
|
||||
@@ -33,33 +29,21 @@ import type PlayerStoryboardSpec from '../classes/PlayerStoryboardSpec.js';
|
||||
|
||||
import type Actions from '../../core/Actions.js';
|
||||
import type { ApiResponse } from '../../core/Actions.js';
|
||||
import type Player from '../../core/Player.js';
|
||||
import type { ObservedArray, YTNode } from '../helpers.js';
|
||||
import type { INextResponse, IPlayerResponse } from '../types/ParsedResponse.js';
|
||||
|
||||
import FormatUtils, { DownloadOptions, FormatFilter, FormatOptions, URLTransformer } from '../../utils/FormatUtils.js';
|
||||
import { ObservedArray, YTNode } from '../helpers.js';
|
||||
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import { MediaInfo } from '../../core/MediaInfo.js';
|
||||
|
||||
class VideoInfo {
|
||||
#page: [IPlayerResponse, INextResponse?];
|
||||
|
||||
#actions: Actions;
|
||||
#player?: Player;
|
||||
#cpn?: string;
|
||||
class VideoInfo extends MediaInfo {
|
||||
#watch_next_continuation?: ContinuationItem;
|
||||
|
||||
basic_info;
|
||||
streaming_data;
|
||||
playability_status;
|
||||
annotations?: ObservedArray<PlayerAnnotationsExpanded>;
|
||||
storyboards?: PlayerStoryboardSpec | PlayerLiveStoryboardSpec;
|
||||
endscreen?: Endscreen;
|
||||
captions?: PlayerCaptionsTracklist;
|
||||
cards?: CardCollection;
|
||||
|
||||
#playback_tracking;
|
||||
|
||||
primary_info?: VideoPrimaryInfo | null;
|
||||
secondary_info?: VideoSecondaryInfo | null;
|
||||
playlist?;
|
||||
@@ -78,18 +62,10 @@ class VideoInfo {
|
||||
* @param player - Player instance.
|
||||
* @param cpn - Client Playback Nonce.
|
||||
*/
|
||||
constructor(data: [ApiResponse, ApiResponse?], actions: Actions, player?: Player, cpn?: string) {
|
||||
this.#actions = actions;
|
||||
this.#player = player;
|
||||
this.#cpn = cpn;
|
||||
constructor(data: [ApiResponse, ApiResponse?], actions: Actions, cpn: string) {
|
||||
super(data, actions, cpn);
|
||||
|
||||
const info = Parser.parseResponse<IPlayerResponse>(data[0].data);
|
||||
const next = data?.[1]?.data ? Parser.parseResponse<INextResponse>(data[1].data) : undefined;
|
||||
|
||||
this.#page = [ info, next ];
|
||||
|
||||
if (info.playability_status?.status === 'ERROR')
|
||||
throw new InnertubeError('This video is unavailable', info.playability_status);
|
||||
const [ info, next ] = this.page;
|
||||
|
||||
if (info.microformat && !info.microformat?.is(PlayerMicroformat, MicroformatData))
|
||||
throw new InnertubeError('Invalid microformat', info.microformat);
|
||||
@@ -114,16 +90,12 @@ class VideoInfo {
|
||||
is_disliked: undefined as boolean | undefined
|
||||
};
|
||||
|
||||
this.streaming_data = info.streaming_data;
|
||||
this.playability_status = info.playability_status;
|
||||
this.annotations = info.annotations;
|
||||
this.storyboards = info.storyboards;
|
||||
this.endscreen = info.endscreen;
|
||||
this.captions = info.captions;
|
||||
this.cards = info.cards;
|
||||
|
||||
this.#playback_tracking = info.playback_tracking;
|
||||
|
||||
const two_col = next?.contents?.item().as(TwoColumnWatchNextResults);
|
||||
|
||||
const results = two_col?.results;
|
||||
@@ -200,7 +172,7 @@ class VideoInfo {
|
||||
|
||||
if (cloud_chip.is_selected) return this;
|
||||
|
||||
const response = await cloud_chip.endpoint?.call(this.#actions, { parse: true });
|
||||
const response = await cloud_chip.endpoint?.call(this.actions, { parse: true });
|
||||
const data = response?.on_response_received_endpoints?.get({ target_id: 'watch-next-feed' });
|
||||
|
||||
this.watch_next_feed = data?.contents;
|
||||
@@ -212,24 +184,7 @@ class VideoInfo {
|
||||
* Adds video to the watch history.
|
||||
*/
|
||||
async addToWatchHistory(): Promise<Response> {
|
||||
if (!this.#playback_tracking)
|
||||
throw new InnertubeError('Playback tracking not available');
|
||||
|
||||
const url_params = {
|
||||
cpn: this.#cpn,
|
||||
fmt: 251,
|
||||
rtn: 0,
|
||||
rt: 0
|
||||
};
|
||||
|
||||
const url = this.#playback_tracking.videostats_playback_url.replace('https://s.', 'https://www.');
|
||||
|
||||
const response = await this.#actions.stats(url, {
|
||||
client_name: Constants.CLIENTS.WEB.NAME,
|
||||
client_version: Constants.CLIENTS.WEB.VERSION
|
||||
}, url_params);
|
||||
|
||||
return response;
|
||||
return super.addToWatchHistory();
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +195,7 @@ class VideoInfo {
|
||||
if (!this.#watch_next_continuation)
|
||||
throw new InnertubeError('Watch next feed continuation not found');
|
||||
|
||||
const response = await this.#watch_next_continuation?.endpoint.call(this.#actions, { parse: true });
|
||||
const response = await this.#watch_next_continuation?.endpoint.call(this.actions, { parse: true });
|
||||
const data = response?.on_response_received_endpoints?.get({ type: 'appendContinuationItemsAction' });
|
||||
|
||||
if (!data)
|
||||
@@ -272,7 +227,7 @@ class VideoInfo {
|
||||
if (button.is_toggled)
|
||||
throw new InnertubeError('This video is already liked', { video_id: this.basic_info.id });
|
||||
|
||||
const response = await button.endpoint.call(this.#actions);
|
||||
const response = await button.endpoint.call(this.actions);
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -293,7 +248,7 @@ class VideoInfo {
|
||||
if (button.is_toggled)
|
||||
throw new InnertubeError('This video is already disliked', { video_id: this.basic_info.id });
|
||||
|
||||
const response = await button.endpoint.call(this.#actions);
|
||||
const response = await button.endpoint.call(this.actions);
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -321,7 +276,7 @@ class VideoInfo {
|
||||
if (!button)
|
||||
throw new InnertubeError('This video is not liked/disliked', { video_id: this.basic_info.id });
|
||||
|
||||
const response = await button.toggled_endpoint.call(this.#actions);
|
||||
const response = await button.toggled_endpoint.call(this.actions);
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -343,38 +298,12 @@ class VideoInfo {
|
||||
if (this.has_trailer) {
|
||||
const player_response = this.playability_status.error_screen?.as(PlayerLegacyDesktopYpcTrailer).trailer?.player_response;
|
||||
if (player_response) {
|
||||
return new VideoInfo([ { data: player_response } as ApiResponse ], this.#actions, this.#player, this.#cpn);
|
||||
return new VideoInfo([ { data: player_response } as ApiResponse ], this.actions, this.cpn);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the format that best matches the given options.
|
||||
* @param options - Options
|
||||
*/
|
||||
chooseFormat(options: FormatOptions): Format {
|
||||
return FormatUtils.chooseFormat(options, this.streaming_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a DASH manifest from the streaming data.
|
||||
* @param url_transformer - Function to transform the URLs.
|
||||
* @param format_filter - Function to filter the formats.
|
||||
* @returns DASH manifest
|
||||
*/
|
||||
async toDash(url_transformer?: URLTransformer, format_filter?: FormatFilter): Promise<string> {
|
||||
return await FormatUtils.toDash(this.streaming_data, url_transformer, format_filter, this.#cpn, this.#player, this.#actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the video.
|
||||
* @param options - Download options.
|
||||
*/
|
||||
async download(options: DownloadOptions = {}): Promise<ReadableStream<Uint8Array>> {
|
||||
return FormatUtils.download(options, this.#actions, this.playability_status, this.streaming_data, this.#actions.session.player, this.cpn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch next feed filters.
|
||||
*/
|
||||
@@ -382,20 +311,6 @@ class VideoInfo {
|
||||
return this.related_chip_cloud?.chips?.map((chip) => chip.text?.toString()) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions instance.
|
||||
*/
|
||||
get actions(): Actions {
|
||||
return this.#actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content Playback Nonce.
|
||||
*/
|
||||
get cpn(): string | undefined {
|
||||
return this.#cpn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if continuation is available for the watch next feed.
|
||||
*/
|
||||
@@ -455,13 +370,6 @@ class VideoInfo {
|
||||
*/
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Original parsed InnerTube response.
|
||||
*/
|
||||
get page(): [IPlayerResponse, INextResponse?] {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
|
||||
export default VideoInfo;
|
||||
@@ -1,59 +1,32 @@
|
||||
import Parser from '../index.js';
|
||||
|
||||
import ItemSection from '../classes/ItemSection.js';
|
||||
import NavigationEndpoint from '../classes/NavigationEndpoint.js';
|
||||
import PlayerOverlay from '../classes/PlayerOverlay.js';
|
||||
import SlimVideoMetadata from '../classes/SlimVideoMetadata.js';
|
||||
import TwoColumnWatchNextResults from '../classes/TwoColumnWatchNextResults.js';
|
||||
|
||||
import type Format from '../classes/misc/Format.js';
|
||||
import type Actions from '../../core/Actions.js';
|
||||
import type { ApiResponse } from '../../core/Actions.js';
|
||||
import type { ObservedArray, YTNode } from '../helpers.js';
|
||||
import type { INextResponse, IPlayerResponse } from '../types/ParsedResponse.js';
|
||||
|
||||
import { Constants } from '../../utils/index.js';
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
|
||||
import FormatUtils, { DownloadOptions, FormatFilter, FormatOptions, URLTransformer } from '../../utils/FormatUtils.js';
|
||||
|
||||
class VideoInfo {
|
||||
#page: [IPlayerResponse, INextResponse?];
|
||||
#actions: Actions;
|
||||
#cpn: string;
|
||||
import { MediaInfo } from '../../core/MediaInfo.js';
|
||||
|
||||
class VideoInfo extends MediaInfo {
|
||||
basic_info;
|
||||
streaming_data;
|
||||
playability_status;
|
||||
captions;
|
||||
|
||||
#playback_tracking;
|
||||
|
||||
slim_video_metadata?: SlimVideoMetadata;
|
||||
watch_next_feed?: ObservedArray<YTNode>;
|
||||
current_video_endpoint?: NavigationEndpoint;
|
||||
player_overlays?: PlayerOverlay;
|
||||
|
||||
constructor(data: [ApiResponse, ApiResponse?], actions: Actions, cpn: string) {
|
||||
this.#actions = actions;
|
||||
super(data, actions, cpn);
|
||||
|
||||
const info = Parser.parseResponse<IPlayerResponse>(data[0].data);
|
||||
const next = data?.[1]?.data ? Parser.parseResponse<INextResponse>(data[1].data) : undefined;
|
||||
|
||||
this.#page = [ info, next ];
|
||||
this.#cpn = cpn;
|
||||
|
||||
if (info.playability_status?.status === 'ERROR')
|
||||
throw new InnertubeError('This video is unavailable', info.playability_status);
|
||||
const [ info, next ] = this.page;
|
||||
|
||||
this.basic_info = info.video_details;
|
||||
|
||||
this.streaming_data = info.streaming_data;
|
||||
this.playability_status = info.playability_status;
|
||||
this.captions = info.captions;
|
||||
|
||||
this.#playback_tracking = info.playback_tracking;
|
||||
|
||||
const two_col = next?.contents?.item().as(TwoColumnWatchNextResults);
|
||||
|
||||
const results = two_col?.results;
|
||||
@@ -67,72 +40,11 @@ class VideoInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a DASH manifest from the streaming data.
|
||||
* @param url_transformer - Function to transform the URLs.
|
||||
* @param format_filter - Function to filter the formats.
|
||||
* @returns DASH manifest
|
||||
*/
|
||||
async toDash(url_transformer?: URLTransformer, format_filter?: FormatFilter): Promise<string> {
|
||||
return await FormatUtils.toDash(this.streaming_data, url_transformer, format_filter, this.#cpn, this.#actions.session.player, this.#actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the format that best matches the given options.
|
||||
* @param options - Options
|
||||
*/
|
||||
chooseFormat(options: FormatOptions): Format {
|
||||
return FormatUtils.chooseFormat(options, this.streaming_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the video.
|
||||
* @param options - Download options.
|
||||
*/
|
||||
async download(options: DownloadOptions = {}): Promise<ReadableStream<Uint8Array>> {
|
||||
return FormatUtils.download(options, this.#actions, this.playability_status, this.streaming_data, this.#actions.session.player, this.cpn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds video to the watch history.
|
||||
*/
|
||||
async addToWatchHistory(): Promise<Response> {
|
||||
if (!this.#playback_tracking)
|
||||
throw new InnertubeError('Playback tracking not available');
|
||||
|
||||
const url_params = {
|
||||
cpn: this.#cpn,
|
||||
fmt: 251,
|
||||
rtn: 0,
|
||||
rt: 0
|
||||
};
|
||||
|
||||
const url = this.#playback_tracking.videostats_playback_url.replace('https://s.', 'https://www.');
|
||||
|
||||
const response = await this.#actions.stats(url, {
|
||||
client_name: Constants.CLIENTS.WEB.NAME,
|
||||
client_version: Constants.CLIENTS.WEB.VERSION
|
||||
}, url_params);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions instance.
|
||||
*/
|
||||
get actions(): Actions {
|
||||
return this.#actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content Playback Nonce.
|
||||
*/
|
||||
get cpn(): string | undefined {
|
||||
return this.#cpn;
|
||||
}
|
||||
|
||||
get page(): [IPlayerResponse, INextResponse?] {
|
||||
return this.#page;
|
||||
return super.addToWatchHistory();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Parser from '../index.js';
|
||||
import type Actions from '../../core/Actions.js';
|
||||
import type { ApiResponse } from '../../core/Actions.js';
|
||||
|
||||
@@ -19,43 +18,25 @@ import SectionList from '../classes/SectionList.js';
|
||||
import Tab from '../classes/Tab.js';
|
||||
import WatchNextTabbedResults from '../classes/WatchNextTabbedResults.js';
|
||||
|
||||
import type Format from '../classes/misc/Format.js';
|
||||
import type NavigationEndpoint from '../classes/NavigationEndpoint.js';
|
||||
import type PlayerLiveStoryboardSpec from '../classes/PlayerLiveStoryboardSpec.js';
|
||||
import type PlayerStoryboardSpec from '../classes/PlayerStoryboardSpec.js';
|
||||
import type { ObservedArray, YTNode } from '../helpers.js';
|
||||
import type { INextResponse, IPlayerResponse } from '../types/ParsedResponse.js';
|
||||
|
||||
import FormatUtils, { DownloadOptions, FormatFilter, FormatOptions, URLTransformer } from '../../utils/FormatUtils.js';
|
||||
|
||||
class TrackInfo {
|
||||
#page: [ IPlayerResponse, INextResponse? ];
|
||||
#actions: Actions;
|
||||
#cpn: string;
|
||||
import { MediaInfo } from '../../core/MediaInfo.js';
|
||||
|
||||
class TrackInfo extends MediaInfo {
|
||||
basic_info;
|
||||
streaming_data;
|
||||
playability_status;
|
||||
storyboards?: PlayerStoryboardSpec | PlayerLiveStoryboardSpec;
|
||||
endscreen?: Endscreen;
|
||||
|
||||
#playback_tracking;
|
||||
|
||||
tabs?: ObservedArray<Tab>;
|
||||
current_video_endpoint?: NavigationEndpoint;
|
||||
player_overlays?: PlayerOverlay;
|
||||
|
||||
constructor(data: [ApiResponse, ApiResponse?], actions: Actions, cpn: string) {
|
||||
this.#actions = actions;
|
||||
super(data, actions, cpn);
|
||||
|
||||
const info = Parser.parseResponse<IPlayerResponse>(data[0].data);
|
||||
const next = data?.[1]?.data ? Parser.parseResponse<INextResponse>(data[1].data) : undefined;
|
||||
|
||||
this.#page = [ info, next ];
|
||||
this.#cpn = cpn;
|
||||
|
||||
if (info.playability_status?.status === 'ERROR')
|
||||
throw new InnertubeError('This video is unavailable', info.playability_status);
|
||||
const [ info, next ] = this.page;
|
||||
|
||||
if (!info.microformat?.is(MicroformatData))
|
||||
throw new InnertubeError('Invalid microformat', info.microformat);
|
||||
@@ -71,13 +52,9 @@ class TrackInfo {
|
||||
}
|
||||
};
|
||||
|
||||
this.streaming_data = info.streaming_data;
|
||||
this.playability_status = info.playability_status;
|
||||
this.storyboards = info.storyboards;
|
||||
this.endscreen = info.endscreen;
|
||||
|
||||
this.#playback_tracking = info.playback_tracking;
|
||||
|
||||
if (next) {
|
||||
const tabbed_results = next.contents_memo?.getType(WatchNextTabbedResults)?.[0];
|
||||
|
||||
@@ -89,32 +66,6 @@ class TrackInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a DASH manifest from the streaming data.
|
||||
* @param url_transformer - Function to transform the URLs.
|
||||
* @param format_filter - Function to filter the formats.
|
||||
* @returns DASH manifest
|
||||
*/
|
||||
async toDash(url_transformer?: URLTransformer, format_filter?: FormatFilter): Promise<string> {
|
||||
return await FormatUtils.toDash(this.streaming_data, url_transformer, format_filter, this.#cpn, this.#actions.session.player, this.#actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the format that best matches the given options.
|
||||
* @param options - Options
|
||||
*/
|
||||
chooseFormat(options: FormatOptions): Format {
|
||||
return FormatUtils.chooseFormat(options, this.streaming_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the video.
|
||||
* @param options - Download options.
|
||||
*/
|
||||
async download(options: DownloadOptions = {}): Promise<ReadableStream<Uint8Array>> {
|
||||
return FormatUtils.download(options, this.#actions, this.playability_status, this.streaming_data, this.#actions.session.player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves contents of the given tab.
|
||||
*/
|
||||
@@ -133,7 +84,7 @@ class TrackInfo {
|
||||
if (target_tab.content)
|
||||
return target_tab.content;
|
||||
|
||||
const page = await target_tab.endpoint.call(this.#actions, { client: 'YTMUSIC', parse: true });
|
||||
const page = await target_tab.endpoint.call(this.actions, { client: 'YTMUSIC', parse: true });
|
||||
|
||||
if (page.contents?.item().key('type').string() === 'Message')
|
||||
return page.contents.item().as(Message);
|
||||
@@ -161,7 +112,7 @@ class TrackInfo {
|
||||
if (!automix_preview_video)
|
||||
throw new InnertubeError('Automix item not found');
|
||||
|
||||
const page = await automix_preview_video.playlist_video?.endpoint.call(this.#actions, {
|
||||
const page = await automix_preview_video.playlist_video?.endpoint.call(this.actions, {
|
||||
videoId: this.basic_info.id,
|
||||
client: 'YTMUSIC',
|
||||
parse: true
|
||||
@@ -196,33 +147,12 @@ class TrackInfo {
|
||||
* Adds the song to the watch history.
|
||||
*/
|
||||
async addToWatchHistory(): Promise<Response> {
|
||||
if (!this.#playback_tracking)
|
||||
throw new InnertubeError('Playback tracking not available');
|
||||
|
||||
const url_params = {
|
||||
cpn: this.#cpn,
|
||||
fmt: 251,
|
||||
rtn: 0,
|
||||
rt: 0
|
||||
};
|
||||
|
||||
const url = this.#playback_tracking.videostats_playback_url.replace('https://s.', 'https://music.');
|
||||
|
||||
const response = await this.#actions.stats(url, {
|
||||
client_name: Constants.CLIENTS.YTMUSIC.NAME,
|
||||
client_version: Constants.CLIENTS.YTMUSIC.VERSION
|
||||
}, url_params);
|
||||
|
||||
return response;
|
||||
return super.addToWatchHistory(Constants.CLIENTS.YTMUSIC.NAME, Constants.CLIENTS.YTMUSIC.VERSION, 'https://music.');
|
||||
}
|
||||
|
||||
get available_tabs(): string[] {
|
||||
return this.tabs ? this.tabs.map((tab) => tab.title) : [];
|
||||
}
|
||||
|
||||
get page(): [IPlayerResponse, INextResponse?] {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
|
||||
export default TrackInfo;
|
||||
Reference in New Issue
Block a user