YouTube.js
An object-oriented wrapper around the Innertube API, which is what YouTube itself uses.
Report Bug
·
Request Feature
Innertube is an API used across all YouTube clients, it was made to simplify the internal structure of the platform and make it easy to push updates. This library takes advantage of that API, therefore providing a simple & efficient way to interact with YouTube programmatically.
And big thanks to @gatecrasher777 for his research on the workings of the Innertube API!
What can it do?
As of now, this is one of the most advanced & stable YouTube libraries out there, here's a short summary of its features:
- Search videos, playlists, music, albums etc
- Get detailed info about any video or playlist
- Fetch live chat & live stats in real time
- Get notifications
- Get watch history
- Get subscriptions/home feed
- Change notification preferences for a channel
- Subscribe/Unsubscribe/Like/Dislike/Comment etc
- Easily sign in to any Google Account
- Change an account's settings.
- Download videos
Do note that you must be signed-in to perform actions that involve an account, such as commenting, subscribing, sending messages to a live chat, etc.
Do I need an API key to use this?
No, YouTube.js does not use any official API so no API keys are required.
Installation
npm install youtubei.js
Usage
1. Getting Started
2. Interactions
3. Live chats
4. Downloading videos
5. Signing-in
6. 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.
const Innertube = require('youtubei.js');
const youtube = await new Innertube();
Doing a simple search:
const search = await youtube.search('Looking for life on Mars - Documentary');
const search = await youtube.search('Interstellar Main Theme', { client: 'YTMUSIC' });
YouTube Search Output
{
query: string,
corrected_query: string,
estimated_results: number,
videos: [
{
id: string,
title: string,
description: string,
url: string,
metadata:{
view_count: string,
short_view_count_text: {
simple_text: string,
accessibility_label: string
},
thumbnails: [Array],
duration: {
seconds: number,
simple_text: string,
accessibility_label: string
},
published: string,
badges:[Array],
owner_badges:[Array]
}
}
]
}
YouTube Music Search Output
{
songs: [
{
id: string,
title: string,
artist: string,
album: string,
duration: string,
thumbnail: {
thumbnails: [Array]
}
},
],
videos: [
{
id: string,
title: string,
author: string,
views: string,
duration: string,
thumbnail: {
thumbnails: [Array]
}
}
],
albums: [
{
title: string,
author: string,
year: string,
thumbnail: {
thumbnails: [Array]
}
},
],
playlists: [
{
title: string,
description: string,
total_items: number,
duration: string,
year: string,
items: [
{
id: string,
title: string,
author: string,
duration: string,
thumbnail: [Array]
}
]
}
]
}
Get search suggestions:
const suggestions = await youtube.getSearchSuggestions('QUERY', {
client: 'YOUTUBE'
})
Output
[
{
text: string, bold_text: string
},
]
Get details about a given video:
const video = await youtube.getDetails('VIDEO_ID');
Output
{
title: string,
description: string,
thumbnail: {
url: string,
width: number,
height: number
},
metadata: {
embed: {
iframeUrl: string,
flashUrl: string,
width: number,
height: number,
flashSecureUrl: string
},
likes: number,
dislikes: number,
view_count: number,
average_rating: number,
length_seconds: number,
channel_id: string,
channel_url: string,
external_channel_id: string,
allow_ratings: boolean,
is_live_content: boolean,
is_family_safe: boolean,
is_unlisted: boolean,
is_private: boolean,
is_liked: boolean,
is_disliked: boolean,
is_subscribed: boolean,
subscriber_count: string,
current_notification_preference: string,
likes: { count: number, short_count_text: string },
publish_date_text: string,
has_ypc_metadata: boolean,
category: string,
channel_name: string,
publish_date: string,
upload_date: string,
keywords: [Array]
}
}
Get comments:
const comments = await youtube.getComments('VIDEO_ID');
const video = await youtube.getDetails('VIDEO_ID');
const comments = await video.getComments();
const continuation = await comments.getContinuation();
Output
{
comments: [
{
text: string,
author: {
name: string,
thumbnail: [
{
url: string,
width: number,
height: number
}
],
channel_id: string
},
metadata:{
published: string,
is_liked: boolean,
is_channel_owner: boolean,
like_count: number,
reply_count: number,
id: string
}
},
],
comment_count: string
}
Get home feed:
const homefeed = await youtube.getHomeFeed();
Output
[
{
id: string,
title: string,
description: string,
channel: string,
metadata: {
view_count: string,
short_view_count_text: { simple_text: string, accessibility_label: string },
thumbnail: {
url: string,
width: number,
height: number
},
moving_thumbnail: {
url: string,
width: number,
height: number
},
published: string,
duration: {
seconds: number,
simple_text: string,
accessibility_label: string
},
badges: string,
owner_badges: [Array]
}
}
]
Get subscriptions feed:
const mysubsfeed = await youtube.getSubscriptionsFeed();
Output
{
today: [
{
id: string,
title: string,
channel: string,
metadata: {
view_count: string,
short_view_count_text: { simple_text: string, accessibility_label: string },
thumbnail: {
url: string,
width: number,
height: number
},
moving_thumbnail: {
url: string,
width: number,
height: number
},
published: string,
badges: string,
owner_badges: [Array]
}
}
],
yesterday: [
{
id: string,
title: string,
channel: string,
metadata: {
view_count: string,
short_view_count_text: { simple_text: string, accessibility_label: string },
thumbnail: {
url: string,
width: number,
height: number
},
moving_thumbnail: {
url: string,
width: number,
height: number
},
published: string,
badges: string,
owner_badges: [Array]
}
}
],
this_week: [
id: string,
title: string,
channel: string,
metadata: {
view_count: string,
thumbnail: {
url: string,
width: number,
height: number
},
moving_thumbnail: {
url: string,
width: number,
height: number
},
published: string,
badges: string,
owner_badges: [Array]
}
}
]
}
Get watch history:
const history = await youtube.getHistory();
Output
[
{
id: string,
title: string,
description: string,
channel: {
name: string,
url: string
},
metadata: {
view_count: string,
short_view_count_text: { simple_text: string, accessibility_label: string },
thumbnail: {
url: string,
width: number,
height: number
},
moving_thumbnail: {
url: string,
width: number,
height: number
},
published: string,
duration: {
seconds: number,
simple_text: string,
accessibility_label: string
},
badges: string,
owner_badges: [Array]
}
}
]
Get notifications:
const notifications = await youtube.getNotifications();
Output
[
{
title: string,
sent_time: string,
channel_name: string,
channel_thumbnail: {
url: string,
width: number,
height: number
},
video_thumbnail: {
url: string,
width: number,
height: number
},
video_url: string,
read: boolean,
notification_id: string
},
]
Get unseen notifications count:
const notifications = await youtube.getUnseenNotificationsCount();
Get song lyrics:
const search = await youtube.search('Never give you up', { client: 'YTMUSIC' });
const lyrics = await youtube.getLyrics(search.songs[0].id);
Get playlist:
const search = await youtube.search('Interstellar Soundtrack', {
client: 'YTMUSIC'
});
const playlist = await youtube.getPlaylist(search.playlists[0].id, {
client: 'YTMUSIC'
});
const playlist = await youtube.getPlaylist(search.playlists[0].id);
YouTube Music Output
{
title: string,
description: string,
total_items: number,
duration: string,
year: string,
items: [
{
id: string,
title: string,
author: string,
duration: string,
thumbnail: [Array]
},
}
YouTube Output
{
title: string,
description: string,
total_items: string,
last_updated: string,
views: string,
items: [
{
id: string,
title: string,
author: string,
duration: {
seconds: number,
simple_text: string,
accessibility_label: string
},
thumbnail: [Array]
},
]
}
Interactions:
The library makes it easy to interact with YouTube programmatically. However, don't forget that you must be signed in to use the following features!
await youtube.interact.subscribe('CHANNEL_ID');
await youtube.interact.unsubscribe('CHANNEL_ID');
await youtube.interact.like('VIDEO_ID');
await youtube.interact.dislike('VIDEO_ID');
await youtube.interact.removeLike('VIDEO_ID');
await youtube.interact.comment('VIDEO_ID', 'Haha, nice video!');
- Change notification preferences:
await youtube.interact.changeNotificationPreferences('CHANNEL_ID', 'ALL');
These methods will always return { success: true, status_code: 200 }
if successful.
Account Settings
It is also possible to manage an account's settings:
await youtube.account.info();
Output
{
name: string,
photo: [
{
url: string,
width: number,
height: number
}
],
country: string,
language: string;
}
Notification settings:
- Subscription notifications:
await youtube.account.settings.notifications.setSubscriptions(true);
- Recommended content notifications:
await youtube.account.settings.notifications.setRecommendedVideos(true);
- Channel activity notifications:
await youtube.account.settings.notifications.setChannelActivity(true);
- Comment replies notifications:
await youtube.account.settings.notifications.setCommentReplies(true);
- Channel mention notifications:
await youtube.account.settings.notifications.setSharedContent(true);
Privacy settings:
await youtube.account.settings.privacy.setSubscriptionsPrivate(true);
await youtube.account.settings.privacy.setSavedPlaylistsPrivate(true);
Fetching live chats:
YouTube.js isn't able to download live content yet, but it does allow you to fetch live chats plus you can also send messages!
const Innertube = require('youtubei.js');
async function start() {
const youtube = await new Innertube();
const search = await youtube.search('Lofi girl live');
const video = await youtube.getDetails(search.videos[0].id);
const livechat = video.getLivechat();
livechat.on('update-metadata', (data) => {
console.info('Info:', data);
});
livechat.on('chat-update', (message) => {
console.info(`- ${message.author.name}\n${message.text}\n\n`);
if(message.text == '!info') {
livechat.sendMessage('Hello! This message was sent from YouTube.js');
}
});
}
start();
Stop fetching the live chat:
livechat.stop();
Delete a message:
const msg = await livechat.sendMessage('Nice livestream!');
await msg.deleteMessage();
Downloading videos:
YouTube.js provides an easy-to-use and simple downloader:
const fs = require('fs');
const Innertube = require('youtubei.js');
async function start() {
const youtube = await new Innertube();
const search = await youtube.search('Looking for life on Mars - documentary');
const stream = youtube.download(search.videos[0].id, {
format: 'mp4',
quality: '360p',
type: 'videoandaudio'
});
stream.pipe(fs.createWriteStream(`./${search.videos[0].title}.mp4`));
stream.on('start', () => {
console.info('[DOWNLOADER]', 'Starting download now!');
});
stream.on('info', (info) => {
console.info('[DOWNLOADER]', `Downloading ${info.video_details.title} by ${info.video_details.metadata.channel_name}`);
});
stream.on('progress', (info) => {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(`[DOWNLOADER] Downloaded ${info.percentage}% (${info.downloaded_size}MB) of ${info.size}MB`);
});
stream.on('end', () => {
process.stdout.clearLine();
process.stdout.cursorTo(0);
console.info('[DOWNLOADER]', 'Done!');
});
stream.on('error', (err) => console.error('[ERROR]', err));
}
start();
You can also specify a range:
const stream = youtube.download(VIDEO_ID, {
type: 'videoandaudio',
range: { start: 0, end: 1048576 * 5 }
});
Cancelling a download:
stream.cancel();
Alternatively, you can get the deciphered streaming data and handle the download yourself:
const streaming_data = await youtube.getStreamingData(search.videos[0].id, {
format: 'mp4',
quality: '360p',
type: 'videoandaudio'
});
Output
{
selected_format: {
itag: number,
mimeType: string,
bitrate: number,
initRange: { start: string, end: string },
indexRange: { start: string, end: string },
lastModified: string,
contentLength: string,
quality: string,
projectionType: string,
averageBitrate: number,
highReplication: boolean,
audioQuality: string,
approxDurationMs: string,
audioSampleRate: string,
audioChannels: number,
loudnessDb: number,
url: string,
has_audio: boolean,
has_video: boolean
},
formats: [
{
itag: number,
mimeType: string,
bitrate: number,
initRange: { start: string, end: string },
indexRange: { start: string, end: string },
lastModified: string,
contentLength: string,
quality: string,
projectionType: string,
averageBitrate: number,
highReplication: boolean,
audioQuality: string,
approxDurationMs: string,
audioSampleRate: string,
audioChannels: number,
loudnessDb: number,
url: string,
has_audio: boolean,
has_video: boolean
}
]
}
Signing-in:
When signing in to your account, you have two options:
- Use OAuth 2.0; easy, simple & reliable.
- Cookies; usually more complicated to get and unreliable.
OAuth:
const fs = require('fs');
const Innertube = require('youtubei.js');
const creds_path = './yt_oauth_creds.json';
async function start() {
const creds = fs.existsSync(creds_path) && JSON.parse(fs.readFileSync(creds_path).toString()) || {};
const youtube = await new Innertube();
youtube.ev.on('auth', (data) => {
if (data.status === 'AUTHORIZATION_PENDING') {
console.info(`Hello!\nOn your phone or computer, go to ${data.verification_url} and enter the code ${data.code}`);
} else if (data.status === 'SUCCESS') {
fs.writeFileSync(creds_path, JSON.stringify(data.credentials));
console.info('Successfully signed-in, enjoy!');
}
});
youtube.ev.on('update-credentials', (data) => {
fs.writeFileSync(creds_path, JSON.stringify(data.credentials));
console.info('Credentials updated!', data);
});
await youtube.signIn(creds);
}
start();
Cookies:
const Innertube = require('youtubei.js');
async function start() {
const youtube = await new Innertube(COOKIE_HERE);
}
start();
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
Disclaimer
This project is not affiliated with, endorsed, or sponsored by YouTube or any of their affiliates or subsidiaries.
All trademarks, logos and brand names are the property of their respective owners.
Should you have any questions or concerns please contact me directly via email.
License
MIT
go to ${data.verification_url} and enter the code ${data.code}`);
} else if (data.status === 'SUCCESS') {
fs.writeFileSync(creds_path, JSON.stringify(data.credentials));
console.info('Successfully signed-in, enjoy!');
}
});
youtube.ev.on('update-credentials', (data) => {
fs.writeFileSync(creds_path, JSON.stringify(data.credentials));
console.info('Credentials updated!', data);
});
await youtube.signIn(creds);
//...
}
start();
Cookies:
```js
const Innertube = require('youtubei.js');
async function start() {
const youtube = await new Innertube(COOKIE_HERE);
//...
}
start();
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
Disclaimer
This project is not affiliated with, endorsed, or sponsored by YouTube or any of their affiliates or subsidiaries.
All trademarks, logos and brand names are the property of their respective owners.
Should you have any questions or concerns please contact me directly via email.
License
MIT