Compare commits

...

12 Commits

Author SHA1 Message Date
github-actions[bot]
3ec1609974 chore(main): release 12.2.0 (#833)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-12 10:32:02 -03:00
Luan
25fd979363 chore(deps): Update jintr 2024-12-12 10:30:45 -03:00
jonz94
0c319aacde feat(parser): Update LiveChatMembershipItem (#836) 2024-12-12 09:39:25 -03:00
jonz94
69d42b291c feat(parser): Update LiveChatBanner (#840)
* Mark `viewerIsCreator` property as optional
* Mark `background_type` property as optional
* Add `banner_type` property
* Add `banner_properties_is_ephemeral` property
* Add `banner_properties_auto_collapse_delay_seconds` property
2024-12-12 09:38:44 -03:00
jonz94
9a9bb76a92 feat: add VideoMetadataCarouselView (#839) 2024-12-12 09:34:07 -03:00
jonz94
0b2b0da957 feat(parser): Add ClientSideToggleMenuItem (#835)
* feat(parser): Add `ClientSideToggleMenuItem`

* feat: parse the command

* feat: handle different type of `ClientSideToggleMenuItem`
2024-12-12 09:31:59 -03:00
jonz94
eeaae6209f feat(parser): Add ButtonCardView (#834) 2024-12-12 09:31:14 -03:00
absidue
13e796123b fix(Player): Fix signature algorithm extraction again (#837) 2024-12-12 09:30:50 -03:00
Luan
5f233ae34e feat(parser): Add ActiveAccountHeader
Found on the 'You' (a.k.a, 'Library') page.
2024-12-10 18:32:31 -03:00
Luan
c1de097ce1 chore(Parser): Don't ignore BackgroundPromo
It turns out this is not an ad at all.
2024-12-10 16:30:23 -03:00
Luan
67f13fffac feat(Actions): Allow auth check to be skipped 2024-12-10 16:12:06 -03:00
Luan
c8173c88e0 feat(parser): Add PlaylistThumbnailOverlay 2024-12-10 13:50:20 -03:00
21 changed files with 371 additions and 46 deletions

View File

@@ -1,5 +1,24 @@
# Changelog
## [12.2.0](https://github.com/LuanRT/YouTube.js/compare/v12.1.0...v12.2.0) (2024-12-12)
### Features
* **Actions:** Allow auth check to be skipped ([67f13ff](https://github.com/LuanRT/YouTube.js/commit/67f13fffacec2c655a03d66c6d8016620d9abcf9))
* add `VideoMetadataCarouselView` ([#839](https://github.com/LuanRT/YouTube.js/issues/839)) ([9a9bb76](https://github.com/LuanRT/YouTube.js/commit/9a9bb76a928594c5c5f3e828c86081bf79c2562d))
* **parser:** Add `ActiveAccountHeader` ([5f233ae](https://github.com/LuanRT/YouTube.js/commit/5f233ae34e278e7f7a0c48e4ba762d9bac9e312f))
* **parser:** Add `ButtonCardView` ([#834](https://github.com/LuanRT/YouTube.js/issues/834)) ([eeaae62](https://github.com/LuanRT/YouTube.js/commit/eeaae6209f238b838b9b7fdd9bbef89f4f858fa3))
* **parser:** Add `ClientSideToggleMenuItem` ([#835](https://github.com/LuanRT/YouTube.js/issues/835)) ([0b2b0da](https://github.com/LuanRT/YouTube.js/commit/0b2b0da9577f8d6ad19393700071ea9f26d4da10))
* **parser:** Add `PlaylistThumbnailOverlay` ([c8173c8](https://github.com/LuanRT/YouTube.js/commit/c8173c88e0e17ec4bb4e93af1867c55d07611cc0))
* **parser:** Update `LiveChatBanner` ([#840](https://github.com/LuanRT/YouTube.js/issues/840)) ([69d42b2](https://github.com/LuanRT/YouTube.js/commit/69d42b291c927abb9d84f97ed03518c4ddd4506e))
* **parser:** Update `LiveChatMembershipItem` ([#836](https://github.com/LuanRT/YouTube.js/issues/836)) ([0c319aa](https://github.com/LuanRT/YouTube.js/commit/0c319aacdeba106d84b7f44505a7a296a154f97a))
### Bug Fixes
* **Player:** Fix signature algorithm extraction again ([#837](https://github.com/LuanRT/YouTube.js/issues/837)) ([13e7961](https://github.com/LuanRT/YouTube.js/commit/13e796123b87136f2d5d3b3c9b3ed079a014bf46))
## [12.1.0](https://github.com/LuanRT/YouTube.js/compare/v12.0.0...v12.1.0) (2024-12-10)

12
package-lock.json generated
View File

@@ -1,19 +1,19 @@
{
"name": "youtubei.js",
"version": "12.1.0",
"version": "12.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "youtubei.js",
"version": "12.1.0",
"version": "12.2.0",
"funding": [
"https://github.com/sponsors/LuanRT"
],
"license": "MIT",
"dependencies": {
"@bufbuild/protobuf": "^2.0.0",
"jintr": "^3.1.0",
"jintr": "^3.2.0",
"tslib": "^2.5.0",
"undici": "^5.19.1"
},
@@ -6155,9 +6155,9 @@
}
},
"node_modules/jintr": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jintr/-/jintr-3.1.0.tgz",
"integrity": "sha512-azhCHApkRfBH8INpiUCwKBYaNCdB5G+x3NApsI2MxQXSlgFAx7rap3YwE3JAkN08GO8f3ilZsGB0Yvc+412ntQ==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jintr/-/jintr-3.2.0.tgz",
"integrity": "sha512-psD1yf05kMKDNsUdW1l5YhO59pHScQ6OIHHb8W5SKSM2dCOFPsqolmIuSHgVA8+3Dc47NJR181CXZ4alCAPTkA==",
"funding": [
"https://github.com/sponsors/LuanRT"
],

View File

@@ -1,6 +1,6 @@
{
"name": "youtubei.js",
"version": "12.1.0",
"version": "12.2.0",
"description": "A JavaScript client for YouTube's private API, known as InnerTube.",
"type": "module",
"types": "./dist/src/platform/lib.d.ts",
@@ -107,7 +107,7 @@
"license": "MIT",
"dependencies": {
"@bufbuild/protobuf": "^2.0.0",
"jintr": "^3.1.0",
"jintr": "^3.2.0",
"tslib": "^2.5.0",
"undici": "^5.19.1"
},

View File

@@ -1,9 +1,14 @@
import type {
IBrowseResponse, IGetNotificationsMenuResponse, INextResponse,
IParsedResponse, IPlayerResponse, IRawResponse,
IResolveURLResponse, ISearchResponse, IUpdatedMetadataResponse
IBrowseResponse,
IGetNotificationsMenuResponse,
INextResponse,
IParsedResponse,
IPlayerResponse,
IRawResponse,
IResolveURLResponse,
ISearchResponse,
IUpdatedMetadataResponse
} from '../parser/index.js';
import { NavigateAction, Parser } from '../parser/index.js';
import { InnertubeError } from '../utils/Utils.js';
@@ -15,17 +20,25 @@ export interface ApiResponse {
data: IRawResponse;
}
export type InnertubeEndpoint = '/player' | '/search' | '/browse' | '/next' | '/reel' | '/updated_metadata' | '/notification/get_notification_menu' | string;
export type InnertubeEndpoint =
'/player'
| '/search'
| '/browse'
| '/next'
| '/reel'
| '/updated_metadata'
| '/notification/get_notification_menu'
| string;
export type ParsedResponse<T> =
T extends '/player' ? IPlayerResponse :
T extends '/search' ? ISearchResponse :
T extends '/browse' ? IBrowseResponse :
T extends '/next' ? INextResponse :
T extends '/updated_metadata' ? IUpdatedMetadataResponse :
T extends '/navigation/resolve_url' ? IResolveURLResponse :
T extends '/notification/get_notification_menu' ? IGetNotificationsMenuResponse :
IParsedResponse;
T extends '/search' ? ISearchResponse :
T extends '/browse' ? IBrowseResponse :
T extends '/next' ? INextResponse :
T extends '/updated_metadata' ? IUpdatedMetadataResponse :
T extends '/navigation/resolve_url' ? IResolveURLResponse :
T extends '/notification/get_notification_menu' ? IGetNotificationsMenuResponse :
IParsedResponse;
export default class Actions {
public session: Session;
@@ -52,7 +65,9 @@ export default class Actions {
* @param client - The client to use.
* @param params - Call parameters.
*/
async stats(url: string, client: { client_name: string; client_version: string }, params: { [key: string]: any }): Promise<Response> {
async stats(url: string, client: { client_name: string; client_version: string }, params: {
[key: string]: any
}): Promise<Response> {
const s_url = new URL(url);
s_url.searchParams.set('ver', '2');
@@ -72,18 +87,39 @@ export default class Actions {
* @param endpoint - The endpoint to call.
* @param args - Call arguments
*/
async execute<T extends InnertubeEndpoint>(endpoint: T, args: { [key: string]: any; parse: true; protobuf?: false; serialized_data?: any }): Promise<ParsedResponse<T>>;
async execute<T extends InnertubeEndpoint>(endpoint: T, args?: { [key: string]: any; parse?: false; protobuf?: true; serialized_data?: any }): Promise<ApiResponse>;
async execute<T extends InnertubeEndpoint>(endpoint: T, args?: { [key: string]: any; parse?: boolean; protobuf?: boolean; serialized_data?: any }): Promise<ParsedResponse<T> | ApiResponse> {
async execute<T extends InnertubeEndpoint>(endpoint: T, args: {
[key: string]: any;
parse: true;
protobuf?: false;
serialized_data?: any;
skip_auth_check?: boolean
}): Promise<ParsedResponse<T>>;
async execute<T extends InnertubeEndpoint>(endpoint: T, args?: {
[key: string]: any;
parse?: false;
protobuf?: true;
serialized_data?: any;
skip_auth_check?: boolean
}): Promise<ApiResponse>;
async execute<T extends InnertubeEndpoint>(endpoint: T, args?: {
[key: string]: any;
parse?: boolean;
protobuf?: boolean;
serialized_data?: any;
skip_auth_check?: boolean
}): Promise<ParsedResponse<T> | ApiResponse> {
let data;
if (args && !args.protobuf) {
data = { ...args };
if (Reflect.has(data, 'browseId')) {
if (Reflect.has(data, 'browseId') && !args.skip_auth_check) {
if (this.#needsLogin(data.browseId) && !this.session.logged_in)
throw new InnertubeError('You must be signed in to perform this operation.');
}
if (Reflect.has(data, 'skip_auth_check'))
delete data.skip_auth_check;
if (Reflect.has(data, 'override_endpoint'))
delete data.override_endpoint;

View File

@@ -223,22 +223,23 @@ export default class Player {
return parseInt(getStringBetweenStrings(data, 'signatureTimestamp:', ',') || '0');
}
static extractSigSourceCode(data: string): string {
let calls = getStringBetweenStrings(data, 'function(a){a=a.split("")', 'return a.join("")}');
let var_name = 'a';
static extractSigSourceCode(data: string): string | undefined {
const match = data.match(/function\(([A-Za-z_0-9]+)\)\{([A-Za-z_0-9]+=[A-Za-z_0-9]+\.split\(""\)(.+?)\.join\(""\))\}/);
if (!calls) {
calls = getStringBetweenStrings(data, 'function(J){J=J.split("")', 'return J.join("")}');
var_name = 'J';
if (!match) {
Log.warn(TAG, 'Failed to extract signature decipher algorithm.');
return;
}
const obj_name = calls?.split(/\.|\[/)?.[0]?.replace(';', '')?.trim();
const var_name = match[1];
const obj_name = match[3].split(/\.|\[/)[0]?.replace(';', '').trim();
const functions = getStringBetweenStrings(data, `var ${obj_name}={`, '};');
if (!functions || !calls)
if (!functions || !var_name)
Log.warn(TAG, 'Failed to extract signature decipher algorithm.');
return `function descramble_sig(${var_name}) { ${var_name} = ${var_name}.split(""); let ${obj_name}={${functions}}${calls} return ${var_name}.join("") } descramble_sig(sig);`;
return `function descramble_sig(${var_name}) { let ${obj_name}={${functions}}; ${match[2]} } descramble_sig(sig);`;
}
static extractNSigSourceCode(data: string): string | undefined {
@@ -262,6 +263,6 @@ export default class Player {
}
static get LIBRARY_VERSION(): number {
return 12;
return 13;
}
}

View File

@@ -0,0 +1,24 @@
import { YTNode } from '../helpers.js';
import { type RawNode } from '../index.js';
import Text from './misc/Text.js';
import Thumbnail from './misc/Thumbnail.js';
import NavigationEndpoint from './NavigationEndpoint.js';
export default class ActiveAccountHeader extends YTNode {
static type = 'ActiveAccountHeader';
public account_name: Text;
public account_photo: Thumbnail[];
public endpoint: NavigationEndpoint;
public manage_account_title: Text;
public channel_handle: Text;
constructor(data: RawNode) {
super();
this.account_name = new Text(data.accountName);
this.account_photo = Thumbnail.fromResponse(data.accountPhoto);
this.endpoint = new NavigationEndpoint(data.serviceEndpoint);
this.manage_account_title = new Text(data.manageAccountTitle);
this.channel_handle = new Text(data.channelHandle);
}
}

View File

@@ -0,0 +1,25 @@
import { YTNode } from '../helpers.js';
import { Parser, type RawNode } from '../index.js';
import Text from './misc/Text.js';
import Button from './Button.js';
import ButtonView from './ButtonView.js';
export default class BackgroundPromo extends YTNode {
static type = 'BackgroundPromo';
public body_text?: Text;
public cta_button?: Button | ButtonView | null;
public icon_type?: string;
public title?: Text;
constructor(data: RawNode) {
super();
this.body_text = new Text(data.bodyText);
this.cta_button = Parser.parseItem(data.ctaButton, [ Button, ButtonView ]);
if (Reflect.has(data, 'icon'))
this.icon_type = data.icon.iconType;
this.title = new Text(data.title);
}
}

View File

@@ -0,0 +1,18 @@
import NavigationEndpoint from './NavigationEndpoint.js';
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
export default class ButtonCardView extends YTNode {
static type = 'ButtonCardView';
title: string;
icon_name: string;
on_tap_endpoint: NavigationEndpoint;
constructor(data: RawNode) {
super();
this.title = data.title;
this.icon_name = data.icon.sources[0].clientResource.imageName;
this.on_tap_endpoint = new NavigationEndpoint(data.rendererContext.commandContext.onTap);
}
}

View File

@@ -0,0 +1,16 @@
import { YTNode } from '../helpers.js';
import { Parser, type RawNode } from '../index.js';
import TextCarouselItemView from './TextCarouselItemView.js';
export default class CarouselItemView extends YTNode {
static type = 'CarouselItemView';
item_type: string;
carousel_item: TextCarouselItemView | null;
constructor(data: RawNode) {
super();
this.item_type = data.itemType;
this.carousel_item = Parser.parseItem(data.carouselItem, TextCarouselItemView);
}
}

View File

@@ -0,0 +1,18 @@
import { YTNode } from '../helpers.js';
import { Parser, type RawNode } from '../index.js';
import ButtonView from './ButtonView.js';
export default class CarouselTitleView extends YTNode {
static type = 'CarouselTitleView';
title: string;
previous_button: ButtonView | null;
next_button: ButtonView | null;
constructor(data: RawNode) {
super();
this.title = data.title;
this.previous_button = Parser.parseItem(data.previousButton, ButtonView);
this.next_button = Parser.parseItem(data.nextButton, ButtonView);
}
}

View File

@@ -0,0 +1,46 @@
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import Text from './misc/Text.js';
export default class ClientSideToggleMenuItem extends YTNode {
static type = 'ClientSideToggleMenuItem';
text: Text;
icon_type: string;
toggled_text: Text;
toggled_icon_type: string;
is_toggled?: boolean;
menu_item_identifier: string;
endpoint: NavigationEndpoint;
logging_directives?: {
visibility: {
types: string;
},
enable_displaylogger_experiment: boolean;
};
constructor(data: RawNode) {
super();
this.text = new Text(data.defaultText);
this.icon_type = data.defaultIcon.iconType;
this.toggled_text = new Text(data.toggledText);
this.toggled_icon_type = data.toggledIcon.iconType;
if (Reflect.has(data, 'isToggled')) {
this.is_toggled = data.isToggled;
}
this.menu_item_identifier = data.menuItemIdentifier;
this.endpoint = new NavigationEndpoint(data.command);
if (Reflect.has(data, 'loggingDirectives')) {
this.logging_directives = {
visibility: {
types: data.loggingDirectives.visibility.types
},
enable_displaylogger_experiment: data.loggingDirectives.enableDisplayloggerExperiment
};
}
}
}

View File

@@ -0,0 +1,17 @@
import { YTNode } from '../helpers.js';
import { type RawNode } from '../index.js';
import Text from './misc/Text.js';
export default class PlaylistThumbnailOverlay extends YTNode {
static type = 'PlaylistThumbnailOverlay';
public icon_type?: string;
public text: Text;
constructor(data: RawNode) {
super();
if (Reflect.has(data, 'icon'))
this.icon_type = data.icon.iconType;
this.text = new Text(data.text);
}
}

View File

@@ -0,0 +1,22 @@
import { YTNode } from '../helpers.js';
import { Parser, type RawNode } from '../index.js';
import { Text } from '../misc.js';
import ButtonView from './ButtonView.js';
import NavigationEndpoint from './NavigationEndpoint.js';
export default class TextCarouselItemView extends YTNode {
static type = 'TextCarouselItemView';
icon_name: string;
text: Text;
on_tap_endpoint: NavigationEndpoint;
button: ButtonView | null;
constructor(data: RawNode) {
super();
this.icon_name = data.iconName;
this.text = Text.fromAttributed(data.text);
this.on_tap_endpoint = new NavigationEndpoint(data.onTap);
this.button = Parser.parseItem(data.button, ButtonView);
}
}

View File

@@ -38,6 +38,7 @@ export default class Video extends YTNode {
show_action_menu: boolean;
is_watched: boolean;
menu: Menu | null;
byline_text?: Text;
search_video_result_entity_key?: string;
constructor(data: RawNode) {
@@ -91,6 +92,10 @@ export default class Video extends YTNode {
if (Reflect.has(data, 'searchVideoResultEntityKey')) {
this.search_video_result_entity_key = data.searchVideoResultEntityKey;
}
if (Reflect.has(data, 'bylineText')) {
this.byline_text = new Text(data.bylineText);
}
}
get description(): string {

View File

@@ -5,16 +5,20 @@ import Video from './Video.js';
export default class VideoCard extends Video {
static type = 'VideoCard';
public metadata_text?: Text;
public byline_text?: Text;
constructor(data: RawNode) {
super(data);
if (Reflect.has(data, 'metadataText')) {
const metadata = new Text(data.metadataText);
if (metadata.text) {
this.short_view_count = new Text({ simpleText: metadata.text.split('·')[0].trim() } as RawNode);
this.published = new Text({ simpleText: metadata.text.split('·')[1].trim() } as RawNode);
this.metadata_text = new Text(data.metadataText);
if (this.metadata_text.text) {
this.short_view_count = new Text({ simpleText: this.metadata_text.text.split('·')[0]?.trim() } as RawNode);
this.published = new Text({ simpleText: this.metadata_text.text.split('·')[1]?.trim() } as RawNode);
}
}
if (Reflect.has(data, 'bylineText')) {
this.author = new Author(data.bylineText, data.ownerBadges, data.channelThumbnailSupportedRenderers?.channelThumbnailWithLinkRenderer?.thumbnail);
}

View File

@@ -0,0 +1,17 @@
import { YTNode, type ObservedArray } from '../helpers.js';
import { Parser, type RawNode } from '../index.js';
import CarouselItemView from './CarouselItemView.js';
import CarouselTitleView from './CarouselTitleView.js';
export default class VideoMetadataCarouselView extends YTNode {
static type = 'VideoMetadataCarouselView';
carousel_titles: ObservedArray<CarouselTitleView> | null;
carousel_items: ObservedArray<CarouselItemView> | null;
constructor(data: RawNode) {
super();
this.carousel_titles = Parser.parse(data.carouselTitles, true, CarouselTitleView);
this.carousel_items = Parser.parse(data.carouselItems, true, CarouselItemView);
}
}

View File

@@ -9,19 +9,46 @@ export default class LiveChatBanner extends YTNode {
header: LiveChatBannerHeader | null;
contents: YTNode;
action_id: string;
viewer_is_creator: boolean;
viewer_is_creator?: boolean;
target_id: string;
is_stackable: boolean;
background_type: string;
background_type?: string;
banner_type: string;
banner_properties_is_ephemeral?: boolean;
banner_properties_auto_collapse_delay_seconds?: string;
constructor(data: RawNode) {
super();
this.header = Parser.parseItem(data.header, LiveChatBannerHeader);
this.contents = Parser.parseItem(data.contents);
this.action_id = data.actionId;
this.viewer_is_creator = data.viewerIsCreator;
if (Reflect.has(data, 'viewerIsCreator')) {
this.viewer_is_creator = data.viewerIsCreator;
}
this.target_id = data.targetId;
this.is_stackable = data.isStackable;
this.background_type = data.backgroundType;
if (Reflect.has(data, 'backgroundType')) {
this.background_type = data.backgroundType;
}
this.banner_type = data.bannerType;
if (
Reflect.has(data, 'bannerProperties') &&
Reflect.has(data.bannerProperties, 'isEphemeral')
) {
this.banner_properties_is_ephemeral = Boolean(data.bannerProperties.isEphemeral);
}
if (
Reflect.has(data, 'bannerProperties') &&
Reflect.has(data.bannerProperties, 'autoCollapseDelay') &&
Reflect.has(data.bannerProperties.autoCollapseDelay, 'seconds')
) {
this.banner_properties_auto_collapse_delay_seconds = data.bannerProperties.autoCollapseDelay.seconds;
}
}
}

View File

@@ -9,16 +9,37 @@ export default class LiveChatMembershipItem extends YTNode {
id: string;
timestamp: number;
timestamp_usec: string;
timestamp_text?: Text;
header_primary_text?: Text;
header_subtext: Text;
message?: Text;
author: Author;
menu_endpoint: NavigationEndpoint;
context_menu_accessibility_label: string;
constructor(data: RawNode) {
super();
this.id = data.id;
this.timestamp = Math.floor(parseInt(data.timestampUsec) / 1000);
this.timestamp_usec = data.timestampUsec;
if (Reflect.has(data, 'timestampText')) {
this.timestamp_text = new Text(data.timestampText);
}
if (Reflect.has(data, 'headerPrimaryText')) {
this.header_primary_text = new Text(data.headerPrimaryText);
}
this.header_subtext = new Text(data.headerSubtext);
if (Reflect.has(data, 'message')) {
this.message = new Text(data.message);
}
this.author = new Author(data.authorName, data.authorBadges, data.authorPhoto, data.authorExternalChannelId);
this.menu_endpoint = new NavigationEndpoint(data.contextMenuEndpoint);
this.context_menu_accessibility_label = data.contextMenuAccessibility.accessibilityData.label;
}
}

View File

@@ -8,20 +8,21 @@ import SegmentedLikeDislikeButtonView from '../SegmentedLikeDislikeButtonView.js
import MenuFlexibleItem from './MenuFlexibleItem.js';
import LikeButton from '../LikeButton.js';
import ToggleButton from '../ToggleButton.js';
import FlexibleActionsView from '../FlexibleActionsView.js';
export default class Menu extends YTNode {
static type = 'Menu';
public items: ObservedArray<YTNode>;
public flexible_items: ObservedArray<MenuFlexibleItem>;
public top_level_buttons: ObservedArray<ToggleButton | LikeButton | Button |ButtonView | SegmentedLikeDislikeButtonView>;
public top_level_buttons: ObservedArray<ToggleButton | LikeButton | Button |ButtonView | SegmentedLikeDislikeButtonView | FlexibleActionsView>;
public label?: string;
constructor(data: RawNode) {
super();
this.items = Parser.parseArray(data.items);
this.flexible_items = Parser.parseArray(data.flexibleItems, MenuFlexibleItem);
this.top_level_buttons = Parser.parseArray(data.topLevelButtons, [ ToggleButton, LikeButton, Button, ButtonView, SegmentedLikeDislikeButtonView ]);
this.top_level_buttons = Parser.parseArray(data.topLevelButtons, [ ToggleButton, LikeButton, Button, ButtonView, SegmentedLikeDislikeButtonView, FlexibleActionsView ]);
if (Reflect.has(data, 'accessibility') && Reflect.has(data.accessibility, 'accessibilityData')) {
this.label = data.accessibility.accessibilityData.label;

View File

@@ -17,6 +17,7 @@ export { default as SignalAction } from './classes/actions/SignalAction.js';
export { default as UpdateChannelSwitcherPageAction } from './classes/actions/UpdateChannelSwitcherPageAction.js';
export { default as UpdateEngagementPanelAction } from './classes/actions/UpdateEngagementPanelAction.js';
export { default as UpdateSubscribeButtonAction } from './classes/actions/UpdateSubscribeButtonAction.js';
export { default as ActiveAccountHeader } from './classes/ActiveAccountHeader.js';
export { default as AddToPlaylist } from './classes/AddToPlaylist.js';
export { default as Alert } from './classes/Alert.js';
export { default as AlertWithButton } from './classes/AlertWithButton.js';
@@ -24,6 +25,7 @@ export { default as AttributionView } from './classes/AttributionView.js';
export { default as AudioOnlyPlayability } from './classes/AudioOnlyPlayability.js';
export { default as AutomixPreviewVideo } from './classes/AutomixPreviewVideo.js';
export { default as AvatarView } from './classes/AvatarView.js';
export { default as BackgroundPromo } from './classes/BackgroundPromo.js';
export { default as BackstageImage } from './classes/BackstageImage.js';
export { default as BackstagePost } from './classes/BackstagePost.js';
export { default as BackstagePostThread } from './classes/BackstagePostThread.js';
@@ -31,6 +33,7 @@ export { default as BadgeView } from './classes/BadgeView.js';
export { default as BrowseFeedActions } from './classes/BrowseFeedActions.js';
export { default as BrowserMediaSession } from './classes/BrowserMediaSession.js';
export { default as Button } from './classes/Button.js';
export { default as ButtonCardView } from './classes/ButtonCardView.js';
export { default as ButtonView } from './classes/ButtonView.js';
export { default as C4TabbedHeader } from './classes/C4TabbedHeader.js';
export { default as CallToActionButton } from './classes/CallToActionButton.js';
@@ -38,7 +41,9 @@ export { default as Card } from './classes/Card.js';
export { default as CardCollection } from './classes/CardCollection.js';
export { default as CarouselHeader } from './classes/CarouselHeader.js';
export { default as CarouselItem } from './classes/CarouselItem.js';
export { default as CarouselItemView } from './classes/CarouselItemView.js';
export { default as CarouselLockup } from './classes/CarouselLockup.js';
export { default as CarouselTitleView } from './classes/CarouselTitleView.js';
export { default as Channel } from './classes/Channel.js';
export { default as ChannelAboutFullMetadata } from './classes/ChannelAboutFullMetadata.js';
export { default as ChannelAgeGate } from './classes/ChannelAgeGate.js';
@@ -62,6 +67,7 @@ export { default as ChipBarView } from './classes/ChipBarView.js';
export { default as ChipCloud } from './classes/ChipCloud.js';
export { default as ChipCloudChip } from './classes/ChipCloudChip.js';
export { default as ChipView } from './classes/ChipView.js';
export { default as ClientSideToggleMenuItem } from './classes/ClientSideToggleMenuItem.js';
export { default as ClipAdState } from './classes/ClipAdState.js';
export { default as ClipCreation } from './classes/ClipCreation.js';
export { default as ClipCreationScrubber } from './classes/ClipCreationScrubber.js';
@@ -361,6 +367,7 @@ export { default as PlaylistPanelVideoWrapper } from './classes/PlaylistPanelVid
export { default as PlaylistSidebar } from './classes/PlaylistSidebar.js';
export { default as PlaylistSidebarPrimaryInfo } from './classes/PlaylistSidebarPrimaryInfo.js';
export { default as PlaylistSidebarSecondaryInfo } from './classes/PlaylistSidebarSecondaryInfo.js';
export { default as PlaylistThumbnailOverlay } from './classes/PlaylistThumbnailOverlay.js';
export { default as PlaylistVideo } from './classes/PlaylistVideo.js';
export { default as PlaylistVideoList } from './classes/PlaylistVideoList.js';
export { default as PlaylistVideoThumbnail } from './classes/PlaylistVideoThumbnail.js';
@@ -436,6 +443,7 @@ export { default as SubscriptionNotificationToggleButton } from './classes/Subsc
export { default as Tab } from './classes/Tab.js';
export { default as Tabbed } from './classes/Tabbed.js';
export { default as TabbedSearchResults } from './classes/TabbedSearchResults.js';
export { default as TextCarouselItemView } from './classes/TextCarouselItemView.js';
export { default as TextFieldView } from './classes/TextFieldView.js';
export { default as TextHeader } from './classes/TextHeader.js';
export { default as ThirdPartyShareTargetSection } from './classes/ThirdPartyShareTargetSection.js';
@@ -489,6 +497,7 @@ export { default as VideoDescriptionInfocardsSection } from './classes/VideoDesc
export { default as VideoDescriptionMusicSection } from './classes/VideoDescriptionMusicSection.js';
export { default as VideoDescriptionTranscriptSection } from './classes/VideoDescriptionTranscriptSection.js';
export { default as VideoInfoCardContent } from './classes/VideoInfoCardContent.js';
export { default as VideoMetadataCarouselView } from './classes/VideoMetadataCarouselView.js';
export { default as VideoOwner } from './classes/VideoOwner.js';
export { default as VideoPrimaryInfo } from './classes/VideoPrimaryInfo.js';
export { default as VideoSecondaryInfo } from './classes/VideoSecondaryInfo.js';

View File

@@ -76,7 +76,6 @@ const IGNORED_LIST = new Set([
'SearchPyv',
'MealbarPromo',
'PrimetimePromo',
'BackgroundPromo',
'PromotedSparklesWeb',
'CompactPromotedVideo',
'BrandVideoShelf',