mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-14 18:12:10 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b5027eb06 | ||
|
|
0c9f7135bf | ||
|
|
ce8a109398 | ||
|
|
6aaa3360e8 | ||
|
|
89c018c431 | ||
|
|
339a01f3a9 | ||
|
|
dd3f4c0009 | ||
|
|
7cd41e1d8a | ||
|
|
6ac8561af2 | ||
|
|
b4607d531f | ||
|
|
b3a1cdc1cd | ||
|
|
fd662df93d | ||
|
|
8a1f4b4e55 | ||
|
|
4ff83bdc3f | ||
|
|
c81e8e29ac | ||
|
|
d5f884ff9b | ||
|
|
5517c2f202 | ||
|
|
3493a82765 |
681
README.md
681
README.md
@@ -20,7 +20,7 @@ As of now, this is one of the most advanced & stable YouTube libraries out there
|
||||
- Get detailed info about any video
|
||||
- Fetch live chat & live stats in real time
|
||||
- Get notifications
|
||||
- Get subscriptions feed
|
||||
- Get subscriptions/home feed
|
||||
- Change notification preferences for a channel
|
||||
- Subscribe/Unsubscribe/Like/Dislike/Comment etc
|
||||
- Easily sign in to any Google Account
|
||||
@@ -40,17 +40,17 @@ npm install youtubei.js
|
||||
|
||||
## Usage
|
||||
|
||||
[1. Basic Usage](https://github.com/LuanRT/YouTube.js#usage)
|
||||
[1. Basic Usage](#usage)
|
||||
|
||||
[2. Interactions](https://github.com/LuanRT/YouTube.js#interactions)
|
||||
[2. Interactions](#interactions)
|
||||
|
||||
[3. Live chats](https://github.com/LuanRT/YouTube.js#fetching-live-chats)
|
||||
[3. Live chats](#fetching-live-chats)
|
||||
|
||||
[4. Downloading videos](https://github.com/LuanRT/YouTube.js#downloading-videos)
|
||||
[4. Downloading videos](#downloading-videos)
|
||||
|
||||
[5. Signing-in](https://github.com/LuanRT/YouTube.js#signing-in)
|
||||
[5. Signing-in](#signing-in)
|
||||
|
||||
[6. Disclaimer](https://github.com/LuanRT/YouTube.js#disclaimer)
|
||||
[6. Disclaimer](#disclaimer)
|
||||
|
||||
First of all we're gonna start by initializing the Innertube instance.
|
||||
And to make things faster, you should do this only once and reuse the Innertube object when needed.
|
||||
@@ -62,11 +62,15 @@ const youtube = await new Innertube();
|
||||
Doing a simple search:
|
||||
|
||||
```js
|
||||
// YouTube:
|
||||
const search = await youtube.search('Looking for life on Mars - Documentary');
|
||||
|
||||
// YTMusic:
|
||||
const search = await youtube.search('Never Gonna Give You Up', { client: 'YTMUSIC' });
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Output</summary>
|
||||
<summary>YT Search Output</summary>
|
||||
<p>
|
||||
|
||||
```js
|
||||
@@ -124,9 +128,233 @@ const search = await youtube.search('Looking for life on Mars - Documentary');
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YTMusic Search Output</summary>
|
||||
<p>
|
||||
|
||||
|
||||
Get details about a specific video:
|
||||
```js
|
||||
{
|
||||
"songs":[
|
||||
{
|
||||
"id":"AhCWdFBd_fM",
|
||||
"title":"Never Gonna Give You Up",
|
||||
"artist":"Barry White",
|
||||
"album":"Barry White - His Greatest Hits",
|
||||
"duration":"4:50",
|
||||
"thumbnail":{
|
||||
"thumbnails":[
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/S69XTGGI63L8rJJlCx7rJgp0decIo3EdDKGWR_y8B3SE9fEYp-VGu3ygZ3DrlS3tTTyqN8CUfpajjsA=w60-h60-l90-rj",
|
||||
"width":60,
|
||||
"height":60
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/S69XTGGI63L8rJJlCx7rJgp0decIo3EdDKGWR_y8B3SE9fEYp-VGu3ygZ3DrlS3tTTyqN8CUfpajjsA=w120-h120-l90-rj",
|
||||
"width":120,
|
||||
"height":120
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"C5ITdcx-wRA",
|
||||
"title":"Never Gonna Give You Up",
|
||||
"artist":"Home Free",
|
||||
"album":"Never Gonna Give You Up",
|
||||
"duration":"3:03",
|
||||
"thumbnail":{
|
||||
"thumbnails":[
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/isnABE6M8Dzq5WG7ZrJRGdKVmF8M8xJDJTM6aMf7d0C-XpqGIHFd1n7i6lgZM_K7LbHXuEuRaXhfnqc=w60-h60-l90-rj",
|
||||
"width":60,
|
||||
"height":60
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/isnABE6M8Dzq5WG7ZrJRGdKVmF8M8xJDJTM6aMf7d0C-XpqGIHFd1n7i6lgZM_K7LbHXuEuRaXhfnqc=w120-h120-l90-rj",
|
||||
"width":120,
|
||||
"height":120
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"XxWzZxuo6Vk",
|
||||
"title":"Never Gonna Give You Up",
|
||||
"artist":"Barry White",
|
||||
"album":"Dinah Washington",
|
||||
"duration":"The Unforgettable Voices: 30 Best Of Dinah Washington & Barry White",
|
||||
"thumbnail":{
|
||||
"thumbnails":[
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/KXnlog1Me9ONfcdtT1O4fHr2uRqYrhH1cHWksFmzNpJwzX8lNr_S5TlQsE8ICtihijkGCLp9y8ypI7BiOA=w60-h60-l90-rj",
|
||||
"width":60,
|
||||
"height":60
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/KXnlog1Me9ONfcdtT1O4fHr2uRqYrhH1cHWksFmzNpJwzX8lNr_S5TlQsE8ICtihijkGCLp9y8ypI7BiOA=w120-h120-l90-rj",
|
||||
"width":120,
|
||||
"height":120
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"videos":[
|
||||
{
|
||||
"id":"dMbZfdrzILA",
|
||||
"title":"Rick Astley - Never Gonna Give You Up • TopPop",
|
||||
"author":"TopPop",
|
||||
"views":"313K views",
|
||||
"duration":"3:25",
|
||||
"thumbnail":{
|
||||
"thumbnails":[
|
||||
{
|
||||
"url":"https://i.ytimg.com/vi/dMbZfdrzILA/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kLVbR-3q7t0AIjayar6gbsUe-OoQ",
|
||||
"width":400,
|
||||
"height":225
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"IO9XlQrEt2Y",
|
||||
"title":"Never Gonna Give You Up",
|
||||
"author":"Rick Astley",
|
||||
"views":"7.1M views",
|
||||
"duration":"2:58",
|
||||
"thumbnail":{
|
||||
"thumbnails":[
|
||||
{
|
||||
"url":"https://i.ytimg.com/vi/IO9XlQrEt2Y/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3mqRLVI-_NB9-t7Qy-j2J030ejrKA",
|
||||
"width":400,
|
||||
"height":225
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"a9WHZ5M8I8w",
|
||||
"title":"Rick Astley - Never Gonna Give You Up - Festival de Viña del Mar 2016 HD",
|
||||
"author":"FESTIVALDEVINACHILE",
|
||||
"views":"2.8M views",
|
||||
"duration":"8:13",
|
||||
"thumbnail":{
|
||||
"thumbnails":[
|
||||
{
|
||||
"url":"https://i.ytimg.com/vi/a9WHZ5M8I8w/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3mJDPxemHrcREbbK-9uBM3SIRm-Og",
|
||||
"width":400,
|
||||
"height":225
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"albums":[
|
||||
{
|
||||
"title":"Never Gonna Give You Up",
|
||||
"author":"Royal Reggie",
|
||||
"year":"2022",
|
||||
"thumbnail":{
|
||||
"thumbnails":[
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/DeMUgtcPxCOye_JYJaywQjuJHZjdzSYqU-231p8ZADV1EmPC5w3poMQRfm3_U83wLQR0nUbXnJ_Ujyq8=w60-h60-l90-rj",
|
||||
"width":60,
|
||||
"height":60
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/DeMUgtcPxCOye_JYJaywQjuJHZjdzSYqU-231p8ZADV1EmPC5w3poMQRfm3_U83wLQR0nUbXnJ_Ujyq8=w120-h120-l90-rj",
|
||||
"width":120,
|
||||
"height":120
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/DeMUgtcPxCOye_JYJaywQjuJHZjdzSYqU-231p8ZADV1EmPC5w3poMQRfm3_U83wLQR0nUbXnJ_Ujyq8=w226-h226-l90-rj",
|
||||
"width":226,
|
||||
"height":226
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/DeMUgtcPxCOye_JYJaywQjuJHZjdzSYqU-231p8ZADV1EmPC5w3poMQRfm3_U83wLQR0nUbXnJ_Ujyq8=w544-h544-l90-rj",
|
||||
"width":544,
|
||||
"height":544
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"title":"Never Gonna Give You Up",
|
||||
"author":"Carlos Marin",
|
||||
"year":"2020",
|
||||
"thumbnail":{
|
||||
"thumbnails":[
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/i6JslNpviVqPZfLCBvLvmDyscy8ZQ2CShRz38hGj-PUz_H4wLTR0gkKU6tQoREDoARwKBl1krytJdBQ=w60-h60-l90-rj",
|
||||
"width":60,
|
||||
"height":60
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/i6JslNpviVqPZfLCBvLvmDyscy8ZQ2CShRz38hGj-PUz_H4wLTR0gkKU6tQoREDoARwKBl1krytJdBQ=w120-h120-l90-rj",
|
||||
"width":120,
|
||||
"height":120
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/i6JslNpviVqPZfLCBvLvmDyscy8ZQ2CShRz38hGj-PUz_H4wLTR0gkKU6tQoREDoARwKBl1krytJdBQ=w226-h226-l90-rj",
|
||||
"width":226,
|
||||
"height":226
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/i6JslNpviVqPZfLCBvLvmDyscy8ZQ2CShRz38hGj-PUz_H4wLTR0gkKU6tQoREDoARwKBl1krytJdBQ=w544-h544-l90-rj",
|
||||
"width":544,
|
||||
"height":544
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"title":"Never Gonna Give You Up",
|
||||
"author":"Magnus Carlsson",
|
||||
"year":"2021",
|
||||
"thumbnail":{
|
||||
"thumbnails":[
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/43cP--d3Eqj3L5FeBbYFkdsnisf7nYb5-Rq4yZDf1kvH6_pEk4RW4Nd004L4ZwG6VzzT4JzFDVPS-8w=w60-h60-l90-rj",
|
||||
"width":60,
|
||||
"height":60
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/43cP--d3Eqj3L5FeBbYFkdsnisf7nYb5-Rq4yZDf1kvH6_pEk4RW4Nd004L4ZwG6VzzT4JzFDVPS-8w=w120-h120-l90-rj",
|
||||
"width":120,
|
||||
"height":120
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/43cP--d3Eqj3L5FeBbYFkdsnisf7nYb5-Rq4yZDf1kvH6_pEk4RW4Nd004L4ZwG6VzzT4JzFDVPS-8w=w226-h226-l90-rj",
|
||||
"width":226,
|
||||
"height":226
|
||||
},
|
||||
{
|
||||
"url":"https://lh3.googleusercontent.com/43cP--d3Eqj3L5FeBbYFkdsnisf7nYb5-Rq4yZDf1kvH6_pEk4RW4Nd004L4ZwG6VzzT4JzFDVPS-8w=w544-h544-l90-rj",
|
||||
"width":544,
|
||||
"height":544
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
<br>
|
||||
|
||||
Find lyrics for a song, directly from YTMusic!
|
||||
```js
|
||||
const search = await youtube.search('7 Years', { client: 'YTMUSIC' });
|
||||
const lyrics = await search.songs[0].getLyrics();
|
||||
|
||||
// Or:
|
||||
const lyrics = await youtube.getLyrics(search.songs[0].id);
|
||||
```
|
||||
Get video details:
|
||||
|
||||
```js
|
||||
const video = await youtube.getDetails(search.videos[0].id);
|
||||
@@ -276,9 +504,442 @@ const comments_continuation = await comments.getContinuation();
|
||||
</p>
|
||||
</details>
|
||||
|
||||
Get home feed:
|
||||
```js
|
||||
const home_feed = await youtube.getHomeFeed();
|
||||
```
|
||||
<details>
|
||||
<summary>Output</summary>
|
||||
<p>
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
"id":"TVcKu_c1C7E",
|
||||
"title":"The Things We Don't Know About the Universe",
|
||||
"channel":"Sciencephile the AI",
|
||||
"metadata":{
|
||||
"view_count":"233,835 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/TVcKu_c1C7E/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLByf2X2pKs1wbWyLIx-FnXFt1PVZw",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/TVcKu_c1C7E/mqdefault_6s.webp?du=3000&sqp=COGe-48G&rs=AOn4CLDslQ2RYSgaF3aEY8pcuyIzvfLJbQ",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"3 months ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"1Tdx9ZZuf_w",
|
||||
"title":"Life is Strange: Before The Storm Remastered Episode 1 Gameplay Walkthrough",
|
||||
"channel":"DomTheBomb",
|
||||
"metadata":{
|
||||
"view_count":"N/A",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/1Tdx9ZZuf_w/hq720_live.jpg?sqp=CMzD-48G-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLC9N4GPouauc-Jw3Y69uCXWVEfBiQ",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":[
|
||||
|
||||
],
|
||||
"published":"N/A",
|
||||
"badges":[
|
||||
"LIVE NOW"
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"C776bXWlgSk",
|
||||
"title":"Probably my Most BRUTAL BIRDEATER TARANTULA ever .",
|
||||
"channel":"Exotics Lair",
|
||||
"metadata":{
|
||||
"view_count":"346,246 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/C776bXWlgSk/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDgWQrLwmFTG-KzLUYRdR9KFA5bDw",
|
||||
"width":480,
|
||||
"height":270
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/C776bXWlgSk/mqdefault_6s.webp?du=3000&sqp=COal-48G&rs=AOn4CLAfsyW-NUXvgFqAHBJr_3pJDZlfhA",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"5 months ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"nrvnpFCcZeA",
|
||||
"title":"Run Windows 11 on phone! And play PC games?!!!",
|
||||
"channel":"Geekerwan",
|
||||
"metadata":{
|
||||
"view_count":"519,217 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/nrvnpFCcZeA/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA0ny_9_K9iLL_u7grHZ4PoAJkH4Q",
|
||||
"width":480,
|
||||
"height":270
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/nrvnpFCcZeA/mqdefault_6s.webp?du=3000&sqp=CKCR-48G&rs=AOn4CLB9atuuBjdMCWTHgP-v4N75agxvPA",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"1 month ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"owtvGIQDdiU",
|
||||
"title":"This Is What The Clouds Of Titan Sound Like! Huygens Probe Sound Recording 2005 (4K UHD)",
|
||||
"channel":"V101 Science",
|
||||
"metadata":{
|
||||
"view_count":"744,350 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/owtvGIQDdiU/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDbQLvaaOO3jW9bGY1SaQAdxIUJOQ",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/owtvGIQDdiU/mqdefault_6s.webp?du=3000&sqp=CImy-48G&rs=AOn4CLDXD3UPQ6iix8orhjJs3yDPpJlckQ",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"1 year ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"pI7ytZ1oyY4",
|
||||
"title":"My Pet Praying Mantis Passed Away. Goodbye, Kiwi..",
|
||||
"channel":"제발돼라 PleaseBee",
|
||||
"metadata":{
|
||||
"view_count":"3,311,251 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/pI7ytZ1oyY4/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDisjK7ZecKov3aFUsE-Xe5x92VEQ",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/pI7ytZ1oyY4/mqdefault_6s.webp?du=3000&sqp=CKbE-48G&rs=AOn4CLD_wWCno1Q6EqhLYp7kMkCPc0y0dg",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"3 weeks ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"dXdoim96v5A",
|
||||
"title":"8-bit CPU control logic: Part 1",
|
||||
"channel":"Ben Eater",
|
||||
"metadata":{
|
||||
"view_count":"718,780 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/dXdoim96v5A/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCBSzOjvO-0645gRSb8WezGKYSvUg",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/dXdoim96v5A/mqdefault_6s.webp?du=3000&sqp=CKCO-48G&rs=AOn4CLArMCkZu073j8k_QRHCm_4IuB2Uyw",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"4 years ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"AawLM81gIHo",
|
||||
"title":"playlist to study like a medieval philosopher having the truth revealed by divine grace",
|
||||
"channel":"Filosofia Acadêmica",
|
||||
"metadata":{
|
||||
"view_count":"3,182,206 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/AawLM81gIHo/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA_6jX1WlJ8JgjdzJ4vBdmajwkZmA",
|
||||
"width":480,
|
||||
"height":270
|
||||
},
|
||||
"moving_thumbnail":[
|
||||
|
||||
],
|
||||
"published":"4 months ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"ILyQ4A3ZYu8",
|
||||
"title":"LIGHTWEIGHT Mirrorless Macro Photography w/ 7Artisans 60mm f/2.8 mkII macro",
|
||||
"channel":"Thomas Shahan",
|
||||
"metadata":{
|
||||
"view_count":"26,594 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/ILyQ4A3ZYu8/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAYn-kG_hpBdu0BZ0QLeb9EdnkXfg",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/ILyQ4A3ZYu8/mqdefault_6s.webp?du=3000&sqp=CMit-48G&rs=AOn4CLBdzzV12LamKVEyPdPUXeHqLwqOzg",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"5 months ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"8E2l4K0OvMc",
|
||||
"title":"Linkin Park & Eminem - Soldiers (2021) Official Music Video",
|
||||
"channel":"zwieR.Z.",
|
||||
"metadata":{
|
||||
"view_count":"2,949,402 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/8E2l4K0OvMc/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAqJt07Iwc5IFmUv-HazVO9OZWzFw",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":[
|
||||
|
||||
],
|
||||
"published":"1 month ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"XFqn3uy238E",
|
||||
"title":"...And We'll Do it Again",
|
||||
"channel":"Kurzgesagt – In a Nutshell",
|
||||
"metadata":{
|
||||
"view_count":"8,782,886 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/XFqn3uy238E/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLATvhGBLlbbTmMV4aDwmsBRqlh3rQ",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/XFqn3uy238E/mqdefault_6s.webp?du=3000&sqp=COGu-48G&rs=AOn4CLCghw3iwEHMhpOZU2Sd79eQAUZg9g",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"1 month ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"Ks7mmrer37g",
|
||||
"title":"Using 5000 Minecarts to LAG a Pay-To-Win Server in Minecraft - Part 1",
|
||||
"channel":"The Horizon",
|
||||
"metadata":{
|
||||
"view_count":"3,351,437 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/Ks7mmrer37g/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA16Zi1QZ5YACpDMM0We5k5C6Cc0w",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/Ks7mmrer37g/mqdefault_6s.webp?du=3000&sqp=COip-48G&rs=AOn4CLDY3tU8sodjfeSM61JtfSovZIcmww",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"13 days ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"y7fMTCJaWjQ",
|
||||
"title":"2004 PC Power On",
|
||||
"channel":"hardrivethrutown",
|
||||
"metadata":{
|
||||
"view_count":"336,545 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/y7fMTCJaWjQ/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB2GeuZo6-IUkJKl6M0VYhtAowNqQ",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/y7fMTCJaWjQ/mqdefault_6s.webp?du=3000&sqp=CKjB-48G&rs=AOn4CLAUUDj7VcH8uIysPlgcVZ7rx2AFaQ",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"1 month ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"oq2-RJH_3NE",
|
||||
"title":"What if we replace the Moon with other Space Objects?",
|
||||
"channel":"Sciencephile the AI",
|
||||
"metadata":{
|
||||
"view_count":"128,961 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/oq2-RJH_3NE/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBuCNxw_LAFkvrGfnWZ5rI_fI2cuw",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/oq2-RJH_3NE/mqdefault_6s.webp?du=3000&sqp=CNSk-48G&rs=AOn4CLAKV2gb8ZMH6_pEGroEC6FIZmyTjg",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"7 days ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"pB-nGyqJMy0",
|
||||
"title":"for 4 years I thought PISSY was a boy ~ Maybe that’s why SHE’s always PISSED at me !!!",
|
||||
"channel":"Exotics Lair",
|
||||
"metadata":{
|
||||
"view_count":"974,222 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/pB-nGyqJMy0/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAf7vp-L054e_PSMrs5z_GSoIKC1w",
|
||||
"width":480,
|
||||
"height":270
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/pB-nGyqJMy0/mqdefault_6s.webp?du=3000&sqp=CMqL-48G&rs=AOn4CLC-yzDWXwJo45NudEmzQBMQ_8nsNg",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"1 year ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"RW4s2WtwoUY",
|
||||
"title":"This Is Why Crabs Hate Stingrays",
|
||||
"channel":"WATOP",
|
||||
"metadata":{
|
||||
"view_count":"4,329,223 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/RW4s2WtwoUY/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD5iz1haM6niPfCZxeZFfublo_tPQ",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":{
|
||||
"url":"https://i.ytimg.com/an_webp/RW4s2WtwoUY/mqdefault_6s.webp?du=3000&sqp=CMS1-48G&rs=AOn4CLBkPjwEFtz6e6iNOpB13tWcuik4pw",
|
||||
"width":320,
|
||||
"height":180
|
||||
},
|
||||
"published":"8 months ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Verified"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"0wj8ueoUjt8",
|
||||
"title":"Rag’n’Bone Man & Calvin Harris - Giant (Live at BRITs 2019)",
|
||||
"channel":"Rag'n'Bone Man",
|
||||
"metadata":{
|
||||
"view_count":"5,636,727 views",
|
||||
"thumbnail":{
|
||||
"url":"https://i.ytimg.com/vi/0wj8ueoUjt8/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDgmKnz4ilXGAdfU3zBBhZVPe9ERw",
|
||||
"width":720,
|
||||
"height":404
|
||||
},
|
||||
"moving_thumbnail":[
|
||||
|
||||
],
|
||||
"published":"2 years ago",
|
||||
"badges":[
|
||||
|
||||
],
|
||||
"owner_badges":[
|
||||
"Official Artist Channel"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
Get subscriptions feed:
|
||||
```js
|
||||
const mysubfeed = await youtube.getSubscriptionsFeed();
|
||||
const mysubsfeed = await youtube.getSubscriptionsFeed();
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
@@ -63,8 +63,8 @@ async function engage(session, engagement_type, args = {}) {
|
||||
* @param {string} action_type Type of action.
|
||||
* @returns {object} { success: boolean, status_code: number, data: object } | { success: boolean, status_code: number, message: string }
|
||||
*/
|
||||
async function browse(session, action_type, args) {
|
||||
if (!session.logged_in) throw new Error('You are not signed-in');
|
||||
async function browse(session, action_type, args = {}) {
|
||||
if (!session.logged_in && action_type != 'lyrics') throw new Error('You are not signed-in');
|
||||
|
||||
let data;
|
||||
switch (action_type) { // TODO: Handle more actions
|
||||
@@ -80,12 +80,24 @@ async function browse(session, action_type, args) {
|
||||
browseId: 'FEsubscriptions'
|
||||
};
|
||||
break;
|
||||
case 'lyrics':
|
||||
const yt_music_context = JSON.parse(JSON.stringify(session.context)); // deep copy the context obj so we don't accidentally change it
|
||||
|
||||
yt_music_context.client.originalUrl = Constants.URLS.YT_MUSIC_URL;
|
||||
yt_music_context.client.clientVersion = '1.20211213.00.00';
|
||||
yt_music_context.client.clientName = 'WEB_REMIX';
|
||||
|
||||
data = {
|
||||
context: yt_music_context,
|
||||
browseId: args.browse_id
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
const client_domain = args.ytmusic && Constants.URLS.YT_MUSIC_URL || Constants.URLS.YT_BASE_URL;
|
||||
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/browse${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`,
|
||||
JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session })).catch((error) => error);
|
||||
const response = await Axios.post(`${client_domain}/youtubei/v1/browse${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`,
|
||||
JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session, ytmusic: args.ytmusic })).catch((error) => error);
|
||||
|
||||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
|
||||
|
||||
@@ -266,7 +278,6 @@ async function getVideoInfo(session, args = {}) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Requests continuation for previously performed actions.
|
||||
*
|
||||
|
||||
@@ -48,8 +48,8 @@ module.exports = {
|
||||
INNERTUBE_REQOPTS: (info) => {
|
||||
info.desktop === undefined && (info.desktop = true);
|
||||
const origin = info.ytmusic && 'https://music.youtube.com' ||
|
||||
info.desktop && 'https://www.youtube.com' || 'https://m.youtube.com';
|
||||
|
||||
info.desktop && 'https://www.youtube.com' || 'https://m.youtube.com';
|
||||
|
||||
let req_opts = {
|
||||
params: info.params || {},
|
||||
headers: {
|
||||
@@ -105,14 +105,14 @@ module.exports = {
|
||||
'owner_profile_url'
|
||||
],
|
||||
BLACKLISTED_KEYS: [
|
||||
'is_owner_viewing', 'is_unplugged_corpus',
|
||||
'is_owner_viewing', 'is_unplugged_corpus',
|
||||
'is_crawlable', 'allow_ratings', 'author'
|
||||
],
|
||||
BASE64_DIALECT: {
|
||||
NORMAL: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'.split(''),
|
||||
REVERSE: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'.split('')
|
||||
},
|
||||
FUNCS_REGEX: /d\.push\(e\)|d\.reverse\(\)|d\[0\]\)\[0\]\)|f=d\[0];d\[0\]|d\.length;d\.splice\(e,1\)|function\(\){for\(var|function\(d,e,f\){var k=f|function\(d\){for\(var|reverse\(\)\.forEach|unshift\(d\.pop\(\)\)|function\(d,e\){for\(var f/,
|
||||
FUNCS_REGEX: /d\.push\(e\)|d\.reverse\(\)|d\[0\]\)\[0\]\)|f=d\[0];d\[0\]|d\.length;d\.splice\(e,1\)|function\(\){for\(var|function\(d,e,f\){var|function\(d\){for\(var|reverse\(\)\.forEach|unshift\(d\.pop\(\)\)|function\(d,e\){for\(var f/,
|
||||
FUNCS: {
|
||||
PUSH: 'd.push(e)',
|
||||
REVERSE_1: 'd.reverse()',
|
||||
@@ -124,16 +124,6 @@ module.exports = {
|
||||
ROTATE_2: 'unshift(d.pop())',
|
||||
BASE64_DIA: 'function(){for(var',
|
||||
TRANSLATE_1: 'function(d,e){for(var f',
|
||||
TRANSLATE_2: 'function(d,e,f){var k=f'
|
||||
},
|
||||
// Just a helper function, felt like Utils.js wasn't the right place for it:
|
||||
formatNTransformData: (data) => {
|
||||
return data
|
||||
.replace(/function\(d,e\)/g, '"function(d,e)').replace(/function\(d\)/g, '"function(d)')
|
||||
.replace(/function\(\)/g, '"function()').replace(/function\(d,e,f\)/g, '"function(d,e,f)')
|
||||
.replace(/\[function\(d,e,f\)/g, '["function(d,e,f)').replace(/,b,/g, ',"b",')
|
||||
.replace(/,b/g, ',"b"').replace(/b,/g, '"b",').replace(/b]/g, '"b"]')
|
||||
.replace(/\[b/g, '["b"').replace(/}]/g, '"]').replace(/},/g, '}",')
|
||||
.replace(/""/g, '').replace(/length]\)}"/g, 'length])}');
|
||||
TRANSLATE_2: 'function(d,e,f){var'
|
||||
}
|
||||
};
|
||||
@@ -150,6 +150,25 @@ class Innertube {
|
||||
return refined_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the lyrics for a given song
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {string} Song lyrics
|
||||
*/
|
||||
async getLyrics(id) {
|
||||
const data_continuation = await Actions.getContinuation(this, { video_id: id, ytmusic: true });
|
||||
|
||||
const lyrics_tab = data_continuation.data.contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer
|
||||
.watchNextTabbedResultsRenderer.tabs.find((obj) => obj.tabRenderer.title == 'Lyrics');
|
||||
|
||||
const response = await Actions.browse(this, 'lyrics', { ytmusic: true, browse_id: lyrics_tab.tabRenderer.endpoint.browseEndpoint.browseId });
|
||||
if (!response.data.contents.sectionListRenderer) throw new Error(response.data.contents.messageRenderer.text.runs[0].text);
|
||||
|
||||
const lyrics = response.data.contents.sectionListRenderer.contents[0].musicDescriptionShelfRenderer.description.runs[0].text;
|
||||
return lyrics
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the comments section of a video.
|
||||
*
|
||||
@@ -228,8 +247,8 @@ class Innertube {
|
||||
thumbnail: content.videoRenderer.thumbnail && content.videoRenderer.thumbnail.thumbnails.slice(-1)[0] || [],
|
||||
moving_thumbnail: content.videoRenderer.richThumbnail && content.videoRenderer.richThumbnail.movingThumbnailRenderer.movingThumbnailDetails.thumbnails[0] || [],
|
||||
published: content.videoRenderer.publishedTimeText && content.videoRenderer.publishedTimeText.simpleText || 'N/A',
|
||||
badges: content.videoRenderer.badges && content.videoRenderer.badges.map((badge) => badge.metadataBadgeRenderer.label) || 'N/A',
|
||||
owner_badges: content.videoRenderer.ownerBadges && content.videoRenderer.ownerBadges.map((badge) => badge.metadataBadgeRenderer.tooltip) || 'N/A'
|
||||
badges: content.videoRenderer.badges && content.videoRenderer.badges.map((badge) => badge.metadataBadgeRenderer.label) || [],
|
||||
owner_badges: content.videoRenderer.ownerBadges && content.videoRenderer.ownerBadges.map((badge) => badge.metadataBadgeRenderer.tooltip) || []
|
||||
}
|
||||
}
|
||||
}).filter((video) => video);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const Axios = require('axios');
|
||||
const Actions = require('./Actions');
|
||||
const Constants = require('./Constants');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
class Livechat extends EventEmitter {
|
||||
|
||||
@@ -6,8 +6,8 @@ const Constants = require('./Constants');
|
||||
class NToken {
|
||||
constructor(raw_code) {
|
||||
this.raw_code = raw_code;
|
||||
this.null_placeholder_regex = /c\[(.*?)\]=c/g;
|
||||
this.transformation_calls_regex = /c\[(.*?)\]\((.+?)\)/g;
|
||||
this.placeholders_regex = /c\[(.*?)\]=c/g;
|
||||
this.calls_regex = /c\[(.*?)\]\((.+?)\)/g;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,21 +41,21 @@ class NToken {
|
||||
return el;
|
||||
});
|
||||
|
||||
// Fills the null placeholders with a copy of the transformations array
|
||||
const null_placeholder_positions = [...this.raw_code.matchAll(this.null_placeholder_regex)].map((item) => parseInt(item[1]));
|
||||
null_placeholder_positions.forEach((pos) => transformations[pos] = transformations);
|
||||
// Fills all placeholders with the transformations array
|
||||
const placeholder_indexes = [...this.raw_code.matchAll(this.placeholders_regex)].map((item) => parseInt(item[1]));
|
||||
placeholder_indexes.forEach((i) => transformations[i] = transformations);
|
||||
|
||||
// Parses and emulates calls to the functions of the transformations array
|
||||
const transformation_calls = [...Utils.getStringBetweenStrings(this.raw_code.replace(/\n/g, ''), 'try{', '}catch')
|
||||
.matchAll(this.transformation_calls_regex)].map((params) => ({ index: params[1], params: params[2] }));
|
||||
const function_calls = [...Utils.getStringBetweenStrings(this.raw_code.replace(/\n/g, ''), 'try{', '}catch')
|
||||
.matchAll(this.calls_regex)].map((params) => ({ index: params[1], params: params[2] }));
|
||||
|
||||
transformation_calls.forEach((data) => {
|
||||
function_calls.forEach((data) => {
|
||||
const param_index = data.params.split(',').map((param) => param.match(/c\[(.*?)\]/)[1]);
|
||||
const base64_dia = (param_index[2] && transformations[param_index[2]]());
|
||||
transformations[data.index](transformations[param_index[0]], transformations[param_index[1]], base64_dia);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`Could not transform n-token (${n}), download may be throttled:`, err)
|
||||
console.error(`Could not transform n-token (${n}), download may be throttled:`, err.message);
|
||||
return n;
|
||||
}
|
||||
return n_token.join('');
|
||||
@@ -71,7 +71,7 @@ class NToken {
|
||||
*/
|
||||
#getTransformationData() {
|
||||
const data = `[${Utils.getStringBetweenStrings(this.raw_code.replace(/\n/g, ''), 'c=[', '];c')}]`;
|
||||
return JSON.parse(Constants.formatNTransformData(data));
|
||||
return JSON.parse(Utils.refineNTokenData(data));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@ class OAuth extends EventEmitter {
|
||||
this.scope = Constants.OAUTH.SCOPE;
|
||||
|
||||
this.auth_script_regex = /<script id=\"base-js\" src=\"(.*?)\" nonce=".*?"><\/script>/;
|
||||
this.identity_regex = /.+?={};var .+?={clientId:\"(?<id>.+?)\",si:\"(?<secret>.+?)\"},/;
|
||||
this.identity_regex = /.+?={};var .+?={clientId:\"(?<id>.+?)\",.+?:\"(?<secret>.+?)\"},/;
|
||||
|
||||
if (auth_info.access_token) return;
|
||||
this.#requestAuthCode();
|
||||
|
||||
@@ -77,14 +77,6 @@ class Parser {
|
||||
const tabs = this.data.contents.tabbedSearchResultsRenderer.tabs;
|
||||
const contents = tabs[0].tabRenderer.content.sectionListRenderer.contents;
|
||||
|
||||
/**
|
||||
* WIP
|
||||
**/
|
||||
const getLyrics = async (id) => {
|
||||
// const data_continuation = await Actions.getContinuation(this.session, { video_id: id, ytmusic: true });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const songs_ms = contents.find((content) => content.musicShelfRenderer.title.runs[0].text == 'Songs');
|
||||
const songs = songs_ms.musicShelfRenderer.contents.map((item) => {
|
||||
const list_item = item.musicResponsiveListItemRenderer;
|
||||
@@ -95,7 +87,7 @@ class Parser {
|
||||
album: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[4].text,
|
||||
duration: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[6].text,
|
||||
thumbnail: list_item.thumbnail.musicThumbnailRenderer.thumbnail,
|
||||
getLyrics: () => getLyrics(list_item.playlistItemData.videoId)
|
||||
getLyrics: () => this.session.getLyrics(list_item.playlistItemData.videoId)
|
||||
};
|
||||
});
|
||||
|
||||
@@ -109,7 +101,7 @@ class Parser {
|
||||
views: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[4].text,
|
||||
duration: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[6].text,
|
||||
thumbnail: list_item.thumbnail.musicThumbnailRenderer.thumbnail,
|
||||
getLyrics: () => getLyrics(list_item.playlistItemData.videoId)
|
||||
getLyrics: () => this.session.getLyrics(list_item.playlistItemData.videoId)
|
||||
};
|
||||
});
|
||||
|
||||
@@ -133,7 +125,7 @@ class Parser {
|
||||
const playability_status = desktop_v && this.data.playabilityStatus ||
|
||||
this.data[2].playerResponse.playabilityStatus;
|
||||
|
||||
if (playability_status.status == 'ERROR')
|
||||
if (playability_status.status == 'ERROR')
|
||||
throw new Error(`Could not retrieve details for this video: ${playability_status.status} - ${playability_status.reason}`);
|
||||
|
||||
const details = desktop_v && this.data.videoDetails ||
|
||||
|
||||
@@ -25,7 +25,7 @@ class Player {
|
||||
// Caches the current player so we don't have to download it all the time.
|
||||
Fs.mkdirSync(this.tmp_cache_dir, { recursive: true });
|
||||
Fs.writeFileSync(`${this.tmp_cache_dir}/${this.player_name}.js`, response.data);
|
||||
} catch (err) {}
|
||||
} catch (err) { }
|
||||
|
||||
this.sig_decipher_sc = this.#getSigDecipherCode(response.data);
|
||||
this.ntoken_sc = this.#getNEncoder(response.data);
|
||||
|
||||
@@ -12,9 +12,9 @@ class SigDecipher {
|
||||
this.actions_regex = /;.{2}\.(.{2})\(.*?,(.*?)\)/g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deciphers signature.
|
||||
*/
|
||||
/**
|
||||
* Deciphers signature.
|
||||
*/
|
||||
decipher() {
|
||||
const args = QueryString.parse(this.url);
|
||||
const functions = this.#getFunctions();
|
||||
|
||||
25
lib/Utils.js
25
lib/Utils.js
@@ -156,9 +156,9 @@ function encodeFilter(period, duration, order) {
|
||||
const youtube_proto = Proto(Fs.readFileSync(`${__dirname}/proto/youtube.proto`));
|
||||
|
||||
const periods = { 'any': null, 'hour': 1, 'day': 2, 'week': 3, 'month': 4, 'year': 5 };
|
||||
const durations = { 'any': null, 'short' : 1, 'long': 2 };
|
||||
const orders = { 'relevance': null, 'rating': 1, 'age': 2, 'views' : 3 };
|
||||
|
||||
const durations = { 'any': null, 'short': 1, 'long': 2 };
|
||||
const orders = { 'relevance': null, 'rating': 1, 'age': 2, 'views': 3 };
|
||||
|
||||
const search_filter_buff = youtube_proto.SearchFilter.encode({
|
||||
number: orders[order],
|
||||
filter: {
|
||||
@@ -167,8 +167,23 @@ function encodeFilter(period, duration, order) {
|
||||
param_2: durations[duration]
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return encodeURIComponent(Buffer.from(search_filter_buff).toString('base64'));
|
||||
}
|
||||
|
||||
module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, camelToSnake, timeToSeconds, encodeMessageParams, encodeCommentParams, encodeNotificationPref, encodeFilter };
|
||||
/**
|
||||
* Turns the ntoken transform data into a valid json array
|
||||
*
|
||||
* @param {string} data
|
||||
*/
|
||||
function refineNTokenData(data) {
|
||||
return data
|
||||
.replace(/function\(d,e\)/g, '"function(d,e)').replace(/function\(d\)/g, '"function(d)')
|
||||
.replace(/function\(\)/g, '"function()').replace(/function\(d,e,f\)/g, '"function(d,e,f)')
|
||||
.replace(/\[function\(d,e,f\)/g, '["function(d,e,f)').replace(/,b,/g, ',"b",')
|
||||
.replace(/,b/g, ',"b"').replace(/b,/g, '"b",').replace(/b]/g, '"b"]')
|
||||
.replace(/\[b/g, '["b"').replace(/}]/g, '"]').replace(/},/g, '}",')
|
||||
.replace(/""/g, '').replace(/length]\)}"/g, 'length])}');
|
||||
}
|
||||
|
||||
module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, camelToSnake, timeToSeconds, encodeMessageParams, encodeCommentParams, encodeNotificationPref, encodeFilter, refineNTokenData };
|
||||
26
package-lock.json
generated
26
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "youtubei.js",
|
||||
"version": "1.2.9",
|
||||
"version": "1.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
@@ -77,9 +77,9 @@
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
|
||||
},
|
||||
"node_modules/multiformats": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.6.2.tgz",
|
||||
"integrity": "sha512-1dKng7RkBelbEZQQD2zvdzYKgUmtggpWl+GXQBYhnEGGkV6VIYfWgV3VSeyhcUFFEelI5q4D0etCJZ7fbuiamQ=="
|
||||
"version": "9.6.3",
|
||||
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.6.3.tgz",
|
||||
"integrity": "sha512-yfXKI66fL0nFzt0nJl26i4wV1qAqbAEIBvfFbkbsne9GrLz6IHvHUoRyxUtlJcdP181ssOgjama6E/VSk4pbrA=="
|
||||
},
|
||||
"node_modules/protocol-buffers-schema": {
|
||||
"version": "3.6.0",
|
||||
@@ -127,9 +127,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/user-agents": {
|
||||
"version": "1.0.912",
|
||||
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.912.tgz",
|
||||
"integrity": "sha512-vvFMlK3XcaF/ZRF1Ky4IrBASSNXflZys+5ArPRanNeggWtOYeD2k3FD/6wrv9h7lFlvh5KS+X45E/siw26+EJg==",
|
||||
"version": "1.0.918",
|
||||
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.918.tgz",
|
||||
"integrity": "sha512-rHqVET1f+DsrIY8ejUSSgjRKb8qJN8//TJ7K2jIgSDR45OJiZWYVMTtg4qkMAKzkaXhcc6BeQQE2M70cXvHqWw==",
|
||||
"dependencies": {
|
||||
"dot-json": "^1.2.2",
|
||||
"lodash.clonedeep": "^4.5.0"
|
||||
@@ -189,9 +189,9 @@
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
|
||||
},
|
||||
"multiformats": {
|
||||
"version": "9.6.2",
|
||||
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.6.2.tgz",
|
||||
"integrity": "sha512-1dKng7RkBelbEZQQD2zvdzYKgUmtggpWl+GXQBYhnEGGkV6VIYfWgV3VSeyhcUFFEelI5q4D0etCJZ7fbuiamQ=="
|
||||
"version": "9.6.3",
|
||||
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.6.3.tgz",
|
||||
"integrity": "sha512-yfXKI66fL0nFzt0nJl26i4wV1qAqbAEIBvfFbkbsne9GrLz6IHvHUoRyxUtlJcdP181ssOgjama6E/VSk4pbrA=="
|
||||
},
|
||||
"protocol-buffers-schema": {
|
||||
"version": "3.6.0",
|
||||
@@ -239,9 +239,9 @@
|
||||
}
|
||||
},
|
||||
"user-agents": {
|
||||
"version": "1.0.912",
|
||||
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.912.tgz",
|
||||
"integrity": "sha512-vvFMlK3XcaF/ZRF1Ky4IrBASSNXflZys+5ArPRanNeggWtOYeD2k3FD/6wrv9h7lFlvh5KS+X45E/siw26+EJg==",
|
||||
"version": "1.0.918",
|
||||
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.918.tgz",
|
||||
"integrity": "sha512-rHqVET1f+DsrIY8ejUSSgjRKb8qJN8//TJ7K2jIgSDR45OJiZWYVMTtg4qkMAKzkaXhcc6BeQQE2M70cXvHqWw==",
|
||||
"requires": {
|
||||
"dot-json": "^1.2.2",
|
||||
"lodash.clonedeep": "^4.5.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "youtubei.js",
|
||||
"version": "1.2.9",
|
||||
"version": "1.3.0",
|
||||
"description": "An object-oriented library that allows you to search, get detailed info about videos, subscribe, unsubscribe, like, dislike, comment, download videos and much more!",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -10,7 +10,7 @@ let failed_tests = 0;
|
||||
|
||||
async function performTests() {
|
||||
const youtube = await new Innertube().catch((error) => error);
|
||||
assert(youtube instanceof Error ? false : true, `should retrieve Innertube configuration data`, youtube);
|
||||
assert(!(youtube instanceof Error), `should retrieve Innertube configuration data`, youtube);
|
||||
|
||||
if (!(youtube instanceof Error)) {
|
||||
const search = await youtube.search('Carl Sagan - Documentary').catch((error) => error);
|
||||
@@ -58,4 +58,4 @@ function assert(outcome, description, data) {
|
||||
return outcome;
|
||||
}
|
||||
|
||||
performTests();
|
||||
performTests();
|
||||
|
||||
Reference in New Issue
Block a user