mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-13 09:32:12 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac9341c769 | ||
|
|
cac762569a | ||
|
|
9978ebf085 | ||
|
|
b036e2fcdc | ||
|
|
e37f42f41b | ||
|
|
883a023624 | ||
|
|
506834b253 | ||
|
|
87e7ef77eb | ||
|
|
27fdd8268a | ||
|
|
d4ea87b8b0 | ||
|
|
ec87eea20d | ||
|
|
e43ad202f4 | ||
|
|
104c36b450 | ||
|
|
f5d61d70f2 | ||
|
|
c76f5f478d | ||
|
|
49d1432b5a | ||
|
|
be157ef016 | ||
|
|
9f703203b6 | ||
|
|
516eeeff45 |
135
README.md
135
README.md
@@ -1,5 +1,3 @@
|
||||
<!-- Hi there, fellow coder :) -->
|
||||
|
||||
<!-- BADGE LINKS -->
|
||||
[npm]: https://www.npmjs.com/package/youtubei.js
|
||||
[versions]: https://www.npmjs.com/package/youtubei.js?activeTab=versions
|
||||
@@ -10,45 +8,27 @@
|
||||
|
||||
<!-- OTHER LINKS -->
|
||||
[project]: https://github.com/LuanRT/YouTube.js
|
||||
[twitter]: https://twitter.com/lrt_nooneknows
|
||||
[twitter]: https://twitter.com/thesciencephile
|
||||
[discord]: https://discord.gg/syDu7Yks54
|
||||
[nodejs]: https://nodejs.org
|
||||
|
||||
<!-- INTRODUCTION -->
|
||||
<h1 align=center>
|
||||
YouTube.js
|
||||
</h1>
|
||||
<h1 align=center>YouTube.js</h1>
|
||||
|
||||
<p align=center>
|
||||
<i>
|
||||
A full-featured wrapper around the InnerTube API, which is what YouTube itself uses.
|
||||
</i>
|
||||
</p>
|
||||
<p align=center>A full-featured wrapper around the InnerTube API, which is what YouTube itself uses</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/LuanRT/YouTube.js/issues">
|
||||
Report Bug
|
||||
</a>
|
||||
·
|
||||
<a href="https://github.com/LuanRT/YouTube.js/issues">
|
||||
Request Feature
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<!-- BADGES -->
|
||||
<div align="center">
|
||||
|
||||
[][actions]
|
||||
[][versions]
|
||||
[][versions]
|
||||
[][codefactor]
|
||||
[][npm]
|
||||
[][discord]
|
||||
[][say-thanks]
|
||||
<br>
|
||||
[][github-sponsors]
|
||||
|
||||
</div>
|
||||
|
||||
<!-- SPONSORS -->
|
||||
|
||||
<p align="center">
|
||||
<a><sub>Special thanks to:<sub></a>
|
||||
</p>
|
||||
@@ -57,7 +37,7 @@
|
||||
<body>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://serpapi.com/">
|
||||
<a href="https://serpapi.com/" target="_blank">
|
||||
<img width="80" alt="SerpApi" src="https://luanrt.is-a.dev/assets/img/serpapi.svg" />
|
||||
<br>
|
||||
<b>
|
||||
@@ -73,7 +53,6 @@
|
||||
|
||||
___
|
||||
|
||||
<!-- TABLE OF CONTENTS -->
|
||||
<details>
|
||||
<summary>Table of Contents</summary>
|
||||
<ol>
|
||||
@@ -95,23 +74,20 @@ ___
|
||||
<li><a href="#api">API</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#implementing-custom-functionality">Implementing custom functionality </a></li>
|
||||
<li><a href="#extending-the-library">Extending the library</a></li>
|
||||
<li><a href="#contributing">Contributing</a></li>
|
||||
<li><a href="#contributors">Contributors</a></li>
|
||||
<li><a href="#contact">Contact</a></li>
|
||||
<li><a href="#disclaimer">Disclaimer</a></li>
|
||||
<li><a href="#license">License</a></li>
|
||||
</ol>
|
||||
</details>
|
||||
|
||||
<!-- ABOUT THE PROJECT -->
|
||||
## About
|
||||
## Description
|
||||
|
||||
InnerTube is an API used across all YouTube clients, it was created to simplify[^1] the internal structure of the platform in a way that updates, tweaks, and experiments can be easily made. This library handles all the low-level communication with InnerTube, providing a simple, fast, and efficient way to interact with YouTube programmatically.
|
||||
InnerTube is an API used across all YouTube clients. It was created to simplify the deployment of new features and experiments across the platform[^1]. This library handles all the low-level communication with InnerTube, providing a simple, and efficient way to interact with YouTube programmatically. It is designed to emulate an actual client as closely as possible, including how API responses are parsed.
|
||||
|
||||
If you have any questions or need help, feel free to contact us on our chat server [here](https://discord.gg/syDu7Yks54).
|
||||
If you have any questions or need help, feel free to reach out to us on our [Discord server][discord] or open an issue [here](https://github.com/LuanRT/YouTube.js/issues).
|
||||
|
||||
<!-- GETTING STARTED -->
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
@@ -138,7 +114,6 @@ npm install github:LuanRT/YouTube.js
|
||||
|
||||
**TODO:** Deno install instructions (esm.sh possibly?)
|
||||
|
||||
<!-- USAGE -->
|
||||
## Usage
|
||||
Create an InnerTube instance:
|
||||
```ts
|
||||
@@ -245,7 +220,7 @@ const yt = await Innertube.create({
|
||||
* `Innertube`
|
||||
|
||||
<details>
|
||||
<summary>objects</summary>
|
||||
<summary>Objects</summary>
|
||||
<p>
|
||||
|
||||
* [.session](https://github.com/LuanRT/YouTube.js/blob/main/docs/API/session.md)
|
||||
@@ -260,7 +235,7 @@ const yt = await Innertube.create({
|
||||
|
||||
|
||||
<details>
|
||||
<summary>methods</summary>
|
||||
<summary>Methods</summary>
|
||||
<p>
|
||||
|
||||
* [.getInfo(video_id, client?)](#getinfo)
|
||||
@@ -597,48 +572,60 @@ Utility to call navigation endpoints.
|
||||
| --- | --- | --- |
|
||||
| endpoint | `NavigationEndpoint` | The target endpoint |
|
||||
| args? | `object` | Additional payload arguments |
|
||||
|
||||
## Extending the library
|
||||
|
||||
## Implementing custom functionality
|
||||
YouTube.js is completely modular and easy to extend. Almost all methods, classes, and utilities used internally are exposed and can be used to implement your own extensions without having to modify the library's source code.
|
||||
|
||||
Something cool about YouTube.js is that it is completely modular and easy to tinker with. Almost all methods, classes, and utilities used internally are exposed and can be used to implement your own extensions without having to modify the library's source code.
|
||||
|
||||
For example, you may want to call an endpoint directly, that can be achieved with the `Actions` class:
|
||||
For example, let's say we want to implement a method to retrieve video info manually. We can do that by using an instance of the `Actions` class:
|
||||
|
||||
```ts
|
||||
// ...
|
||||
import { Innertube } from 'youtubei.js';
|
||||
|
||||
const payload = {
|
||||
// ...
|
||||
videoId: 'jLTOuvBTLxA',
|
||||
client: 'YTMUSIC', // InnerTube client, can be ANDROID, YTMUSIC, YTMUSIC_ANDROID, WEB or TV_EMBEDDED
|
||||
parse: true // tells YouTube.js to parse the response, this is not sent to InnerTube.
|
||||
};
|
||||
(async () => {
|
||||
const yt = await Innertube.create();
|
||||
|
||||
const response = await yt.actions.execute('/player', payload);
|
||||
async function getVideoInfo(videoId: string) {
|
||||
const videoInfo = await yt.actions.execute('/player', {
|
||||
// anything added here will be merged with the default payload and sent to InnerTube.
|
||||
videoId,
|
||||
client: 'YTMUSIC', // InnerTube client, can be ANDROID, YTMUSIC, YTMUSIC_ANDROID, WEB or TV_EMBEDDED
|
||||
parse: true // tells YouTube.js to parse the response, this is not sent to InnerTube.
|
||||
});
|
||||
|
||||
console.info(response);
|
||||
return videoInfo;
|
||||
}
|
||||
|
||||
const videoInfo = await getVideoInfo('jLTOuvBTLxA');
|
||||
console.info(videoInfo);
|
||||
})();
|
||||
```
|
||||
|
||||
Or maybe there's an interesting `NavigationEndpoint` in a parsed response and we want to call it to see what happens:
|
||||
Or perhaps there's a `NavigationEndpoint` in a parsed response and we want to call it to see what happens:
|
||||
|
||||
```ts
|
||||
// ...
|
||||
const artist = await yt.music.getArtist('UC52ZqHVQz5OoGhvbWiRal6g');
|
||||
const albums = artist.sections[1].as(YTNodes.MusicCarouselShelf);
|
||||
import { Innertube, YTNodes } from 'youtubei.js';
|
||||
|
||||
// Say we have a button and want to “click” it
|
||||
const button = albums.as(YTNodes.MusicCarouselShelf).header?.more_content;
|
||||
|
||||
if (button) {
|
||||
// To do that, we can call its navigation endpoint:
|
||||
const page = await button.endpoint.call(yt.actions, { parse: true, client: 'YTMUSIC' });
|
||||
console.info(page);
|
||||
}
|
||||
(async () => {
|
||||
const yt = await Innertube.create();
|
||||
|
||||
const artist = await yt.music.getArtist('UC52ZqHVQz5OoGhvbWiRal6g');
|
||||
const albums = artist.sections[1].as(YTNodes.MusicCarouselShelf);
|
||||
|
||||
// Say we want to click the “More” button:
|
||||
const button = albums.as(YTNodes.MusicCarouselShelf).header?.more_content;
|
||||
|
||||
if (button) {
|
||||
// After making sure it exists, we can call its navigation endpoint:
|
||||
const page = await button.endpoint.call(yt.actions);
|
||||
console.info(page);
|
||||
}
|
||||
})();
|
||||
```
|
||||
|
||||
### Parser
|
||||
|
||||
If you're working on an extension for the library or just want to have nicely typed and sanitized InnerTube responses for a project then have a look at our powerful parser!
|
||||
YouTube.js' parser allows you to parse InnerTube responses and turn their nodes into strongly typed objects that can be easily manipulated. It also provides a set of utility methods that make working with InnerTube much easier.
|
||||
|
||||
Example:
|
||||
```ts
|
||||
@@ -656,10 +643,12 @@ const header = page.header?.item().as(YTNodes.MusicImmersiveHeader, YTNodes.Musi
|
||||
|
||||
console.info('Header:', header);
|
||||
|
||||
// The parser encapsulates all arrays in a proxy object.
|
||||
// A proxy intercepts access to the actual data, allowing
|
||||
// the parser to add type safety and many utility methods
|
||||
// that make working with InnerTube much easier.
|
||||
/**
|
||||
* The parser encapsulates all arrays in a proxy object.
|
||||
* A proxy intercepts access to the actual data, allowing
|
||||
* the parser to add type safety and many utility methods
|
||||
* that make working with InnerTube much easier.
|
||||
*/
|
||||
const tab = page.contents.item().as(YTNodes.SingleColumnBrowseResults).tabs.firstOfType(YTNodes.Tab);
|
||||
|
||||
|
||||
@@ -674,23 +663,21 @@ const sections = tab.content?.as(YTNodes.SectionList).contents.array().as(YTNode
|
||||
console.info('Sections:', sections);
|
||||
```
|
||||
|
||||
Detailed documentation can be found [here](https://github.com/LuanRT/YouTube.js/blob/main/src/parser).
|
||||
Documentation for the parser can be found [here](https://github.com/LuanRT/YouTube.js/blob/main/src/parser).
|
||||
|
||||
<!-- CONTRIBUTING -->
|
||||
## Contributing
|
||||
Contributions, issues, and feature requests are welcome.
|
||||
Feel free to check the [issues page](https://github.com/LuanRT/YouTube.js/issues) and our [guidelines](https://github.com/LuanRT/YouTube.js/blob/main/CONTRIBUTING.md) if you want to contribute.
|
||||
|
||||
<!-- CONTRIBUTORS -->
|
||||
## Contributors
|
||||
Thank you to all the wonderful people who have contributed to this project:
|
||||
<a href="https://github.com/LuanRT/YouTube.js/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=LuanRT/YouTube.js" />
|
||||
</a>
|
||||
|
||||
<!-- CONTACT -->
|
||||
## Contact
|
||||
|
||||
LuanRT - [@lrt_nooneknows][twitter] - luan.lrt4@gmail.com
|
||||
LuanRT - [@thesciencephile][twitter] - luan.lrt4@gmail.com
|
||||
|
||||
Project Link: [https://github.com/LuanRT/YouTube.js][project]
|
||||
|
||||
@@ -700,10 +687,8 @@ All trademarks, logos, and brand names are the property of their respective owne
|
||||
|
||||
Should you have any questions or concerns please contact me directly via email.
|
||||
|
||||
<!-- Footnotes -->
|
||||
[^1]: https://gizmodo.com/how-project-innertube-helped-pull-youtube-out-of-the-gu-1704946491
|
||||
|
||||
<!-- LICENSE -->
|
||||
## License
|
||||
Distributed under the [MIT](https://choosealicense.com/licenses/mit/) License.
|
||||
|
||||
|
||||
@@ -4,42 +4,42 @@ import { streamToIterable } from 'youtubei.js/dist/src/utils/Utils';
|
||||
|
||||
(async () => {
|
||||
const yt = await Innertube.create({ cache: new UniversalCache() });
|
||||
|
||||
|
||||
const search = await yt.music.search('No Copyright Background Music', { type: 'album' });
|
||||
|
||||
|
||||
if (!search.results)
|
||||
throw new Error('Filter "type" must be used');
|
||||
|
||||
|
||||
const album = await yt.music.getAlbum(search.results[0].id as string);
|
||||
|
||||
|
||||
if (!album.contents)
|
||||
throw new Error('Album appears to be empty');
|
||||
|
||||
console.info(`Album "${album.header.title.toString()}" by ${album.header.author?.name}`, '\n');
|
||||
|
||||
|
||||
console.info(`Album "${album.header?.title.toString()}" by ${album.header?.author?.name}`, '\n');
|
||||
|
||||
for (const song of album.contents) {
|
||||
const stream = await yt.download(song.id as string, {
|
||||
type: 'audio', // audio, video or audio+video
|
||||
quality: 'best', // best, bestefficiency, 144p, 240p, 480p, 720p and so on.
|
||||
format: 'mp4' // media container format
|
||||
});
|
||||
|
||||
|
||||
console.info(`Downloading ${song.title} (${song.id})`);
|
||||
|
||||
const dir = `./${album.header.title.toString()}`;
|
||||
|
||||
|
||||
const dir = `./${album.header?.title.toString()}`;
|
||||
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir);
|
||||
}
|
||||
|
||||
|
||||
const file = createWriteStream(`${dir}/${song.title?.replace(/\//g, '')}.m4a`);
|
||||
|
||||
|
||||
for await (const chunk of streamToIterable(stream)) {
|
||||
file.write(chunk);
|
||||
}
|
||||
|
||||
|
||||
console.info(`${song.id} - Done!`, '\n');
|
||||
}
|
||||
|
||||
console.info(`Downloaded ${album.header.song_count}!`);
|
||||
|
||||
console.info(`Downloaded ${album.header?.song_count}!`);
|
||||
})();
|
||||
412
package-lock.json
generated
412
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "youtubei.js",
|
||||
"version": "2.5.0",
|
||||
"version": "2.6.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "youtubei.js",
|
||||
"version": "2.5.0",
|
||||
"version": "2.6.0",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
@@ -58,30 +58,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz",
|
||||
"integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
|
||||
"integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz",
|
||||
"integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
|
||||
"integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.1.0",
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/generator": "^7.20.2",
|
||||
"@babel/generator": "^7.20.5",
|
||||
"@babel/helper-compilation-targets": "^7.20.0",
|
||||
"@babel/helper-module-transforms": "^7.20.2",
|
||||
"@babel/helpers": "^7.20.1",
|
||||
"@babel/parser": "^7.20.2",
|
||||
"@babel/helpers": "^7.20.5",
|
||||
"@babel/parser": "^7.20.5",
|
||||
"@babel/template": "^7.18.10",
|
||||
"@babel/traverse": "^7.20.1",
|
||||
"@babel/types": "^7.20.2",
|
||||
"@babel/traverse": "^7.20.5",
|
||||
"@babel/types": "^7.20.5",
|
||||
"convert-source-map": "^1.7.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.2",
|
||||
@@ -106,12 +106,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz",
|
||||
"integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz",
|
||||
"integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.20.2",
|
||||
"@babel/types": "^7.20.5",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"jsesc": "^2.5.1"
|
||||
},
|
||||
@@ -286,14 +286,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz",
|
||||
"integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==",
|
||||
"version": "7.20.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
|
||||
"integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.18.10",
|
||||
"@babel/traverse": "^7.20.1",
|
||||
"@babel/types": "^7.20.0"
|
||||
"@babel/traverse": "^7.20.5",
|
||||
"@babel/types": "^7.20.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -385,9 +385,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.20.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz",
|
||||
"integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz",
|
||||
"integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -573,19 +573,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz",
|
||||
"integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz",
|
||||
"integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/generator": "^7.20.1",
|
||||
"@babel/generator": "^7.20.5",
|
||||
"@babel/helper-environment-visitor": "^7.18.9",
|
||||
"@babel/helper-function-name": "^7.19.0",
|
||||
"@babel/helper-hoist-variables": "^7.18.6",
|
||||
"@babel/helper-split-export-declaration": "^7.18.6",
|
||||
"@babel/parser": "^7.20.1",
|
||||
"@babel/types": "^7.20.0",
|
||||
"@babel/parser": "^7.20.5",
|
||||
"@babel/types": "^7.20.5",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -603,9 +603,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz",
|
||||
"integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz",
|
||||
"integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.19.4",
|
||||
@@ -1284,9 +1284,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@sinonjs/commons": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz",
|
||||
"integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==",
|
||||
"version": "1.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz",
|
||||
"integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"type-detect": "4.0.8"
|
||||
@@ -1334,9 +1334,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__traverse": {
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz",
|
||||
"integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==",
|
||||
"version": "7.18.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz",
|
||||
"integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.3.0"
|
||||
@@ -1416,9 +1416,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
"version": "17.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.14.tgz",
|
||||
"integrity": "sha512-9Pj7abXoW1RSTcZaL2Hk6G2XyLMlp5ECdVC/Zf2p/KBjC3srijLGgRAXOBjtFrJoIrvxdTKyKDA14bEcbxBaWw==",
|
||||
"version": "17.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.17.tgz",
|
||||
"integrity": "sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
@@ -1431,14 +1431,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.44.0.tgz",
|
||||
"integrity": "sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.0.tgz",
|
||||
"integrity": "sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.44.0",
|
||||
"@typescript-eslint/type-utils": "5.44.0",
|
||||
"@typescript-eslint/utils": "5.44.0",
|
||||
"@typescript-eslint/scope-manager": "5.46.0",
|
||||
"@typescript-eslint/type-utils": "5.46.0",
|
||||
"@typescript-eslint/utils": "5.46.0",
|
||||
"debug": "^4.3.4",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
@@ -1464,14 +1464,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz",
|
||||
"integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz",
|
||||
"integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.44.0",
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/typescript-estree": "5.44.0",
|
||||
"@typescript-eslint/scope-manager": "5.46.0",
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"@typescript-eslint/typescript-estree": "5.46.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1491,13 +1491,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz",
|
||||
"integrity": "sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz",
|
||||
"integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/visitor-keys": "5.44.0"
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"@typescript-eslint/visitor-keys": "5.46.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -1508,13 +1508,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.44.0.tgz",
|
||||
"integrity": "sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.0.tgz",
|
||||
"integrity": "sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "5.44.0",
|
||||
"@typescript-eslint/utils": "5.44.0",
|
||||
"@typescript-eslint/typescript-estree": "5.46.0",
|
||||
"@typescript-eslint/utils": "5.46.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
},
|
||||
@@ -1535,9 +1535,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.44.0.tgz",
|
||||
"integrity": "sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz",
|
||||
"integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -1548,13 +1548,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz",
|
||||
"integrity": "sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz",
|
||||
"integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/visitor-keys": "5.44.0",
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"@typescript-eslint/visitor-keys": "5.46.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -1575,16 +1575,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.44.0.tgz",
|
||||
"integrity": "sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.0.tgz",
|
||||
"integrity": "sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.44.0",
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/typescript-estree": "5.44.0",
|
||||
"@typescript-eslint/scope-manager": "5.46.0",
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"@typescript-eslint/typescript-estree": "5.46.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"semver": "^7.3.7"
|
||||
@@ -1601,12 +1601,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz",
|
||||
"integrity": "sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz",
|
||||
"integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1941,9 +1941,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001434",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
|
||||
"integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==",
|
||||
"version": "1.0.30001439",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz",
|
||||
"integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2654,9 +2654,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz",
|
||||
"integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==",
|
||||
"version": "8.29.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz",
|
||||
"integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint/eslintrc": "^1.3.3",
|
||||
@@ -2975,9 +2975,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
|
||||
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
|
||||
"integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
@@ -3157,9 +3157,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
|
||||
"integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz",
|
||||
"integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
@@ -3270,9 +3270,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
|
||||
"integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
@@ -5314,9 +5314,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
|
||||
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@@ -5332,9 +5332,9 @@
|
||||
"integrity": "sha512-o0QVGuFg24FK765Qdd5kk0zU/U4dEsCtN/GSiwNI9i8xsSVtjIAOdTaVhLwZ1nrbWxFVMxNDDl+9fednsOMsBw=="
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.12.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.12.0.tgz",
|
||||
"integrity": "sha512-zMLamCG62PGjd9HHMpo05bSLvvwWOZgGeiWlN/vlqu3+lRo3elxktVGEyLMX+IO7c2eflLjcW74AlkhEZm15mg==",
|
||||
"version": "5.14.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz",
|
||||
"integrity": "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==",
|
||||
"dependencies": {
|
||||
"busboy": "^1.6.0"
|
||||
},
|
||||
@@ -5536,27 +5536,27 @@
|
||||
}
|
||||
},
|
||||
"@babel/compat-data": {
|
||||
"version": "7.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz",
|
||||
"integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
|
||||
"integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/core": {
|
||||
"version": "7.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz",
|
||||
"integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
|
||||
"integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@ampproject/remapping": "^2.1.0",
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/generator": "^7.20.2",
|
||||
"@babel/generator": "^7.20.5",
|
||||
"@babel/helper-compilation-targets": "^7.20.0",
|
||||
"@babel/helper-module-transforms": "^7.20.2",
|
||||
"@babel/helpers": "^7.20.1",
|
||||
"@babel/parser": "^7.20.2",
|
||||
"@babel/helpers": "^7.20.5",
|
||||
"@babel/parser": "^7.20.5",
|
||||
"@babel/template": "^7.18.10",
|
||||
"@babel/traverse": "^7.20.1",
|
||||
"@babel/types": "^7.20.2",
|
||||
"@babel/traverse": "^7.20.5",
|
||||
"@babel/types": "^7.20.5",
|
||||
"convert-source-map": "^1.7.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.2",
|
||||
@@ -5573,12 +5573,12 @@
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.20.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz",
|
||||
"integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz",
|
||||
"integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.20.2",
|
||||
"@babel/types": "^7.20.5",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"jsesc": "^2.5.1"
|
||||
},
|
||||
@@ -5709,14 +5709,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helpers": {
|
||||
"version": "7.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz",
|
||||
"integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==",
|
||||
"version": "7.20.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
|
||||
"integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/template": "^7.18.10",
|
||||
"@babel/traverse": "^7.20.1",
|
||||
"@babel/types": "^7.20.0"
|
||||
"@babel/traverse": "^7.20.5",
|
||||
"@babel/types": "^7.20.5"
|
||||
}
|
||||
},
|
||||
"@babel/highlight": {
|
||||
@@ -5789,9 +5789,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.20.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz",
|
||||
"integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz",
|
||||
"integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/plugin-syntax-async-generators": {
|
||||
@@ -5923,19 +5923,19 @@
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz",
|
||||
"integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz",
|
||||
"integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/generator": "^7.20.1",
|
||||
"@babel/generator": "^7.20.5",
|
||||
"@babel/helper-environment-visitor": "^7.18.9",
|
||||
"@babel/helper-function-name": "^7.19.0",
|
||||
"@babel/helper-hoist-variables": "^7.18.6",
|
||||
"@babel/helper-split-export-declaration": "^7.18.6",
|
||||
"@babel/parser": "^7.20.1",
|
||||
"@babel/types": "^7.20.0",
|
||||
"@babel/parser": "^7.20.5",
|
||||
"@babel/types": "^7.20.5",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -5949,9 +5949,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz",
|
||||
"integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==",
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz",
|
||||
"integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-string-parser": "^7.19.4",
|
||||
@@ -6480,9 +6480,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@sinonjs/commons": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz",
|
||||
"integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==",
|
||||
"version": "1.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz",
|
||||
"integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-detect": "4.0.8"
|
||||
@@ -6530,9 +6530,9 @@
|
||||
}
|
||||
},
|
||||
"@types/babel__traverse": {
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz",
|
||||
"integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==",
|
||||
"version": "7.18.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz",
|
||||
"integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.3.0"
|
||||
@@ -6612,9 +6612,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "17.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.14.tgz",
|
||||
"integrity": "sha512-9Pj7abXoW1RSTcZaL2Hk6G2XyLMlp5ECdVC/Zf2p/KBjC3srijLGgRAXOBjtFrJoIrvxdTKyKDA14bEcbxBaWw==",
|
||||
"version": "17.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.17.tgz",
|
||||
"integrity": "sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
@@ -6627,14 +6627,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.44.0.tgz",
|
||||
"integrity": "sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.0.tgz",
|
||||
"integrity": "sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.44.0",
|
||||
"@typescript-eslint/type-utils": "5.44.0",
|
||||
"@typescript-eslint/utils": "5.44.0",
|
||||
"@typescript-eslint/scope-manager": "5.46.0",
|
||||
"@typescript-eslint/type-utils": "5.46.0",
|
||||
"@typescript-eslint/utils": "5.46.0",
|
||||
"debug": "^4.3.4",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
@@ -6644,53 +6644,53 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz",
|
||||
"integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz",
|
||||
"integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.44.0",
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/typescript-estree": "5.44.0",
|
||||
"@typescript-eslint/scope-manager": "5.46.0",
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"@typescript-eslint/typescript-estree": "5.46.0",
|
||||
"debug": "^4.3.4"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz",
|
||||
"integrity": "sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz",
|
||||
"integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/visitor-keys": "5.44.0"
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"@typescript-eslint/visitor-keys": "5.46.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/type-utils": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.44.0.tgz",
|
||||
"integrity": "sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.0.tgz",
|
||||
"integrity": "sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/typescript-estree": "5.44.0",
|
||||
"@typescript-eslint/utils": "5.44.0",
|
||||
"@typescript-eslint/typescript-estree": "5.46.0",
|
||||
"@typescript-eslint/utils": "5.46.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.44.0.tgz",
|
||||
"integrity": "sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz",
|
||||
"integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz",
|
||||
"integrity": "sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz",
|
||||
"integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/visitor-keys": "5.44.0",
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"@typescript-eslint/visitor-keys": "5.46.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -6699,28 +6699,28 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.44.0.tgz",
|
||||
"integrity": "sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.0.tgz",
|
||||
"integrity": "sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.44.0",
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/typescript-estree": "5.44.0",
|
||||
"@typescript-eslint/scope-manager": "5.46.0",
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"@typescript-eslint/typescript-estree": "5.46.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"semver": "^7.3.7"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz",
|
||||
"integrity": "sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==",
|
||||
"version": "5.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz",
|
||||
"integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/types": "5.46.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
}
|
||||
},
|
||||
@@ -6959,9 +6959,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001434",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
|
||||
"integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==",
|
||||
"version": "1.0.30001439",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz",
|
||||
"integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
@@ -7379,9 +7379,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"eslint": {
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz",
|
||||
"integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==",
|
||||
"version": "8.29.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz",
|
||||
"integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint/eslintrc": "^1.3.3",
|
||||
@@ -7628,9 +7628,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"fastq": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
|
||||
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
|
||||
"integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"reusify": "^1.0.4"
|
||||
@@ -7755,9 +7755,9 @@
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
|
||||
"integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz",
|
||||
"integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
@@ -7848,9 +7848,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
|
||||
"integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==",
|
||||
"dev": true
|
||||
},
|
||||
"import-fresh": {
|
||||
@@ -9349,9 +9349,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
|
||||
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"dev": true
|
||||
},
|
||||
"uhyphen": {
|
||||
@@ -9360,9 +9360,9 @@
|
||||
"integrity": "sha512-o0QVGuFg24FK765Qdd5kk0zU/U4dEsCtN/GSiwNI9i8xsSVtjIAOdTaVhLwZ1nrbWxFVMxNDDl+9fednsOMsBw=="
|
||||
},
|
||||
"undici": {
|
||||
"version": "5.12.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.12.0.tgz",
|
||||
"integrity": "sha512-zMLamCG62PGjd9HHMpo05bSLvvwWOZgGeiWlN/vlqu3+lRo3elxktVGEyLMX+IO7c2eflLjcW74AlkhEZm15mg==",
|
||||
"version": "5.14.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz",
|
||||
"integrity": "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==",
|
||||
"requires": {
|
||||
"busboy": "^1.6.0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "youtubei.js",
|
||||
"version": "2.5.0",
|
||||
"version": "2.6.0",
|
||||
"description": "Full-featured wrapper around YouTube's private API. Supports YouTube, YouTube Music and YouTube Studio (WIP).",
|
||||
"main": "./dist/index.js",
|
||||
"browser": "./bundle/browser.js",
|
||||
|
||||
@@ -11,9 +11,6 @@ import Playlist from '../parser/ytmusic/Playlist';
|
||||
import Recap from '../parser/ytmusic/Recap';
|
||||
|
||||
import Tab from '../parser/classes/Tab';
|
||||
import Tabbed from '../parser/classes/Tabbed';
|
||||
import SingleColumnMusicWatchNextResults from '../parser/classes/SingleColumnMusicWatchNextResults';
|
||||
import WatchNextTabbedResults from '../parser/classes/WatchNextTabbedResults';
|
||||
import SectionList from '../parser/classes/SectionList';
|
||||
|
||||
import Message from '../parser/classes/Message';
|
||||
@@ -235,13 +232,9 @@ class Music {
|
||||
parse: true
|
||||
});
|
||||
|
||||
const tabs = data.contents.item()
|
||||
.as(SingleColumnMusicWatchNextResults).contents.item()
|
||||
.as(Tabbed).contents.item()
|
||||
.as(WatchNextTabbedResults)
|
||||
.tabs.array().as(Tab);
|
||||
const tabs = data.contents_memo.getType(Tab);
|
||||
|
||||
const tab = tabs.get({ title: 'Up next' });
|
||||
const tab = tabs?.[0];
|
||||
|
||||
if (!tab)
|
||||
throw new InnertubeError('Could not find target tab.');
|
||||
@@ -287,20 +280,16 @@ class Music {
|
||||
parse: true
|
||||
});
|
||||
|
||||
const tabs = data.contents.item()
|
||||
.as(SingleColumnMusicWatchNextResults).contents.item()
|
||||
.as(Tabbed).contents.item()
|
||||
.as(WatchNextTabbedResults)
|
||||
.tabs.array().as(Tab);
|
||||
const tabs = data.contents_memo.getType(Tab);
|
||||
|
||||
const tab = tabs.get({ title: 'Related' });
|
||||
const tab = tabs?.matchCondition((tab) => tab.endpoint.payload.browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType === 'MUSIC_PAGE_TYPE_TRACK_RELATED');
|
||||
|
||||
if (!tab)
|
||||
throw new InnertubeError('Could not find target tab.');
|
||||
|
||||
const page = await tab.endpoint.call(this.#actions, { client: 'YTMUSIC', parse: true });
|
||||
|
||||
const shelves = page.contents.item().as(SectionList).contents.array().as(MusicCarouselShelf, MusicDescriptionShelf);
|
||||
const shelves = page.contents.item().as(SectionList).contents.as(MusicCarouselShelf, MusicDescriptionShelf);
|
||||
|
||||
return shelves;
|
||||
}
|
||||
@@ -318,13 +307,9 @@ class Music {
|
||||
parse: true
|
||||
});
|
||||
|
||||
const tabs = data.contents.item()
|
||||
.as(SingleColumnMusicWatchNextResults).contents.item()
|
||||
.as(Tabbed).contents.item()
|
||||
.as(WatchNextTabbedResults)
|
||||
.tabs.array().as(Tab);
|
||||
const tabs = data.contents_memo.getType(Tab);
|
||||
|
||||
const tab = tabs.get({ title: 'Lyrics' });
|
||||
const tab = tabs?.matchCondition((tab) => tab.endpoint.payload.browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType === 'MUSIC_PAGE_TYPE_TRACK_LYRICS');
|
||||
|
||||
if (!tab)
|
||||
throw new InnertubeError('Could not find target tab.');
|
||||
@@ -334,7 +319,7 @@ class Music {
|
||||
if (page.contents.item().key('type').string() === 'Message')
|
||||
throw new InnertubeError(page.contents.item().as(Message).text, video_id);
|
||||
|
||||
const section_list = page.contents.item().as(SectionList).contents.array();
|
||||
const section_list = page.contents.item().as(SectionList).contents;
|
||||
return section_list.firstOfType(MusicDescriptionShelf);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,13 +167,13 @@ export default class Player {
|
||||
|
||||
static extractSigSourceCode(data: string) {
|
||||
const calls = getStringBetweenStrings(data, 'function(a){a=a.split("")', 'return a.join("")}');
|
||||
const obj_name = calls?.split('.')?.[0]?.replace(';', '');
|
||||
const functions = getStringBetweenStrings(data, `var ${obj_name}=`, '};');
|
||||
const obj_name = calls?.split(/\.|\[/)?.[0]?.replace(';', '')?.trim();
|
||||
const functions = getStringBetweenStrings(data, `var ${obj_name}={`, '};');
|
||||
|
||||
if (!functions || !calls)
|
||||
console.warn(new PlayerError('Failed to extract signature decipher algorithm'));
|
||||
|
||||
return `function descramble_sig(a) { a = a.split(""); let ${obj_name}=${functions}}${calls} return a.join("") } descramble_sig(sig);`;
|
||||
return `function descramble_sig(a) { a = a.split(""); let ${obj_name}={${functions}}${calls} return a.join("") } descramble_sig(sig);`;
|
||||
}
|
||||
|
||||
static extractNSigSourceCode(data: string) {
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface Context {
|
||||
|
||||
export interface SessionOptions {
|
||||
lang?: string;
|
||||
location?: string;
|
||||
account_index?: number;
|
||||
device_category?: DeviceCategory;
|
||||
client_type?: ClientType;
|
||||
@@ -112,6 +113,7 @@ export default class Session extends EventEmitterLike {
|
||||
static async create(options: SessionOptions = {}) {
|
||||
const { context, api_key, api_version, account_index } = await Session.getSessionData(
|
||||
options.lang,
|
||||
options.location,
|
||||
options.account_index,
|
||||
options.device_category,
|
||||
options.client_type,
|
||||
@@ -123,6 +125,7 @@ export default class Session extends EventEmitterLike {
|
||||
|
||||
static async getSessionData(
|
||||
lang = 'en-US',
|
||||
location = '',
|
||||
account_index = 0,
|
||||
device_category: DeviceCategory = 'desktop',
|
||||
client_name: ClientType = ClientType.WEB,
|
||||
@@ -157,7 +160,7 @@ export default class Session extends EventEmitterLike {
|
||||
const context: Context = {
|
||||
client: {
|
||||
hl: device_info[0],
|
||||
gl: device_info[2],
|
||||
gl: location || device_info[2],
|
||||
remoteHost: device_info[3],
|
||||
screenDensityFloat: 1,
|
||||
screenHeightPoints: 720,
|
||||
|
||||
@@ -17,7 +17,7 @@ class TabbedFeed extends Feed {
|
||||
return this.#tabs.map((tab) => tab.title.toString());
|
||||
}
|
||||
|
||||
async getTab(title: string) {
|
||||
async getTabByName(title: string) {
|
||||
const tab = this.#tabs.find((tab) => tab.title.toLowerCase() === title.toLowerCase());
|
||||
|
||||
if (!tab)
|
||||
@@ -28,8 +28,19 @@ class TabbedFeed extends Feed {
|
||||
|
||||
const response = await tab.endpoint.call(this.#actions);
|
||||
|
||||
if (!response)
|
||||
throw new InnertubeError('Failed to call endpoint');
|
||||
return new TabbedFeed(this.#actions, response.data, false);
|
||||
}
|
||||
|
||||
async getTabByURL(url: string) {
|
||||
const tab = this.#tabs.find((tab) => tab.endpoint.metadata.url?.split('/').pop() === url);
|
||||
|
||||
if (!tab)
|
||||
throw new InnertubeError(`Tab "${url}" not found`);
|
||||
|
||||
if (tab.selected)
|
||||
return this;
|
||||
|
||||
const response = await tab.endpoint.call(this.#actions);
|
||||
|
||||
return new TabbedFeed(this.#actions, response.data, false);
|
||||
}
|
||||
|
||||
@@ -6,17 +6,21 @@ import { YTNode } from '../helpers';
|
||||
class Button extends YTNode {
|
||||
static type = 'Button';
|
||||
|
||||
text: string;
|
||||
text?: string;
|
||||
|
||||
label;
|
||||
tooltip;
|
||||
icon_type;
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
icon_type?: string;
|
||||
is_disabled?: boolean;
|
||||
|
||||
endpoint: NavigationEndpoint;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.text = new Text(data.text).toString();
|
||||
|
||||
if (data.text) {
|
||||
this.text = new Text(data.text).toString();
|
||||
}
|
||||
|
||||
if (data.accessibility?.label) {
|
||||
this.label = data.accessibility?.label;
|
||||
@@ -30,6 +34,10 @@ class Button extends YTNode {
|
||||
this.icon_type = data.icon?.iconType;
|
||||
}
|
||||
|
||||
if (Reflect.has(data, 'isDisabled')) {
|
||||
this.is_disabled = data.isDisabled;
|
||||
}
|
||||
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint || data.serviceEndpoint || data.command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
import Parser from '../index';
|
||||
import Author from './misc/Author';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
|
||||
import type Button from './Button';
|
||||
import type ChannelHeaderLinks from './ChannelHeaderLinks';
|
||||
import type SubscribeButton from './SubscribeButton';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class C4TabbedHeader extends YTNode {
|
||||
static type = 'C4TabbedHeader';
|
||||
|
||||
author;
|
||||
banner;
|
||||
tv_banner;
|
||||
mobile_banner;
|
||||
subscribers;
|
||||
sponsor_button;
|
||||
subscribe_button;
|
||||
header_links;
|
||||
author: Author;
|
||||
banner: Thumbnail[];
|
||||
tv_banner: Thumbnail[];
|
||||
mobile_banner: Thumbnail[];
|
||||
subscribers: Text;
|
||||
videos_count: Text;
|
||||
sponsor_button: Button | null;
|
||||
subscribe_button: SubscribeButton | null;
|
||||
header_links: ChannelHeaderLinks | null;
|
||||
channel_handle: Text;
|
||||
channel_id: string;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
@@ -23,13 +31,16 @@ class C4TabbedHeader extends YTNode {
|
||||
navigationEndpoint: data.navigationEndpoint
|
||||
}, data.badges, data.avatar);
|
||||
|
||||
this.banner = data.banner ? Thumbnail.fromResponse(data.banner) : [];
|
||||
this.tv_banner = data.tvBanner ? Thumbnail.fromResponse(data.tvBanner) : [];
|
||||
this.mobile_banner = data.mobileBanner ? Thumbnail.fromResponse(data.mobileBanner) : [];
|
||||
this.banner = Thumbnail.fromResponse(data.banner);
|
||||
this.tv_banner = Thumbnail.fromResponse(data.tvBanner);
|
||||
this.mobile_banner = Thumbnail.fromResponse(data.mobileBanner);
|
||||
this.subscribers = new Text(data.subscriberCountText);
|
||||
this.sponsor_button = data.sponsorButton ? Parser.parseItem(data.sponsorButton) : undefined;
|
||||
this.subscribe_button = data.subscribeButton ? Parser.parseItem(data.subscribeButton) : undefined;
|
||||
this.header_links = data.headerLinks ? Parser.parse(data.headerLinks) : undefined;
|
||||
this.videos_count = new Text(data.videosCountText);
|
||||
this.sponsor_button = Parser.parseItem<Button>(data.sponsorButton);
|
||||
this.subscribe_button = Parser.parseItem<SubscribeButton>(data.subscribeButton);
|
||||
this.header_links = Parser.parseItem<ChannelHeaderLinks>(data.headerLinks);
|
||||
this.channel_handle = new Text(data.channelHandleText);
|
||||
this.channel_id = data.channelId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Parser from '..';
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
export default class CarouselHeader extends YTNode {
|
||||
class CarouselHeader extends YTNode {
|
||||
static type = 'CarouselHeader';
|
||||
|
||||
contents: YTNode[];
|
||||
@@ -10,4 +10,6 @@ export default class CarouselHeader extends YTNode {
|
||||
super();
|
||||
this.contents = Parser.parseArray(data.contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default CarouselHeader;
|
||||
@@ -3,7 +3,7 @@ import { YTNode } from '../helpers';
|
||||
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
|
||||
export default class CarouselItem extends YTNode {
|
||||
class CarouselItem extends YTNode {
|
||||
static type = 'CarouselItem';
|
||||
|
||||
items: YTNode[];
|
||||
@@ -20,4 +20,6 @@ export default class CarouselItem extends YTNode {
|
||||
this.pagination_thumbnails = Thumbnail.fromResponse(data.paginationThumbnails);
|
||||
this.paginator_alignment = data.paginatorAlignment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default CarouselItem;
|
||||
@@ -1,6 +1,11 @@
|
||||
import Parser from '..';
|
||||
|
||||
import Text from './misc/Text';
|
||||
import Author from './misc/Author';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import Text from './misc/Text';
|
||||
|
||||
import type SubscribeButton from './SubscribeButton';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class Channel extends YTNode {
|
||||
@@ -10,7 +15,10 @@ class Channel extends YTNode {
|
||||
author: Author;
|
||||
subscribers: Text;
|
||||
videos: Text;
|
||||
long_byline: Text;
|
||||
short_byline: Text;
|
||||
endpoint: NavigationEndpoint;
|
||||
subscribe_button: SubscribeButton | null;
|
||||
description_snippet: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
@@ -22,9 +30,13 @@ class Channel extends YTNode {
|
||||
navigationEndpoint: data.navigationEndpoint
|
||||
}, data.ownerBadges, data.thumbnail);
|
||||
|
||||
// TODO: subscriberCountText is now the channel's handle and videoCountText is the subscriber count. Why haven't they renamed the properties?
|
||||
this.subscribers = new Text(data.subscriberCountText);
|
||||
this.videos = new Text(data.videoCountText);
|
||||
this.long_byline = new Text(data.longBylineText);
|
||||
this.short_byline = new Text(data.shortBylineText);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.subscribe_button = Parser.parseItem<SubscribeButton>(data.subscribeButton);
|
||||
this.description_snippet = new Text(data.descriptionSnippet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import Parser from '../index';
|
||||
|
||||
import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import Text from './misc/Text';
|
||||
import Parser from '../index';
|
||||
|
||||
import type Button from './Button';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class ChannelAboutFullMetadata extends YTNode {
|
||||
@@ -11,13 +15,20 @@ class ChannelAboutFullMetadata extends YTNode {
|
||||
name: Text;
|
||||
avatar: Thumbnail[];
|
||||
canonical_channel_url: string;
|
||||
|
||||
primary_links: {
|
||||
endpoint: NavigationEndpoint;
|
||||
icon: Thumbnail[];
|
||||
title: Text;
|
||||
}[];
|
||||
|
||||
views: Text;
|
||||
joined: Text;
|
||||
description: Text;
|
||||
email_reveal: NavigationEndpoint;
|
||||
can_reveal_email: boolean;
|
||||
country: Text;
|
||||
buttons;
|
||||
buttons: Button[];
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
@@ -25,13 +36,20 @@ class ChannelAboutFullMetadata extends YTNode {
|
||||
this.name = new Text(data.title);
|
||||
this.avatar = Thumbnail.fromResponse(data.avatar);
|
||||
this.canonical_channel_url = data.canonicalChannelUrl;
|
||||
|
||||
this.primary_links = data.primaryLinks.map((link: any) => ({
|
||||
endpoint: new NavigationEndpoint(link.navigationEndpoint),
|
||||
icon: Thumbnail.fromResponse(link.icon),
|
||||
title: new Text(link.title)
|
||||
}));
|
||||
|
||||
this.views = new Text(data.viewCountText);
|
||||
this.joined = new Text(data.joinedDateText);
|
||||
this.description = new Text(data.description);
|
||||
this.email_reveal = new NavigationEndpoint(data.onBusinessEmailRevealClickCommand);
|
||||
this.can_reveal_email = !data.signInForBusinessEmail;
|
||||
this.country = new Text(data.country);
|
||||
this.buttons = Parser.parse(data.actionButtons);
|
||||
this.buttons = Parser.parseArray<Button>(data.actionButtons);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
|
||||
export default class CompactStation extends YTNode {
|
||||
class CompactStation extends YTNode {
|
||||
static type = 'CompactStation';
|
||||
|
||||
title: Text;
|
||||
@@ -22,4 +22,6 @@ export default class CompactStation extends YTNode {
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default CompactStation;
|
||||
@@ -4,6 +4,8 @@ import Author from './misc/Author';
|
||||
import { timeToSeconds } from '../../utils/Utils';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import type Menu from './menus/Menu';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class CompactVideo extends YTNode {
|
||||
@@ -25,7 +27,7 @@ class CompactVideo extends YTNode {
|
||||
|
||||
thumbnail_overlays;
|
||||
endpoint: NavigationEndpoint;
|
||||
menu;
|
||||
menu: Menu | null;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
@@ -43,9 +45,9 @@ class CompactVideo extends YTNode {
|
||||
seconds: timeToSeconds(new Text(data.lengthText).toString())
|
||||
};
|
||||
|
||||
this.thumbnail_overlays = Parser.parse(data.thumbnailOverlays);
|
||||
this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.menu = Parser.parse(data.menu);
|
||||
this.menu = Parser.parseItem<Menu>(data.menu);
|
||||
}
|
||||
|
||||
get best_thumbnail() {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { YTNode } from '../helpers';
|
||||
import Text from './misc/Text';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
|
||||
export default class DefaultPromoPanel extends YTNode {
|
||||
class DefaultPromoPanel extends YTNode {
|
||||
static type = 'DefaultPromoPanel';
|
||||
|
||||
title: Text;
|
||||
@@ -33,4 +33,6 @@ export default class DefaultPromoPanel extends YTNode {
|
||||
this.metadata_order = data.metadataOrder;
|
||||
this.panel_layout = data.panelLayout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DefaultPromoPanel;
|
||||
41
src/parser/classes/ExpandableMetadata.ts
Normal file
41
src/parser/classes/ExpandableMetadata.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import Parser from '..';
|
||||
|
||||
import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
|
||||
import Button from './Button';
|
||||
import HorizontalCardList from './HorizontalCardList';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class ExpandableMetadata extends YTNode {
|
||||
static type = 'ExpandableMetadata';
|
||||
|
||||
header: {
|
||||
collapsed_title: Text;
|
||||
collapsed_thumbnail: Thumbnail[];
|
||||
collapsed_label: Text;
|
||||
expanded_title: Text;
|
||||
};
|
||||
|
||||
expanded_content: HorizontalCardList | null;
|
||||
expand_button: Button | null;
|
||||
collapse_button: Button | null;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
|
||||
this.header = {
|
||||
collapsed_title: new Text(data.header.collapsedTitle),
|
||||
collapsed_thumbnail: Thumbnail.fromResponse(data.header.collapsedThumbnail),
|
||||
collapsed_label: new Text(data.header.collapsedLabel),
|
||||
expanded_title: new Text(data.header.expandedTitle)
|
||||
};
|
||||
|
||||
this.expanded_content = Parser.parseItem<HorizontalCardList>(data.expandedContent);
|
||||
this.expand_button = Parser.parseItem<Button>(data.expandButton);
|
||||
this.collapse_button = Parser.parseItem<Button>(data.collapseButton);
|
||||
}
|
||||
}
|
||||
|
||||
export default ExpandableMetadata;
|
||||
@@ -2,7 +2,7 @@ import Parser from '../index';
|
||||
import { YTNode } from '../helpers';
|
||||
import ChipCloudChip from './ChipCloudChip';
|
||||
|
||||
export default class FeedFilterChipBar extends YTNode {
|
||||
class FeedFilterChipBar extends YTNode {
|
||||
static type = 'FeedFilterChipBar';
|
||||
|
||||
contents;
|
||||
@@ -11,4 +11,6 @@ export default class FeedFilterChipBar extends YTNode {
|
||||
super();
|
||||
this.contents = Parser.parseArray<ChipCloudChip>(data.contents, ChipCloudChip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FeedFilterChipBar;
|
||||
@@ -1,7 +1,7 @@
|
||||
import Parser from '..';
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
export default class GameCard extends YTNode {
|
||||
class GameCard extends YTNode {
|
||||
static type = 'GameCard';
|
||||
|
||||
game;
|
||||
@@ -10,4 +10,6 @@ export default class GameCard extends YTNode {
|
||||
super();
|
||||
this.game = Parser.parseItem(data.game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default GameCard;
|
||||
@@ -4,7 +4,7 @@ import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
|
||||
export default class GameDetails extends YTNode {
|
||||
class GameDetails extends YTNode {
|
||||
static type = 'GameDetails';
|
||||
|
||||
title: Text;
|
||||
@@ -21,4 +21,6 @@ export default class GameDetails extends YTNode {
|
||||
this.endpoint = new NavigationEndpoint(data.endpoint);
|
||||
this.is_official_box_art = data.isOfficialBoxArt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default GameDetails;
|
||||
@@ -3,6 +3,9 @@ import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import Author from './misc/Author';
|
||||
|
||||
import type Menu from './menus/Menu';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class GridVideo extends YTNode {
|
||||
@@ -14,12 +17,12 @@ class GridVideo extends YTNode {
|
||||
thumbnail_overlays;
|
||||
rich_thumbnail;
|
||||
published: Text;
|
||||
duration: Text | string;
|
||||
duration: Text | null;
|
||||
author: Author;
|
||||
views: Text;
|
||||
short_view_count: Text;
|
||||
endpoint: NavigationEndpoint;
|
||||
menu;
|
||||
menu: Menu | null;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
@@ -27,15 +30,15 @@ class GridVideo extends YTNode {
|
||||
this.id = data.videoId;
|
||||
this.title = new Text(data.title);
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.thumbnail_overlays = Parser.parse(data.thumbnailOverlays);
|
||||
this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
|
||||
this.rich_thumbnail = data.richThumbnail && Parser.parse(data.richThumbnail);
|
||||
this.published = new Text(data.publishedTimeText);
|
||||
this.duration = data.lengthText ? new Text(data.lengthText) : length_alt?.text ? new Text(length_alt.text) : '';
|
||||
this.duration = data.lengthText ? new Text(data.lengthText) : length_alt?.text ? new Text(length_alt.text) : null;
|
||||
this.author = data.shortBylineText && new Author(data.shortBylineText, data.ownerBadges);
|
||||
this.views = new Text(data.viewCountText);
|
||||
this.short_view_count = new Text(data.shortViewCountText);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.menu = Parser.parse(data.menu);
|
||||
this.menu = Parser.parseItem<Menu>(data.menu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { YTNode } from '../helpers';
|
||||
class Panel {
|
||||
static type = 'Panel';
|
||||
|
||||
thumbnail: {
|
||||
thumbnail?: {
|
||||
image: {
|
||||
url: string;
|
||||
width: number;
|
||||
@@ -43,13 +43,15 @@ class Panel {
|
||||
};
|
||||
|
||||
constructor(data: any) {
|
||||
this.thumbnail = {
|
||||
image: data.thumbnail.image.sources,
|
||||
endpoint: new NavigationEndpoint(data.thumbnail.onTap),
|
||||
on_long_press_endpoint: new NavigationEndpoint(data.thumbnail.onLongPress),
|
||||
content_mode: data.thumbnail.contentMode,
|
||||
crop_options: data.thumbnail.cropOptions
|
||||
};
|
||||
if (data.thumbnail) {
|
||||
this.thumbnail = {
|
||||
image: data.thumbnail.image.sources,
|
||||
endpoint: new NavigationEndpoint(data.thumbnail.onTap),
|
||||
on_long_press_endpoint: new NavigationEndpoint(data.thumbnail.onLongPress),
|
||||
content_mode: data.thumbnail.contentMode,
|
||||
crop_options: data.thumbnail.cropOptions
|
||||
};
|
||||
}
|
||||
|
||||
this.background_image = {
|
||||
image: data.backgroundImage.image.sources,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import Parser from '../index';
|
||||
import { YTNode } from '../helpers';
|
||||
import SearchRefinementCard from './SearchRefinementCard';
|
||||
import Button from './Button';
|
||||
import MacroMarkersListItem from './MacroMarkersListItem';
|
||||
|
||||
class HorizontalCardList extends YTNode {
|
||||
static type = 'HorizontalCardList';
|
||||
@@ -11,10 +14,10 @@ class HorizontalCardList extends YTNode {
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.cards = Parser.parse(data.cards);
|
||||
this.header = Parser.parse(data.header);
|
||||
this.previous_button = Parser.parse(data.previousButton);
|
||||
this.next_button = Parser.parse(data.nextButton);
|
||||
this.cards = Parser.parseArray<SearchRefinementCard | MacroMarkersListItem>(data.cards);
|
||||
this.header = Parser.parseItem(data.header);
|
||||
this.previous_button = Parser.parseItem<Button>(data.previousButton, Button);
|
||||
this.next_button = Parser.parseItem<Button>(data.nextButton, Button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import SubscribeButton from './SubscribeButton';
|
||||
import MetadataBadge from './MetadataBadge';
|
||||
import Button from './Button';
|
||||
|
||||
export default class InteractiveTabbedHeader extends YTNode {
|
||||
class InteractiveTabbedHeader extends YTNode {
|
||||
static type = 'InteractiveTabbedHeader';
|
||||
|
||||
header_type: string;
|
||||
@@ -33,4 +33,6 @@ export default class InteractiveTabbedHeader extends YTNode {
|
||||
this.buttons = Parser.parseArray<SubscribeButton | Button>(data.buttons, [ SubscribeButton, Button ]);
|
||||
this.auto_generated = new Text(data.autoGenerated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default InteractiveTabbedHeader;
|
||||
29
src/parser/classes/MacroMarkersListItem.ts
Normal file
29
src/parser/classes/MacroMarkersListItem.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class MacroMarkersListItem extends YTNode {
|
||||
static type = 'MacroMarkersListItem';
|
||||
|
||||
title: Text;
|
||||
time_description: Text;
|
||||
thumbnail: Thumbnail[];
|
||||
on_tap_endpoint: NavigationEndpoint;
|
||||
layout: string;
|
||||
is_highlighted: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
|
||||
this.title = new Text(data.title);
|
||||
this.time_description = new Text(data.timeDescription);
|
||||
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.on_tap_endpoint = new NavigationEndpoint(data.onTap);
|
||||
this.layout = data.layout;
|
||||
this.is_highlighted = data.isHighlighted;
|
||||
}
|
||||
}
|
||||
|
||||
export default MacroMarkersListItem;
|
||||
@@ -5,7 +5,8 @@ class MetadataBadge extends YTNode {
|
||||
|
||||
icon_type?: string;
|
||||
style?: string;
|
||||
tooltip: string | null;
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
@@ -18,7 +19,13 @@ class MetadataBadge extends YTNode {
|
||||
this.style = data.style;
|
||||
}
|
||||
|
||||
this.tooltip = data?.tooltip || data?.iconTooltip || null;
|
||||
if (data?.label) {
|
||||
this.style = data.label;
|
||||
}
|
||||
|
||||
if (data?.tooltip || data?.iconTooltip) {
|
||||
this.tooltip = data.tooltip || data.iconTooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { YTNode } from '../helpers';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
|
||||
export default class PlaylistCustomThumbnail extends YTNode {
|
||||
class PlaylistCustomThumbnail extends YTNode {
|
||||
static type = 'PlaylistCustomThumbnail';
|
||||
|
||||
thumbnail: Thumbnail[];
|
||||
@@ -10,4 +10,6 @@ export default class PlaylistCustomThumbnail extends YTNode {
|
||||
super();
|
||||
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PlaylistCustomThumbnail;
|
||||
@@ -76,8 +76,8 @@ class PlaylistPanelVideo extends YTNode {
|
||||
}));
|
||||
}
|
||||
|
||||
this.badges = Parser.parse(data.badges);
|
||||
this.menu = Parser.parse(data.menu);
|
||||
this.badges = Parser.parseArray(data.badges);
|
||||
this.menu = Parser.parseItem(data.menu);
|
||||
this.set_video_id = data.playlistSetVideoId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import Parser from '../index';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import PlaylistAuthor from './misc/PlaylistAuthor';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import type Menu from './menus/Menu';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class PlaylistVideo extends YTNode {
|
||||
@@ -17,7 +19,7 @@ class PlaylistVideo extends YTNode {
|
||||
set_video_id: string | undefined;
|
||||
endpoint: NavigationEndpoint;
|
||||
is_playable: boolean;
|
||||
menu;
|
||||
menu: Menu | null;
|
||||
|
||||
duration: {
|
||||
text: string;
|
||||
@@ -35,7 +37,7 @@ class PlaylistVideo extends YTNode {
|
||||
this.set_video_id = data?.setVideoId;
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.is_playable = data.isPlayable;
|
||||
this.menu = Parser.parse(data.menu);
|
||||
this.menu = Parser.parseItem<Menu>(data.menu);
|
||||
this.duration = {
|
||||
text: new Text(data.lengthText).text,
|
||||
seconds: parseInt(data.lengthSeconds)
|
||||
|
||||
@@ -5,7 +5,7 @@ import Button from './Button';
|
||||
import Text from './misc/Text';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
|
||||
export default class RecognitionShelf extends YTNode {
|
||||
class RecognitionShelf extends YTNode {
|
||||
static type = 'RecognitionShelf';
|
||||
|
||||
title: Text;
|
||||
@@ -23,4 +23,6 @@ export default class RecognitionShelf extends YTNode {
|
||||
this.button = Parser.parseItem<Button>(data.button, Button);
|
||||
this.surface = data.surface;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default RecognitionShelf;
|
||||
@@ -16,7 +16,7 @@ class SectionList extends YTNode {
|
||||
}
|
||||
|
||||
// TODO: this should be Parser#parseArray
|
||||
this.contents = Parser.parse(data.contents);
|
||||
this.contents = Parser.parseArray(data.contents);
|
||||
|
||||
if (data.continuations) {
|
||||
if (data.continuations[0].nextContinuationData) {
|
||||
|
||||
@@ -20,14 +20,14 @@ class Shelf extends YTNode {
|
||||
this.endpoint = new NavigationEndpoint(data.endpoint);
|
||||
}
|
||||
|
||||
this.content = Parser.parse(data.content) || null;
|
||||
this.content = Parser.parseItem(data.content) || null;
|
||||
|
||||
if (data.icon?.iconType) {
|
||||
this.icon_type = data.icon?.iconType;
|
||||
}
|
||||
|
||||
if (data.menu) {
|
||||
this.menu = Parser.parse(data.menu);
|
||||
this.menu = Parser.parseItem(data.menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { YTNode } from '../helpers';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
|
||||
export default class ThumbnailLandscapePortrait extends YTNode {
|
||||
class ThumbnailLandscapePortrait extends YTNode {
|
||||
static type = 'ThumbnailLandscapePortrait';
|
||||
|
||||
landscape: Thumbnail[];
|
||||
@@ -12,4 +12,6 @@ export default class ThumbnailLandscapePortrait extends YTNode {
|
||||
this.landscape = Thumbnail.fromResponse(data.landscape);
|
||||
this.portrait = Thumbnail.fromResponse(data.portrait);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ThumbnailLandscapePortrait;
|
||||
@@ -6,7 +6,7 @@ import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import SubscribeButton from './SubscribeButton';
|
||||
|
||||
export default class TopicChannelDetails extends YTNode {
|
||||
class TopicChannelDetails extends YTNode {
|
||||
static type = 'TopicChannelDetails';
|
||||
|
||||
title: Text;
|
||||
@@ -24,4 +24,6 @@ export default class TopicChannelDetails extends YTNode {
|
||||
this.subscribe_button = Parser.parseItem<SubscribeButton>(data.subscribeButton, SubscribeButton);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TopicChannelDetails;
|
||||
@@ -1,4 +1,5 @@
|
||||
import Parser from '../index';
|
||||
import Text from './misc/Text';
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class UniversalWatchCard extends YTNode {
|
||||
@@ -7,13 +8,17 @@ class UniversalWatchCard extends YTNode {
|
||||
header;
|
||||
call_to_action;
|
||||
sections;
|
||||
collapsed_label?: Text;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
// TODO: use parseItem / parseArray for these
|
||||
this.header = Parser.parse(data.header);
|
||||
this.call_to_action = Parser.parse(data.callToAction);
|
||||
this.sections = Parser.parse(data.sections);
|
||||
this.header = Parser.parseItem(data.header);
|
||||
this.call_to_action = Parser.parseItem(data.callToAction);
|
||||
this.sections = Parser.parseArray(data.sections);
|
||||
|
||||
if (data.collapsedLabel) {
|
||||
this.collapsed_label = new Text(data.collapsedLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class VerticalWatchCardList extends YTNode {
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.items = Parser.parse(data.items);
|
||||
this.items = Parser.parseArray(data.items);
|
||||
this.contents = this.items; // XXX: alias for consistency
|
||||
this.view_all_text = new Text(data.viewAllText);
|
||||
this.view_all_endpoint = new NavigationEndpoint(data.viewAllEndpoint);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import Parser from '../index';
|
||||
import Parser from '..';
|
||||
import Text from './misc/Text';
|
||||
import Author from './misc/Author';
|
||||
import Menu from './menus/Menu';
|
||||
import Thumbnail from './misc/Thumbnail';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import MetadataBadge from './MetadataBadge';
|
||||
import ExpandableMetadata from './ExpandableMetadata';
|
||||
|
||||
import { timeToSeconds } from '../../utils/Utils';
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
@@ -17,11 +20,13 @@ class Video extends YTNode {
|
||||
text: Text;
|
||||
hover_text: Text;
|
||||
}[];
|
||||
expandable_metadata: ExpandableMetadata | null;
|
||||
|
||||
thumbnails: Thumbnail[];
|
||||
thumbnail_overlays;
|
||||
rich_thumbnail;
|
||||
author: Author;
|
||||
badges: MetadataBadge[];
|
||||
endpoint: NavigationEndpoint;
|
||||
published: Text;
|
||||
view_count: Text;
|
||||
@@ -35,7 +40,8 @@ class Video extends YTNode {
|
||||
|
||||
show_action_menu: boolean;
|
||||
is_watched: boolean;
|
||||
menu;
|
||||
menu: Menu | null;
|
||||
search_video_result_entity_key: string;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
@@ -53,10 +59,13 @@ class Video extends YTNode {
|
||||
hover_text: new Text(snippet.snippetHoverText)
|
||||
})) || [];
|
||||
|
||||
this.expandable_metadata = Parser.parseItem<ExpandableMetadata>(data.expandableMetadata);
|
||||
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
|
||||
this.rich_thumbnail = data.richThumbnail ? Parser.parse(data.richThumbnail) : null;
|
||||
this.rich_thumbnail = data.richThumbnail ? Parser.parseItem(data.richThumbnail) : null;
|
||||
this.author = new Author(data.ownerText, data.ownerBadges, data.channelThumbnailSupportedRenderers?.channelThumbnailWithLinkRenderer?.thumbnail);
|
||||
this.badges = Parser.parseArray(data.badges, MetadataBadge);
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.published = new Text(data.publishedTimeText);
|
||||
this.view_count = new Text(data.viewCountText);
|
||||
@@ -75,6 +84,7 @@ class Video extends YTNode {
|
||||
this.show_action_menu = data.showActionMenu;
|
||||
this.is_watched = data.isWatched || false;
|
||||
this.menu = Parser.parseItem<Menu>(data.menu, Menu);
|
||||
this.search_video_result_entity_key = data.searchVideoResultEntityKey;
|
||||
}
|
||||
|
||||
get description(): string {
|
||||
@@ -84,22 +94,30 @@ class Video extends YTNode {
|
||||
return this.description_snippet?.toString() || '';
|
||||
}
|
||||
|
||||
/*
|
||||
Get is_live() {
|
||||
return this.badges.some((badge) => badge.style === 'BADGE_STYLE_TYPE_LIVE_NOW');
|
||||
get is_live(): boolean {
|
||||
return this.badges.some((badge) => {
|
||||
if (badge.label === 'BADGE_STYLE_TYPE_LIVE_NOW' || badge.style === 'LIVE')
|
||||
return true;
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
get is_upcoming(): boolean | undefined {
|
||||
return this.upcoming && this.upcoming > new Date();
|
||||
}
|
||||
|
||||
/*
|
||||
Get has_captions() {
|
||||
return this.badges.some((badge) => badge.label === 'CC');
|
||||
}*/
|
||||
get is_premiere(): boolean {
|
||||
return this.badges.some((badge) => badge.style === 'PREMIERE');
|
||||
}
|
||||
|
||||
get best_thumbnail(): Thumbnail | undefined{
|
||||
get is_4k(): boolean {
|
||||
return this.badges.some((badge) => badge.style === '4K');
|
||||
}
|
||||
|
||||
get has_captions(): boolean {
|
||||
return this.badges.some((badge) => badge.style === 'CC');
|
||||
}
|
||||
|
||||
get best_thumbnail(): Thumbnail | undefined {
|
||||
return this.thumbnails[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import Video from './Video';
|
||||
|
||||
export default class VideoCard extends Video {
|
||||
class VideoCard extends Video {
|
||||
static type = 'VideoCard';
|
||||
|
||||
constructor(data: any) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default VideoCard;
|
||||
@@ -13,9 +13,9 @@ class WatchCardHeroVideo extends YTNode {
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.call_to_action_button = Parser.parse(data.callToActionButton);
|
||||
this.hero_image = Parser.parse(data.heroImage);
|
||||
this.label = data.lengthText.accessibility.accessibilityData.label;
|
||||
this.call_to_action_button = Parser.parseItem(data.callToActionButton);
|
||||
this.hero_image = Parser.parseItem(data.heroImage);
|
||||
this.label = data.lengthText?.accessibility.accessibilityData.label || '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ class WatchCardSectionSequence extends YTNode {
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.lists = Parser.parse(data.lists);
|
||||
this.lists = Parser.parseArray(data.lists);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,15 @@ import NavigationEndpoint from '../NavigationEndpoint';
|
||||
class TextRun {
|
||||
text: string;
|
||||
endpoint: NavigationEndpoint | undefined;
|
||||
bold: boolean;
|
||||
italics: boolean;
|
||||
strikethrough: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,6 +359,10 @@ export type ObservedArray<T extends YTNode = YTNode> = Array<T> & {
|
||||
* Returns all objects that match the rule.
|
||||
*/
|
||||
getAll: (rule: object, del_items?: boolean) => T[];
|
||||
/**
|
||||
* Returns the first object to match the condition.
|
||||
*/
|
||||
matchCondition: (condition: (node: T) => boolean) => T | undefined;
|
||||
/**
|
||||
* Removes the item at the given index.
|
||||
*/
|
||||
@@ -412,6 +416,14 @@ export function observe<T extends YTNode>(obj: Array<T>) {
|
||||
);
|
||||
}
|
||||
|
||||
if (prop == 'matchCondition') {
|
||||
return (condition: (node: T) => boolean) => (
|
||||
target.find((obj) => {
|
||||
return condition(obj);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (prop == 'filterType') {
|
||||
return (...types: YTNodeConstructor<YTNode>[]) => {
|
||||
return observe(target.filter((node: YTNode) => {
|
||||
|
||||
@@ -73,6 +73,7 @@ import { default as Endscreen } from './classes/Endscreen';
|
||||
import { default as EndscreenElement } from './classes/EndscreenElement';
|
||||
import { default as EndScreenPlaylist } from './classes/EndScreenPlaylist';
|
||||
import { default as EndScreenVideo } from './classes/EndScreenVideo';
|
||||
import { default as ExpandableMetadata } from './classes/ExpandableMetadata';
|
||||
import { default as ExpandableTab } from './classes/ExpandableTab';
|
||||
import { default as ExpandedShelfContents } from './classes/ExpandedShelfContents';
|
||||
import { default as FeedFilterChipBar } from './classes/FeedFilterChipBar';
|
||||
@@ -137,6 +138,7 @@ import { default as LiveChatItemList } from './classes/LiveChatItemList';
|
||||
import { default as LiveChatMessageInput } from './classes/LiveChatMessageInput';
|
||||
import { default as LiveChatParticipant } from './classes/LiveChatParticipant';
|
||||
import { default as LiveChatParticipantsList } from './classes/LiveChatParticipantsList';
|
||||
import { default as MacroMarkersListItem } from './classes/MacroMarkersListItem';
|
||||
import { default as Menu } from './classes/menus/Menu';
|
||||
import { default as MenuNavigationItem } from './classes/menus/MenuNavigationItem';
|
||||
import { default as MenuServiceItem } from './classes/menus/MenuServiceItem';
|
||||
@@ -363,6 +365,7 @@ export const YTNodes = {
|
||||
EndscreenElement,
|
||||
EndScreenPlaylist,
|
||||
EndScreenVideo,
|
||||
ExpandableMetadata,
|
||||
ExpandableTab,
|
||||
ExpandedShelfContents,
|
||||
FeedFilterChipBar,
|
||||
@@ -427,6 +430,7 @@ export const YTNodes = {
|
||||
LiveChatMessageInput,
|
||||
LiveChatParticipant,
|
||||
LiveChatParticipantsList,
|
||||
MacroMarkersListItem,
|
||||
Menu,
|
||||
MenuNavigationItem,
|
||||
MenuServiceItem,
|
||||
|
||||
@@ -9,12 +9,13 @@ import MicroformatData from '../classes/MicroformatData';
|
||||
import SubscribeButton from '../classes/SubscribeButton';
|
||||
import Tab from '../classes/Tab';
|
||||
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
import FeedFilterChipBar from '../classes/FeedFilterChipBar';
|
||||
import ChipCloudChip from '../classes/ChipCloudChip';
|
||||
import FilterableFeed from '../../core/FilterableFeed';
|
||||
import Feed from '../../core/Feed';
|
||||
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
|
||||
export default class Channel extends TabbedFeed {
|
||||
header;
|
||||
metadata;
|
||||
@@ -70,37 +71,37 @@ export default class Channel extends TabbedFeed {
|
||||
}
|
||||
|
||||
async getHome() {
|
||||
const tab = await this.getTab('Home');
|
||||
const tab = await this.getTabByURL('featured');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
|
||||
async getVideos() {
|
||||
const tab = await this.getTab('Videos');
|
||||
const tab = await this.getTabByURL('videos');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
|
||||
async getShorts() {
|
||||
const tab = await this.getTab('Shorts');
|
||||
const tab = await this.getTabByURL('shorts');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
|
||||
async getLiveStreams() {
|
||||
const tab = await this.getTab('Live');
|
||||
const tab = await this.getTabByURL('streams');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
|
||||
async getPlaylists() {
|
||||
const tab = await this.getTab('Playlists');
|
||||
const tab = await this.getTabByURL('playlists');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
|
||||
async getCommunity() {
|
||||
const tab = await this.getTab('Community');
|
||||
const tab = await this.getTabByURL('community');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
|
||||
async getChannels() {
|
||||
const tab = await this.getTab('Channels');
|
||||
const tab = await this.getTabByURL('channels');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
|
||||
@@ -109,7 +110,7 @@ export default class Channel extends TabbedFeed {
|
||||
* Note that this does not return a new {@link Channel} object.
|
||||
*/
|
||||
async getAbout() {
|
||||
const tab = await this.getTab('About');
|
||||
const tab = await this.getTabByURL('about');
|
||||
return tab.memo.getType(ChannelAboutFullMetadata)?.[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -39,10 +39,10 @@ class Library {
|
||||
}
|
||||
|
||||
async #getAll(shelf: Shelf): Promise<Playlist | History | Feed> {
|
||||
if (!shelf.menu?.item().as(Menu).hasKey('top_level_buttons'))
|
||||
if (!shelf.menu?.as(Menu).hasKey('top_level_buttons'))
|
||||
throw new InnertubeError(`The ${shelf.title.text} shelf doesn't have more items`);
|
||||
|
||||
const button = shelf.menu.item().as(Menu).top_level_buttons.get({ text: 'See all' });
|
||||
const button = shelf.menu.as(Menu).top_level_buttons.get({ text: 'See all' });
|
||||
|
||||
if (!button)
|
||||
throw new InnertubeError('Did not find target button.');
|
||||
|
||||
@@ -7,6 +7,7 @@ import VideoOwner from '../classes/VideoOwner';
|
||||
import PlaylistMetadata from '../classes/PlaylistMetadata';
|
||||
import PlaylistSidebarPrimaryInfo from '../classes/PlaylistSidebarPrimaryInfo';
|
||||
import PlaylistSidebarSecondaryInfo from '../classes/PlaylistSidebarSecondaryInfo';
|
||||
import PlaylistCustomThumbnail from '../classes/PlaylistCustomThumbnail';
|
||||
import PlaylistVideoThumbnail from '../classes/PlaylistVideoThumbnail';
|
||||
import PlaylistHeader from '../classes/PlaylistHeader';
|
||||
|
||||
@@ -30,8 +31,8 @@ class Playlist extends Feed {
|
||||
this.info = {
|
||||
...this.page.metadata.item().as(PlaylistMetadata),
|
||||
...{
|
||||
author: secondary_info?.owner.item().as(VideoOwner).author,
|
||||
thumbnails: primary_info?.thumbnail_renderer.item().as(PlaylistVideoThumbnail).thumbnail as Thumbnail[],
|
||||
author: secondary_info?.owner.item().as(VideoOwner).author ?? header?.author,
|
||||
thumbnails: primary_info?.thumbnail_renderer.item().as(PlaylistVideoThumbnail, PlaylistCustomThumbnail).thumbnail as Thumbnail[],
|
||||
total_items: this.#getStat(0, primary_info),
|
||||
views: this.#getStat(1, primary_info),
|
||||
last_updated: this.#getStat(2, primary_info),
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
import Actions from '../../core/Actions';
|
||||
import { observe, ObservedArray, YTNode } from '../helpers';
|
||||
import { ObservedArray, YTNode } from '../helpers';
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
|
||||
import Feed from '../../core/Feed';
|
||||
import SectionList from '../classes/SectionList';
|
||||
import ItemSection from '../classes/ItemSection';
|
||||
import HorizontalCardList from '../classes/HorizontalCardList';
|
||||
import RichListHeader from '../classes/RichListHeader';
|
||||
import SearchRefinementCard from '../classes/SearchRefinementCard';
|
||||
import TwoColumnSearchResults from '../classes/TwoColumnSearchResults';
|
||||
import UniversalWatchCard from '../classes/UniversalWatchCard';
|
||||
import WatchCardHeroVideo from '../classes/WatchCardHeroVideo';
|
||||
import WatchCardSectionSequence from '../classes/WatchCardSectionSequence';
|
||||
|
||||
class Search extends Feed {
|
||||
export default class Search extends Feed {
|
||||
results: ObservedArray<YTNode> | null | undefined;
|
||||
refinements;
|
||||
estimated_results;
|
||||
@@ -24,30 +20,16 @@ class Search extends Feed {
|
||||
super(actions, data, already_parsed);
|
||||
|
||||
const contents =
|
||||
this.page.contents?.item().as(TwoColumnSearchResults).primary_contents?.item().as(SectionList).contents.array() ||
|
||||
this.page.contents_memo.getType(SectionList)?.[0]?.contents ||
|
||||
this.page.on_response_received_commands?.[0].contents;
|
||||
|
||||
const secondary_contents_maybe = this.page.contents?.item().key('secondary_contents');
|
||||
const secondary_contents = secondary_contents_maybe?.isParsed() ? secondary_contents_maybe.parsed().item().key('contents').parsed().array() : undefined;
|
||||
|
||||
this.results = contents.firstOfType(ItemSection)?.contents;
|
||||
|
||||
const card_list = this.results?.get({ type: 'HorizontalCardList' }, true)?.as(HorizontalCardList);
|
||||
const universal_watch_card = secondary_contents?.firstOfType(UniversalWatchCard);
|
||||
|
||||
this.refinements = this.page.refinements || [];
|
||||
this.estimated_results = this.page.estimated_results;
|
||||
|
||||
this.watch_card = {
|
||||
header: universal_watch_card?.header.item() || null,
|
||||
call_to_action: universal_watch_card?.call_to_action?.item()?.as(WatchCardHeroVideo) || null,
|
||||
sections: universal_watch_card?.sections?.array()?.filterType(WatchCardSectionSequence) || []
|
||||
};
|
||||
|
||||
this.refinement_cards = {
|
||||
header: card_list?.header.item().as(RichListHeader) || null,
|
||||
cards: card_list?.cards.array().filterType(SearchRefinementCard) || observe([] as SearchRefinementCard[])
|
||||
};
|
||||
this.watch_card = this.page?.contents_memo.getType(UniversalWatchCard)?.[0];
|
||||
this.refinement_cards = this.results?.get({ type: 'HorizontalCardList' }, true)?.as(HorizontalCardList);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,7 +39,8 @@ class Search extends Feed {
|
||||
let target_card: SearchRefinementCard | undefined;
|
||||
|
||||
if (typeof card === 'string') {
|
||||
target_card = this.refinement_cards.cards.get({ query: card });
|
||||
if (!this.refinement_cards) throw new InnertubeError('No refinement cards found.');
|
||||
target_card = this.refinement_cards?.cards.get({ query: card })?.as(SearchRefinementCard);
|
||||
if (!target_card)
|
||||
throw new InnertubeError(`Refinement card "${card}" not found`, { available_cards: this.refinement_card_queries });
|
||||
} else if (card.type === 'SearchRefinementCard') {
|
||||
@@ -71,8 +54,11 @@ class Search extends Feed {
|
||||
return new Search(this.actions, page, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of refinement card queries.
|
||||
*/
|
||||
get refinement_card_queries() {
|
||||
return this.refinement_cards.cards.map((card) => card.query);
|
||||
return this.refinement_cards?.cards.as(SearchRefinementCard).map((card) => card.query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,6 +68,4 @@ class Search extends Feed {
|
||||
const continuation = await this.getContinuationData();
|
||||
return new Search(this.actions, continuation, true);
|
||||
}
|
||||
}
|
||||
|
||||
export default Search;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ class Settings {
|
||||
if (!tab)
|
||||
throw new InnertubeError('Target tab not found');
|
||||
|
||||
const contents = tab.content?.as(SectionList).contents.array().as(ItemSection);
|
||||
const contents = tab.content?.as(SectionList).contents.as(ItemSection);
|
||||
|
||||
this.introduction = contents?.shift()?.contents?.get({ type: 'PageIntroduction' })?.as(PageIntroduction);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class TimeWatched {
|
||||
if (!tab)
|
||||
throw new InnertubeError('Could not find target tab.');
|
||||
|
||||
this.contents = tab.content?.as(SectionList).contents.array().as(ItemSection);
|
||||
this.contents = tab.content?.as(SectionList).contents.as(ItemSection);
|
||||
}
|
||||
|
||||
get page(): ParsedResponse {
|
||||
|
||||
@@ -28,8 +28,8 @@ class Explore {
|
||||
if (!section_list)
|
||||
throw new InnertubeError('Target tab did not have any content.');
|
||||
|
||||
this.top_buttons = section_list.contents.array().firstOfType(Grid)?.items.as(MusicNavigationButton) || ([] as MusicNavigationButton[]);
|
||||
this.sections = section_list.contents.array().getAll({ type: 'MusicCarouselShelf' }) as MusicCarouselShelf[];
|
||||
this.top_buttons = section_list.contents.firstOfType(Grid)?.items.as(MusicNavigationButton) || ([] as MusicNavigationButton[]);
|
||||
this.sections = section_list.contents.getAll({ type: 'MusicCarouselShelf' }) as MusicCarouselShelf[];
|
||||
}
|
||||
|
||||
get page(): ParsedResponse {
|
||||
|
||||
@@ -33,7 +33,7 @@ class HomeFeed {
|
||||
}
|
||||
|
||||
this.#continuation = tab.content?.as(SectionList).continuation;
|
||||
this.sections = tab.content?.as(SectionList).contents.array().as(MusicCarouselShelf);
|
||||
this.sections = tab.content?.as(SectionList).contents.as(MusicCarouselShelf);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ class Library {
|
||||
const section_list = this.#page.contents_memo.getType(SectionList)?.[0];
|
||||
|
||||
this.header = section_list?.header?.item().as(MusicSideAlignedItem);
|
||||
this.contents = section_list?.contents?.array().as(Grid, MusicShelf);
|
||||
this.contents = section_list?.contents?.as(Grid, MusicShelf);
|
||||
|
||||
this.#continuation = this.contents?.find((list: Grid | MusicShelf) => list.continuation)?.continuation;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class Playlist {
|
||||
/**
|
||||
* Retrieves related playlists
|
||||
*/
|
||||
async getRelated() {
|
||||
async getRelated(): Promise<MusicCarouselShelf> {
|
||||
let section_continuation = this.#page.contents_memo.get('SectionList')?.[0].as(SectionList).continuation;
|
||||
|
||||
while (section_continuation) {
|
||||
@@ -74,19 +74,15 @@ class Playlist {
|
||||
const section_list = data.continuation_contents?.as(SectionListContinuation);
|
||||
const sections = section_list?.contents?.as(MusicCarouselShelf, MusicShelf);
|
||||
|
||||
const related = sections?.filter(
|
||||
(section) =>
|
||||
section.is(MusicCarouselShelf) ? section.header?.title.toString() === 'Related playlists' :
|
||||
section.title.toString() === 'Related playlists'
|
||||
)[0];
|
||||
const related = sections?.matchCondition((section) => section.is(MusicCarouselShelf))?.as(MusicCarouselShelf);
|
||||
|
||||
if (related)
|
||||
return related.contents || [];
|
||||
return related;
|
||||
|
||||
section_continuation = section_list?.continuation;
|
||||
}
|
||||
|
||||
return [];
|
||||
throw new InnertubeError('Target section not found.');
|
||||
}
|
||||
|
||||
async getSuggestions(refresh = true) {
|
||||
@@ -115,9 +111,7 @@ class Playlist {
|
||||
const section_list = page.continuation_contents?.as(SectionListContinuation);
|
||||
const sections = section_list?.contents?.as(MusicCarouselShelf, MusicShelf);
|
||||
|
||||
const suggestions = sections?.filter(
|
||||
(section) => section.is(MusicShelf) && section.title.toString() === 'Suggestions'
|
||||
)[0] as MusicShelf | undefined;
|
||||
const suggestions = sections?.matchCondition((section) => section.is(MusicShelf))?.as(MusicShelf);
|
||||
|
||||
return {
|
||||
items: suggestions?.contents || [],
|
||||
|
||||
@@ -37,7 +37,7 @@ class Recap {
|
||||
if (!tab)
|
||||
throw new InnertubeError('Target tab not found');
|
||||
|
||||
this.sections = tab.content?.as(SectionList).contents.array().as(ItemSection, MusicCarouselShelf, Message);
|
||||
this.sections = tab.content?.as(SectionList).contents.as(ItemSection, MusicCarouselShelf, Message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,7 +49,7 @@ class Search {
|
||||
|
||||
this.header = tab_content.hasKey('header') ? tab_content.header?.item().as(ChipCloud) : null;
|
||||
|
||||
const shelves = tab_content.contents.array().as(MusicShelf, ItemSection);
|
||||
const shelves = tab_content.contents.as(MusicShelf, ItemSection);
|
||||
const item_section = shelves.firstOfType(ItemSection);
|
||||
|
||||
this.did_you_mean = item_section?.contents?.firstOfType(DidYouMean) || null;
|
||||
|
||||
@@ -4,9 +4,7 @@ import Constants from '../../utils/Constants';
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
|
||||
import Tab from '../classes/Tab';
|
||||
import Tabbed from '../classes/Tabbed';
|
||||
import WatchNextTabbedResults from '../classes/WatchNextTabbedResults';
|
||||
import SingleColumnMusicWatchNextResults from '../classes/SingleColumnMusicWatchNextResults';
|
||||
import MicroformatData from '../classes/MicroformatData';
|
||||
import PlayerOverlay from '../classes/PlayerOverlay';
|
||||
import PlaylistPanel from '../classes/PlaylistPanel';
|
||||
@@ -70,8 +68,7 @@ class TrackInfo {
|
||||
this.#playback_tracking = info.playback_tracking;
|
||||
|
||||
if (next) {
|
||||
const single_col = next.contents.item().as(SingleColumnMusicWatchNextResults);
|
||||
const tabbed_results = single_col.contents.item().as(Tabbed).contents.item().as(WatchNextTabbedResults);
|
||||
const tabbed_results = next.contents_memo.getType(WatchNextTabbedResults)?.[0];
|
||||
|
||||
this.tabs = tabbed_results.tabs.array().as(Tab);
|
||||
this.current_video_endpoint = next.current_video_endpoint;
|
||||
@@ -84,14 +81,17 @@ class TrackInfo {
|
||||
/**
|
||||
* Retrieves contents of the given tab.
|
||||
*/
|
||||
async getTab(title: string) {
|
||||
async getTab(title_or_page_type: string) {
|
||||
if (!this.tabs)
|
||||
throw new InnertubeError('Could not find any tab');
|
||||
|
||||
const target_tab = this.tabs.get({ title });
|
||||
const target_tab =
|
||||
this.tabs.get({ title: title_or_page_type }) ||
|
||||
this.tabs.matchCondition((tab) => tab.endpoint.payload.browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType === title_or_page_type) ||
|
||||
this.tabs?.[0];
|
||||
|
||||
if (!target_tab)
|
||||
throw new InnertubeError(`Tab "${title}" not found`, { available_tabs: this.available_tabs });
|
||||
throw new InnertubeError(`Tab "${title_or_page_type}" not found`, { available_tabs: this.available_tabs });
|
||||
|
||||
if (target_tab.content)
|
||||
return target_tab.content;
|
||||
@@ -101,7 +101,7 @@ class TrackInfo {
|
||||
if (page.contents.item().key('type').string() === 'Message')
|
||||
return page.contents.item().as(Message);
|
||||
|
||||
return page.contents.item().as(SectionList).contents.array();
|
||||
return page.contents.item().as(SectionList).contents;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,7 +140,7 @@ class TrackInfo {
|
||||
* Retrieves related content.
|
||||
*/
|
||||
async getRelated(): Promise<ObservedArray<MusicCarouselShelf | MusicDescriptionShelf>> {
|
||||
const tab = await this.getTab('Related') as ObservedArray<MusicDescriptionShelf | MusicDescriptionShelf>;
|
||||
const tab = await this.getTab('MUSIC_PAGE_TYPE_TRACK_RELATED') as ObservedArray<MusicDescriptionShelf | MusicDescriptionShelf>;
|
||||
return tab;
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ class TrackInfo {
|
||||
* Retrieves lyrics.
|
||||
*/
|
||||
async getLyrics(): Promise<MusicDescriptionShelf | undefined> {
|
||||
const tab = await this.getTab('Lyrics') as ObservedArray<MusicCarouselShelf | MusicDescriptionShelf>;
|
||||
const tab = await this.getTab('MUSIC_PAGE_TYPE_TRACK_LYRICS') as ObservedArray<MusicCarouselShelf | MusicDescriptionShelf>;
|
||||
return tab.firstOfType(MusicDescriptionShelf);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ export const CLIENTS = Object.freeze({
|
||||
},
|
||||
YTMUSIC_ANDROID: {
|
||||
NAME: 'ANDROID_MUSIC',
|
||||
VERSION: '5.17.51'
|
||||
VERSION: '5.34.51'
|
||||
},
|
||||
TV_EMBEDDED: {
|
||||
NAME: 'TVHTML5_SIMPLY_EMBEDDED_PLAYER',
|
||||
|
||||
@@ -48,7 +48,7 @@ export default class HTTPClient {
|
||||
const request_headers = new Headers(headers);
|
||||
|
||||
request_headers.set('Accept', '*/*');
|
||||
request_headers.set('Accept-Language', `en-${this.#session.context.client.gl || 'US'}`);
|
||||
request_headers.set('Accept-Language', `${this.#session.context.client.hl}-${this.#session.context.client.gl}`);
|
||||
request_headers.set('x-goog-visitor-id', this.#session.context.client.visitorData || '');
|
||||
request_headers.set('x-origin', request_url.origin);
|
||||
request_headers.set('x-youtube-client-version', this.#session.context.client.clientVersion || '');
|
||||
|
||||
@@ -10,6 +10,10 @@ export const VIDEOS = [
|
||||
{
|
||||
ID: 'I1qsF0WQy8c',
|
||||
QUERY: 'mkbhd',
|
||||
},
|
||||
{
|
||||
ID: 'OqiXFXlYFi8',
|
||||
QUERY: 'formatted comment text'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import fs from 'fs';
|
||||
import Innertube from '..';
|
||||
import { CHANNELS, VIDEOS } from './constants';
|
||||
import { streamToIterable } from '../src/utils/Utils';
|
||||
import TextRun from '../src/parser/classes/misc/TextRun';
|
||||
|
||||
describe('YouTube.js Tests', () => {
|
||||
let yt: Innertube;
|
||||
@@ -68,6 +69,19 @@ describe('YouTube.js Tests', () => {
|
||||
threads = await yt.getComments(VIDEOS[1].ID);
|
||||
expect(threads.contents.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should parse formatted comments', async () => {
|
||||
const threads = await yt.getComments(VIDEOS[3].ID);
|
||||
const authorComment = threads.contents.find(t => t.comment?.author_is_channel_owner)
|
||||
expect(authorComment).not.toBeUndefined();
|
||||
|
||||
expect(authorComment!.comment?.content.runs?.length).toBeGreaterThan(0)
|
||||
const runs = authorComment!.comment!.content.runs! as TextRun[]
|
||||
|
||||
expect(runs[0].bold).toBeTruthy()
|
||||
expect(runs[2].italics).toBeTruthy()
|
||||
expect(runs[4].strikethrough).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should retrieve next batch of comments', async () => {
|
||||
const next = await threads.getContinuation();
|
||||
|
||||
Reference in New Issue
Block a user