Compare commits

...

17 Commits

Author SHA1 Message Date
github-actions[bot]
f66f0bd656 chore(main): release 4.3.0 (#384)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-13 07:52:42 -03:00
LuanRT
05de3ec97a feat(GridVideo): add upcoming, upcoming_text, is_reminder_set and buttons
Closes: #380
2023-04-13 07:50:18 -03:00
LuanRT
a0566969ba feat(ToggleMenuServiceItem): parse default nav endpoint 2023-04-13 06:10:17 -03:00
LuanRT
a9cad49333 feat(ytmusic): add taste builder nodes (#383)
Adds MusicTastebuilderShelf and MusicTastebuilderShelfThumbnail. These usually appear on new accounts.
2023-04-13 05:37:49 -03:00
LuanRT
096bf362c9 feat(MusicResponsiveListItem): make flex/fixed cols public (#382)
Plus refactor a few things.
2023-04-13 05:35:46 -03:00
LuanRT
ec9c0979f5 refactor: fix inconsistencies in the guide nodes (#379) 2023-04-11 05:52:47 -03:00
LuanRT
342d1d95e9 chore: fix readme formatting [skip ci] 2023-04-11 05:06:03 -03:00
github-actions[bot]
dbfc569602 chore(main): release 4.2.0 (#377)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-09 03:40:01 -03:00
LuanRT
c16a967987 chore(docs): minor improvements 2023-04-09 03:38:09 -03:00
LuanRT
a07375eb20 chore(docs): rewording [skip ci] 2023-04-09 03:02:23 -03:00
LuanRT
ce9d9c56b4 feat(parser): ignore PrimetimePromo node
This node is used to display advertisements.
2023-04-09 02:51:29 -03:00
LuanRT
f50ce1a06b chore: simplify Pull Request template 2023-04-09 02:43:19 -03:00
LuanRT
3b6ccfa3d8 chore: fix cjs build 2023-04-09 02:18:53 -03:00
LuanRT
878488d1b3 chore: clean up README.md 2023-04-09 01:20:27 -03:00
LuanRT
3c94c9da4b deps: bump Jinter to v1.0.0 2023-04-08 23:53:59 -03:00
absidue
0b301de6a1 feat: Enable importHelpers in tsconfig to reduce output size (#378) 2023-04-08 20:19:20 -03:00
absidue
c9135e66d3 feat(PlaylistVideo): Extract video_info and accessibility_label texts (#376) 2023-04-07 20:43:20 -03:00
25 changed files with 389 additions and 264 deletions

View File

@@ -1,27 +1,6 @@
# Pull Request Template
## Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
## Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] I have checked my code and corrected any misspellings
<!-- Thank you for submitting a Pull Request! Please:
* Read our contributing guidelines: https://github.com/LuanRT/YouTube.js/blob/main/CONTRIBUTING.md
* Add "Fixes #<issue_number>" to the PR description if you are fixing an issue.
* Ensure that the code is up-to-date with the `main` branch.
* Include a description of the proposed changes and how to test them.
-->

View File

@@ -1,5 +1,24 @@
# Changelog
## [4.3.0](https://github.com/LuanRT/YouTube.js/compare/v4.2.0...v4.3.0) (2023-04-13)
### Features
* **GridVideo:** add `upcoming`, `upcoming_text`, `is_reminder_set` and `buttons` ([05de3ec](https://github.com/LuanRT/YouTube.js/commit/05de3ec97a1fea92543b5e5f84933b86a07ab830)), closes [#380](https://github.com/LuanRT/YouTube.js/issues/380)
* **MusicResponsiveListItem:** make flex/fixed cols public ([#382](https://github.com/LuanRT/YouTube.js/issues/382)) ([096bf36](https://github.com/LuanRT/YouTube.js/commit/096bf362c9bd46a510ecb0d01623c70841e26e26))
* **ToggleMenuServiceItem:** parse default nav endpoint ([a056696](https://github.com/LuanRT/YouTube.js/commit/a0566969ba436f31ca3722d09442a0c6302235d7))
* **ytmusic:** add taste builder nodes ([#383](https://github.com/LuanRT/YouTube.js/issues/383)) ([a9cad49](https://github.com/LuanRT/YouTube.js/commit/a9cad49333aa85c98bbb96e5f2d5b57d9beeb0c7))
## [4.2.0](https://github.com/LuanRT/YouTube.js/compare/v4.1.1...v4.2.0) (2023-04-09)
### Features
* Enable importHelpers in tsconfig to reduce output size ([#378](https://github.com/LuanRT/YouTube.js/issues/378)) ([0b301de](https://github.com/LuanRT/YouTube.js/commit/0b301de6a1e1352a64881c1751a84360922a77cd))
* **parser:** ignore PrimetimePromo node ([ce9d9c5](https://github.com/LuanRT/YouTube.js/commit/ce9d9c56b4f45c0139d74edc95c295ecfd1ee4b1))
* **PlaylistVideo:** Extract video_info and accessibility_label texts ([#376](https://github.com/LuanRT/YouTube.js/issues/376)) ([c9135e6](https://github.com/LuanRT/YouTube.js/commit/c9135e66d3c9c72b8d063eedcf3cc2123800946d))
## [4.1.1](https://github.com/LuanRT/YouTube.js/compare/v4.1.0...v4.1.1) (2023-03-29)

View File

@@ -3,14 +3,12 @@
[versions]: https://www.npmjs.com/package/youtubei.js?activeTab=versions
[codefactor]: https://www.codefactor.io/repository/github/luanrt/youtube.js
[actions]: https://github.com/LuanRT/YouTube.js/actions
[say-thanks]: https://saythanks.io/to/LuanRT
[collaborators]: https://github.com/LuanRT/YouTube.js/blob/main/COLLABORATORS.md
<!-- OTHER LINKS -->
[project]: https://github.com/LuanRT/YouTube.js
[twitter]: https://twitter.com/thesciencephile
[discord]: https://discord.gg/syDu7Yks54
[nodejs]: https://nodejs.org
<h1 align=center>YouTube.js</h1>
@@ -23,33 +21,28 @@
[![Codefactor](https://www.codefactor.io/repository/github/luanrt/youtube.js/badge)][codefactor]
[![Downloads](https://img.shields.io/npm/dt/youtubei.js)][npm]
[![Discord](https://img.shields.io/badge/discord-online-brightgreen.svg)][discord]
[![Say thanks](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)][say-thanks]
<br>
[![Donate](https://img.shields.io/badge/donate-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#white)][collaborators]
</div>
<p align="center">
<a><sub>Special thanks to:<sub></a>
</p>
<table align="center">
<body>
<tr>
<td align="center">
<a href="https://serpapi.com/" target="_blank">
<img width="80" alt="SerpApi" src="https://luanrt.is-a.dev/assets/img/serpapi.svg" />
<br>
<b>
<sub>
Scrape Google and other search engines from a fast, easy and complete API.
</sub>
</b>
</a>
</td>
</tr>
</body>
</table>
<div align="center">
<p>
<sup>Special thanks to:</sup>
<br>
<br>
<a href="https://serpapi.com" target="_blank">
<img width="80" alt="SerpApi" src="https://luanrt.is-a.dev/assets/img/serpapi.svg" />
<br>
<sub>
API to get search engine results with ease.
</sub>
</a>
</p>
</div>
<br>
<hr>
<br>
## Table of Contents
<ol>
@@ -249,7 +242,7 @@ const yt = await Innertube.create({
* `Innertube`
<details>
<summary>Objects</summary>
<summary>Properties</summary>
<p>
* [.session](https://github.com/LuanRT/YouTube.js/blob/main/docs/API/session.md)
@@ -293,7 +286,7 @@ const yt = await Innertube.create({
</details>
<a name="getinfo"></a>
### getInfo(target, client?)
### `getInfo(target, client?)`
Retrieves video info.
@@ -357,7 +350,7 @@ Retrieves video info.
</details>
<a name="getbasicinfo"></a>
### getBasicInfo(video_id, client?)
### `getBasicInfo(video_id, client?)`
Suitable for cases where you only need basic video metadata. Also, it is faster than [`getInfo()`](#getinfo).
@@ -369,7 +362,7 @@ Suitable for cases where you only need basic video metadata. Also, it is faster
| client? | `InnerTubeClient` | `WEB`, `ANDROID`, `YTMUSIC_ANDROID`, `YTMUSIC`, `TV_EMBEDDED` |
<a name="search"></a>
### search(query, filters?)
### `search(query, filters?)`
Searches the given query on YouTube.
@@ -414,7 +407,7 @@ Searches the given query on YouTube.
</details>
<a name="getsearchsuggestions"></a>
### getSearchSuggestions(query)
### `getSearchSuggestions(query)`
Retrieves search suggestions for given query.
**Returns**: `Promise<string[]>`
@@ -424,7 +417,7 @@ Retrieves search suggestions for given query.
| query | `string` | The search query |
<a name="getcomments"></a>
### getComments(video_id, sort_by?)
### `getComments(video_id, sort_by?)`
Retrieves comments for given video.
**Returns**: `Promise<Comments>`
@@ -437,7 +430,7 @@ Retrieves comments for given video.
See [`./examples/comments`](https://github.com/LuanRT/YouTube.js/blob/main/examples/comments) for examples.
<a name="gethomefeed"></a>
### getHomeFeed()
### `getHomeFeed()`
Retrieves YouTube's home feed.
**Returns**: `Promise<HomeFeed>`
@@ -471,13 +464,13 @@ Retrieves YouTube's home feed.
</details>
<a name="getguide"></a>
### getGuide()
### `getGuide()`
Retrieves YouTube's content guide.
**Returns**: `Promise<Guide>`
<a name="getlibrary"></a>
### getLibrary()
### `getLibrary()`
Retrieves the account's library.
**Returns**: `Promise<Library>`
@@ -499,7 +492,7 @@ Retrieves the account's library.
</details>
<a name="gethistory"></a>
### getHistory()
### `getHistory()`
Retrieves watch history.
**Returns**: `Promise<History>`
@@ -518,19 +511,19 @@ Retrieves watch history.
</details>
<a name="gettrending"></a>
### getTrending()
### `getTrending()`
Retrieves trending content.
**Returns**: `Promise<TabbedFeed<IBrowseResponse>>`
<a name="getsubscriptionsfeed"></a>
### getSubscriptionsFeed()
### `getSubscriptionsFeed()`
Retrieves the subscriptions feed.
**Returns**: `Promise<Feed<IBrowseResponse>>`
<a name="getchannel"></a>
### getChannel(id)
### `getChannel(id)`
Retrieves contents for a given channel.
**Returns**: `Promise<Channel>`
@@ -570,7 +563,7 @@ Retrieves contents for a given channel.
See [`./examples/channel`](https://github.com/LuanRT/YouTube.js/blob/main/examples/channel) for examples.
<a name="getnotifications"></a>
### getNotifications()
### `getNotifications()`
Retrieves notifications.
**Returns**: `Promise<NotificationsMenu>`
@@ -586,13 +579,13 @@ Retrieves notifications.
</details>
<a name="getunseennotificationscount"></a>
### getUnseenNotificationsCount()
### `getUnseenNotificationsCount()`
Retrieves unseen notifications count.
**Returns**: `Promise<number>`
<a name="getplaylist"></a>
### getPlaylist(id)
### `getPlaylist(id)`
Retrieves playlist contents.
**Returns**: `Promise<Playlist>`
@@ -615,7 +608,7 @@ Retrieves playlist contents.
</details>
<a name="gethashtag"></a>
### getHashtag(hashtag)
### `getHashtag(hashtag)`
Retrieves a given hashtag's page.
**Returns**: `Promise<HashtagFeed>`
@@ -640,7 +633,7 @@ Retrieves a given hashtag's page.
</details>
<a name="getstreamingdata"></a>
### getStreamingData(video_id, options)
### `getStreamingData(video_id, options)`
Returns deciphered streaming data.
> **Note**
@@ -666,7 +659,7 @@ console.info('Playback url:', url);
| options | `FormatOptions` | Format options |
<a name="download"></a>
### download(video_id, options?)
### `download(video_id, options?)`
Downloads a given video.
**Returns**: `Promise<ReadableStream<Uint8Array>>`
@@ -679,7 +672,7 @@ Downloads a given video.
See [`./examples/download`](https://github.com/LuanRT/YouTube.js/blob/main/examples/download) for examples.
<a name="resolveurl"></a>
### resolveURL(url)
### `resolveURL(url)`
Resolves a given url.
**Returns**: `Promise<NavigationEndpoint>`
@@ -689,7 +682,7 @@ Resolves a given url.
| url | `string` | Url to resolve |
<a name="call"></a>
### call(endpoint, args?)
### `call(endpoint, args?)`
Utility to call navigation endpoints.
**Returns**: `Promise<T extends IParsedResponse | IParsedResponse | ApiResponse>`

75
package-lock.json generated
View File

@@ -1,19 +1,20 @@
{
"name": "youtubei.js",
"version": "4.1.1",
"version": "4.3.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "youtubei.js",
"version": "4.1.1",
"version": "4.3.0",
"funding": [
"https://github.com/sponsors/LuanRT"
],
"license": "MIT",
"dependencies": {
"jintr": "^0.4.1",
"jintr": "^1.0.0",
"linkedom": "^0.14.12",
"tslib": "^2.5.0",
"undici": "^5.19.1"
},
"devDependencies": {
@@ -1574,6 +1575,12 @@
"node": ">=12 <14 || 14.2 - 14.9 || >14.10.0"
}
},
"node_modules/@yarnpkg/fslib/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"node_modules/@yarnpkg/libzip": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@yarnpkg/libzip/-/libzip-2.2.4.tgz",
@@ -1587,6 +1594,12 @@
"node": ">=12 <14 || 14.2 - 14.9 || >14.10.0"
}
},
"node_modules/@yarnpkg/libzip/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"node_modules/acorn": {
"version": "8.8.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
@@ -4394,9 +4407,9 @@
}
},
"node_modules/jintr": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/jintr/-/jintr-0.4.1.tgz",
"integrity": "sha512-R42VuIoTjsGbZuEmtT7WqyErd9JQuuV17Cg05wQwRWkQbmQNm2zO519Af1Ib7P7SBATqSMbhyu2/VcTnb3TcOg==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/jintr/-/jintr-1.0.0.tgz",
"integrity": "sha512-Kbyb5jiIzmTrbhbdjQGt+jjVzn9BPluvL3mZU5ihFQIEGjCHUA4+rsXE2PNDKmg1UlfdTn3947aSwWOVnc5UIw==",
"funding": [
"https://github.com/sponsors/LuanRT"
],
@@ -6193,10 +6206,9 @@
}
},
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
"node_modules/tsutils": {
"version": "3.21.0",
@@ -6213,6 +6225,12 @@
"typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
}
},
"node_modules/tsutils/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -7641,6 +7659,14 @@
"requires": {
"@yarnpkg/libzip": "^2.2.4",
"tslib": "^1.13.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
}
}
},
"@yarnpkg/libzip": {
@@ -7651,6 +7677,14 @@
"requires": {
"@types/emscripten": "^1.38.0",
"tslib": "^1.13.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
}
}
},
"acorn": {
@@ -9619,9 +9653,9 @@
}
},
"jintr": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/jintr/-/jintr-0.4.1.tgz",
"integrity": "sha512-R42VuIoTjsGbZuEmtT7WqyErd9JQuuV17Cg05wQwRWkQbmQNm2zO519Af1Ib7P7SBATqSMbhyu2/VcTnb3TcOg==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/jintr/-/jintr-1.0.0.tgz",
"integrity": "sha512-Kbyb5jiIzmTrbhbdjQGt+jjVzn9BPluvL3mZU5ihFQIEGjCHUA4+rsXE2PNDKmg1UlfdTn3947aSwWOVnc5UIw==",
"requires": {
"acorn": "^8.8.0"
}
@@ -10930,10 +10964,9 @@
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
"tsutils": {
"version": "3.21.0",
@@ -10942,6 +10975,14 @@
"dev": true,
"requires": {
"tslib": "^1.8.1"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
}
}
},
"type-check": {

View File

@@ -1,6 +1,6 @@
{
"name": "youtubei.js",
"version": "4.1.1",
"version": "4.3.0",
"description": "A wrapper around YouTube's private API. Supports YouTube, YouTube Music, YouTube Kids and YouTube Studio (WIP).",
"type": "module",
"types": "./dist/src/platform/lib.d.ts",
@@ -72,7 +72,7 @@
"build:proto": "npx pb-gen-ts --entry-path=\"src/proto\" --out-dir=\"src/proto/generated\" --ext-in-import=\".js\"",
"build:esm": "npx tsc",
"build:deno": "npx cpy ./src ./deno && npx cpy ./package.json ./deno && npx replace \".js';\" \".ts';\" ./deno -r && npx replace '.js\";' '.ts\";' ./deno -r && npx replace \"'linkedom';\" \"'https://esm.sh/linkedom';\" ./deno -r && npx replace \"'jintr';\" \"'https://esm.sh/jintr';\" ./deno -r && npx replace \"new Jinter.default\" \"new Jinter\" ./deno -r",
"bundle:node": "npx esbuild ./dist/src/platform/node.js --bundle --target=node10 --keep-names --format=cjs --platform=node --outfile=./bundle/node.cjs --external:jintr --external:undici --external:linkedom --sourcemap --banner:js=\"/* eslint-disable */\"",
"bundle:node": "npx esbuild ./dist/src/platform/node.js --bundle --target=node10 --keep-names --format=cjs --platform=node --outfile=./bundle/node.cjs --external:jintr --external:undici --external:linkedom --external:tslib --sourcemap --banner:js=\"/* eslint-disable */\"",
"bundle:browser": "npx esbuild ./dist/src/platform/web.js --banner:js=\"/* eslint-disable */\" --bundle --target=chrome58 --keep-names --format=esm --sourcemap --define:global=globalThis --outfile=./bundle/browser.js --platform=browser",
"bundle:browser:prod": "npm run bundle:browser -- --outfile=./bundle/browser.min.js --minify",
"prepare": "npm run build",
@@ -84,8 +84,9 @@
},
"license": "MIT",
"dependencies": {
"jintr": "^0.4.1",
"jintr": "^1.0.0",
"linkedom": "^0.14.12",
"tslib": "^2.5.0",
"undici": "^5.19.1"
},
"devDependencies": {

View File

@@ -2,8 +2,9 @@ import Text from './misc/Text.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
class Button extends YTNode {
export default class Button extends YTNode {
static type = 'Button';
text?: string;
@@ -15,7 +16,7 @@ class Button extends YTNode {
endpoint: NavigationEndpoint;
constructor(data: any) {
constructor(data: RawNode) {
super();
if (data.text) {
@@ -40,6 +41,4 @@ class Button extends YTNode {
this.endpoint = new NavigationEndpoint(data.navigationEndpoint || data.serviceEndpoint || data.command);
}
}
export default Button;
}

View File

@@ -8,7 +8,7 @@ import Menu from './menus/Menu.js';
import { YTNode } from '../helpers.js';
class GridVideo extends YTNode {
export default class GridVideo extends YTNode {
static type = 'GridVideo';
id: string;
@@ -23,10 +23,15 @@ class GridVideo extends YTNode {
short_view_count: Text;
endpoint: NavigationEndpoint;
menu: Menu | null;
buttons?;
upcoming?: Date;
upcoming_text?: Text;
is_reminder_set?: boolean;
constructor(data: RawNode) {
super();
const length_alt = data.thumbnailOverlays.find((overlay: any) => overlay.hasOwnProperty('thumbnailOverlayTimeStatusRenderer'))?.thumbnailOverlayTimeStatusRenderer;
this.id = data.videoId;
this.title = new Text(data.title);
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
@@ -39,7 +44,19 @@ class GridVideo extends YTNode {
this.short_view_count = new Text(data.shortViewCountText);
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
this.menu = Parser.parseItem(data.menu, Menu);
}
}
export default GridVideo;
if (Reflect.has(data, 'buttons')) {
this.buttons = Parser.parseArray(data.buttons);
}
if (Reflect.has(data, 'upcomingEventData')) {
this.upcoming = new Date(Number(`${data.upcomingEventData.startTime}000`));
this.upcoming_text = new Text(data.upcomingEventData.upcomingEventText);
this.is_reminder_set = !!data.upcomingEventData?.isReminderSet;
}
}
get is_upcoming(): boolean {
return Boolean(this.upcoming && this.upcoming > new Date());
}
}

View File

@@ -1,35 +1,19 @@
import Text from './misc/Text.js';
import { YTNode } from '../helpers.js';
import Parser from '../parser.js';
import GuideEntry from './GuideEntry.js';
import type { RawNode } from '../index.js';
import { YTNode } from '../helpers.js';
class GuideCollapsibleEntry extends YTNode {
export default class GuideCollapsibleEntry extends YTNode {
static type = 'GuideCollapsibleEntry';
expander_item: {
title: string,
icon_type: string
};
collapser_item: {
title: string,
icon_type: string
};
expander_item: GuideEntry | null;
collapser_item: GuideEntry | null;
expandable_items;
constructor(data: any) {
constructor(data: RawNode) {
super();
this.expander_item = {
title: new Text(data.expanderItem.guideEntryRenderer.formattedTitle).toString(),
icon_type: data.expanderItem.guideEntryRenderer.icon.iconType
};
this.collapser_item = {
title: new Text(data.collapserItem.guideEntryRenderer.formattedTitle).toString(),
icon_type: data.collapserItem.guideEntryRenderer.icon.iconType
};
this.expander_item = Parser.parseItem(data.expanderItem, GuideEntry);
this.collapser_item = Parser.parseItem(data.collapserItem, GuideEntry);
this.expandable_items = Parser.parseArray(data.expandableItems);
}
}
export default GuideCollapsibleEntry;
}

View File

@@ -1,7 +1,8 @@
import { YTNode } from '../helpers.js';
import Parser from '../parser.js';
import type { RawNode } from '../index.js';
import { YTNode } from '../helpers.js';
class GuideCollapsibleSectionEntry extends YTNode {
export default class GuideCollapsibleSectionEntry extends YTNode {
static type = 'GuideCollapsibleSectionEntry';
header_entry;
@@ -9,15 +10,11 @@ class GuideCollapsibleSectionEntry extends YTNode {
collapser_icon: string;
section_items;
constructor(data: any) {
constructor(data: RawNode) {
super();
this.header_entry = Parser.parseItem(data.headerEntry);
this.expander_icon = data.expanderIcon.iconType;
this.collapser_icon = data.collapserIcon.iconType;
this.section_items = Parser.parseArray(data.sectionItems);
}
}
export default GuideCollapsibleSectionEntry;
}

View File

@@ -1,14 +1,13 @@
import GuideEntry from './GuideEntry.js';
import type { RawNode } from '../index.js';
class GuideDownloadsEntry extends GuideEntry {
export default class GuideDownloadsEntry extends GuideEntry {
static type = 'GuideDownloadsEntry';
always_show: boolean;
constructor(data: any) {
constructor(data: RawNode) {
super(data.entryRenderer.guideEntryRenderer);
this.always_show = !!data.alwaysShow;
}
}
export default GuideDownloadsEntry;
}

View File

@@ -1,9 +1,11 @@
import Text from './misc/Text.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import { YTNode } from '../helpers.js';
import Text from './misc/Text.js';
import Thumbnail from './misc/Thumbnail.js';
class GuideEntry extends YTNode {
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
export default class GuideEntry extends YTNode {
static type = 'GuideEntry';
title: Text;
@@ -13,21 +15,24 @@ class GuideEntry extends YTNode {
badges?: any;
is_primary: boolean;
constructor(data: any) {
constructor(data: RawNode) {
super();
this.title = new Text(data.formattedTitle);
this.endpoint = new NavigationEndpoint(data.navigationEndpoint || data.serviceEndpoint);
if (data.icon?.iconType) {
if (Reflect.has(data, 'icon') && Reflect.has(data.icon, 'iconType')) {
this.icon_type = data.icon.iconType;
}
if (data.thumbnail) {
if (Reflect.has(data, 'thumbnail')) {
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
}
if (data.badges) {
// (LuanRT) XXX: Check this property's data and parse it.
if (Reflect.has(data, 'badges')) {
this.badges = data.badges;
}
this.is_primary = !!data.isPrimary;
}
}
export default GuideEntry;
}

View File

@@ -1,20 +1,20 @@
import Text from './misc/Text.js';
import { YTNode } from '../helpers.js';
import Parser from '../parser.js';
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
class GuideSection extends YTNode {
export default class GuideSection extends YTNode {
static type = 'GuideSection';
title?: Text;
items;
constructor(data: any) {
constructor(data: RawNode) {
super();
if (data.formattedTitle) {
if (Reflect.has(data, 'formattedTitle')) {
this.title = new Text(data.formattedTitle);
}
this.items = Parser.parseArray(data.items);
}
}
export default GuideSection;
}

View File

@@ -1,7 +1,5 @@
import GuideSection from './GuideSection.js';
class GuideSubscriptionsSection extends GuideSection {
export default class GuideSubscriptionsSection extends GuideSection {
static type = 'GuideSubscriptionsSection';
}
export default GuideSubscriptionsSection;
}

View File

@@ -1,33 +1,33 @@
// TODO: this needs a refactor
// Seems like a mess to use
// TODO: Clean up and refactor this.
import Parser from '../index.js';
import Text from './misc/Text.js';
import TextRun from './misc/TextRun.js';
import Thumbnail from './misc/Thumbnail.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import MusicItemThumbnailOverlay from './MusicItemThumbnailOverlay.js';
import MusicResponsiveListItemFlexColumn from './MusicResponsiveListItemFlexColumn.js';
import MusicResponsiveListItemFixedColumn from './MusicResponsiveListItemFixedColumn.js';
import MusicResponsiveListItemFlexColumn from './MusicResponsiveListItemFlexColumn.js';
import MusicThumbnail from './MusicThumbnail.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import Menu from './menus/Menu.js';
import Text from './misc/Text.js';
import { timeToSeconds } from '../../utils/Utils.js';
import { isTextRun, timeToSeconds } from '../../utils/Utils.js';
import type { ObservedArray } from '../helpers.js';
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
class MusicResponsiveListItem extends YTNode {
export default class MusicResponsiveListItem extends YTNode {
static type = 'MusicResponsiveListItem';
#flex_columns;
#fixed_columns;
flex_columns: ObservedArray<MusicResponsiveListItemFlexColumn>;
fixed_columns: ObservedArray<MusicResponsiveListItemFixedColumn>;
#playlist_item_data;
endpoint;
item_type;
index;
thumbnails;
endpoint: NavigationEndpoint | null;
item_type: 'album' | 'playlist' | 'artist' | 'library_artist' | 'video' | 'song' | 'endpoint' | 'unknown' | undefined;
index?: Text;
thumbnail?: MusicThumbnail | null;
badges;
menu;
overlay;
menu?: Menu | null;
overlay?: MusicItemThumbnailOverlay | null;
id?: string;
title?: string;
@@ -59,19 +59,20 @@ class MusicResponsiveListItem extends YTNode {
subtitle?: Text;
subscribers?: string;
song_count?: string;
// TODO: these might be replaceable with Author class
author?: {
name: string,
channel_id?: string
endpoint?: NavigationEndpoint
};
item_count?: string | undefined;
item_count?: string;
year?: string;
constructor(data: any) {
constructor(data: RawNode) {
super();
this.#flex_columns = Parser.parseArray<MusicResponsiveListItemFlexColumn>(data.flexColumns, MusicResponsiveListItemFlexColumn);
this.#fixed_columns = Parser.parseArray<MusicResponsiveListItemFixedColumn>(data.fixedColumns, MusicResponsiveListItemFixedColumn);
this.flex_columns = Parser.parseArray(data.flexColumns, MusicResponsiveListItemFlexColumn);
this.fixed_columns = Parser.parseArray(data.fixedColumns, MusicResponsiveListItemFixedColumn);
this.#playlist_item_data = {
video_id: data?.playlistItemData?.videoId || null,
@@ -101,7 +102,7 @@ class MusicResponsiveListItem extends YTNode {
this.#parseLibraryArtist();
break;
default:
if (this.#flex_columns[1]) {
if (this.flex_columns[1]) {
this.#parseVideoOrSong();
} else {
this.#parseOther();
@@ -113,14 +114,14 @@ class MusicResponsiveListItem extends YTNode {
this.index = new Text(data.index);
}
this.thumbnails = data.thumbnail ? Thumbnail.fromResponse(data.thumbnail.musicThumbnailRenderer?.thumbnail) : [];
this.thumbnail = Parser.parseItem(data.thumbnail, MusicThumbnail);
this.badges = Parser.parseArray(data.badges);
this.menu = Parser.parseItem<Menu>(data.menu, Menu);
this.overlay = Parser.parseItem<MusicItemThumbnailOverlay>(data.overlay, MusicItemThumbnailOverlay);
this.menu = Parser.parseItem(data.menu, Menu);
this.overlay = Parser.parseItem(data.overlay, MusicItemThumbnailOverlay);
}
#parseOther() {
this.title = this.#flex_columns[0].key('title').instanceof(Text).toString();
this.title = this.flex_columns.first().key('title').instanceof(Text).toString();
if (this.endpoint) {
this.item_type = 'endpoint';
@@ -130,7 +131,7 @@ class MusicResponsiveListItem extends YTNode {
}
#parseVideoOrSong() {
const is_video = this.#flex_columns[1].key('title').instanceof(Text).runs?.some((run) => run.text.match(/(.*?) views/));
const is_video = this.flex_columns.at(1)?.key('title').instanceof(Text).runs?.some((run) => run.text.match(/(.*?) views/));
if (is_video) {
this.item_type = 'video';
this.#parseVideo();
@@ -142,105 +143,144 @@ class MusicResponsiveListItem extends YTNode {
#parseSong() {
this.id = this.#playlist_item_data.video_id || this.endpoint?.payload?.videoId;
this.title = this.#flex_columns[0].key('title').instanceof(Text).toString();
this.title = this.flex_columns.first().key('title').instanceof(Text).toString();
const duration_text =
this.#flex_columns[1].key('title').instanceof(Text).runs?.find((run) => (/^\d+$/).test(run.text.replace(/:/g, '')))?.text ||
this.#fixed_columns?.[0]?.key('title').instanceof(Text)?.toString();
const duration_text = this.flex_columns.at(1)?.key('title').instanceof(Text).runs?.find(
(run) => (/^\d+$/).test(run.text.replace(/:/g, '')))?.text || this.fixed_columns.first()?.key('title').instanceof(Text)?.toString();
duration_text && (this.duration = {
text: duration_text,
seconds: timeToSeconds(duration_text)
});
const album = this.#flex_columns[1].key('title').instanceof(Text).runs?.find((run) => Reflect.get(run, 'endpoint')?.payload?.browseId.startsWith('MPR')) as TextRun ||
this.#flex_columns[2]?.key('title').instanceof(Text).runs?.find((run) => Reflect.get(run, 'endpoint')?.payload?.browseId.startsWith('MPR')) as TextRun;
if (album) {
this.album = {
id: album.endpoint?.payload?.browseId,
name: album.text,
endpoint: album.endpoint
if (duration_text) {
this.duration = {
text: duration_text,
seconds: timeToSeconds(duration_text)
};
}
const artists = this.#flex_columns[1].key('title').instanceof(Text).runs?.filter((run) => Reflect.get(run, 'endpoint')?.payload?.browseId.startsWith('UC')) as TextRun[];
if (artists) {
this.artists = artists.map((artist) => ({
name: artist.text,
channel_id: artist.endpoint?.payload?.browseId,
endpoint: artist.endpoint
const album_run =
this.flex_columns.at(1)?.key('title').instanceof(Text).runs?.find(
(run) =>
(isTextRun(run) && run.endpoint) &&
run.endpoint.payload.browseId.startsWith('MPR')
) ||
this.flex_columns.at(2)?.key('title').instanceof(Text).runs?.find(
(run) =>
(isTextRun(run) && run.endpoint) &&
run.endpoint.payload.browseId.startsWith('MPR')
);
if (album_run && isTextRun(album_run)) {
this.album = {
id: album_run.endpoint?.payload?.browseId,
name: album_run.text,
endpoint: album_run.endpoint
};
}
const artist_runs = this.flex_columns.at(1)?.key('title').instanceof(Text).runs?.filter(
(run) => (isTextRun(run) && run.endpoint) && run.endpoint.payload.browseId.startsWith('UC')
);
if (artist_runs) {
this.artists = artist_runs.map((run) => ({
name: run.text,
channel_id: isTextRun(run) ? run.endpoint?.payload?.browseId : undefined,
endpoint: isTextRun(run) ? run.endpoint : undefined
}));
}
}
#parseVideo() {
this.id = this.#playlist_item_data.video_id;
this.title = this.#flex_columns[0].key('title').instanceof(Text).toString();
this.views = this.#flex_columns[1].key('title').instanceof(Text).runs?.find((run) => run.text.match(/(.*?) views/))?.text;
this.title = this.flex_columns.first().key('title').instanceof(Text).toString();
this.views = this.flex_columns.at(1)?.key('title').instanceof(Text).runs?.find((run) => run.text.match(/(.*?) views/))?.toString();
const authors = this.#flex_columns[1].key('title').instanceof(Text).runs?.filter((run) => Reflect.get(run, 'endpoint')?.payload?.browseId.startsWith('UC')) as TextRun[];
if (authors) {
this.authors = authors.map((author) => ({
name: author.text,
channel_id: author.endpoint?.payload?.browseId,
endpoint: author.endpoint
}));
const author_runs = this.flex_columns.at(1)?.key('title').instanceof(Text).runs?.filter(
(run) =>
(isTextRun(run) && run.endpoint) &&
run.endpoint.payload.browseId.startsWith('UC')
);
if (author_runs) {
this.authors = author_runs.map((run) => {
return {
name: run.text,
channel_id: isTextRun(run) ? run.endpoint?.payload?.browseId : undefined,
endpoint: isTextRun(run) ? run.endpoint : undefined
};
});
}
const duration_text = this.#flex_columns[1].key('title').instanceof(Text).runs?.find((run) => (/^\d+$/).test(run.text.replace(/:/g, '')))?.text ||
this.#fixed_columns[0]?.key('title').instanceof(Text).runs?.find((run) => (/^\d+$/).test(run.text.replace(/:/g, '')))?.text;
duration_text && (this.duration = {
text: duration_text,
seconds: timeToSeconds(duration_text)
});
const duration_text = this.flex_columns[1].key('title').instanceof(Text).runs?.find(
(run) => (/^\d+$/).test(run.text.replace(/:/g, '')))?.text || this.fixed_columns.first()?.key('title').instanceof(Text).runs?.find((run) => (/^\d+$/).test(run.text.replace(/:/g, '')))?.text;
if (duration_text) {
this.duration = {
text: duration_text,
seconds: timeToSeconds(duration_text)
};
}
}
#parseArtist() {
this.id = this.endpoint?.payload?.browseId;
this.name = this.#flex_columns[0].key('title').instanceof(Text).toString();
this.subtitle = this.#flex_columns[1].key('title').instanceof(Text);
this.subscribers = this.subtitle.runs?.find((run) => (/^(\d*\.)?\d+[M|K]? subscribers?$/i).test(run.text))?.text || '';
this.name = this.flex_columns.first().key('title').instanceof(Text).toString();
this.subtitle = this.flex_columns.at(1)?.key('title').instanceof(Text);
this.subscribers = this.subtitle?.runs?.find((run) => (/^(\d*\.)?\d+[M|K]? subscribers?$/i).test(run.text))?.text || '';
}
#parseLibraryArtist() {
this.name = this.#flex_columns[0].key('title').instanceof(Text).toString();
this.subtitle = this.#flex_columns[1].key('title').instanceof(Text);
this.name = this.flex_columns.first().key('title').instanceof(Text).toString();
this.subtitle = this.flex_columns.at(1)?.key('title').instanceof(Text);
this.song_count = this.subtitle?.runs?.find((run) => (/^\d+(,\d+)? songs?$/i).test(run.text))?.text || '';
}
#parseAlbum() {
this.id = this.endpoint?.payload?.browseId;
this.title = this.#flex_columns[0].key('title').instanceof(Text).toString();
this.title = this.flex_columns.first().title.toString();
const author = this.#flex_columns[1].key('title').instanceof(Text).runs?.find((run) => Reflect.get(run, 'endpoint')?.payload?.browseId.startsWith('UC')) as TextRun;
author && (this.author = {
name: author.text,
channel_id: author.endpoint?.payload?.browseId,
endpoint: author.endpoint
});
const author_run = this.flex_columns.at(1)?.key('title').instanceof(Text).runs?.find(
(run) =>
(isTextRun(run) && run.endpoint) &&
run.endpoint.payload.browseId.startsWith('UC')
);
this.year = this.#flex_columns[1].key('title').instanceof(Text).runs?.find((run) => (/^[12][0-9]{3}$/).test(run.text))?.text;
if (author_run && isTextRun(author_run)) {
this.author = {
name: author_run.text,
channel_id: author_run.endpoint?.payload?.browseId,
endpoint: author_run.endpoint
};
}
this.year = this.flex_columns.at(1)?.key('title').instanceof(Text).runs?.find(
(run) => (/^[12][0-9]{3}$/).test(run.text)
)?.text;
}
#parsePlaylist() {
this.id = this.endpoint?.payload?.browseId;
this.title = this.#flex_columns[0].key('title').instanceof(Text).toString();
this.title = this.flex_columns.first().title.toString();
const item_count_run = this.#flex_columns[1].key('title')
const item_count_run = this.flex_columns.at(1)?.key('title')
.instanceof(Text).runs?.find((run) => run.text.match(/\d+ (song|songs)/));
this.item_count = item_count_run ? item_count_run.text : undefined;
const author = this.#flex_columns[1].key('title').instanceof(Text).runs?.find((run) => Reflect.get(run, 'endpoint')?.payload?.browseId.startsWith('UC')) as TextRun;
const author_run = this.flex_columns.at(1)?.key('title').instanceof(Text).runs?.find(
(run) =>
(isTextRun(run) && run.endpoint) &&
run.endpoint.payload.browseId.startsWith('UC')
);
if (author) {
if (author_run && isTextRun(author_run)) {
this.author = {
name: author.text,
channel_id: author.endpoint?.payload?.browseId,
endpoint: author.endpoint
name: author_run.text,
channel_id: author_run.endpoint?.payload?.browseId,
endpoint: author_run.endpoint
};
}
}
}
export default MusicResponsiveListItem;
get thumbnails() {
return this.thumbnail?.contents || [];
}
}

View File

@@ -0,0 +1,26 @@
import Parser from '../index.js';
import Button from './Button.js';
import Text from './misc/Text.js';
import MusicTastebuilderShelfThumbnail from './MusicTastebuilderShelfThumbnail.js';
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
export default class MusicTasteBuilderShelf extends YTNode {
static type = 'MusicTasteBuilderShelf';
thumbnail: MusicTastebuilderShelfThumbnail | null;
primary_text: Text;
secondary_text: Text;
action_button: Button | null;
is_visible: boolean;
constructor(data: RawNode) {
super();
this.thumbnail = Parser.parseItem(data.thumbnail, MusicTastebuilderShelfThumbnail);
this.primary_text = new Text(data.primaryText);
this.secondary_text = new Text(data.secondaryText);
this.action_button = Parser.parseItem(data.actionButton, Button);
this.is_visible = data.isVisible;
}
}

View File

@@ -0,0 +1,14 @@
import { YTNode } from '../helpers.js';
import { Thumbnail } from '../misc.js';
import type { RawNode } from '../index.js';
export default class MusicTastebuilderShelfThumbnail extends YTNode {
static type = 'MusicTastebuilderShelfThumbnail';
thumbnail: Thumbnail[];
constructor(data: RawNode) {
super();
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
}
}

View File

@@ -22,6 +22,8 @@ class PlaylistVideo extends YTNode {
is_playable: boolean;
menu: Menu | null;
upcoming;
video_info: Text;
accessibility_label?: string;
duration: {
text: string;
@@ -40,6 +42,8 @@ class PlaylistVideo extends YTNode {
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
this.is_playable = data.isPlayable;
this.menu = Parser.parseItem(data.menu, Menu);
this.video_info = new Text(data.videoInfo);
this.accessibility_label = data.title.accessibility.accessibilityData.label;
const upcoming = data.upcomingEventData && Number(`${data.upcomingEventData.startTime}000`);
if (upcoming) {

View File

@@ -1,24 +1,25 @@
import Text from './misc/Text.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
class ToggleMenuServiceItem extends YTNode {
export default class ToggleMenuServiceItem extends YTNode {
static type = 'ToggleMenuServiceItem';
text: Text;
toggled_text: Text;
icon_type: string;
toggled_icon_type: string;
endpoint: NavigationEndpoint;
default_endpoint: NavigationEndpoint;
toggled_endpoint: NavigationEndpoint;
constructor(data: any) {
constructor(data: RawNode) {
super();
this.text = new Text(data.defaultText);
this.toggled_text = new Text(data.toggledText);
this.icon_type = data.defaultIcon.iconType;
this.toggled_icon_type = data.toggledIcon.iconType;
this.endpoint = new NavigationEndpoint(data.toggledServiceEndpoint);
this.default_endpoint = new NavigationEndpoint(data.defaultServiceEndpoint);
this.toggled_endpoint = new NavigationEndpoint(data.toggledServiceEndpoint);
}
}
export default ToggleMenuServiceItem;
}

View File

@@ -1,5 +1,6 @@
import Button from '../Button.js';
import type { RawNode } from '../../index.js';
class MenuNavigationItem extends Button {
static type = 'MenuNavigationItem';

View File

@@ -219,6 +219,8 @@ export { default as MusicResponsiveListItemFlexColumn } from './classes/MusicRes
export { default as MusicShelf } from './classes/MusicShelf.js';
export { default as MusicSideAlignedItem } from './classes/MusicSideAlignedItem.js';
export { default as MusicSortFilterButton } from './classes/MusicSortFilterButton.js';
export { default as MusicTastebuilderShelf } from './classes/MusicTastebuilderShelf.js';
export { default as MusicTastebuilderShelfThumbnail } from './classes/MusicTastebuilderShelfThumbnail.js';
export { default as MusicThumbnail } from './classes/MusicThumbnail.js';
export { default as MusicTwoRowItem } from './classes/MusicTwoRowItem.js';
export { default as MusicVisualHeader } from './classes/MusicVisualHeader.js';

View File

@@ -497,6 +497,7 @@ export default class Parser {
'DisplayAd',
'SearchPyv',
'MealbarPromo',
'PrimetimePromo',
'BackgroundPromo',
'PromotedSparklesWeb',
'RunAttestationCommand',

View File

@@ -1,20 +1,21 @@
import { InnertubeError } from '../../utils/Utils.js';
import Parser, { SectionListContinuation } from '../index.js';
import MusicCarouselShelf from '../classes/MusicCarouselShelf.js';
import SectionList from '../classes/SectionList.js';
import SingleColumnBrowseResults from '../classes/SingleColumnBrowseResults.js';
import Parser, { SectionListContinuation } from '../index.js';
import MusicTastebuilderShelf from '../classes/MusicTastebuilderShelf.js';
import type Actions from '../../core/Actions.js';
import type { ApiResponse } from '../../core/Actions.js';
import type { ObservedArray } from '../helpers.js';
import type { IBrowseResponse } from '../types/ParsedResponse.js';
import { InnertubeError } from '../../utils/Utils.js';
class HomeFeed {
#page: IBrowseResponse;
#actions: Actions;
#continuation?: string;
sections?: ObservedArray<MusicCarouselShelf>;
sections?: ObservedArray<MusicCarouselShelf | MusicTastebuilderShelf>;
constructor(response: ApiResponse, actions: Actions) {
this.#actions = actions;
@@ -36,7 +37,7 @@ class HomeFeed {
}
this.#continuation = tab.content?.as(SectionList).continuation;
this.sections = tab.content?.as(SectionList).contents.as(MusicCarouselShelf);
this.sections = tab.content?.as(SectionList).contents.as(MusicCarouselShelf, MusicTastebuilderShelf);
}
/**

View File

@@ -1,8 +1,8 @@
import Jinter from 'jintr';
import { Jinter } from 'jintr';
import { VMPrimative } from '../../types/PlatformShim.js';
export default function evaluate(code: string, env: Record<string, VMPrimative>) {
const runtime = new Jinter.default(code);
const runtime = new Jinter(code);
for (const [ key, value ] of Object.entries(env)) {
runtime.scope.set(key, value);
}

View File

@@ -1,4 +1,5 @@
import { Memo } from '../parser/helpers.js';
import { EmojiRun, TextRun } from '../parser/misc.js';
import PlatformShim, { FetchFunction } from '../types/PlatformShim.js';
import userAgents from './user-agents.js';
@@ -221,4 +222,8 @@ export function u8ToBase64(u8: Uint8Array): string {
export function base64ToU8(base64: string): Uint8Array {
return new Uint8Array(atob(base64).split('').map((char) => char.charCodeAt(0)));
}
export function isTextRun(run: TextRun | EmojiRun): run is TextRun {
return !('emoji' in run);
}

View File

@@ -52,7 +52,7 @@
"outDir": "./dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
"importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
@@ -102,9 +102,7 @@
},
"include": [
"src/**/*.ts",
"src/**/*.js",
"index.ts",
"browser.ts"
"src/**/*.js"
],
"exclude": [
"node_modules",