Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

youtubei.js

Package Overview
Dependencies
Maintainers
1
Versions
124
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

youtubei.js - npm Package Compare versions

Comparing version 1.2.7 to 1.2.8-npm

67

lib/Actions.js

@@ -9,4 +9,5 @@ 'use strict';

async function engage(session, engagement_type, args = {}) {
if (!session.logged_in) throw new Error('You are not logged in');
let data = {};
if (!session.logged_in) throw new Error('You are not signed-in');
let data;
switch (engagement_type) {

@@ -40,4 +41,7 @@ case 'like/like':

const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/${engagement_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session, id: args.video_id, data })).catch((error) => error);
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/${engagement_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`,
JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session, id: args.video_id, data })).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
return {

@@ -50,5 +54,6 @@ success: true,

async function browse(session, action_type) {
if (!session.logged_in) throw new Error('You are not logged in');
if (!session.logged_in) throw new Error('You are not signed-in');
let data;
switch (action_type) {
switch (action_type) { // TODO: Handle more actions
case 'subscriptions_feed':

@@ -63,4 +68,7 @@ data = {

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(`${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);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
return {

@@ -73,5 +81,25 @@ success: true,

async function search(session, args = {}) {
if (!args.query) throw new Error('No query was provided');
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/search${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`,
JSON.stringify({
context: session.context,
params: Utils.encodeFilter(args.options.period, args.options.duration, args.options.order),
query: args.query
}), Constants.INNERTUBE_REQOPTS({ session })).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
return {
success: true,
status_code: response.status,
data: response.data
};
}
async function notifications(session, action_type, args = {}) {
if (!session.logged_in) throw new Error('You are not logged in');
if (!session.logged_in) throw new Error('You are not signed-in');
let data;
switch (action_type) {

@@ -99,5 +127,7 @@ case 'modify_channel_preference':

const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/notification/${action_type}${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(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/notification/${action_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`,
JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session })).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
if (action_type === 'modify_channel_preference') return { success: true, status_code: response.status };
return {

@@ -137,4 +167,6 @@ success: true,

const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/${action_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session, params: args.params })).catch((error) => error);
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/${action_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`,
JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session, params: args.params })).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
return {

@@ -147,2 +179,13 @@ success: true,

async function getVideoInfo(session, args = {}) {
let response;
!args.is_desktop && (response = await Axios.get(`${Constants.URLS.YT_WATCH_PAGE}?v=${args.id}&t=8s&pbj=1`, Constants.INNERTUBE_REQOPTS({ session, id: args.id, desktop: false })).catch((error) => error)) ||
(response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/player${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`,
JSON.stringify(Constants.VIDEO_INFO_REQBODY(args.id, session.sts, session.context)), Constants.INNERTUBE_REQOPTS({ session, id: args.id, desktop: true })).catch((error) => error));
if (response instanceof Error) throw new Error(`Could not get video info: ${response.message}`);
return response.data;
}
async function getContinuation(session, info = {}) {

@@ -164,4 +207,6 @@ let data = { context: session.context };

const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/next${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(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/next${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`,
JSON.stringify(data), Constants.INNERTUBE_REQOPTS({ session })).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
return {

@@ -174,2 +219,2 @@ success: true,

module.exports = { engage, browse, notifications, livechat, getContinuation };
module.exports = { engage, browse, search, notifications, livechat, getVideoInfo, getContinuation };

80

lib/Constants.js

@@ -144,2 +144,3 @@ 'use strict';

metadata.keywords = data.videoDetails.keywords || [];
metadata.available_qualities = [...new Set(data.streamingData.adaptiveFormats.filter(v => v.qualityLabel).map(v => v.qualityLabel).sort((a, b) => +a.replace(/\D/gi, '') - +b.replace(/\D/gi, '')))];

@@ -173,2 +174,3 @@ video_details.id = data.videoDetails.videoId;

metadata.keywords = data[2].playerResponse.videoDetails.keywords;
metadata.available_qualities = [...new Set(data[2].playerResponse.streamingData.adaptiveFormats.filter(v => v.qualityLabel).map(v => v.qualityLabel).sort((a, b) => +a.replace(/\D/gi, '') - +b.replace(/\D/gi, '')))];

@@ -180,3 +182,3 @@ video_details.id = data[2].playerResponse.videoDetails.videoId;

// Functions
// Placeholders for functions
video_details.like = () => {};

@@ -196,79 +198,3 @@ video_details.dislike = () => {};

return video_details;
},
filters: (order) => {
return (({
'any,any,relevance': 'EgIQAQ%3D%3D',
'hour,any,relevance': 'EgIIAQ%3D%3D',
'day,any,relevance': 'EgQIAhAB',
'week,any,relevance': 'EgQIAxAB',
'month,any,relevance': 'EgQIBBAB',
'year,any,relevance': 'EgQIBRAB',
'any,short,relevance': 'EgQQARgB',
'hour,short,relevance': 'EgYIARABGAE%3D',
'day,short,relevance': 'EgYIAhABGAE%3D',
'week,short,relevance': 'EgYIAxABGAE%3D',
'month,short,relevance': 'EgYIBBABGAE%3D',
'year,short,relevance': 'EgYIBRABGAE%3D',
'any,long,relevance': 'EgQQARgC',
'hour,long,relevance': 'EgYIARABGAI%3D',
'day,long,relevance': 'EgYIAhABGAI%3D',
'week,long,relevance': 'EgYIAxABGAI%3D',
'month,long,relevance': 'EgYIBBABGAI%3D',
'year,long,relevance': 'EgYIBRABGAI%3D',
'any,any,age': 'CAISAhAB',
'hour,any,age': 'CAISBAgBEAE%3D',
'day,any,age': 'CAISBAgCEAE%3D',
'week,any,age': 'CAISBAgDEAE%3D',
'month,any,age': 'CAISBAgEEAE%3D',
'year,any,age': 'CAISBAgFEAE%3D',
'any,short,age': 'CAISBBABGAE%3D',
'hour,short,age': 'CAISBggBEAEYAQ%3D%3D',
'day,short,age': 'CAISBggCEAEYAQ%3D%3D',
'week,short,age': 'CAISBggDEAEYAQ%3D%3D',
'month,short,age': 'CAISBggEEAEYAQ%3D%3D',
'year,short,age': 'CAISBggFEAEYAQ%3D%3D',
'any,long,age': 'CAISBBABGAI%3D',
'hour,long,age': 'CAISBggBEAEYAg%3D%3D',
'day,long,age': 'CAISBggCEAEYAg%3D%3D',
'week,long,age': 'CAISBggDEAEYAg%3D%3D',
'month,long,age': 'CAISBggEEAEYAg%3D%3D',
'year,long,age': 'CAISBggFEAEYAg%3D%3D',
'any,any,views': 'CAMSAhAB',
'hour,any,views': 'CAMSBAgBEAE%3D',
'day,any,views': 'CAMSBAgCEAE%3D',
'week,any,views': 'CAMSBAgDEAE%3D',
'month,any,views': 'CAMSBAgEEAE%3D',
'year,any,views': 'CAMSBAgFEAE%3D',
'any,short,views': 'CAMSBBABGAE%3D',
'hour,short,views': 'CAMSBggBEAEYAQ%3D%3D',
'day,short,views': 'CAMSBggCEAEYAQ%3D%3D',
'week,short,views': 'CAMSBggDEAEYAQ%3D%3D',
'month,short,views': 'CAMSBggEEAEYAQ%3D%3D',
'year,short,views': 'CAMSBggFEAEYAQ%3D%3D',
'any,long,views': 'CAMSBBABGAI%3D',
'hour,long,views': 'CAMSBggBEAEYAg%3D%3D',
'day,long,views': 'CAMSBggCEAEYAg%3D%3D',
'week,long,views': 'CAMSBggDEAEYAg%3D%3D',
'month,long,views': 'CAMSBggEEAEYAg%3D%3D',
'year,long,views': 'CAMSBggFEAEYAg%3D%3D',
'any,any,rating': 'CAESAhAB',
'hour,any,rating': 'CAESBAgBEAE%3D',
'day,any,rating': 'CAESBAgCEAE%3D',
'week,any,rating': 'CAESBAgDEAE%3D',
'month,any,rating': 'CAESBAgEEAE%3D',
'year,any,rating': 'CAESBAgFEAE%3D',
'any,short,rating': 'CAESBBABGAE%3D',
'hour,short,rating': 'CAESBggBEAEYAQ%3D%3D',
'day,short,rating': 'CAESBggCEAEYAQ%3D%3D',
'week,short,rating': 'CAESBggDEAEYAQ%3D%3D',
'month,short,rating': 'CAESBggEEAEYAQ%3D%3D',
'year,short,rating': 'CAESBggFEAEYAQ%3D%3D',
'any,long,rating': 'CAESBBABGAI%3D',
'hour,long,rating': 'CAESBggBEAEYAg%3D%3D',
'day,long,rating': 'CAESBggCEAEYAg%3D%3D',
'week,long,rating': 'CAESBggDEAEYAg%3D%3D',
'month,long,rating': 'CAESBggEEAEYAg%3D%3D',
'year,long,rating': 'CAESBggFEAEYAg%3D%3D'
})[order] || 'EgIQAQ%3D%3D');
}
};

@@ -17,5 +17,4 @@ 'use strict';

class Innertube extends EventEmitter {
class Innertube {
constructor(cookie) {
super();
this.cookie = cookie || '';

@@ -50,2 +49,4 @@ this.retry_count = 0;

}
this.ev = new EventEmitter();
} else {

@@ -64,36 +65,39 @@ this.retry_count += 1;

signIn(credentials = {}) {
signIn(auth_info = {}) {
return new Promise(async (resolve, reject) => {
const oauth = new OAuth(credentials);
if (credentials.access_token && credentials.refresh_token) {
let token_validity = await oauth.checkTokenValidity(credentials.access_token, this);
if (token_validity === 'VALID') {
this.access_token = credentials.access_token;
this.refresh_token = credentials.refresh_token;
this.logged_in = true;
resolve();
} else {
oauth.refreshAccessToken(credentials.refresh_token);
oauth.on('refresh-token', (data) => {
this.access_token = data.access_token;
this.refresh_token = credentials.refresh_token;
this.logged_in = true;
this.emit('update-credentials', {
access_token: data.access_token,
refresh_token: credentials.refresh_token,
status: data.status
});
resolve();
const oauth = new OAuth(auth_info);
if (auth_info.access_token) {
const is_valid = await oauth.isTokenValid(auth_info.expires);
if (!is_valid) {
const new_tokens = await oauth.refreshAccessToken(auth_info.refresh_token);
auth_info.refresh_token = new_tokens.credentials.refresh_token;
auth_info.access_token = new_tokens.credentials.access_token;
this.ev.emit('update-credentials', {
credentials: new_tokens.credentials,
status: new_tokens.status
});
}
this.access_token = auth_info.access_token;
this.refresh_token = auth_info.refresh_token;
this.logged_in = true;
resolve();
} else {
oauth.on('auth', (data) => {
if (data.status === 'SUCCESS') {
this.emit('auth', data);
this.access_token = data.access_token;
this.refresh_token = data.refresh_token;
this.access_token = data.credentials.access_token;
this.refresh_token = data.credentials.refresh_token;
this.logged_in = true;
this.ev.emit('auth', {
credentials: data.credentials,
status: data.status
});
resolve();
} else {
this.emit('auth', data);
this.ev.emit('auth', data);
}

@@ -106,17 +110,15 @@ });

async search(query, options = { period: 'any', order: 'relevance', duration: 'any' }) {
if (!query) throw new Error('No query was provided');
const response = await Actions.search(this, { query, options });
if (!response.success) throw new Error(`Could not search on YouTube: ${response.message}`);
const content = response.data.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[0].itemSectionRenderer.contents;
const search = {};
const response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/search${this.logged_in && this.cookie.length < 1 ? '' : `?key=${this.key}`}`, JSON.stringify({ context: this.context, params: Constants.filters(options.period + ',' + options.duration + ',' + options.order), query }), Constants.INNERTUBE_REQOPTS({ session: this })).catch((error) => error);
if (response instanceof Error) throw new Error(`Could not search on YouTube: ${response.message}`);
let content = response.data.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[0].itemSectionRenderer.contents;
let search_response = {};
search_response.search_metadata = {};
search_response.search_metadata.query = content[0].showingResultsForRenderer ? content[0].showingResultsForRenderer.originalQuery.simpleText : query;
search_response.search_metadata.corrected_query = content[0].showingResultsForRenderer ? content[0].showingResultsForRenderer.correctedQueryEndpoint.searchEndpoint.query : query;
search_response.search_metadata.estimated_results = parseInt(response.data.estimatedResults);
search_response.videos = content.map((data) => {
search.search_metadata = {};
search.search_metadata.query = content[0].showingResultsForRenderer ? content[0].showingResultsForRenderer.originalQuery.simpleText : query;
search.search_metadata.corrected_query = content[0].showingResultsForRenderer ? content[0].showingResultsForRenderer.correctedQueryEndpoint.searchEndpoint.query : query;
search.search_metadata.estimated_results = parseInt(response.data.estimatedResults);
search.videos = content.map((data) => {
if (!data.videoRenderer) return;
let video = data.videoRenderer;
const video = data.videoRenderer;
return {

@@ -147,9 +149,9 @@ title: video.title.runs[0].text,

}).filter((video_block) => video_block !== undefined);
return search_response;
return search;
}
async getDetails(id) {
if (!id) return { error: 'Missing video id' };
if (!id) throw new Error('You must provide a video id');
const data = await this.requestVideoInfo(id, false);
const data = await Actions.getVideoInfo(this, { id, is_desktop: false });
const video_data = Constants.formatVideoData(data, this, false);

@@ -291,10 +293,2 @@

async requestVideoInfo(id, desktop) {
let response;
!desktop && (response = await Axios.get(`${Constants.URLS.YT_WATCH_PAGE}?v=${id}&t=8s&pbj=1`, Constants.INNERTUBE_REQOPTS({ session: this, id, desktop: false })).catch((error) => error)) ||
(response = await Axios.post(`${Constants.URLS.YT_BASE_URL}/youtubei/v1/player${this.logged_in && this.cookie.length < 1 ? '' : `?key=${this.key}`}`, JSON.stringify(Constants.VIDEO_INFO_REQBODY(id, this.sts, this.context)), Constants.INNERTUBE_REQOPTS({ session: this, id, desktop: true })).catch((error) => error));
if (response instanceof Error) throw new Error('Could not retrieve watch page info: ' + response.message);
return response.data;
}
download(id, options = {}) {

@@ -311,3 +305,3 @@ if (!id) throw new Error('Missing video id');

const stream = new Stream.PassThrough();
this.requestVideoInfo(id, true).then(async (video_data) => {
Actions.getVideoInfo(this, { id, is_desktop: true }).then(async (video_data) => {
let formats = [];

@@ -397,2 +391,3 @@

let downloaded_size = 0;
response.data.on('data', (chunk) => {

@@ -406,7 +401,4 @@ downloaded_size += chunk.length;

response.data.on('error', (err) => {
if (cancelled) {
stream.emit('error', { message: 'The download was cancelled.', type: 'DOWNLOAD_CANCELLED' });
} else {
stream.emit('error', { message: err.message, type: 'DOWNLOAD_ABORTED' });
}
cancelled && stream.emit('error', { message: 'The download was cancelled.', type: 'DOWNLOAD_CANCELLED' })
|| stream.emit('error', { message: err.message, type: 'DOWNLOAD_ABORTED' });
});

@@ -413,0 +405,0 @@

@@ -7,18 +7,13 @@ 'use strict';

const EventEmitter = require('events');
const Uuid = require("uuid");
const Uuid = require('uuid');
class OAuth extends EventEmitter {
constructor(creds) {
constructor(auth_info) {
super();
// Default interval between requests when waiting for authorization.
this.refresh_interval = 5;
// OAuth URLs:
this.oauth_code_url = `${Constants.URLS.YT_BASE_URL}/o/oauth2/device/code`;
this.oauth_token_url = `${Constants.URLS.YT_BASE_URL}/o/oauth2/token`;
// Used to check whether an access token is valid or not.
this.guide_url = `${Constants.URLS.YT_BASE_URL}/youtubei/v1/guide`;
// These are always the same, so we shouldn't have any problems for now.
this.model_name = Constants.OAUTH.MODEL_NAME;

@@ -28,9 +23,6 @@ this.grant_type = Constants.OAUTH.GRANT_TYPE;

// Script that contains important information such as client id and client secret.
this.auth_script_regex = /<script id=\"base-js\" src=\"(.*?)\" nonce=".*?"><\/script>/;
// Used to find the credentials inside the script.
this.identity_regex = /var .+?=\"(?<id>.+?)\",.+?=\"(?<secret>.+?)\"/;
if (creds.access_token != undefined && creds.refresh_token != undefined) return;
if (auth_info.access_token) return;
this.requestAuthCode();

@@ -41,2 +33,3 @@ }

const identity = await this.getClientIdentity();
this.client_id = identity.id;

@@ -53,2 +46,3 @@ this.client_secret = identity.secret;

const response = await Axios.post(this.oauth_code_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error);
if (response instanceof Error)

@@ -73,18 +67,2 @@ return this.emit('auth', {

async getClientIdentity() {
// The first request is made to get the auth script url, hard-coding it isn't viable as it changes overtime.
const yttv_response = await Axios.get(`${Constants.URLS.YT_BASE_URL}/tv`, Constants.OAUTH.HEADERS).catch((error) => error);
if (yttv_response instanceof Error) throw new Error(`Could not extract client identify: ${yttv_response.message}`);
// Here we get the script and extract the necessary data to proceed with the auth flow.
const url_body = this.auth_script_regex.exec(yttv_response.data)[1];
const script_url = `${Constants.URLS.YT_BASE_URL}/${url_body}`;
const response = await Axios.get(script_url, Constants.DEFAULT_HEADERS).catch((error) => error);
if (response instanceof Error) throw new Error(`Could not extract client identify: ${response.message}`);
const identity_function = Utils.getStringBetweenStrings(response.data, 'setQuery("");', '{useGaiaSandbox:');
const client_identity = identity_function.replace(/\n/g, '').match(this.identity_regex);
return client_identity.groups;
}
waitForAuth(device_code) {

@@ -102,3 +80,3 @@ const data = {

return this.emit('auth', {
error: 'Could not get auth token.',
error: 'Could not get authentication token.',
status: 'FAILED'

@@ -115,3 +93,3 @@ });

this.emit('auth', {
error: 'The access was denied.',
error: 'Access was denied.',
status: 'ACCESS_DENIED'

@@ -130,8 +108,11 @@ });

} else {
const expiration_date = new Date(new Date().getTime() + response.data.expires_in * 1000);
this.emit('auth', {
access_token: response.data.access_token,
refresh_token: response.data.refresh_token,
credentials: {
access_token: response.data.access_token,
refresh_token: response.data.refresh_token,
expires: expiration_date,
},
token_type: response.data.token_type,
expires: response.data.expires_in,
scope: response.data.scope,
status: 'SUCCESS'

@@ -152,26 +133,55 @@ });

};
const response = await Axios.post(this.oauth_token_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error);
if (response instanceof Error)
return this.emit('refresh-token', {
if (response instanceof Error) {
this.emit('auth', {
error: 'Could not refresh access token.',
status: 'FAILED'
});
this.emit('refresh-token', {
access_token: response.data.access_token,
return {
credentials: {
access_token: this.auth_info.access_token,
refresh_token: this.auth_info.refresh_token,
expires: this.auth_info.expires
},
status: 'FAILED'
};
}
const expiration_date = new Date(new Date().getTime() + response.data.expires_in * 1000);
return {
credentials: {
refresh_token: refresh_token,
access_token: response.data.access_token,
expires: expiration_date
},
token_type: response.data.token_type,
expires: response.data.expires_in,
scope: response.data.scope,
status: 'SUCCESS'
});
};
}
async isTokenValid(expiration_date) {
const timestamp = new Date(expiration_date).getTime();
const is_valid = new Date().getTime() < timestamp;
return is_valid;
}
async getClientIdentity() {
// The first request is made to get the auth script url, hard-coding it isn't viable as it changes overtime.
const yttv_response = await Axios.get(`${Constants.URLS.YT_BASE_URL}/tv`, Constants.OAUTH.HEADERS).catch((error) => error);
if (yttv_response instanceof Error) throw new Error(`Could not extract client identity: ${yttv_response.message}`);
async checkTokenValidity(access_token, session) {
let headers = Constants.INNERTUBE_REQOPTS({ session }).headers;
headers.authorization = `Bearer ${access_token}`;
// Here we get the script and extract the necessary data to proceed with the auth flow.
const url_body = this.auth_script_regex.exec(yttv_response.data)[1];
const script_url = `${Constants.URLS.YT_BASE_URL}/${url_body}`;
const response = await Axios.post(this.guide_url, JSON.stringify({ context: session.context }), { headers }).catch((error) => error);
if (response instanceof Error) return 'INVALID';
return 'VALID';
const response = await Axios.get(script_url, Constants.DEFAULT_HEADERS).catch((error) => error);
if (response instanceof Error) throw new Error(`Could not extract client identity: ${response.message}`);
const identity_function = Utils.getStringBetweenStrings(response.data, 'setQuery("");', '{useGaiaSandbox:');
const client_identity = identity_function.replace(/\n/g, '').match(this.identity_regex);
return client_identity.groups;
}

@@ -178,0 +188,0 @@ }

@@ -86,2 +86,21 @@ 'use strict';

module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, generateMessageParams, generateCommentParams, encodeNotificationPref };
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 search_filter_buff = youtube_proto.SearchFilter.encode({
number: orders[order],
filter: {
param_0: periods[period],
param_1: (period == 'hour' && order == 'relevance') ? null : 1,
param_2: durations[duration]
}
});
return encodeURIComponent(Buffer.from(search_filter_buff).toString('base64'));
}
module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, generateMessageParams, generateCommentParams, encodeNotificationPref, encodeFilter };
{
"name": "youtubei.js",
"version": "1.2.7",
"version": "1.2.8-npm",
"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!",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -653,8 +653,7 @@ # YouTube.js

// Only triggered when signing-in.
youtube.on('auth', (data) => {
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({ access_token: data.access_token, refresh_token: data.refresh_token }));
fs.writeFileSync(creds_path, JSON.stringify(data.credentials));
console.info('Successfully signed-in, enjoy!');

@@ -664,5 +663,4 @@ }

// Triggered whenever the access token is refreshed.
youtube.on('update-credentials', (data) => {
fs.writeFileSync(creds_path, JSON.stringify({ access_token: data.access_token, refresh_token: data.refresh_token }));
youtube.ev.on('update-credentials', (data) => {
fs.writeFileSync(creds_path, JSON.stringify(data.credentials));
console.info('Credentials updated!', data);

@@ -669,0 +667,0 @@ });

@@ -13,23 +13,23 @@ 'use strict';

const youtube = await new Innertube().catch((error) => error);
assert(youtube instanceof Error ? false : true, `should retrieve Innertube configuration data`);
assert(youtube instanceof Error ? false : true, `should retrieve Innertube configuration data`, youtube);
if (!(youtube instanceof Error)) {
const search = await youtube.search('Carl Sagan - Documentary').catch((error) => error);
assert((search instanceof Error ? false : true) && search.videos.length >= 1, `should search videos`);
assert((search instanceof Error ? false : true) && search.videos.length >= 1, `should search videos`, search);
const details = await youtube.getDetails(Constants.test_video_id).catch((error) => error);
assert(details instanceof Error ? false : true, `should retrieve details for ${Constants.test_video_id}`);
assert(details instanceof Error ? false : true, `should retrieve details for ${Constants.test_video_id}`, details);
const comments = await youtube.getComments(Constants.test_video_id).catch((error) => error);
assert(comments instanceof Error ? false : true, `should retrieve comments for ${Constants.test_video_id}`);
assert(comments instanceof Error ? false : true, `should retrieve comments for ${Constants.test_video_id}`, comments);
const video = await downloadVideo(Constants.test_video_id_1, youtube).catch((error) => error);
assert(video instanceof Error ? false : true, `should download video (${Constants.test_video_id_1})`);
assert(video instanceof Error ? false : true, `should download video (${Constants.test_video_id_1})`, video);
}
const n_token = new NToken(Constants.n_scramble_sc).transform(Constants.original_ntoken);
assert(n_token == Constants.expected_ntoken, `should transform n token into ${Constants.expected_ntoken}`);
assert(n_token == Constants.expected_ntoken, `should transform n token into ${Constants.expected_ntoken}`, n_token);
const transformed_url = new SigDecipher(Constants.test_url, Constants.client_version, { sig_decipher_sc: Constants.sig_decipher_sc, ntoken_sc: Constants.n_scramble_sc }).decipher();
assert(transformed_url == Constants.expected_url, `should correctly decipher signature`);
assert(transformed_url == Constants.expected_url, `should correctly decipher signature`, transformed_url);

@@ -47,10 +47,10 @@ if (failed_tests > 0)

stream.on('info', () => got_video_info = true);
stream.on('error', () => reject());
stream.on('error', (err) => reject(err));
});
}
function assert(outcome, description) {
function assert(outcome, description, data) {
const pass_fail = outcome ? 'pass' : 'fail';
!outcome && (failed_tests += 1);
console.info(pass_fail, ':', description);
console.info(pass_fail, ':', description, !outcome && `\nError: ${data}` || '');
return outcome;

@@ -57,0 +57,0 @@ }

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc