mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-13 09:32:12 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b29244b41 | ||
|
|
f9754f5ac6 | ||
|
|
b2253df802 | ||
|
|
f3517708ff | ||
|
|
0d35fe0ca5 | ||
|
|
3e3dc351bb | ||
|
|
197bb759cd | ||
|
|
c76b24b3f4 | ||
|
|
574b67a1f7 | ||
|
|
9b2738f128 | ||
|
|
95f1d4077f | ||
|
|
a511608f18 | ||
|
|
cf8a33c79f | ||
|
|
cfc1a183e0 | ||
|
|
95033e723e | ||
|
|
2cc7b8bcd6 | ||
|
|
2d774e26aa | ||
|
|
214aa147ce | ||
|
|
ce53ac1843 | ||
|
|
0ad26f28d9 | ||
|
|
4c7b8a3403 | ||
|
|
33a6e740d7 | ||
|
|
0b1840a62c | ||
|
|
f4e0f30e6e | ||
|
|
200632f374 | ||
|
|
f933cb45bc | ||
|
|
a0e6cef00f | ||
|
|
a0bfe16427 | ||
|
|
9d352b58eb |
57
CHANGELOG.md
57
CHANGELOG.md
@@ -1,5 +1,62 @@
|
||||
# Changelog
|
||||
|
||||
## [3.3.0](https://github.com/LuanRT/YouTube.js/compare/v3.2.0...v3.3.0) (2023-03-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **parser:** add `ConversationBar` node ([b2253df](https://github.com/LuanRT/YouTube.js/commit/b2253df8022217dc486155d8cacbf22db04dd9c2))
|
||||
* **VideoInfo:** support get by endpoint + more info ([#342](https://github.com/LuanRT/YouTube.js/issues/342)) ([0d35fe0](https://github.com/LuanRT/YouTube.js/commit/0d35fe0ca5e87a877b76cbb6cf3c92843eac5a99))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **MultiMarkersPlayerBar:** avoid observing undefined objects ([f351770](https://github.com/LuanRT/YouTube.js/commit/f3517708ff34093a544c09d6f5f1ec806130d5cc))
|
||||
* **SharedPost:** import `Menu` node directly (oops) ([3e3dc35](https://github.com/LuanRT/YouTube.js/commit/3e3dc351bb44faec87616d9b922924d14a95f29f))
|
||||
* **ytmusic:** use static visitor id to avoid empty API responses ([f9754f5](https://github.com/LuanRT/YouTube.js/commit/f9754f5ac61d0f11b025f37f93783f971560268b)), closes [#279](https://github.com/LuanRT/YouTube.js/issues/279)
|
||||
|
||||
## [3.2.0](https://github.com/LuanRT/YouTube.js/compare/v3.1.1...v3.2.0) (2023-03-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add support for descriptive audio tracks ([#338](https://github.com/LuanRT/YouTube.js/issues/338)) ([574b67a](https://github.com/LuanRT/YouTube.js/commit/574b67a1f707a32378586dd2fe7b2f36f4ab6ddb))
|
||||
* export `FormatUtils`' types ([2d774e2](https://github.com/LuanRT/YouTube.js/commit/2d774e26aae79f3d1b115e0e85c148ae80985529))
|
||||
* **parser:** add `banner` to `PlaylistHeader` ([#337](https://github.com/LuanRT/YouTube.js/issues/337)) ([95033e7](https://github.com/LuanRT/YouTube.js/commit/95033e723ef912706e4d176de6b2760f017184e1))
|
||||
* **parser:** SharedPost ([#332](https://github.com/LuanRT/YouTube.js/issues/332)) ([ce53ac1](https://github.com/LuanRT/YouTube.js/commit/ce53ac18435cbcb20d6d4c4ab52fd156091e7592))
|
||||
* **VideoInfo:** add `game_info` and `category` ([#333](https://github.com/LuanRT/YouTube.js/issues/333)) ([214aa14](https://github.com/LuanRT/YouTube.js/commit/214aa147ce6306e37a6bf860a7bed5635db4797e))
|
||||
* **YouTube/Search:** add `SearchSubMenu` node ([#340](https://github.com/LuanRT/YouTube.js/issues/340)) ([a511608](https://github.com/LuanRT/YouTube.js/commit/a511608f18b37b0d9f2c7958ed5128330fabcfa0))
|
||||
* **yt:** add `getGuide()` ([#335](https://github.com/LuanRT/YouTube.js/issues/335)) ([2cc7b8b](https://github.com/LuanRT/YouTube.js/commit/2cc7b8bcd6938c7fb3af4f854a1d78b86d153873))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentedLikeDislikeButton:** like/dislike buttons can also be a simple `Button` ([9b2738f](https://github.com/LuanRT/YouTube.js/commit/9b2738f1285b278c3e83541857651be9a6248288))
|
||||
* **YouTube:** fix warnings when retrieving members-only content ([#341](https://github.com/LuanRT/YouTube.js/issues/341)) ([95f1d40](https://github.com/LuanRT/YouTube.js/commit/95f1d4077ff3775f36967dca786139a09e2830a2))
|
||||
* **ytmusic:** export search filters type ([cf8a33c](https://github.com/LuanRT/YouTube.js/commit/cf8a33c79f5432136b865d535fd0ecedc2393382))
|
||||
|
||||
## [3.1.1](https://github.com/LuanRT/YouTube.js/compare/v3.1.0...v3.1.1) (2023-03-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Channel:** getting community continuations ([#329](https://github.com/LuanRT/YouTube.js/issues/329)) ([4c7b8a3](https://github.com/LuanRT/YouTube.js/commit/4c7b8a34030effa26c4ea186d3e9509128aec31c))
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
```
|
||||
34
README.md
34
README.md
@@ -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)
|
||||
@@ -240,12 +248,13 @@ const yt = await Innertube.create({
|
||||
<summary>Methods</summary>
|
||||
<p>
|
||||
|
||||
* [.getInfo(video_id, client?)](#getinfo)
|
||||
* [.getInfo(target, client?)](#getinfo)
|
||||
* [.getBasicInfo(video_id, client?)](#getbasicinfo)
|
||||
* [.search(query, filters?)](#search)
|
||||
* [.getSearchSuggestions(query)](#getsearchsuggestions)
|
||||
* [.getComments(video_id, sort_by?)](#getcomments)
|
||||
* [.getHomeFeed()](#gethomefeed)
|
||||
* [.getGuide()](#getguide)
|
||||
* [.getLibrary()](#getlibrary)
|
||||
* [.getHistory()](#gethistory)
|
||||
* [.getTrending()](#gettrending)
|
||||
@@ -264,7 +273,7 @@ const yt = await Innertube.create({
|
||||
</details>
|
||||
|
||||
<a name="getinfo"></a>
|
||||
### getInfo(video_id, client?)
|
||||
### getInfo(target, client?)
|
||||
|
||||
Retrieves video info, including playback data and even layout elements such as menus, buttons, etc — all nicely parsed.
|
||||
|
||||
@@ -272,7 +281,7 @@ Retrieves video info, including playback data and even layout elements such as m
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| video_id | `string` | The id of the video |
|
||||
| target | `string` \| `NavigationEndpoint` | If `string`, the id of the video. If `NavigationEndpoint`, the endpoint of watchable elements such as `Video`, `Mix` and `Playlist`. To clarify, valid endpoints have payloads containing at least `videoId` and optionally `playlistId`, `params` and `index`. |
|
||||
| client? | `InnerTubeClient` | `WEB`, `ANDROID`, `YTMUSIC`, `YTMUSIC_ANDROID` or `TV_EMBEDDED` |
|
||||
|
||||
<details>
|
||||
@@ -312,6 +321,9 @@ Retrieves video info, including playback data and even layout elements such as m
|
||||
- `<info>#addToWatchHistory()`
|
||||
- Adds the video to the watch history.
|
||||
|
||||
- `<info>#autoplay_video_endpoint`
|
||||
- Returns the endpoint of the video for Autoplay.
|
||||
|
||||
- `<info>#page`
|
||||
- Returns original InnerTube response (sanitized).
|
||||
|
||||
@@ -418,6 +430,12 @@ Retrieves YouTube's home feed.
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<a name="getguide"></a>
|
||||
### getGuide()
|
||||
Retrieves YouTube's content guide.
|
||||
|
||||
**Returns**: `Promise<Guide>`
|
||||
|
||||
<a name="getlibrary"></a>
|
||||
### getLibrary()
|
||||
Retrieves the account's library.
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Updating the parser
|
||||
|
||||
YouTube is constantly changing, so it is not rare to see YouTube crawlers/scrapers breaking every now and then.
|
||||
YouTube is constantly changing, so it is not rare to see YouTube crawlers/scrapers breaking every now and then.
|
||||
|
||||
Our parser, on the other hand, was written so that it behaves similarly to an official client, parsing and mapping renderers (a.k.a YTNodes) dynamically without hard-coding their path in the response. This way, whenever a new renderer pops up (e.g; YouTube adds a new feature / minor UI changes) the library will print a warning similar to this:
|
||||
|
||||
Our parser, on the other hand, was written so that it behaves similarly to an official client, parsing and mapping renderers (a.k.a YTNodes) dynamically without hard-coding their path in the response. This way, whenever a new renderer pops up (e.g; YouTube adds a new feature / minor UI changes) the library will print a warning similar to this:
|
||||
```
|
||||
InnertubeError: SomeRenderer not found!
|
||||
This is a bug, want to help us fix it? Follow the instructions at https://github.com/LuanRT/YouTube.js/blob/main/docs/updating-the-parser.md or report it at https://github.com/LuanRT/YouTube.js/issues!
|
||||
@@ -26,17 +27,19 @@ Thanks to the modularity of the parser, a renderer can be implemented by simply
|
||||
For example, say we found a new renderer named `verticalListRenderer`, to let the parser know it exists we would have to create a file with the following structure:
|
||||
|
||||
> `../classes/VerticalList.ts`
|
||||
|
||||
```ts
|
||||
import Parser from '..';
|
||||
import { YTNode } from '../helpers';
|
||||
import type { RawNode } from '../index.js';
|
||||
|
||||
class VerticalList extends YTNode {
|
||||
static type = 'VerticalList';
|
||||
|
||||
|
||||
header;
|
||||
contents;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
// parse the data here, ex;
|
||||
this.header = Parser.parseItem(data.header);
|
||||
this.contents = Parser.parseArray(data.contents);
|
||||
@@ -47,8 +50,9 @@ export default VerticalList;
|
||||
```
|
||||
|
||||
Then update the parser map:
|
||||
|
||||
```bash
|
||||
npm run build:parser-map
|
||||
```
|
||||
|
||||
And that's it!
|
||||
And that's it!
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "youtubei.js",
|
||||
"version": "3.0.0",
|
||||
"version": "3.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "youtubei.js",
|
||||
"version": "3.0.0",
|
||||
"version": "3.3.0",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "youtubei.js",
|
||||
"version": "3.0.0",
|
||||
"version": "3.3.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",
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ import PlaylistManager from './core/PlaylistManager.js';
|
||||
import YTStudio from './core/Studio.js';
|
||||
import TabbedFeed from './core/TabbedFeed.js';
|
||||
import HomeFeed from './parser/youtube/HomeFeed.js';
|
||||
import Guide from './parser/youtube/Guide.js';
|
||||
import Proto from './proto/index.js';
|
||||
import Constants from './utils/Constants.js';
|
||||
|
||||
@@ -30,7 +31,7 @@ import type Format from './parser/classes/misc/Format.js';
|
||||
import type { ApiResponse } from './core/Actions.js';
|
||||
import type { IBrowseResponse, IParsedResponse } from './parser/types/index.js';
|
||||
import type { DownloadOptions, FormatOptions } from './utils/FormatUtils.js';
|
||||
import { generateRandomString, throwIfMissing } from './utils/Utils.js';
|
||||
import { generateRandomString, InnertubeError, throwIfMissing } from './utils/Utils.js';
|
||||
|
||||
export type InnertubeConfig = SessionOptions;
|
||||
|
||||
@@ -71,16 +72,48 @@ class Innertube {
|
||||
|
||||
/**
|
||||
* Retrieves video info.
|
||||
* @param video_id - The video id.
|
||||
* @param target - The video id or `NavigationEndpoint`.
|
||||
* @param client - The client to use.
|
||||
*/
|
||||
async getInfo(video_id: string, client?: InnerTubeClient): Promise<VideoInfo> {
|
||||
throwIfMissing({ video_id });
|
||||
async getInfo(target: string | NavigationEndpoint, client?: InnerTubeClient): Promise<VideoInfo> {
|
||||
throwIfMissing({ target });
|
||||
|
||||
let payload: {
|
||||
videoId: string,
|
||||
playlistId?: string,
|
||||
params?: string,
|
||||
playlistIndex?: number
|
||||
};
|
||||
|
||||
if (target instanceof NavigationEndpoint) {
|
||||
const video_id = target.payload?.videoId;
|
||||
if (!video_id) {
|
||||
throw new InnertubeError('Missing video id in endpoint payload.', target);
|
||||
}
|
||||
payload = {
|
||||
videoId: video_id
|
||||
};
|
||||
if (target.payload.playlistId) {
|
||||
payload.playlistId = target.payload.playlistId;
|
||||
}
|
||||
if (target.payload.params) {
|
||||
payload.params = target.payload.params;
|
||||
}
|
||||
if (target.payload.index) {
|
||||
payload.playlistIndex = target.payload.index;
|
||||
}
|
||||
} else if (typeof target === 'string') {
|
||||
payload = {
|
||||
videoId: target
|
||||
};
|
||||
} else {
|
||||
throw new InnertubeError('Invalid target, expected either a video id or a valid NavigationEndpoint', target);
|
||||
}
|
||||
|
||||
const cpn = generateRandomString(16);
|
||||
|
||||
const initial_info = this.actions.getVideoInfo(video_id, cpn, client);
|
||||
const continuation = this.actions.execute('/next', { videoId: video_id });
|
||||
const initial_info = this.actions.getVideoInfo(payload.videoId, cpn, client);
|
||||
const continuation = this.actions.execute('/next', payload);
|
||||
|
||||
const response = await Promise.all([ initial_info, continuation ]);
|
||||
return new VideoInfo(response, this.actions, this.session.player, cpn);
|
||||
@@ -170,6 +203,14 @@ class Innertube {
|
||||
return new HomeFeed(this.actions, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves YouTube's content guide.
|
||||
*/
|
||||
async getGuide(): Promise<Guide> {
|
||||
const response = await this.actions.execute('/guide');
|
||||
return new Guide(response.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the account's library.
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ import { concatMemos, InnertubeError } from '../utils/Utils.js';
|
||||
import type Actions from './Actions.js';
|
||||
|
||||
import BackstagePost from '../parser/classes/BackstagePost.js';
|
||||
import SharedPost from '../parser/classes/SharedPost.js';
|
||||
import Channel from '../parser/classes/Channel.js';
|
||||
import CompactVideo from '../parser/classes/CompactVideo.js';
|
||||
import GridChannel from '../parser/classes/GridChannel.js';
|
||||
@@ -100,7 +101,7 @@ class Feed<T extends IParsedResponse = IParsedResponse> {
|
||||
* Get all the community posts in the feed
|
||||
*/
|
||||
get posts() {
|
||||
return this.#memo.getType<Post | BackstagePost>([ BackstagePost, Post ]);
|
||||
return this.#memo.getType<Post | BackstagePost | SharedPost>([ BackstagePost, Post, SharedPost ]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,10 @@ import type { ObservedArray, YTNode } from '../parser/helpers.js';
|
||||
import type Actions from './Actions.js';
|
||||
import type Session from './Session.js';
|
||||
|
||||
export type SearchFilters = {
|
||||
type?: 'all' | 'song' | 'video' | 'album' | 'playlist' | 'artist';
|
||||
};
|
||||
|
||||
class Music {
|
||||
#session: Session;
|
||||
#actions: Actions;
|
||||
@@ -108,9 +112,7 @@ class Music {
|
||||
* @param query - Search query.
|
||||
* @param filters - Search filters.
|
||||
*/
|
||||
async search(query: string, filters: {
|
||||
type?: 'all' | 'song' | 'video' | 'album' | 'playlist' | 'artist';
|
||||
} = {}): Promise<Search> {
|
||||
async search(query: string, filters: SearchFilters = {}): Promise<Search> {
|
||||
throwIfMissing({ query });
|
||||
|
||||
const payload: {
|
||||
|
||||
@@ -4,7 +4,7 @@ import Actions from './Actions.js';
|
||||
import Player from './Player.js';
|
||||
|
||||
import HTTPClient from '../utils/HTTPClient.js';
|
||||
import { Platform, DeviceCategory, generateRandomString, getRandomUserAgent, InnertubeError, SessionError } from '../utils/Utils.js';
|
||||
import { Platform, DeviceCategory, getRandomUserAgent, InnertubeError, SessionError } from '../utils/Utils.js';
|
||||
import OAuth, { Credentials, OAuthAuthErrorEventHandler, OAuthAuthEventHandler, OAuthAuthPendingEventHandler } from './OAuth.js';
|
||||
import Proto from '../proto/index.js';
|
||||
import { ICache } from '../types/Cache.js';
|
||||
@@ -232,7 +232,7 @@ export default class Session extends EventEmitterLike {
|
||||
'user-agent': getRandomUserAgent('desktop'),
|
||||
'accept': '*/*',
|
||||
'referer': 'https://www.youtube.com/sw.js',
|
||||
'cookie': `PREF=tz=${options.time_zone.replace('/', '.')}`
|
||||
'cookie': `PREF=tz=${options.time_zone.replace('/', '.')};VISITOR_INFO1_LIVE=${Constants.CLIENTS.WEB.STATIC_VISITOR_ID};`
|
||||
}
|
||||
});
|
||||
|
||||
@@ -294,7 +294,7 @@ export default class Session extends EventEmitterLike {
|
||||
client_name: string;
|
||||
enable_safety_mode: boolean
|
||||
}): SessionData {
|
||||
const id = generateRandomString(11);
|
||||
const id = Constants.CLIENTS.WEB.STATIC_VISITOR_ID;
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
const context: Context = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Text from './misc/Text.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
|
||||
class AccountChannel extends YTNode {
|
||||
static type = 'AccountChannel';
|
||||
@@ -8,7 +9,7 @@ class AccountChannel extends YTNode {
|
||||
title: Text;
|
||||
endpoint: NavigationEndpoint;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
|
||||
@@ -6,6 +6,7 @@ import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import AccountItemSectionHeader from './AccountItemSectionHeader.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
|
||||
class AccountItem {
|
||||
static type = 'AccountItem';
|
||||
@@ -18,7 +19,7 @@ class AccountItem {
|
||||
endpoint: NavigationEndpoint;
|
||||
account_byline: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
this.account_name = new Text(data.accountName);
|
||||
this.account_photo = Thumbnail.fromResponse(data.accountPhoto);
|
||||
this.is_selected = data.isSelected;
|
||||
@@ -35,7 +36,7 @@ class AccountItemSection extends YTNode {
|
||||
contents;
|
||||
header;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.contents = data.contents.map((ac: any) => new AccountItem(ac.accountItem));
|
||||
this.header = Parser.parseItem<AccountItemSectionHeader>(data.header, AccountItemSectionHeader);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Text from './misc/Text.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
import type { RawNode } from '../index.js';
|
||||
class AccountItemSectionHeader extends YTNode {
|
||||
static type = 'AccountItemSectionHeader';
|
||||
|
||||
title: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ import AccountChannel from './AccountChannel.js';
|
||||
import AccountItemSection from './AccountItemSection.js';
|
||||
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
import type { RawNode } from '../index.js';
|
||||
class AccountSectionList extends YTNode {
|
||||
static type = 'AccountSectionList';
|
||||
|
||||
contents;
|
||||
footers;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.contents = Parser.parseItem<AccountItemSection>(data.contents[0], AccountItemSection);
|
||||
this.footers = Parser.parseItem<AccountChannel>(data.footers[0], AccountChannel);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Text from './misc/Text.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
import type { RawNode } from '../index.js';
|
||||
class Alert extends YTNode {
|
||||
static type = 'Alert';
|
||||
|
||||
text: Text;
|
||||
alert_type: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.text = new Text(data.text);
|
||||
this.alert_type = data.type;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
import type { RawNode } from '../index.js';
|
||||
class AudioOnlyPlayability extends YTNode {
|
||||
static type = 'AudioOnlyPlayability';
|
||||
|
||||
audio_only_availability: string;
|
||||
|
||||
constructor (data: any) {
|
||||
constructor (data: RawNode) {
|
||||
super();
|
||||
this.audio_only_availability = data.audioOnlyAvailability;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
|
||||
import type { RawNode } from '../index.js';
|
||||
class AutomixPreviewVideo extends YTNode {
|
||||
static type = 'AutomixPreviewVideo';
|
||||
|
||||
playlist_video?: { endpoint: NavigationEndpoint };
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
if (data?.content?.automixPlaylistVideoRenderer?.navigationEndpoint) {
|
||||
this.playlist_video = {
|
||||
|
||||
16
src/parser/classes/ConversationBar.ts
Normal file
16
src/parser/classes/ConversationBar.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Parser, { RawNode } from '../index.js';
|
||||
import Message from './Message.js';
|
||||
|
||||
class ConversationBar extends YTNode {
|
||||
static type = 'ConversationBar';
|
||||
|
||||
availability_message: Message | null;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.availability_message = Parser.parseItem<Message>(data.availabilityMessage, Message);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConversationBar;
|
||||
@@ -2,6 +2,7 @@ import Parser from '../index.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type Button from './Button.js';
|
||||
import type MultiMarkersPlayerBar from './MultiMarkersPlayerBar.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
|
||||
class DecoratedPlayerBar extends YTNode {
|
||||
static type = 'DecoratedPlayerBar';
|
||||
@@ -9,7 +10,7 @@ class DecoratedPlayerBar extends YTNode {
|
||||
player_bar: MultiMarkersPlayerBar | null;
|
||||
player_bar_action_button: Button | null;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.player_bar = Parser.parseItem<MultiMarkersPlayerBar>(data.playerBar);
|
||||
this.player_bar_action_button = Parser.parseItem<Button>(data.playerBarActionButton);
|
||||
|
||||
35
src/parser/classes/GuideCollapsibleEntry.ts
Normal file
35
src/parser/classes/GuideCollapsibleEntry.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import Text from './misc/Text.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Parser from '../parser.js';
|
||||
|
||||
class GuideCollapsibleEntry extends YTNode {
|
||||
static type = 'GuideCollapsibleEntry';
|
||||
|
||||
expander_item: {
|
||||
title: string,
|
||||
icon_type: string
|
||||
};
|
||||
collapser_item: {
|
||||
title: string,
|
||||
icon_type: string
|
||||
};
|
||||
expandable_items;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
|
||||
this.expander_item = {
|
||||
title: new Text(data.expanderItem.guideEntryRenderer.formattedTitle).toString(),
|
||||
icon_type: data.expanderItem.guideEntryRenderer.icon.iconType
|
||||
};
|
||||
|
||||
this.collapser_item = {
|
||||
title: new Text(data.collapserItem.guideEntryRenderer.formattedTitle).toString(),
|
||||
icon_type: data.collapserItem.guideEntryRenderer.icon.iconType
|
||||
};
|
||||
|
||||
this.expandable_items = Parser.parseArray(data.expandableItems);
|
||||
}
|
||||
}
|
||||
|
||||
export default GuideCollapsibleEntry;
|
||||
23
src/parser/classes/GuideCollapsibleSectionEntry.ts
Normal file
23
src/parser/classes/GuideCollapsibleSectionEntry.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Parser from '../parser.js';
|
||||
|
||||
class GuideCollapsibleSectionEntry extends YTNode {
|
||||
static type = 'GuideCollapsibleSectionEntry';
|
||||
|
||||
header_entry;
|
||||
expander_icon: string;
|
||||
collapser_icon: string;
|
||||
section_items;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
|
||||
this.header_entry = Parser.parseItem(data.headerEntry);
|
||||
this.expander_icon = data.expanderIcon.iconType;
|
||||
this.collapser_icon = data.collapserIcon.iconType;
|
||||
this.section_items = Parser.parseArray(data.sectionItems);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default GuideCollapsibleSectionEntry;
|
||||
14
src/parser/classes/GuideDownloadsEntry.ts
Normal file
14
src/parser/classes/GuideDownloadsEntry.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import GuideEntry from './GuideEntry.js';
|
||||
|
||||
class GuideDownloadsEntry extends GuideEntry {
|
||||
static type = 'GuideDownloadsEntry';
|
||||
|
||||
always_show: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
super(data.entryRenderer.guideEntryRenderer);
|
||||
this.always_show = !!data.alwaysShow;
|
||||
}
|
||||
}
|
||||
|
||||
export default GuideDownloadsEntry;
|
||||
33
src/parser/classes/GuideEntry.ts
Normal file
33
src/parser/classes/GuideEntry.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import Text from './misc/Text.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
|
||||
class GuideEntry extends YTNode {
|
||||
static type = 'GuideEntry';
|
||||
|
||||
title: Text;
|
||||
endpoint: NavigationEndpoint;
|
||||
icon_type?: string;
|
||||
thumbnails?: Thumbnail[];
|
||||
badges?: any;
|
||||
is_primary: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.title = new Text(data.formattedTitle);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint || data.serviceEndpoint);
|
||||
if (data.icon?.iconType) {
|
||||
this.icon_type = data.icon.iconType;
|
||||
}
|
||||
if (data.thumbnail) {
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
}
|
||||
if (data.badges) {
|
||||
this.badges = data.badges;
|
||||
}
|
||||
this.is_primary = !!data.isPrimary;
|
||||
}
|
||||
}
|
||||
|
||||
export default GuideEntry;
|
||||
20
src/parser/classes/GuideSection.ts
Normal file
20
src/parser/classes/GuideSection.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import Text from './misc/Text.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Parser from '../parser.js';
|
||||
|
||||
class GuideSection extends YTNode {
|
||||
static type = 'GuideSection';
|
||||
|
||||
title?: Text;
|
||||
items;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
if (data.formattedTitle) {
|
||||
this.title = new Text(data.formattedTitle);
|
||||
}
|
||||
this.items = Parser.parseArray(data.items);
|
||||
}
|
||||
}
|
||||
|
||||
export default GuideSection;
|
||||
7
src/parser/classes/GuideSubscriptionsSection.ts
Normal file
7
src/parser/classes/GuideSubscriptionsSection.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import GuideSection from './GuideSection.js';
|
||||
|
||||
class GuideSubscriptionsSection extends GuideSection {
|
||||
static type = 'GuideSubscriptionsSection';
|
||||
}
|
||||
|
||||
export default GuideSubscriptionsSection;
|
||||
19
src/parser/classes/HeroPlaylistThumbnail.ts
Normal file
19
src/parser/classes/HeroPlaylistThumbnail.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
|
||||
class HeroPlaylistThumbnail extends YTNode {
|
||||
static type = 'HeroPlaylistThumbnail';
|
||||
|
||||
thumbnails: Thumbnail[];
|
||||
on_tap_endpoint: NavigationEndpoint;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.on_tap_endpoint = new NavigationEndpoint(data.onTap);
|
||||
}
|
||||
}
|
||||
|
||||
export default HeroPlaylistThumbnail;
|
||||
@@ -1,6 +1,7 @@
|
||||
import Parser from '../index.js';
|
||||
import type Chapter from './Chapter.js';
|
||||
import type Heatmap from './Heatmap.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
|
||||
import { observe, ObservedArray, YTNode } from '../helpers.js';
|
||||
|
||||
@@ -13,7 +14,7 @@ class Marker extends YTNode {
|
||||
chapters?: Chapter[];
|
||||
};
|
||||
|
||||
constructor (data: any) {
|
||||
constructor (data: RawNode) {
|
||||
super();
|
||||
this.marker_key = data.key;
|
||||
|
||||
@@ -34,9 +35,12 @@ class MultiMarkersPlayerBar extends YTNode {
|
||||
|
||||
markers_map: ObservedArray<Marker>;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.markers_map = observe(data.markersMap?.map((marker: { key: string; value: { [key: string ]: any }}) => new Marker(marker)));
|
||||
this.markers_map = observe(data.markersMap?.map((marker: {
|
||||
key: string;
|
||||
value: { [key: string ]: any
|
||||
}}) => new Marker(marker)) || []);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
21
src/parser/classes/PlayerLegacyDesktopYpcOffer.ts
Normal file
21
src/parser/classes/PlayerLegacyDesktopYpcOffer.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
|
||||
class PlayerLegacyDesktopYpcOffer extends YTNode {
|
||||
static type = 'PlayerLegacyDesktopYpcOffer';
|
||||
|
||||
title: string;
|
||||
thumbnail: string;
|
||||
offer_description: string;
|
||||
offer_id: string;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = data.itemTitle;
|
||||
this.thumbnail = data.itemThumbnail;
|
||||
this.offer_description = data.offerDescription;
|
||||
this.offer_id = data.offerId;
|
||||
}
|
||||
}
|
||||
|
||||
export default PlayerLegacyDesktopYpcOffer;
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ class PlaylistHeader extends YTNode {
|
||||
save_button;
|
||||
shuffle_play_button;
|
||||
menu;
|
||||
banner;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
@@ -39,6 +40,7 @@ class PlaylistHeader extends YTNode {
|
||||
this.save_button = Parser.parse(data.saveButton);
|
||||
this.shuffle_play_button = Parser.parse(data.shufflePlayButton);
|
||||
this.menu = Parser.parse(data.moreActionsMenu);
|
||||
this.banner = Parser.parseItem(data.playlistHeaderBanner);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
32
src/parser/classes/RichMetadata.ts
Normal file
32
src/parser/classes/RichMetadata.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import Text from './misc/Text.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
class RichMetadata extends YTNode {
|
||||
static type = 'RichMetadata';
|
||||
|
||||
thumbnail: Thumbnail[];
|
||||
title: Text;
|
||||
subtitle?: Text;
|
||||
call_to_action: Text;
|
||||
icon_type?: string;
|
||||
endpoint: NavigationEndpoint;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
|
||||
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.title = new Text(data.title);
|
||||
this.subtitle = new Text(data.subtitle);
|
||||
this.call_to_action = new Text(data.callToAction);
|
||||
|
||||
if (data.callToActionIcon?.iconType) {
|
||||
this.icon_type = data.callToActionIcon?.iconType;
|
||||
}
|
||||
|
||||
this.endpoint = new NavigationEndpoint(data.endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
export default RichMetadata;
|
||||
15
src/parser/classes/RichMetadataRow.ts
Normal file
15
src/parser/classes/RichMetadataRow.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import Parser from '../index.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
|
||||
class RichMetadataRow extends YTNode {
|
||||
static type = 'RichMetadataRow';
|
||||
|
||||
contents;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.contents = Parser.parseArray(data.contents);
|
||||
}
|
||||
}
|
||||
|
||||
export default RichMetadataRow;
|
||||
22
src/parser/classes/SearchFilter.ts
Normal file
22
src/parser/classes/SearchFilter.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
import Text from './misc/Text.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
|
||||
class SearchFilter extends YTNode {
|
||||
static type = 'SearchFilter';
|
||||
|
||||
label: Text;
|
||||
endpoint: NavigationEndpoint;
|
||||
tooltip: string;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
this.label = new Text(data.label);
|
||||
this.endpoint = new NavigationEndpoint(data.endpoint);
|
||||
this.tooltip = data.tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
export default SearchFilter;
|
||||
21
src/parser/classes/SearchFilterGroup.ts
Normal file
21
src/parser/classes/SearchFilterGroup.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ObservedArray, YTNode } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
import { Parser } from '../index.js';
|
||||
import Text from './misc/Text.js';
|
||||
import SearchFilter from './SearchFilter.js';
|
||||
|
||||
class SearchFilterGroup extends YTNode {
|
||||
static type = 'SearchFilterGroup';
|
||||
|
||||
title: Text;
|
||||
filters: ObservedArray<SearchFilter> | null;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
this.title = new Text(data.title);
|
||||
this.filters = Parser.parseArray(data.filters, SearchFilter);
|
||||
}
|
||||
}
|
||||
|
||||
export default SearchFilterGroup;
|
||||
23
src/parser/classes/SearchSubMenu.ts
Normal file
23
src/parser/classes/SearchSubMenu.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ObservedArray, YTNode } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
import Parser from '../index.js';
|
||||
import Text from './misc/Text.js';
|
||||
import SearchFilterGroup from './SearchFilterGroup.js';
|
||||
import ToggleButton from './ToggleButton.js';
|
||||
|
||||
class SearchSubMenu extends YTNode {
|
||||
static type = 'SearchSubMenu';
|
||||
|
||||
title: Text;
|
||||
groups: ObservedArray<SearchFilterGroup> | null;
|
||||
button: ToggleButton | null;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
this.groups = Parser.parseArray(data.groups, SearchFilterGroup);
|
||||
this.button = Parser.parseItem(data.button, ToggleButton);
|
||||
}
|
||||
}
|
||||
|
||||
export default SearchSubMenu;
|
||||
@@ -1,17 +1,19 @@
|
||||
import Parser from '../index.js';
|
||||
import ToggleButton from './ToggleButton.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
import Parser from '../index.js';
|
||||
import Button from './Button.js';
|
||||
import ToggleButton from './ToggleButton.js';
|
||||
|
||||
class SegmentedLikeDislikeButton extends YTNode {
|
||||
static type = 'SegmentedLikeDislikeButton';
|
||||
|
||||
like_button: ToggleButton | null;
|
||||
dislike_button: ToggleButton | null;
|
||||
like_button: ToggleButton | Button | null;
|
||||
dislike_button: ToggleButton | Button | null;
|
||||
|
||||
constructor (data: any) {
|
||||
constructor (data: RawNode) {
|
||||
super();
|
||||
this.like_button = Parser.parseItem<ToggleButton>(data.likeButton, ToggleButton);
|
||||
this.dislike_button = Parser.parseItem<ToggleButton>(data.dislikeButton, ToggleButton);
|
||||
this.like_button = Parser.parseItem<ToggleButton | Button>(data.likeButton, [ ToggleButton, Button ]);
|
||||
this.dislike_button = Parser.parseItem<ToggleButton | Button>(data.dislikeButton, [ ToggleButton, Button ]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
38
src/parser/classes/SharedPost.ts
Normal file
38
src/parser/classes/SharedPost.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Parser from '../parser.js';
|
||||
import BackstagePost from './BackstagePost.js';
|
||||
import Button from './Button.js';
|
||||
import Menu from './menus/Menu.js';
|
||||
import Author from './misc/Author.js';
|
||||
import Text from './misc/Text.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
|
||||
class SharedPost extends YTNode {
|
||||
static type = 'SharedPost';
|
||||
|
||||
thumbnail: Thumbnail[];
|
||||
content: Text;
|
||||
published: Text;
|
||||
menu: Menu | null;
|
||||
original_post: BackstagePost | null;
|
||||
id: string;
|
||||
endpoint: NavigationEndpoint;
|
||||
expand_button: Button | null;
|
||||
author: Author;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.content = new Text(data.content);
|
||||
this.published = new Text(data.publishedTimeText);
|
||||
this.menu = Parser.parseItem(data.actionMenu, Menu);
|
||||
this.original_post = Parser.parseItem(data.originalPost, BackstagePost);
|
||||
this.id = data.postId;
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.expand_button = Parser.parseItem(data.expandButton, Button);
|
||||
this.author = new Author(data.displayName, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
export default SharedPost;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import Parser from '../index.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Text from './misc/Text.js';
|
||||
import PlaylistAuthor from './misc/PlaylistAuthor.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
|
||||
import type Menu from './menus/Menu.js';
|
||||
|
||||
type AutoplaySet = {
|
||||
autoplay_video: NavigationEndpoint,
|
||||
next_button_video?: NavigationEndpoint
|
||||
};
|
||||
|
||||
class TwoColumnWatchNextResults extends YTNode {
|
||||
static type = 'TwoColumnWatchNextResults';
|
||||
@@ -7,12 +17,66 @@ class TwoColumnWatchNextResults extends YTNode {
|
||||
results;
|
||||
secondary_results;
|
||||
conversation_bar;
|
||||
playlist?: {
|
||||
id: string,
|
||||
title: string,
|
||||
author: Text | PlaylistAuthor,
|
||||
contents: YTNode[],
|
||||
current_index: number,
|
||||
is_infinite: boolean,
|
||||
menu: Menu | null
|
||||
};
|
||||
autoplay?: {
|
||||
sets: AutoplaySet[],
|
||||
modified_sets?: AutoplaySet[],
|
||||
count_down_secs?: number
|
||||
};
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.results = Parser.parseArray(data.results?.results.contents);
|
||||
this.secondary_results = Parser.parseArray(data.secondaryResults?.secondaryResults.results);
|
||||
this.conversation_bar = Parser.parseItem(data?.conversationBar);
|
||||
|
||||
const playlistData = data.playlist?.playlist;
|
||||
if (playlistData) {
|
||||
this.playlist = {
|
||||
id: playlistData.playlistId,
|
||||
title: playlistData.title,
|
||||
author: playlistData.shortBylineText?.simpleText ?
|
||||
new Text(playlistData.shortBylineText) :
|
||||
new PlaylistAuthor(playlistData.longBylineText),
|
||||
contents: Parser.parseArray(playlistData.contents),
|
||||
current_index: playlistData.currentIndex,
|
||||
is_infinite: !!playlistData.isInfinite,
|
||||
menu: Parser.parseItem<Menu>(playlistData.menu)
|
||||
};
|
||||
}
|
||||
|
||||
const autoplayData = data.autoplay?.autoplay;
|
||||
if (autoplayData) {
|
||||
this.autoplay = {
|
||||
sets: autoplayData.sets.map((set: any) => this.#parseAutoplaySet(set))
|
||||
};
|
||||
if (autoplayData.modifiedSets) {
|
||||
this.autoplay.modified_sets = autoplayData.modifiedSets.map((set: any) => this.#parseAutoplaySet(set));
|
||||
}
|
||||
if (autoplayData.countDownSecs) {
|
||||
this.autoplay.count_down_secs = autoplayData.countDownSecs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#parseAutoplaySet(data: any): AutoplaySet {
|
||||
const result = {
|
||||
autoplay_video: new NavigationEndpoint(data.autoplayVideo)
|
||||
} as AutoplaySet;
|
||||
|
||||
if (data.nextButtonVideo) {
|
||||
result.next_button_video = new NavigationEndpoint(data.nextButtonVideo);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import type { RawNode } from '../index.js';
|
||||
import type { ObservedArray } from '../helpers.js';
|
||||
import Parser from '../index.js';
|
||||
import Text from './misc/Text.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import MetadataBadge from './MetadataBadge.js';
|
||||
import Menu from './menus/Menu.js';
|
||||
|
||||
class VideoPrimaryInfo extends YTNode {
|
||||
@@ -10,16 +13,20 @@ class VideoPrimaryInfo extends YTNode {
|
||||
super_title_link: Text;
|
||||
view_count: Text;
|
||||
short_view_count: Text;
|
||||
badges: ObservedArray<MetadataBadge>;
|
||||
published: Text;
|
||||
menu;
|
||||
relative_date: Text;
|
||||
menu: Menu | null;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
this.super_title_link = new Text(data.superTitleLink);
|
||||
this.view_count = new Text(data.viewCount.videoViewCountRenderer.viewCount);
|
||||
this.short_view_count = new Text(data.viewCount.videoViewCountRenderer.shortViewCount);
|
||||
this.view_count = new Text(data.viewCount?.videoViewCountRenderer?.viewCount);
|
||||
this.short_view_count = new Text(data.viewCount?.videoViewCountRenderer?.shortViewCount);
|
||||
this.badges = Parser.parseArray(data.badges, MetadataBadge);
|
||||
this.published = new Text(data.dateText);
|
||||
this.relative_date = new Text(data.relativeDateText);
|
||||
this.menu = Parser.parseItem(data.videoActions, Menu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -1,5 +1,6 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AppendContinuationItemsAction extends YTNode {
|
||||
static type = 'AppendContinuationItemsAction';
|
||||
@@ -7,7 +8,7 @@ class AppendContinuationItemsAction extends YTNode {
|
||||
items;
|
||||
target: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.items = Parser.parse(data.continuationItems);
|
||||
this.target = data.target;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class OpenPopupAction extends YTNode {
|
||||
static type = 'OpenPopupAction';
|
||||
@@ -7,7 +8,7 @@ class OpenPopupAction extends YTNode {
|
||||
popup;
|
||||
popup_type;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.popup = Parser.parse(data.popup);
|
||||
this.popup_type = data.popupType;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import DataModelSection from './DataModelSection.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AnalyticsMainAppKeyMetrics extends YTNode {
|
||||
static type = 'AnalyticsMainAppKeyMetrics';
|
||||
@@ -7,7 +8,7 @@ class AnalyticsMainAppKeyMetrics extends YTNode {
|
||||
period: string;
|
||||
sections: DataModelSection[];
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.period = data.cardData.periodLabel;
|
||||
const metrics_data = data.cardData.sections[0].analyticsKeyMetricsData;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AnalyticsRoot extends YTNode {
|
||||
static type = 'AnalyticsRoot';
|
||||
@@ -19,7 +20,7 @@ class AnalyticsRoot extends YTNode {
|
||||
}[];
|
||||
}[];
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
const cards = data.analyticsTableCarouselData.data.tableCards;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
import NavigationEndpoint from '../NavigationEndpoint.js';
|
||||
|
||||
class AnalyticsShortsCarouselCard extends YTNode {
|
||||
@@ -11,7 +12,7 @@ class AnalyticsShortsCarouselCard extends YTNode {
|
||||
endpoint: NavigationEndpoint;
|
||||
}[];
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = data.title;
|
||||
this.shorts = data.shortsCarouselData.shorts.map((short: any) => ({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AnalyticsVideo extends YTNode {
|
||||
static type = 'AnalyticsVideo';
|
||||
@@ -13,7 +14,7 @@ class AnalyticsVideo extends YTNode {
|
||||
is_short: boolean;
|
||||
};
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = data.videoTitle;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Video from './AnalyticsVideo.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AnalyticsVodCarouselCard extends YTNode {
|
||||
static type = 'AnalyticsVodCarouselCard';
|
||||
@@ -8,7 +9,7 @@ class AnalyticsVodCarouselCard extends YTNode {
|
||||
videos: Video[] | null;
|
||||
no_data_message?: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = data.title;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class CtaGoToCreatorStudio extends YTNode {
|
||||
static type = 'CtaGoToCreatorStudio';
|
||||
@@ -6,7 +7,7 @@ class CtaGoToCreatorStudio extends YTNode {
|
||||
title: string;
|
||||
use_new_specs: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = data.buttonLabel;
|
||||
this.use_new_specs = data.useNewSpecs;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class DataModelSection extends YTNode {
|
||||
static type = 'DataModelSection';
|
||||
@@ -36,7 +37,7 @@ class DataModelSection extends YTNode {
|
||||
}
|
||||
};
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
this.title = data.title;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Text from '../misc/Text.js';
|
||||
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class StatRow extends YTNode {
|
||||
static type = 'StatRow';
|
||||
@@ -8,7 +9,7 @@ class StatRow extends YTNode {
|
||||
title: Text;
|
||||
contents: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
this.contents = new Text(data.contents);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AuthorCommentBadge extends YTNode {
|
||||
static type = 'AuthorCommentBadge';
|
||||
@@ -9,7 +10,7 @@ class AuthorCommentBadge extends YTNode {
|
||||
tooltip: string;
|
||||
style?: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
this.icon_type = data.icon?.iconType || null;
|
||||
|
||||
@@ -16,6 +16,7 @@ import type Actions from '../../../core/Actions.js';
|
||||
import Proto from '../../../proto/index.js';
|
||||
import { InnertubeError } from '../../../utils/Utils.js';
|
||||
import { YTNode, SuperParsedResult } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class Comment extends YTNode {
|
||||
static type = 'Comment';
|
||||
@@ -44,7 +45,7 @@ class Comment extends YTNode {
|
||||
is_pinned: boolean;
|
||||
is_member: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.content = new Text(data.contentText);
|
||||
this.published = new Text(data.publishedTimeText);
|
||||
|
||||
@@ -3,6 +3,7 @@ import type Button from '../Button.js';
|
||||
import type ToggleButton from '../ToggleButton.js';
|
||||
import type CreatorHeart from './CreatorHeart.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class CommentActionButtons extends YTNode {
|
||||
static type = 'CommentActionButtons';
|
||||
@@ -12,7 +13,7 @@ class CommentActionButtons extends YTNode {
|
||||
reply_button;
|
||||
creator_heart;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.like_button = Parser.parseItem<ToggleButton>(data.likeButton);
|
||||
this.dislike_button = Parser.parseItem<ToggleButton>(data.dislikeButton);
|
||||
|
||||
@@ -4,6 +4,7 @@ import Thumbnail from '../misc/Thumbnail.js';
|
||||
import type Button from '../Button.js';
|
||||
import type EmojiPicker from './EmojiPicker.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class CommentDialog extends YTNode {
|
||||
static type = 'CommentDialog';
|
||||
@@ -16,7 +17,7 @@ class CommentDialog extends YTNode {
|
||||
emoji_button: Button | null;
|
||||
emoji_picker: any | null;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.editable_text = new Text(data.editableText);
|
||||
this.author_thumbnail = Thumbnail.fromResponse(data.authorThumbnail);
|
||||
|
||||
@@ -2,7 +2,7 @@ import Parser from '../../index.js';
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import type Button from '../Button.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class CommentReplies extends YTNode {
|
||||
static type = 'CommentReplies';
|
||||
|
||||
@@ -12,7 +12,7 @@ class CommentReplies extends YTNode {
|
||||
view_replies_creator_thumbnail: Thumbnail[];
|
||||
has_channel_owner_replied: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.contents = Parser.parseArray(data.contents);
|
||||
this.view_replies = Parser.parseItem<Button>(data.viewReplies);
|
||||
|
||||
@@ -3,6 +3,7 @@ import Thumbnail from '../misc/Thumbnail.js';
|
||||
import Text from '../misc/Text.js';
|
||||
import type Button from '../Button.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class CommentReplyDialog extends YTNode {
|
||||
static type = 'CommentReplyDialog';
|
||||
@@ -13,7 +14,7 @@ class CommentReplyDialog extends YTNode {
|
||||
placeholder: Text;
|
||||
error_message: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.reply_button = Parser.parseItem<Button>(data.replyButton);
|
||||
this.cancel_button = Parser.parseItem<Button>(data.cancelButton);
|
||||
|
||||
@@ -3,6 +3,7 @@ import Thumbnail from '../misc/Thumbnail.js';
|
||||
import Text from '../misc/Text.js';
|
||||
import type Button from '../Button.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class CommentSimplebox extends YTNode {
|
||||
static type = 'CommentSimplebox';
|
||||
@@ -13,7 +14,7 @@ class CommentSimplebox extends YTNode {
|
||||
placeholder: Text;
|
||||
avatar_size;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.submit_button = Parser.parseItem<Button>(data.submitButton);
|
||||
this.cancel_button = Parser.parseItem<Button>(data.cancelButton);
|
||||
|
||||
@@ -7,6 +7,7 @@ import type Actions from '../../../core/Actions.js';
|
||||
import type { ObservedArray } from '../../helpers.js';
|
||||
import { InnertubeError } from '../../../utils/Utils.js';
|
||||
import { observe, YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class CommentThread extends YTNode {
|
||||
static type = 'CommentThread';
|
||||
@@ -20,7 +21,7 @@ class CommentThread extends YTNode {
|
||||
is_moderated_elq_comment: boolean;
|
||||
has_replies: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.comment = Parser.parseItem<Comment>(data.comment, Comment);
|
||||
this.comment_replies_data = Parser.parseItem<CommentReplies>(data.replies);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class CommentsEntryPointHeader extends YTNode {
|
||||
static type = 'CommentsEntryPointHeader';
|
||||
@@ -11,7 +12,7 @@ class CommentsEntryPointHeader extends YTNode {
|
||||
teaser_content?: Text;
|
||||
simplebox_placeholder?: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
|
||||
if (data.header) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import Text from '../misc/Text.js';
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import type SortFilterSubMenu from '../SortFilterSubMenu.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class CommentsHeader extends YTNode {
|
||||
static type = 'CommentsHeader';
|
||||
@@ -21,7 +22,7 @@ class CommentsHeader extends YTNode {
|
||||
is_custom_emoji: boolean;
|
||||
}[] | null;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = new Text(data.titleText);
|
||||
this.count = new Text(data.countText);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class CreatorHeart extends YTNode {
|
||||
static type = 'CreatorHeart';
|
||||
@@ -16,7 +17,7 @@ class CreatorHeart extends YTNode {
|
||||
is_enabled: boolean;
|
||||
kennedy_heart_color_string: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.creator_thumbnail = Thumbnail.fromResponse(data.creatorThumbnail);
|
||||
this.heart_icon_type = data.heartIcon?.iconType;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import Parser from '../../index.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class EmojiPicker extends YTNode {
|
||||
static type = 'EmojiPicker';
|
||||
@@ -19,7 +20,7 @@ class EmojiPicker extends YTNode {
|
||||
skin_tone_medium_dark_label: string;
|
||||
skin_tone_dark_label: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.id = data.id;
|
||||
this.categories = Parser.parseArray(data.categories);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class PdgCommentChip extends YTNode {
|
||||
static type = 'PdgCommentChip';
|
||||
@@ -11,7 +12,7 @@ class PdgCommentChip extends YTNode {
|
||||
};
|
||||
icon_type: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.text = new Text(data.chipText);
|
||||
this.color_pallette = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Thumbnail from '../misc/Thumbnail.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class SponsorCommentBadge extends YTNode {
|
||||
static type = 'SponsorCommentBadge';
|
||||
@@ -7,7 +8,7 @@ class SponsorCommentBadge extends YTNode {
|
||||
custom_badge: Thumbnail[];
|
||||
tooltip: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.custom_badge = Thumbnail.fromResponse(data.customBadge);
|
||||
this.tooltip = data.tooltip;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type LiveChatBanner from './items/LiveChatBanner.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AddBannerToLiveChatCommand extends YTNode {
|
||||
static type = 'AddBannerToLiveChatCommand';
|
||||
|
||||
banner: LiveChatBanner | null;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.banner = Parser.parseItem<LiveChatBanner>(data.bannerRenderer);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AddChatItemAction extends YTNode {
|
||||
static type = 'AddChatItemAction';
|
||||
@@ -7,7 +8,7 @@ class AddChatItemAction extends YTNode {
|
||||
item;
|
||||
client_id: string | null;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.item = Parser.parseItem(data.item);
|
||||
this.client_id = data.clientId || null;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
class AddLiveChatTickerItemAction extends YTNode {
|
||||
static type = 'AddLiveChatTickerItemAction';
|
||||
@@ -7,7 +8,7 @@ class AddLiveChatTickerItemAction extends YTNode {
|
||||
item;
|
||||
duration_sec: string; // TODO: check this assumption
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.item = Parser.parseItem(data.item);
|
||||
this.duration_sec = data.durationSec;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class DimChatItemAction extends YTNode {
|
||||
static type = 'DimChatItemAction';
|
||||
|
||||
client_assigned_id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.client_assigned_id = data.clientAssignedId;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class LiveChatActionPanel extends YTNode {
|
||||
static type = 'LiveChatActionPanel';
|
||||
|
||||
@@ -8,7 +8,7 @@ class LiveChatActionPanel extends YTNode {
|
||||
contents;
|
||||
target_id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.id = data.id;
|
||||
this.contents = Parser.parse(data.contents);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class MarkChatItemAsDeletedAction extends YTNode {
|
||||
static type = 'MarkChatItemAsDeletedAction';
|
||||
|
||||
deleted_state_message: Text;
|
||||
target_item_id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.deleted_state_message = new Text(data.deletedStateMessage);
|
||||
this.target_item_id = data.targetItemId;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class MarkChatItemsByAuthorAsDeletedAction extends YTNode {
|
||||
static type = 'MarkChatItemsByAuthorAsDeletedAction';
|
||||
|
||||
deleted_state_message: Text;
|
||||
channel_id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.deleted_state_message = new Text(data.deletedStateMessage);
|
||||
this.channel_id = data.externalChannelId;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class RemoveBannerForLiveChatCommand extends YTNode {
|
||||
static type = 'RemoveBannerForLiveChatCommand';
|
||||
|
||||
target_action_id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.target_action_id = data.targetActionId;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class RemoveChatItemAction extends YTNode {
|
||||
static type = 'RemoveChatItemAction';
|
||||
|
||||
target_item_id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.target_item_id = data.targetItemId;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class RemoveChatItemByAuthorAction extends YTNode {
|
||||
static type = 'RemoveChatItemByAuthorAction';
|
||||
|
||||
external_channel_id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.external_channel_id = data.externalChannelId;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class ReplaceChatItemAction extends YTNode {
|
||||
static type = 'ReplaceChatItemAction';
|
||||
|
||||
target_item_id: string;
|
||||
replacement_item;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.target_item_id = data.targetItemId;
|
||||
this.replacement_item = Parser.parseItem(data.replacementItem);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class ReplayChatItemAction extends YTNode {
|
||||
static type = 'ReplayChatItemAction';
|
||||
|
||||
actions;
|
||||
video_offset_time_msec: string; // Or number?
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.actions = Parser.parseArray(data.actions?.map((action: any) => {
|
||||
delete action.clickTrackingParams;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
import LiveChatActionPanel from './LiveChatActionPanel.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class ShowLiveChatActionPanelAction extends YTNode {
|
||||
static type = 'ShowLiveChatActionPanelAction';
|
||||
|
||||
panel_to_show: LiveChatActionPanel | null;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.panel_to_show = Parser.parseItem<LiveChatActionPanel>(data.panelToShow, LiveChatActionPanel);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class ShowLiveChatDialogAction extends YTNode {
|
||||
static type = 'ShowLiveChatDialogAction';
|
||||
|
||||
dialog;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.dialog = Parser.parseItem(data.dialog);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class ShowLiveChatTooltipCommand extends YTNode {
|
||||
static type = 'ShowLiveChatTooltipCommand';
|
||||
|
||||
tooltip;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.tooltip = Parser.parseItem(data.tooltip);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class UpdateDateTextAction extends YTNode {
|
||||
static type = 'UpdateDateTextAction';
|
||||
|
||||
date_text: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.date_text = new Text(data.dateText).toString();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class UpdateDescriptionAction extends YTNode {
|
||||
static type = 'UpdateDescriptionAction';
|
||||
|
||||
description: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.description = new Text(data.description);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Parser from '../../index.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class UpdateLiveChatPollAction extends YTNode {
|
||||
static type = 'UpdateLiveChatPollAction';
|
||||
|
||||
poll_to_update;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.poll_to_update = Parser.parseItem(data.pollToUpdate);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class UpdateTitleAction extends YTNode {
|
||||
static type = 'UpdateTitleAction';
|
||||
|
||||
title: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.title = new Text(data.title);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class UpdateToggleButtonTextAction extends YTNode {
|
||||
static type = 'UpdateToggleButtonTextAction';
|
||||
|
||||
@@ -8,7 +8,7 @@ class UpdateToggleButtonTextAction extends YTNode {
|
||||
toggled_text: string;
|
||||
button_id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.default_text = new Text(data.defaultText).toString();
|
||||
this.toggled_text = new Text(data.toggledText).toString();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Text from '../misc/Text.js';
|
||||
import { YTNode } from '../../helpers.js';
|
||||
|
||||
import type { RawNode } from '../../index.js';
|
||||
class UpdateViewershipAction extends YTNode {
|
||||
static type = 'UpdateViewershipAction';
|
||||
|
||||
@@ -8,7 +8,7 @@ class UpdateViewershipAction extends YTNode {
|
||||
extra_short_view_count: Text;
|
||||
is_live: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
const view_count_renderer = data.viewCount.videoViewCountRenderer;
|
||||
this.view_count = new Text(view_count_renderer.viewCount);
|
||||
|
||||
@@ -3,6 +3,7 @@ import Parser from '../../../index.js';
|
||||
import Button from '../../Button.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import NavigationEndpoint from '../../NavigationEndpoint.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
|
||||
class LiveChatAutoModMessage extends YTNode {
|
||||
static type = 'LiveChatAutoModMessage';
|
||||
@@ -15,7 +16,7 @@ class LiveChatAutoModMessage extends YTNode {
|
||||
timestamp: number;
|
||||
id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.menu_endpoint = new NavigationEndpoint(data.contextMenuEndpoint);
|
||||
this.moderation_buttons = Parser.parseArray<Button>(data.moderationButtons, [ Button ]);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { YTNode } from '../../../helpers.js';
|
||||
import Parser from '../../../index.js';
|
||||
import type LiveChatBannerHeader from './LiveChatBannerHeader.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
|
||||
class LiveChatBanner extends YTNode {
|
||||
static type = 'LiveChatBanner';
|
||||
@@ -13,7 +14,7 @@ class LiveChatBanner extends YTNode {
|
||||
is_stackable: boolean;
|
||||
background_type: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.header = Parser.parseItem<LiveChatBannerHeader>(data.header);
|
||||
this.contents = Parser.parseItem(data.contents);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { YTNode } from '../../../helpers.js';
|
||||
import Parser from '../../../index.js';
|
||||
import type Button from '../../Button.js';
|
||||
import Text from '../../misc/Text.js';
|
||||
import type { RawNode } from '../../../index.js';
|
||||
|
||||
class LiveChatBannerHeader extends YTNode {
|
||||
static type = 'LiveChatBannerHeader';
|
||||
@@ -10,7 +11,7 @@ class LiveChatBannerHeader extends YTNode {
|
||||
icon_type: string;
|
||||
context_menu_button: Button | null;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.text = new Text(data.text).toString();
|
||||
this.icon_type = data.icon?.iconType;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user