Compare commits

...

8 Commits

Author SHA1 Message Date
github-actions[bot]
33a6e740d7 chore(main): release 3.1.0 (#318)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-26 20:35:20 -03:00
LuanRT
0b1840a62c chore(docs): update examples to reflect recent changes [skip ci] 2023-02-26 20:28:16 -03:00
LuanRT
f4e0f30e6e fix: send correct UA for Android requests
Related: #322
2023-02-26 19:21:41 -03:00
LuanRT
200632f374 fix(parser): export YTNodes individually so they can be used as types
Related: #321
2023-02-26 18:56:04 -03:00
LuanRT
f933cb45bc feat(VideoSecondaryInfo): add support for attributed descriptions (#325) 2023-02-26 16:47:47 -03:00
absidue
a0e6cef00f fix(PlayerMicroformat): Make the embed field optional (#320) 2023-02-25 12:11:03 -03:00
absidue
a0bfe16427 feat: Add upcoming and live info to playlist videos (#317) 2023-02-20 18:25:53 -03:00
Daniel Wykerd
9d352b58eb docs: update imports for platforms (#315)
* docs: fix browser import

* docs: add deno.land instructions

As mentioned in issue #314
2023-02-17 14:53:06 -03:00
25 changed files with 536 additions and 64 deletions

View File

@@ -1,5 +1,20 @@
# Changelog
## [3.1.0](https://github.com/LuanRT/YouTube.js/compare/v3.0.0...v3.1.0) (2023-02-26)
### Features
* Add upcoming and live info to playlist videos ([#317](https://github.com/LuanRT/YouTube.js/issues/317)) ([a0bfe16](https://github.com/LuanRT/YouTube.js/commit/a0bfe164279ec27b0c49c6b0c32222c1a92df5c3))
* **VideoSecondaryInfo:** add support for attributed descriptions ([#325](https://github.com/LuanRT/YouTube.js/issues/325)) ([f933cb4](https://github.com/LuanRT/YouTube.js/commit/f933cb45bcb92c07b3bc063d63869a51cbff4eb0))
### Bug Fixes
* **parser:** export YTNodes individually so they can be used as types ([200632f](https://github.com/LuanRT/YouTube.js/commit/200632f374d5e0e105b600d579a2665a6fb36e38)), closes [#321](https://github.com/LuanRT/YouTube.js/issues/321)
* **PlayerMicroformat:** Make the embed field optional ([#320](https://github.com/LuanRT/YouTube.js/issues/320)) ([a0e6cef](https://github.com/LuanRT/YouTube.js/commit/a0e6cef00fb9e3f52593cec22704f7ddc1f7553e))
* send correct UA for Android requests ([f4e0f30](https://github.com/LuanRT/YouTube.js/commit/f4e0f30e6e94b347b28d67d9a86284ea2d23ee15)), closes [#322](https://github.com/LuanRT/YouTube.js/issues/322)
## [3.0.0](https://github.com/LuanRT/YouTube.js/compare/v2.9.0...v3.0.0) (2023-02-17)

View File

@@ -3,16 +3,16 @@
Thank you for taking the time to contribute!
The following is a set of guidelines for contributing to YouTube.js.
___
* [Issues](#issues)
* [Create a new issue](#issue-1)
* [Solve an issue](#issue-2)
* [Make changes](#changes)
* [Commit your updates](#changes-1)
* [Create a PR](#changes-2)
* [Run tests](#test)
* [Lint your code](#lint)
* [Build](#build)
- [Contributing to YouTube.js](#contributing-to-youtubejs)
- [Issues](#issues)
- [Create a new issue](#create-a-new-issue)
- [Solve an issue](#solve-an-issue)
- [Make changes](#make-changes)
- [Commit your updates](#commit-your-updates)
- [Pull Request](#pull-request)
- [Test](#test)
- [Lint](#lint)
- [Build](#build)
## Issues
@@ -66,16 +66,26 @@ npm run lint:fix
#### Build
```bash
# Node
npm run build:node
# Browser
npm run build:browser
npm run build:browser:prod
# Build all
npm run build
# Protobuf
npm run build:proto
# Parser map
npm run build:parser-map
# Deno
npm run build:deno
# ES Module
npm run build:esm
# Node
npm run bundle:node
# Browser
npm run bundle:browser
npm run bundle:browser:prod
```

View File

@@ -112,7 +112,10 @@ yarn add youtubei.js@latest
npm install github:LuanRT/YouTube.js
```
**TODO:** Deno install instructions (deno.land)
When using Deno, you can import YouTube.js directly from deno.land:
```ts
import { Innertube } from 'https://deno.land/x/youtubei/deno.ts';
```
## Usage
Create an InnerTube instance:
@@ -128,8 +131,13 @@ To use YouTube.js in the browser you must proxy requests through your own server
You may provide your own fetch implementation to be used by YouTube.js. Which we will use here to modify and send the requests through our proxy. See [`examples/browser/web`](https://github.com/LuanRT/YouTube.js/tree/main/examples/browser/web) for a simple example using [Vite](https://vitejs.dev/).
```ts
// Pre-bundled version for the web
import { Innertube } from 'youtubei.js/bundle/browser';
// We provide multiple exports for the web.
// Unbundled ESM version
import { Innertube } from 'youtubei.js/web';
// Bundled ESM version
// import { Innertube } from 'youtubei.js/web.bundle';
// Production Bundled ESM version
// import { Innertube } from 'youtubei.js/web.bundle.min';
await Innertube.create({
fetch: async (input: RequestInfo | URL, init?: RequestInit) => {
// Modify the request
@@ -147,7 +155,7 @@ YouTube.js supports streaming of videos in the browser by converting YouTube's s
The example below uses [`dash.js`](https://github.com/Dash-Industry-Forum/dash.js) to play the video.
```ts
import { Innertube } from 'youtubei.js';
import { Innertube } from 'youtubei.js/web';
import dashjs from 'dashjs';
const youtube = await Innertube.create({ /* setup - see above */ });
@@ -202,7 +210,7 @@ Our cache uses the `node:fs` module in Node-like environments, `Deno.writeFile`
import { Innertube, UniversalCache } from 'youtubei.js';
// By default, cache stores files in the OS temp directory (or indexedDB in browsers).
const yt = await Innertube.create({
cache: new UniversalCache()
cache: new UniversalCache(false)
});
// You may wish to make the cache persistent (on Node and Deno)

View File

@@ -3,7 +3,7 @@ const { Innertube, UniversalCache } = require('youtubei.js');
(async () => {
const yt = await Innertube.create({
// required if you wish to use OAuth#cacheCredentials
cache: new UniversalCache()
cache: new UniversalCache(false)
});
// 'auth-pending' is fired with the info needed to sign in via OAuth.

View File

@@ -9,7 +9,7 @@ To use YouTube.js in the browser you must proxy requests through your own server
We'll use our own fetch implementation to proxy requests through our server. This is a simple example, but you can use any fetch implementation you want.
```ts
import { Innertube } from "youtubei.js/build/browser";
import { Innertube } from "youtubei.js/web.bundle.min";
const yt = await Innertube.create({
fetch: async (input, init) => {

View File

@@ -1,7 +1,7 @@
import { Innertube, UniversalCache, YTNodes } from 'youtubei.js';
(async () => {
const yt = await Innertube.create({ cache: new UniversalCache(), generate_session_locally: true });
const yt = await Innertube.create({ cache: new UniversalCache(false), generate_session_locally: true });
const channel = await yt.getChannel('UCX6OQ3DkcsbYNE6H8uQQuVA');

View File

@@ -1,7 +1,7 @@
import { Innertube, UniversalCache } from 'youtubei.js';
(async () => {
const yt = await Innertube.create({ cache: new UniversalCache(), generate_session_locally: true });
const yt = await Innertube.create({ cache: new UniversalCache(false), generate_session_locally: true });
const comment_section = await yt.getComments('a-rqu-hjobc');

View File

@@ -1,4 +1,4 @@
import { Innertube } from '../../bundle/browser.js';
import { Innertube } from 'https://deno.land/x/youtubei/deno.ts';
const yt = await Innertube.create();

View File

@@ -1,9 +1,8 @@
import { Innertube, UniversalCache } from 'youtubei.js';
import { readFileSync, existsSync, mkdirSync, createWriteStream } from 'fs';
import { streamToIterable } from 'youtubei.js/dist/src/utils/Utils';
import { Innertube, UniversalCache, Utils } from 'youtubei.js';
import { existsSync, mkdirSync, createWriteStream } from 'fs';
(async () => {
const yt = await Innertube.create({ cache: new UniversalCache(), generate_session_locally: true });
const yt = await Innertube.create({ cache: new UniversalCache(false), generate_session_locally: true });
const search = await yt.music.search('No Copyright Background Music', { type: 'album' });
@@ -34,7 +33,7 @@ import { streamToIterable } from 'youtubei.js/dist/src/utils/Utils';
const file = createWriteStream(`${dir}/${song.title?.replace(/\//g, '')}.m4a`);
for await (const chunk of streamToIterable(stream)) {
for await (const chunk of Utils.streamToIterable(stream)) {
file.write(chunk);
}

View File

@@ -1,9 +1,8 @@
import { Innertube, UniversalCache, YTNodes } from 'youtubei.js';
import { LiveChatContinuation } from 'youtubei.js/dist/src/parser';
import { Innertube, UniversalCache, YTNodes, LiveChatContinuation } from 'youtubei.js';
import { ChatAction, LiveMetadata } from 'youtubei.js/dist/src/parser/youtube/LiveChat';
(async () => {
const yt = await Innertube.create({ cache: new UniversalCache(), generate_session_locally: true });
const yt = await Innertube.create({ cache: new UniversalCache(false), generate_session_locally: true });
const search = await yt.search('lofi hip hop radio - beats to relax/study to');
const info = await yt.getInfo(search.videos[0].as(YTNodes.Video).id);

View File

@@ -5,7 +5,7 @@ const creds_path = './my_yt_creds.json';
const creds = existsSync(creds_path) ? JSON.parse(readFileSync(creds_path).toString()) : undefined;
(async () => {
const yt = await Innertube.create({ cache: new UniversalCache() });
const yt = await Innertube.create({ cache: new UniversalCache(false) });
yt.session.on('auth-pending', (data: any) => {
console.info(`Hello!\nOn your phone or computer, go to ${data.verification_url} and enter the code ${data.user_code}`);

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "youtubei.js",
"version": "3.0.0",
"version": "3.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "youtubei.js",
"version": "3.0.0",
"version": "3.1.0",
"funding": [
"https://github.com/sponsors/LuanRT"
],

View File

@@ -1,6 +1,6 @@
{
"name": "youtubei.js",
"version": "3.0.0",
"version": "3.1.0",
"description": "A wrapper around YouTube's private API. Supports YouTube, YouTube Music, YouTube Kids and YouTube Studio (WIP).",
"type": "module",
"types": "./dist/src/platform/lib.d.ts",

View File

@@ -19,7 +19,8 @@ glob.sync('../src/parser/classes/**/*.{js,ts}', { cwd: __dirname })
import_list.push(`import { default as ${class_name} } from './classes/${file}.js';`);
misc_exports.push(class_name);
} else {
import_list.push(`import { default as ${import_name} } from './classes/${file}.js';`);
import_list.push(`import { default as ${import_name} } from './classes/${file}.js';
export { ${import_name} };`);
json.push(import_name);
}
});
@@ -32,7 +33,7 @@ import { YTNodeConstructor } from './helpers.js';
${import_list.join('\n')}
export const YTNodes = {
const map: Record<string, YTNodeConstructor> = {
${json.join(',\n ')}
};
@@ -40,8 +41,6 @@ export const Misc = {
${misc_exports.join(',\n ')}
};
const map: Record<string, YTNodeConstructor> = YTNodes;
/**
* @param name - Name of the node to be parsed
*/

View File

@@ -21,7 +21,6 @@ class NavigationEndpoint extends YTNode {
constructor(data: any) {
super();
// This is only present in Android nav endpoints
if (Reflect.has(data || {}, 'innertubeCommand'))
data = data.innertubeCommand;

View File

@@ -16,7 +16,7 @@ class PlayerMicroformat extends YTNode {
// TODO: check these
width: any;
height: any;
};
} | null;
length_seconds: number;
@@ -42,13 +42,17 @@ class PlayerMicroformat extends YTNode {
this.description = new Text(data.description);
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
this.embed = {
iframe_url: data.embed.iframeUrl,
flash_url: data.embed.flashUrl,
flash_secure_url: data.embed.flashSecureUrl,
width: data.embed.width,
height: data.embed.height
};
if (data.embed) {
this.embed = {
iframe_url: data.embed.iframeUrl,
flash_url: data.embed.flashUrl,
flash_secure_url: data.embed.flashSecureUrl,
width: data.embed.width,
height: data.embed.height
};
} else {
this.embed = null;
}
this.length_seconds = parseInt(data.lengthSeconds);

View File

@@ -3,6 +3,7 @@ import Parser from '../index.js';
import Thumbnail from './misc/Thumbnail.js';
import PlaylistAuthor from './misc/PlaylistAuthor.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import ThumbnailOverlayTimeStatus from './ThumbnailOverlayTimeStatus.js';
import type Menu from './menus/Menu.js';
import { YTNode } from '../helpers.js';
@@ -20,6 +21,7 @@ class PlaylistVideo extends YTNode {
endpoint: NavigationEndpoint;
is_playable: boolean;
menu: Menu | null;
upcoming;
duration: {
text: string;
@@ -33,16 +35,30 @@ class PlaylistVideo extends YTNode {
this.title = new Text(data.title);
this.author = new PlaylistAuthor(data.shortBylineText);
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
this.thumbnail_overlays = Parser.parse(data.thumbnailOverlays);
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);
const upcoming = data.upcomingEventData && Number(`${data.upcomingEventData.startTime}000`);
if (upcoming) {
this.upcoming = new Date(upcoming);
}
this.duration = {
text: new Text(data.lengthText).text,
seconds: parseInt(data.lengthSeconds)
};
}
get is_live(): boolean {
return this.thumbnail_overlays.firstOfType(ThumbnailOverlayTimeStatus)?.style === 'LIVE';
}
get is_upcoming(): boolean {
return this.thumbnail_overlays.firstOfType(ThumbnailOverlayTimeStatus)?.style === 'UPCOMING';
}
}
export default PlaylistVideo;

View File

@@ -5,10 +5,12 @@ class ThumbnailOverlayTimeStatus extends YTNode {
static type = 'ThumbnailOverlayTimeStatus';
text: string;
style: string;
constructor(data: any) {
super();
this.text = new Text(data.text).toString();
this.style = data.style;
}
}

View File

@@ -1,4 +1,4 @@
import Parser from '../index.js';
import Parser, { RawNode } from '../index.js';
import Text from './misc/Text.js';
import Button from './Button.js';
import VideoOwner from './VideoOwner.js';
@@ -9,19 +9,24 @@ import { YTNode } from '../helpers.js';
class VideoSecondaryInfo extends YTNode {
static type = 'VideoSecondaryInfo';
owner: VideoOwner | null;// TODO: VideoOwner?
owner: VideoOwner | null;
description: Text;
subscribe_button;
subscribe_button: SubscribeButton | Button | null;
metadata: MetadataRowContainer | null;
show_more_text: string;
show_less_text: string;
default_expanded: string;
description_collapsed_lines: string;
constructor(data: any) {
constructor(data: RawNode) {
super();
this.owner = Parser.parseItem<VideoOwner>(data.owner);
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.metadata = Parser.parseItem<MetadataRowContainer>(data.metadataRowContainer, MetadataRowContainer);
this.show_more_text = data.showMoreText;
@@ -29,6 +34,74 @@ class VideoSecondaryInfo extends YTNode {
this.default_expanded = data.defaultExpanded;
this.description_collapsed_lines = data.descriptionCollapsedLines;
}
#convertAttributedDescriptionToRuns(description: RawNode) {
const runs: {
text: string,
navigationEndpoint?: RawNode,
attachment?: RawNode
}[] = [];
const content = description.content;
const command_runs = description.commandRuns;
let last_end_index = 0;
if (command_runs) {
for (const item of command_runs) {
const length: number = item.length;
const start_index: number = item.startIndex;
if (start_index > last_end_index) {
runs.push({
text: content.slice(last_end_index, start_index)
});
}
if (Reflect.has(item, 'onTap')) {
let attachment = null;
if (Reflect.has(description, 'attachmentRuns')) {
const attachment_runs = description.attachmentRuns;
for (const attatchment_run of attachment_runs) {
if ((attatchment_run.startIndex - 2) == start_index) {
attachment = attatchment_run;
break;
}
}
}
if (attachment) {
runs.push({
text: content.slice(start_index, start_index + length),
navigationEndpoint: item.onTap,
attachment
});
} else {
runs.push({
text: content.slice(start_index, start_index + length),
navigationEndpoint: item.onTap
});
}
}
last_end_index = start_index + length;
}
if (last_end_index < content.length) {
runs.push({
text: content.slice(last_end_index)
});
}
} else {
runs.push({
text: content
});
}
return { runs };
}
}
export default VideoSecondaryInfo;

View File

@@ -1,5 +1,6 @@
import NavigationEndpoint from '../NavigationEndpoint.js';
import { escape, Run } from './Text.js';
import type { RawNode } from '../../index.js';
class TextRun implements Run {
text: string;
@@ -7,13 +8,15 @@ class TextRun implements Run {
bold: boolean;
italics: boolean;
strikethrough: boolean;
attachment;
constructor(data: any) {
constructor(data: RawNode) {
this.text = data.text;
this.bold = Boolean(data.bold);
this.italics = Boolean(data.italics);
this.strikethrough = Boolean(data.strikethrough);
this.endpoint = data.navigationEndpoint ? new NavigationEndpoint(data.navigationEndpoint) : undefined;
this.attachment = data.attachment;
}
toString() {
@@ -22,16 +25,30 @@ class TextRun implements Run {
toHTML(): string {
const tags: string[] = [];
if (this.bold) tags.push('b');
if (this.italics) tags.push('i');
if (this.strikethrough) tags.push('s');
const escaped_text = escape(this.text);
const styled_text = tags.map((tag) => `<${tag}>`).join('') + escaped_text + tags.map((tag) => `</${tag}>`).join('');
const wrapped_text = `<span style="white-space: pre-wrap;">${styled_text}</span>`;
if (this.attachment) {
if (this.attachment.element.type.imageType.image.sources.length) {
const { url } = this.attachment.element.type.imageType.image.sources[0];
if (this.endpoint) {
const nav_url = this.endpoint.toURL();
if (nav_url) return `<a href="${nav_url}" class="yt-ch-link" display: block; width: fit-content; font-size: small;><img src="${url}" style="vertical-align: middle; height: ${this.attachment.element.properties.layoutProperties.height.value}px; width: ${this.attachment.element.properties.layoutProperties.width.value}px;">${wrapped_text}</a>`;
}
}
}
if (this.endpoint) {
const url = this.endpoint.toURL();
if (url) return `<a href="${url}">${wrapped_text}</a>`;
}
return wrapped_text;
}
}

View File

@@ -1,10 +1,11 @@
export { default as Parser } from './parser.js';
export * from './parser.js';
export * from './types/index.js';
export { YTNodes, Misc } from '../parser/map.js';
export { Misc } from '../parser/map.js';
export * as YTNodes from '../parser/map.js';
export * as YT from './youtube/index.js';
export * as YTMusic from './ytmusic/index.js';
export * as YTKids from './ytkids/index.js';
export * as Helpers from './helpers.js';
import Parser from './parser.js';
export default Parser;

View File

@@ -3,181 +3,357 @@
import { YTNodeConstructor } from './helpers.js';
import { default as AccountChannel } from './classes/AccountChannel.js';
export { AccountChannel };
import { default as AccountItemSection } from './classes/AccountItemSection.js';
export { AccountItemSection };
import { default as AccountItemSectionHeader } from './classes/AccountItemSectionHeader.js';
export { AccountItemSectionHeader };
import { default as AccountSectionList } from './classes/AccountSectionList.js';
export { AccountSectionList };
import { default as AppendContinuationItemsAction } from './classes/actions/AppendContinuationItemsAction.js';
export { AppendContinuationItemsAction };
import { default as OpenPopupAction } from './classes/actions/OpenPopupAction.js';
export { OpenPopupAction };
import { default as Alert } from './classes/Alert.js';
export { Alert };
import { default as AnalyticsMainAppKeyMetrics } from './classes/analytics/AnalyticsMainAppKeyMetrics.js';
export { AnalyticsMainAppKeyMetrics };
import { default as AnalyticsRoot } from './classes/analytics/AnalyticsRoot.js';
export { AnalyticsRoot };
import { default as AnalyticsShortsCarouselCard } from './classes/analytics/AnalyticsShortsCarouselCard.js';
export { AnalyticsShortsCarouselCard };
import { default as AnalyticsVideo } from './classes/analytics/AnalyticsVideo.js';
export { AnalyticsVideo };
import { default as AnalyticsVodCarouselCard } from './classes/analytics/AnalyticsVodCarouselCard.js';
export { AnalyticsVodCarouselCard };
import { default as CtaGoToCreatorStudio } from './classes/analytics/CtaGoToCreatorStudio.js';
export { CtaGoToCreatorStudio };
import { default as DataModelSection } from './classes/analytics/DataModelSection.js';
export { DataModelSection };
import { default as StatRow } from './classes/analytics/StatRow.js';
export { StatRow };
import { default as AudioOnlyPlayability } from './classes/AudioOnlyPlayability.js';
export { AudioOnlyPlayability };
import { default as AutomixPreviewVideo } from './classes/AutomixPreviewVideo.js';
export { AutomixPreviewVideo };
import { default as BackstageImage } from './classes/BackstageImage.js';
export { BackstageImage };
import { default as BackstagePost } from './classes/BackstagePost.js';
export { BackstagePost };
import { default as BackstagePostThread } from './classes/BackstagePostThread.js';
export { BackstagePostThread };
import { default as BrowseFeedActions } from './classes/BrowseFeedActions.js';
export { BrowseFeedActions };
import { default as BrowserMediaSession } from './classes/BrowserMediaSession.js';
export { BrowserMediaSession };
import { default as Button } from './classes/Button.js';
export { Button };
import { default as C4TabbedHeader } from './classes/C4TabbedHeader.js';
export { C4TabbedHeader };
import { default as CallToActionButton } from './classes/CallToActionButton.js';
export { CallToActionButton };
import { default as Card } from './classes/Card.js';
export { Card };
import { default as CardCollection } from './classes/CardCollection.js';
export { CardCollection };
import { default as CarouselHeader } from './classes/CarouselHeader.js';
export { CarouselHeader };
import { default as CarouselItem } from './classes/CarouselItem.js';
export { CarouselItem };
import { default as Channel } from './classes/Channel.js';
export { Channel };
import { default as ChannelAboutFullMetadata } from './classes/ChannelAboutFullMetadata.js';
export { ChannelAboutFullMetadata };
import { default as ChannelAgeGate } from './classes/ChannelAgeGate.js';
export { ChannelAgeGate };
import { default as ChannelFeaturedContent } from './classes/ChannelFeaturedContent.js';
export { ChannelFeaturedContent };
import { default as ChannelHeaderLinks } from './classes/ChannelHeaderLinks.js';
export { ChannelHeaderLinks };
import { default as ChannelMetadata } from './classes/ChannelMetadata.js';
export { ChannelMetadata };
import { default as ChannelMobileHeader } from './classes/ChannelMobileHeader.js';
export { ChannelMobileHeader };
import { default as ChannelOptions } from './classes/ChannelOptions.js';
export { ChannelOptions };
import { default as ChannelSubMenu } from './classes/ChannelSubMenu.js';
export { ChannelSubMenu };
import { default as ChannelThumbnailWithLink } from './classes/ChannelThumbnailWithLink.js';
export { ChannelThumbnailWithLink };
import { default as ChannelVideoPlayer } from './classes/ChannelVideoPlayer.js';
export { ChannelVideoPlayer };
import { default as Chapter } from './classes/Chapter.js';
export { Chapter };
import { default as ChildVideo } from './classes/ChildVideo.js';
export { ChildVideo };
import { default as ChipCloud } from './classes/ChipCloud.js';
export { ChipCloud };
import { default as ChipCloudChip } from './classes/ChipCloudChip.js';
export { ChipCloudChip };
import { default as CollaboratorInfoCardContent } from './classes/CollaboratorInfoCardContent.js';
export { CollaboratorInfoCardContent };
import { default as CollageHeroImage } from './classes/CollageHeroImage.js';
export { CollageHeroImage };
import { default as AuthorCommentBadge } from './classes/comments/AuthorCommentBadge.js';
export { AuthorCommentBadge };
import { default as Comment } from './classes/comments/Comment.js';
export { Comment };
import { default as CommentActionButtons } from './classes/comments/CommentActionButtons.js';
export { CommentActionButtons };
import { default as CommentDialog } from './classes/comments/CommentDialog.js';
export { CommentDialog };
import { default as CommentReplies } from './classes/comments/CommentReplies.js';
export { CommentReplies };
import { default as CommentReplyDialog } from './classes/comments/CommentReplyDialog.js';
export { CommentReplyDialog };
import { default as CommentsEntryPointHeader } from './classes/comments/CommentsEntryPointHeader.js';
export { CommentsEntryPointHeader };
import { default as CommentsHeader } from './classes/comments/CommentsHeader.js';
export { CommentsHeader };
import { default as CommentSimplebox } from './classes/comments/CommentSimplebox.js';
export { CommentSimplebox };
import { default as CommentThread } from './classes/comments/CommentThread.js';
export { CommentThread };
import { default as CreatorHeart } from './classes/comments/CreatorHeart.js';
export { CreatorHeart };
import { default as EmojiPicker } from './classes/comments/EmojiPicker.js';
export { EmojiPicker };
import { default as PdgCommentChip } from './classes/comments/PdgCommentChip.js';
export { PdgCommentChip };
import { default as SponsorCommentBadge } from './classes/comments/SponsorCommentBadge.js';
export { SponsorCommentBadge };
import { default as CompactChannel } from './classes/CompactChannel.js';
export { CompactChannel };
import { default as CompactLink } from './classes/CompactLink.js';
export { CompactLink };
import { default as CompactMix } from './classes/CompactMix.js';
export { CompactMix };
import { default as CompactPlaylist } from './classes/CompactPlaylist.js';
export { CompactPlaylist };
import { default as CompactStation } from './classes/CompactStation.js';
export { CompactStation };
import { default as CompactVideo } from './classes/CompactVideo.js';
export { CompactVideo };
import { default as ConfirmDialog } from './classes/ConfirmDialog.js';
export { ConfirmDialog };
import { default as ContinuationItem } from './classes/ContinuationItem.js';
export { ContinuationItem };
import { default as CopyLink } from './classes/CopyLink.js';
export { CopyLink };
import { default as CreatePlaylistDialog } from './classes/CreatePlaylistDialog.js';
export { CreatePlaylistDialog };
import { default as DecoratedPlayerBar } from './classes/DecoratedPlayerBar.js';
export { DecoratedPlayerBar };
import { default as DefaultPromoPanel } from './classes/DefaultPromoPanel.js';
export { DefaultPromoPanel };
import { default as DidYouMean } from './classes/DidYouMean.js';
export { DidYouMean };
import { default as DownloadButton } from './classes/DownloadButton.js';
export { DownloadButton };
import { default as Dropdown } from './classes/Dropdown.js';
export { Dropdown };
import { default as DropdownItem } from './classes/DropdownItem.js';
export { DropdownItem };
import { default as Element } from './classes/Element.js';
export { Element };
import { default as EmergencyOnebox } from './classes/EmergencyOnebox.js';
export { EmergencyOnebox };
import { default as EmojiPickerCategory } from './classes/EmojiPickerCategory.js';
export { EmojiPickerCategory };
import { default as EmojiPickerCategoryButton } from './classes/EmojiPickerCategoryButton.js';
export { EmojiPickerCategoryButton };
import { default as EmojiPickerUpsellCategory } from './classes/EmojiPickerUpsellCategory.js';
export { EmojiPickerUpsellCategory };
import { default as Endscreen } from './classes/Endscreen.js';
export { Endscreen };
import { default as EndscreenElement } from './classes/EndscreenElement.js';
export { EndscreenElement };
import { default as EndScreenPlaylist } from './classes/EndScreenPlaylist.js';
export { EndScreenPlaylist };
import { default as EndScreenVideo } from './classes/EndScreenVideo.js';
export { EndScreenVideo };
import { default as ExpandableMetadata } from './classes/ExpandableMetadata.js';
export { ExpandableMetadata };
import { default as ExpandableTab } from './classes/ExpandableTab.js';
export { ExpandableTab };
import { default as ExpandedShelfContents } from './classes/ExpandedShelfContents.js';
export { ExpandedShelfContents };
import { default as FeedFilterChipBar } from './classes/FeedFilterChipBar.js';
export { FeedFilterChipBar };
import { default as FeedTabbedHeader } from './classes/FeedTabbedHeader.js';
export { FeedTabbedHeader };
import { default as GameCard } from './classes/GameCard.js';
export { GameCard };
import { default as GameDetails } from './classes/GameDetails.js';
export { GameDetails };
import { default as Grid } from './classes/Grid.js';
export { Grid };
import { default as GridChannel } from './classes/GridChannel.js';
export { GridChannel };
import { default as GridHeader } from './classes/GridHeader.js';
export { GridHeader };
import { default as GridPlaylist } from './classes/GridPlaylist.js';
export { GridPlaylist };
import { default as GridVideo } from './classes/GridVideo.js';
export { GridVideo };
import { default as HashtagHeader } from './classes/HashtagHeader.js';
export { HashtagHeader };
import { default as Heatmap } from './classes/Heatmap.js';
export { Heatmap };
import { default as HeatMarker } from './classes/HeatMarker.js';
export { HeatMarker };
import { default as HighlightsCarousel } from './classes/HighlightsCarousel.js';
export { HighlightsCarousel };
import { default as HistorySuggestion } from './classes/HistorySuggestion.js';
export { HistorySuggestion };
import { default as HorizontalCardList } from './classes/HorizontalCardList.js';
export { HorizontalCardList };
import { default as HorizontalList } from './classes/HorizontalList.js';
export { HorizontalList };
import { default as IconLink } from './classes/IconLink.js';
export { IconLink };
import { default as InteractiveTabbedHeader } from './classes/InteractiveTabbedHeader.js';
export { InteractiveTabbedHeader };
import { default as ItemSection } from './classes/ItemSection.js';
export { ItemSection };
import { default as ItemSectionHeader } from './classes/ItemSectionHeader.js';
export { ItemSectionHeader };
import { default as ItemSectionTab } from './classes/ItemSectionTab.js';
export { ItemSectionTab };
import { default as ItemSectionTabbedHeader } from './classes/ItemSectionTabbedHeader.js';
export { ItemSectionTabbedHeader };
import { default as LikeButton } from './classes/LikeButton.js';
export { LikeButton };
import { default as LiveChat } from './classes/LiveChat.js';
export { LiveChat };
import { default as AddBannerToLiveChatCommand } from './classes/livechat/AddBannerToLiveChatCommand.js';
export { AddBannerToLiveChatCommand };
import { default as AddChatItemAction } from './classes/livechat/AddChatItemAction.js';
export { AddChatItemAction };
import { default as AddLiveChatTickerItemAction } from './classes/livechat/AddLiveChatTickerItemAction.js';
export { AddLiveChatTickerItemAction };
import { default as DimChatItemAction } from './classes/livechat/DimChatItemAction.js';
export { DimChatItemAction };
import { default as LiveChatAutoModMessage } from './classes/livechat/items/LiveChatAutoModMessage.js';
export { LiveChatAutoModMessage };
import { default as LiveChatBanner } from './classes/livechat/items/LiveChatBanner.js';
export { LiveChatBanner };
import { default as LiveChatBannerHeader } from './classes/livechat/items/LiveChatBannerHeader.js';
export { LiveChatBannerHeader };
import { default as LiveChatBannerPoll } from './classes/livechat/items/LiveChatBannerPoll.js';
export { LiveChatBannerPoll };
import { default as LiveChatMembershipItem } from './classes/livechat/items/LiveChatMembershipItem.js';
export { LiveChatMembershipItem };
import { default as LiveChatPaidMessage } from './classes/livechat/items/LiveChatPaidMessage.js';
export { LiveChatPaidMessage };
import { default as LiveChatPaidSticker } from './classes/livechat/items/LiveChatPaidSticker.js';
export { LiveChatPaidSticker };
import { default as LiveChatPlaceholderItem } from './classes/livechat/items/LiveChatPlaceholderItem.js';
export { LiveChatPlaceholderItem };
import { default as LiveChatProductItem } from './classes/livechat/items/LiveChatProductItem.js';
export { LiveChatProductItem };
import { default as LiveChatRestrictedParticipation } from './classes/livechat/items/LiveChatRestrictedParticipation.js';
export { LiveChatRestrictedParticipation };
import { default as LiveChatTextMessage } from './classes/livechat/items/LiveChatTextMessage.js';
export { LiveChatTextMessage };
import { default as LiveChatTickerPaidMessageItem } from './classes/livechat/items/LiveChatTickerPaidMessageItem.js';
export { LiveChatTickerPaidMessageItem };
import { default as LiveChatTickerPaidStickerItem } from './classes/livechat/items/LiveChatTickerPaidStickerItem.js';
export { LiveChatTickerPaidStickerItem };
import { default as LiveChatTickerSponsorItem } from './classes/livechat/items/LiveChatTickerSponsorItem.js';
export { LiveChatTickerSponsorItem };
import { default as LiveChatViewerEngagementMessage } from './classes/livechat/items/LiveChatViewerEngagementMessage.js';
export { LiveChatViewerEngagementMessage };
import { default as PollHeader } from './classes/livechat/items/PollHeader.js';
export { PollHeader };
import { default as LiveChatActionPanel } from './classes/livechat/LiveChatActionPanel.js';
export { LiveChatActionPanel };
import { default as MarkChatItemAsDeletedAction } from './classes/livechat/MarkChatItemAsDeletedAction.js';
export { MarkChatItemAsDeletedAction };
import { default as MarkChatItemsByAuthorAsDeletedAction } from './classes/livechat/MarkChatItemsByAuthorAsDeletedAction.js';
export { MarkChatItemsByAuthorAsDeletedAction };
import { default as RemoveBannerForLiveChatCommand } from './classes/livechat/RemoveBannerForLiveChatCommand.js';
export { RemoveBannerForLiveChatCommand };
import { default as RemoveChatItemAction } from './classes/livechat/RemoveChatItemAction.js';
export { RemoveChatItemAction };
import { default as RemoveChatItemByAuthorAction } from './classes/livechat/RemoveChatItemByAuthorAction.js';
export { RemoveChatItemByAuthorAction };
import { default as ReplaceChatItemAction } from './classes/livechat/ReplaceChatItemAction.js';
export { ReplaceChatItemAction };
import { default as ReplayChatItemAction } from './classes/livechat/ReplayChatItemAction.js';
export { ReplayChatItemAction };
import { default as ShowLiveChatActionPanelAction } from './classes/livechat/ShowLiveChatActionPanelAction.js';
export { ShowLiveChatActionPanelAction };
import { default as ShowLiveChatDialogAction } from './classes/livechat/ShowLiveChatDialogAction.js';
export { ShowLiveChatDialogAction };
import { default as ShowLiveChatTooltipCommand } from './classes/livechat/ShowLiveChatTooltipCommand.js';
export { ShowLiveChatTooltipCommand };
import { default as UpdateDateTextAction } from './classes/livechat/UpdateDateTextAction.js';
export { UpdateDateTextAction };
import { default as UpdateDescriptionAction } from './classes/livechat/UpdateDescriptionAction.js';
export { UpdateDescriptionAction };
import { default as UpdateLiveChatPollAction } from './classes/livechat/UpdateLiveChatPollAction.js';
export { UpdateLiveChatPollAction };
import { default as UpdateTitleAction } from './classes/livechat/UpdateTitleAction.js';
export { UpdateTitleAction };
import { default as UpdateToggleButtonTextAction } from './classes/livechat/UpdateToggleButtonTextAction.js';
export { UpdateToggleButtonTextAction };
import { default as UpdateViewershipAction } from './classes/livechat/UpdateViewershipAction.js';
export { UpdateViewershipAction };
import { default as LiveChatAuthorBadge } from './classes/LiveChatAuthorBadge.js';
export { LiveChatAuthorBadge };
import { default as LiveChatDialog } from './classes/LiveChatDialog.js';
export { LiveChatDialog };
import { default as LiveChatHeader } from './classes/LiveChatHeader.js';
export { LiveChatHeader };
import { default as LiveChatItemList } from './classes/LiveChatItemList.js';
export { LiveChatItemList };
import { default as LiveChatMessageInput } from './classes/LiveChatMessageInput.js';
export { LiveChatMessageInput };
import { default as LiveChatParticipant } from './classes/LiveChatParticipant.js';
export { LiveChatParticipant };
import { default as LiveChatParticipantsList } from './classes/LiveChatParticipantsList.js';
export { LiveChatParticipantsList };
import { default as MacroMarkersListItem } from './classes/MacroMarkersListItem.js';
export { MacroMarkersListItem };
import { default as Menu } from './classes/menus/Menu.js';
export { Menu };
import { default as MenuNavigationItem } from './classes/menus/MenuNavigationItem.js';
export { MenuNavigationItem };
import { default as MenuServiceItem } from './classes/menus/MenuServiceItem.js';
export { MenuServiceItem };
import { default as MenuServiceItemDownload } from './classes/menus/MenuServiceItemDownload.js';
export { MenuServiceItemDownload };
import { default as MultiPageMenu } from './classes/menus/MultiPageMenu.js';
export { MultiPageMenu };
import { default as MultiPageMenuNotificationSection } from './classes/menus/MultiPageMenuNotificationSection.js';
export { MultiPageMenuNotificationSection };
import { default as MusicMenuItemDivider } from './classes/menus/MusicMenuItemDivider.js';
export { MusicMenuItemDivider };
import { default as MusicMultiSelectMenu } from './classes/menus/MusicMultiSelectMenu.js';
export { MusicMultiSelectMenu };
import { default as MusicMultiSelectMenuItem } from './classes/menus/MusicMultiSelectMenuItem.js';
export { MusicMultiSelectMenuItem };
import { default as SimpleMenuHeader } from './classes/menus/SimpleMenuHeader.js';
export { SimpleMenuHeader };
import { default as MerchandiseItem } from './classes/MerchandiseItem.js';
export { MerchandiseItem };
import { default as MerchandiseShelf } from './classes/MerchandiseShelf.js';
export { MerchandiseShelf };
import { default as Message } from './classes/Message.js';
export { Message };
import { default as MetadataBadge } from './classes/MetadataBadge.js';
export { MetadataBadge };
import { default as MetadataRow } from './classes/MetadataRow.js';
export { MetadataRow };
import { default as MetadataRowContainer } from './classes/MetadataRowContainer.js';
export { MetadataRowContainer };
import { default as MetadataRowHeader } from './classes/MetadataRowHeader.js';
export { MetadataRowHeader };
import { default as MetadataScreen } from './classes/MetadataScreen.js';
export { MetadataScreen };
import { default as MicroformatData } from './classes/MicroformatData.js';
export { MicroformatData };
import { default as Author } from './classes/misc/Author.js';
import { default as ChildElement } from './classes/misc/ChildElement.js';
import { default as EmojiRun } from './classes/misc/EmojiRun.js';
@@ -189,151 +365,295 @@ import { default as TextRun } from './classes/misc/TextRun.js';
import { default as Thumbnail } from './classes/misc/Thumbnail.js';
import { default as VideoDetails } from './classes/misc/VideoDetails.js';
import { default as Mix } from './classes/Mix.js';
export { Mix };
import { default as Movie } from './classes/Movie.js';
export { Movie };
import { default as MovingThumbnail } from './classes/MovingThumbnail.js';
export { MovingThumbnail };
import { default as MultiMarkersPlayerBar } from './classes/MultiMarkersPlayerBar.js';
export { MultiMarkersPlayerBar };
import { default as MusicCarouselShelf } from './classes/MusicCarouselShelf.js';
export { MusicCarouselShelf };
import { default as MusicCarouselShelfBasicHeader } from './classes/MusicCarouselShelfBasicHeader.js';
export { MusicCarouselShelfBasicHeader };
import { default as MusicDescriptionShelf } from './classes/MusicDescriptionShelf.js';
export { MusicDescriptionShelf };
import { default as MusicDetailHeader } from './classes/MusicDetailHeader.js';
export { MusicDetailHeader };
import { default as MusicDownloadStateBadge } from './classes/MusicDownloadStateBadge.js';
export { MusicDownloadStateBadge };
import { default as MusicEditablePlaylistDetailHeader } from './classes/MusicEditablePlaylistDetailHeader.js';
export { MusicEditablePlaylistDetailHeader };
import { default as MusicElementHeader } from './classes/MusicElementHeader.js';
export { MusicElementHeader };
import { default as MusicHeader } from './classes/MusicHeader.js';
export { MusicHeader };
import { default as MusicImmersiveHeader } from './classes/MusicImmersiveHeader.js';
export { MusicImmersiveHeader };
import { default as MusicInlineBadge } from './classes/MusicInlineBadge.js';
export { MusicInlineBadge };
import { default as MusicItemThumbnailOverlay } from './classes/MusicItemThumbnailOverlay.js';
export { MusicItemThumbnailOverlay };
import { default as MusicLargeCardItemCarousel } from './classes/MusicLargeCardItemCarousel.js';
export { MusicLargeCardItemCarousel };
import { default as MusicNavigationButton } from './classes/MusicNavigationButton.js';
export { MusicNavigationButton };
import { default as MusicPlayButton } from './classes/MusicPlayButton.js';
export { MusicPlayButton };
import { default as MusicPlaylistShelf } from './classes/MusicPlaylistShelf.js';
export { MusicPlaylistShelf };
import { default as MusicQueue } from './classes/MusicQueue.js';
export { MusicQueue };
import { default as MusicResponsiveListItem } from './classes/MusicResponsiveListItem.js';
export { MusicResponsiveListItem };
import { default as MusicResponsiveListItemFixedColumn } from './classes/MusicResponsiveListItemFixedColumn.js';
export { MusicResponsiveListItemFixedColumn };
import { default as MusicResponsiveListItemFlexColumn } from './classes/MusicResponsiveListItemFlexColumn.js';
export { MusicResponsiveListItemFlexColumn };
import { default as MusicShelf } from './classes/MusicShelf.js';
export { MusicShelf };
import { default as MusicSideAlignedItem } from './classes/MusicSideAlignedItem.js';
export { MusicSideAlignedItem };
import { default as MusicSortFilterButton } from './classes/MusicSortFilterButton.js';
export { MusicSortFilterButton };
import { default as MusicThumbnail } from './classes/MusicThumbnail.js';
export { MusicThumbnail };
import { default as MusicTwoRowItem } from './classes/MusicTwoRowItem.js';
export { MusicTwoRowItem };
import { default as MusicVisualHeader } from './classes/MusicVisualHeader.js';
export { MusicVisualHeader };
import { default as NavigationEndpoint } from './classes/NavigationEndpoint.js';
export { NavigationEndpoint };
import { default as Notification } from './classes/Notification.js';
export { Notification };
import { default as PageIntroduction } from './classes/PageIntroduction.js';
export { PageIntroduction };
import { default as PlayerAnnotationsExpanded } from './classes/PlayerAnnotationsExpanded.js';
export { PlayerAnnotationsExpanded };
import { default as PlayerCaptionsTracklist } from './classes/PlayerCaptionsTracklist.js';
export { PlayerCaptionsTracklist };
import { default as PlayerErrorMessage } from './classes/PlayerErrorMessage.js';
export { PlayerErrorMessage };
import { default as PlayerLiveStoryboardSpec } from './classes/PlayerLiveStoryboardSpec.js';
export { PlayerLiveStoryboardSpec };
import { default as PlayerMicroformat } from './classes/PlayerMicroformat.js';
export { PlayerMicroformat };
import { default as PlayerOverlay } from './classes/PlayerOverlay.js';
export { PlayerOverlay };
import { default as PlayerOverlayAutoplay } from './classes/PlayerOverlayAutoplay.js';
export { PlayerOverlayAutoplay };
import { default as PlayerStoryboardSpec } from './classes/PlayerStoryboardSpec.js';
export { PlayerStoryboardSpec };
import { default as Playlist } from './classes/Playlist.js';
export { Playlist };
import { default as PlaylistCustomThumbnail } from './classes/PlaylistCustomThumbnail.js';
export { PlaylistCustomThumbnail };
import { default as PlaylistHeader } from './classes/PlaylistHeader.js';
export { PlaylistHeader };
import { default as PlaylistInfoCardContent } from './classes/PlaylistInfoCardContent.js';
export { PlaylistInfoCardContent };
import { default as PlaylistMetadata } from './classes/PlaylistMetadata.js';
export { PlaylistMetadata };
import { default as PlaylistPanel } from './classes/PlaylistPanel.js';
export { PlaylistPanel };
import { default as PlaylistPanelVideo } from './classes/PlaylistPanelVideo.js';
export { PlaylistPanelVideo };
import { default as PlaylistPanelVideoWrapper } from './classes/PlaylistPanelVideoWrapper.js';
export { PlaylistPanelVideoWrapper };
import { default as PlaylistSidebar } from './classes/PlaylistSidebar.js';
export { PlaylistSidebar };
import { default as PlaylistSidebarPrimaryInfo } from './classes/PlaylistSidebarPrimaryInfo.js';
export { PlaylistSidebarPrimaryInfo };
import { default as PlaylistSidebarSecondaryInfo } from './classes/PlaylistSidebarSecondaryInfo.js';
export { PlaylistSidebarSecondaryInfo };
import { default as PlaylistVideo } from './classes/PlaylistVideo.js';
export { PlaylistVideo };
import { default as PlaylistVideoList } from './classes/PlaylistVideoList.js';
export { PlaylistVideoList };
import { default as PlaylistVideoThumbnail } from './classes/PlaylistVideoThumbnail.js';
export { PlaylistVideoThumbnail };
import { default as Poll } from './classes/Poll.js';
export { Poll };
import { default as Post } from './classes/Post.js';
export { Post };
import { default as PostMultiImage } from './classes/PostMultiImage.js';
export { PostMultiImage };
import { default as ProfileColumn } from './classes/ProfileColumn.js';
export { ProfileColumn };
import { default as ProfileColumnStats } from './classes/ProfileColumnStats.js';
export { ProfileColumnStats };
import { default as ProfileColumnStatsEntry } from './classes/ProfileColumnStatsEntry.js';
export { ProfileColumnStatsEntry };
import { default as ProfileColumnUserInfo } from './classes/ProfileColumnUserInfo.js';
export { ProfileColumnUserInfo };
import { default as RecognitionShelf } from './classes/RecognitionShelf.js';
export { RecognitionShelf };
import { default as ReelItem } from './classes/ReelItem.js';
export { ReelItem };
import { default as ReelShelf } from './classes/ReelShelf.js';
export { ReelShelf };
import { default as RelatedChipCloud } from './classes/RelatedChipCloud.js';
export { RelatedChipCloud };
import { default as RichGrid } from './classes/RichGrid.js';
export { RichGrid };
import { default as RichItem } from './classes/RichItem.js';
export { RichItem };
import { default as RichListHeader } from './classes/RichListHeader.js';
export { RichListHeader };
import { default as RichSection } from './classes/RichSection.js';
export { RichSection };
import { default as RichShelf } from './classes/RichShelf.js';
export { RichShelf };
import { default as SearchBox } from './classes/SearchBox.js';
export { SearchBox };
import { default as SearchRefinementCard } from './classes/SearchRefinementCard.js';
export { SearchRefinementCard };
import { default as SearchSuggestion } from './classes/SearchSuggestion.js';
export { SearchSuggestion };
import { default as SearchSuggestionsSection } from './classes/SearchSuggestionsSection.js';
export { SearchSuggestionsSection };
import { default as SecondarySearchContainer } from './classes/SecondarySearchContainer.js';
export { SecondarySearchContainer };
import { default as SectionList } from './classes/SectionList.js';
export { SectionList };
import { default as SegmentedLikeDislikeButton } from './classes/SegmentedLikeDislikeButton.js';
export { SegmentedLikeDislikeButton };
import { default as SettingBoolean } from './classes/SettingBoolean.js';
export { SettingBoolean };
import { default as SettingsCheckbox } from './classes/SettingsCheckbox.js';
export { SettingsCheckbox };
import { default as SettingsOptions } from './classes/SettingsOptions.js';
export { SettingsOptions };
import { default as SettingsSidebar } from './classes/SettingsSidebar.js';
export { SettingsSidebar };
import { default as SettingsSwitch } from './classes/SettingsSwitch.js';
export { SettingsSwitch };
import { default as Shelf } from './classes/Shelf.js';
export { Shelf };
import { default as ShowingResultsFor } from './classes/ShowingResultsFor.js';
export { ShowingResultsFor };
import { default as SimpleCardContent } from './classes/SimpleCardContent.js';
export { SimpleCardContent };
import { default as SimpleCardTeaser } from './classes/SimpleCardTeaser.js';
export { SimpleCardTeaser };
import { default as SimpleTextSection } from './classes/SimpleTextSection.js';
export { SimpleTextSection };
import { default as SingleActionEmergencySupport } from './classes/SingleActionEmergencySupport.js';
export { SingleActionEmergencySupport };
import { default as SingleColumnBrowseResults } from './classes/SingleColumnBrowseResults.js';
export { SingleColumnBrowseResults };
import { default as SingleColumnMusicWatchNextResults } from './classes/SingleColumnMusicWatchNextResults.js';
export { SingleColumnMusicWatchNextResults };
import { default as SingleHeroImage } from './classes/SingleHeroImage.js';
export { SingleHeroImage };
import { default as SlimOwner } from './classes/SlimOwner.js';
export { SlimOwner };
import { default as SlimVideoMetadata } from './classes/SlimVideoMetadata.js';
export { SlimVideoMetadata };
import { default as SortFilterSubMenu } from './classes/SortFilterSubMenu.js';
export { SortFilterSubMenu };
import { default as SubFeedOption } from './classes/SubFeedOption.js';
export { SubFeedOption };
import { default as SubFeedSelector } from './classes/SubFeedSelector.js';
export { SubFeedSelector };
import { default as SubscribeButton } from './classes/SubscribeButton.js';
export { SubscribeButton };
import { default as SubscriptionNotificationToggleButton } from './classes/SubscriptionNotificationToggleButton.js';
export { SubscriptionNotificationToggleButton };
import { default as Tab } from './classes/Tab.js';
export { Tab };
import { default as Tabbed } from './classes/Tabbed.js';
export { Tabbed };
import { default as TabbedSearchResults } from './classes/TabbedSearchResults.js';
export { TabbedSearchResults };
import { default as TextHeader } from './classes/TextHeader.js';
export { TextHeader };
import { default as ThumbnailLandscapePortrait } from './classes/ThumbnailLandscapePortrait.js';
export { ThumbnailLandscapePortrait };
import { default as ThumbnailOverlayBottomPanel } from './classes/ThumbnailOverlayBottomPanel.js';
export { ThumbnailOverlayBottomPanel };
import { default as ThumbnailOverlayEndorsement } from './classes/ThumbnailOverlayEndorsement.js';
export { ThumbnailOverlayEndorsement };
import { default as ThumbnailOverlayHoverText } from './classes/ThumbnailOverlayHoverText.js';
export { ThumbnailOverlayHoverText };
import { default as ThumbnailOverlayInlineUnplayable } from './classes/ThumbnailOverlayInlineUnplayable.js';
export { ThumbnailOverlayInlineUnplayable };
import { default as ThumbnailOverlayLoadingPreview } from './classes/ThumbnailOverlayLoadingPreview.js';
export { ThumbnailOverlayLoadingPreview };
import { default as ThumbnailOverlayNowPlaying } from './classes/ThumbnailOverlayNowPlaying.js';
export { ThumbnailOverlayNowPlaying };
import { default as ThumbnailOverlayPinking } from './classes/ThumbnailOverlayPinking.js';
export { ThumbnailOverlayPinking };
import { default as ThumbnailOverlayPlaybackStatus } from './classes/ThumbnailOverlayPlaybackStatus.js';
export { ThumbnailOverlayPlaybackStatus };
import { default as ThumbnailOverlayResumePlayback } from './classes/ThumbnailOverlayResumePlayback.js';
export { ThumbnailOverlayResumePlayback };
import { default as ThumbnailOverlaySidePanel } from './classes/ThumbnailOverlaySidePanel.js';
export { ThumbnailOverlaySidePanel };
import { default as ThumbnailOverlayTimeStatus } from './classes/ThumbnailOverlayTimeStatus.js';
export { ThumbnailOverlayTimeStatus };
import { default as ThumbnailOverlayToggleButton } from './classes/ThumbnailOverlayToggleButton.js';
export { ThumbnailOverlayToggleButton };
import { default as TimedMarkerDecoration } from './classes/TimedMarkerDecoration.js';
export { TimedMarkerDecoration };
import { default as TitleAndButtonListHeader } from './classes/TitleAndButtonListHeader.js';
export { TitleAndButtonListHeader };
import { default as ToggleButton } from './classes/ToggleButton.js';
export { ToggleButton };
import { default as ToggleMenuServiceItem } from './classes/ToggleMenuServiceItem.js';
export { ToggleMenuServiceItem };
import { default as Tooltip } from './classes/Tooltip.js';
export { Tooltip };
import { default as TopicChannelDetails } from './classes/TopicChannelDetails.js';
export { TopicChannelDetails };
import { default as TwoColumnBrowseResults } from './classes/TwoColumnBrowseResults.js';
export { TwoColumnBrowseResults };
import { default as TwoColumnSearchResults } from './classes/TwoColumnSearchResults.js';
export { TwoColumnSearchResults };
import { default as TwoColumnWatchNextResults } from './classes/TwoColumnWatchNextResults.js';
export { TwoColumnWatchNextResults };
import { default as UniversalWatchCard } from './classes/UniversalWatchCard.js';
export { UniversalWatchCard };
import { default as UpsellDialog } from './classes/UpsellDialog.js';
export { UpsellDialog };
import { default as VerticalList } from './classes/VerticalList.js';
export { VerticalList };
import { default as VerticalWatchCardList } from './classes/VerticalWatchCardList.js';
export { VerticalWatchCardList };
import { default as Video } from './classes/Video.js';
export { Video };
import { default as VideoCard } from './classes/VideoCard.js';
export { VideoCard };
import { default as VideoInfoCardContent } from './classes/VideoInfoCardContent.js';
export { VideoInfoCardContent };
import { default as VideoOwner } from './classes/VideoOwner.js';
export { VideoOwner };
import { default as VideoPrimaryInfo } from './classes/VideoPrimaryInfo.js';
export { VideoPrimaryInfo };
import { default as VideoSecondaryInfo } from './classes/VideoSecondaryInfo.js';
export { VideoSecondaryInfo };
import { default as WatchCardCompactVideo } from './classes/WatchCardCompactVideo.js';
export { WatchCardCompactVideo };
import { default as WatchCardHeroVideo } from './classes/WatchCardHeroVideo.js';
export { WatchCardHeroVideo };
import { default as WatchCardRichHeader } from './classes/WatchCardRichHeader.js';
export { WatchCardRichHeader };
import { default as WatchCardSectionSequence } from './classes/WatchCardSectionSequence.js';
export { WatchCardSectionSequence };
import { default as WatchNextEndScreen } from './classes/WatchNextEndScreen.js';
export { WatchNextEndScreen };
import { default as WatchNextTabbedResults } from './classes/WatchNextTabbedResults.js';
export { WatchNextTabbedResults };
import { default as AnchoredSection } from './classes/ytkids/AnchoredSection.js';
export { AnchoredSection };
import { default as KidsCategoriesHeader } from './classes/ytkids/KidsCategoriesHeader.js';
export { KidsCategoriesHeader };
import { default as KidsCategoryTab } from './classes/ytkids/KidsCategoryTab.js';
export { KidsCategoryTab };
import { default as KidsHomeScreen } from './classes/ytkids/KidsHomeScreen.js';
export { KidsHomeScreen };
export const YTNodes = {
const map: Record<string, YTNodeConstructor> = {
AccountChannel,
AccountItemSection,
AccountItemSectionHeader,
@@ -669,8 +989,6 @@ export const Misc = {
VideoDetails
};
const map: Record<string, YTNodeConstructor> = YTNodes;
/**
* @param name - Name of the node to be parsed
*/

View File

@@ -49,8 +49,9 @@ export const CLIENTS = Object.freeze({
},
ANDROID: {
NAME: 'ANDROID',
VERSION: '17.34.35',
SDK_VERSION: '29'
VERSION: '18.06.35',
SDK_VERSION: '29',
USER_AGENT: 'com.google.android.youtube/18.06.35 (Linux; U; Android 10; US)'
},
YTSTUDIO_ANDROID: {
NAME: 'ANDROID_CREATOR',

View File

@@ -91,6 +91,13 @@ export default class HTTPClient {
delete n_body.client;
if (Platform.shim.server) {
if (n_body.context.client.clientName === 'ANDROID' || n_body.context.client.clientName === 'ANDROID_MUSIC') {
request_headers.set('User-Agent', Constants.CLIENTS.ANDROID.USER_AGENT);
}
}
is_web_kids = n_body.context.client.clientName === 'WEB_KIDS';
request_body = JSON.stringify(n_body);
}
@@ -146,12 +153,14 @@ export default class HTTPClient {
ctx.client.clientFormFactor = 'SMALL_FORM_FACTOR';
ctx.client.clientName = Constants.CLIENTS.ANDROID.NAME;
ctx.client.androidSdkVersion = Constants.CLIENTS.ANDROID.SDK_VERSION;
ctx.client.platform = 'MOBILE';
break;
case 'YTMUSIC_ANDROID':
ctx.client.clientVersion = Constants.CLIENTS.YTMUSIC_ANDROID.VERSION;
ctx.client.clientFormFactor = 'SMALL_FORM_FACTOR';
ctx.client.clientName = Constants.CLIENTS.YTMUSIC_ANDROID.NAME;
ctx.client.androidSdkVersion = Constants.CLIENTS.ANDROID.SDK_VERSION;
ctx.client.platform = 'MOBILE';
break;
case 'YTSTUDIO_ANDROID':
ctx.client.clientVersion = Constants.CLIENTS.YTSTUDIO_ANDROID.VERSION;

View File

@@ -4,6 +4,8 @@ export * as Constants from './Constants.js';
export { default as EventEmitter } from './EventEmitterLike.js';
export { default as FormatUtils } from './FormatUtils.js';
export { default as HTTPClient } from './HTTPClient.js';
export * from './HTTPClient.js';