youtubei.js
Advanced tools
Comparing version 1.4.2-d.1 to 1.4.2-d.2
@@ -17,3 +17,3 @@ 'use strict'; | ||
#auth_info = {}; | ||
#refresh_interval = 5; | ||
#polling_interval = 5; | ||
#ev = null; | ||
@@ -65,3 +65,3 @@ | ||
this.refresh_interval = response.data.interval; | ||
this.#polling_interval = response.data.interval; | ||
@@ -126,3 +126,3 @@ this.#waitForAuth(response.data.device_code); | ||
} | ||
}, 1000 * this.#refresh_interval); | ||
}, 1000 * this.#polling_interval); | ||
} | ||
@@ -129,0 +129,0 @@ |
@@ -26,4 +26,3 @@ 'use strict'; | ||
#actions; | ||
#retry_count; | ||
/** | ||
@@ -45,3 +44,2 @@ * ```js | ||
this.config = config || {}; | ||
this.#retry_count = 0; | ||
return this.#init(); | ||
@@ -93,4 +91,4 @@ } | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -102,4 +100,4 @@ setSubscriptions: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.SUBSCRIPTIONS, 'SPaccount_notifications', new_value), | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -111,4 +109,4 @@ setRecommendedVideos: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.RECOMMENDED_VIDEOS, 'SPaccount_notifications', new_value), | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -120,4 +118,4 @@ setChannelActivity: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.CHANNEL_ACTIVITY, 'SPaccount_notifications', new_value), | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -129,4 +127,4 @@ setCommentReplies: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.COMMENT_REPLIES, 'SPaccount_notifications', new_value), | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -138,4 +136,4 @@ setMentions: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.USER_MENTION, 'SPaccount_notifications', new_value), | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -148,4 +146,4 @@ setSharedContent: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.SHARED_CONTENT, 'SPaccount_notifications', new_value) | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -157,4 +155,4 @@ setSubscriptionsPrivate: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.SUBSCRIPTIONS_PRIVACY, 'SPaccount_privacy', new_value), | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -171,3 +169,3 @@ setSavedPlaylistsPrivate: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.PLAYLISTS_PRIVACY, 'SPaccount_privacy', new_value) | ||
* @param {string} video_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -180,3 +178,3 @@ like: (video_id) => this.actions.engage('like/like', { video_id }), | ||
* @param {string} video_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -189,3 +187,3 @@ dislike: (video_id) => this.actions.engage('like/dislike', { video_id }), | ||
* @param {string} video_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -199,3 +197,3 @@ removeLike: (video_id) => this.actions.engage('like/removelike', { video_id }), | ||
* @param {string} text | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -213,3 +211,3 @@ comment: (video_id, text) => this.actions.engage('comment/create_comment', { video_id, text }), | ||
* | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; translated_content: string; data: object; }>} | ||
*/ | ||
@@ -230,3 +228,4 @@ translate: async (text, target_language, args = {}) => { | ||
status_code: response.status_code, | ||
translated_content: translated_content.content | ||
translated_content: translated_content.content, | ||
data: response.data | ||
} | ||
@@ -239,3 +238,3 @@ }, | ||
* @param {string} channel_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -248,3 +247,3 @@ subscribe: (channel_id) => this.actions.engage('subscription/subscribe', { channel_id }), | ||
* @param {string} channel_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -259,3 +258,3 @@ unsubscribe: (channel_id) => this.actions.engage('subscription/unsubscribe', { channel_id }), | ||
* @param {string} type PERSONALIZED | ALL | NONE | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
@@ -270,14 +269,14 @@ setNotificationPreferences: (channel_id, type) => this.actions.notifications('modify_channel_preference', { channel_id, pref: type || 'NONE' }), | ||
* @param {string} title | ||
* @param {string} video_ids | ||
* @param {Array.<string>} video_ids | ||
* | ||
* @returns {Promise.<{ success: boolean; status_code: string; playlist_id: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>} | ||
*/ | ||
create: async (title, video_ids) => { | ||
Utils.throwIfMissing({ title, video_ids }); | ||
const response = await this.actions.playlist('playlist/create', { title, ids: video_ids }); | ||
if (!response.success) return response; | ||
return { | ||
success: true, | ||
success: response.success, | ||
status_code: response.status_code, | ||
playlist_id: response.data.playlistId | ||
playlist_id: response.data.playlistId, | ||
data: response.data | ||
} | ||
@@ -290,12 +289,12 @@ }, | ||
* @param {string} playlist_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; playlist_id: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>} | ||
*/ | ||
delete: async (playlist_id) => { | ||
Utils.throwIfMissing({ playlist_id }); | ||
const response = await this.actions.playlist('playlist/delete', { playlist_id }); | ||
if (!response.success) return response; | ||
return { | ||
success: true, | ||
success: response.success, | ||
status_code: response.status_code, | ||
playlist_id | ||
playlist_id: playlistId, | ||
data: response.data | ||
} | ||
@@ -309,5 +308,6 @@ }, | ||
* @param {Array.<string>} video_ids | ||
* @returns {Promise.<{ success: boolean; status_code: string; playlist_id: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>} | ||
*/ | ||
addVideos: async (playlist_id, video_ids) => { | ||
Utils.throwIfMissing({ playlist_id, video_ids }); | ||
const response = await this.actions.playlist('browse/edit_playlist', { | ||
@@ -319,8 +319,7 @@ action: 'ACTION_ADD_VIDEO', | ||
if (!response.success) return response; | ||
return { | ||
success: true, | ||
success: response.success, | ||
status_code: response.status_code, | ||
playlist_id | ||
playlist_id: playlistId, | ||
data: response.data | ||
} | ||
@@ -334,5 +333,7 @@ }, | ||
* @param {Array.<string>} video_ids | ||
* @returns {Promise.<{ success: boolean; status_code: string; playlist_id: string; }>} | ||
* | ||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>} | ||
*/ | ||
removeVideos: async (playlist_id, video_ids) => { | ||
Utils.throwIfMissing({ playlist_id, video_ids }); | ||
const plinfo = await this.actions.browse(`VL${playlist_id}`); | ||
@@ -351,9 +352,8 @@ | ||
}); | ||
if (!response.success) return response; | ||
return { | ||
success: true, | ||
success: response.success, | ||
status_code: response.status_code, | ||
playlist_id | ||
playlist_id: playlist_id, | ||
data: response.data | ||
} | ||
@@ -370,11 +370,17 @@ } | ||
* @param {string} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
async #setSetting(setting_id, type, new_value) { | ||
Utils.throwIfMissing({ setting_id, type, new_value }); | ||
const values = { ON: true, OFF: false }; | ||
if (!values.hasOwnProperty(new_value)) | ||
throw new Utils.InnertubeError('Invalid option', { option: new_value, available_options: Object.keys(values) }); | ||
const response = await this.actions.browse(type); | ||
if (!response.success) return response; | ||
const contents = ({ | ||
account_notifications: () => Utils.findNode(response.data, 'contents', 'Your preferences', 13, false).options, | ||
account_privacy: () => Utils.findNode(response.data, 'contents', 'settingsSwitchRenderer', 13, false).options | ||
SPaccount_notifications: () => Utils.findNode(response.data, 'contents', 'Your preferences', 13, false).options, | ||
SPaccount_privacy: () => Utils.findNode(response.data, 'contents', 'settingsSwitchRenderer', 13, false).options | ||
})[type.trim()](); | ||
@@ -385,8 +391,8 @@ | ||
const setting_item_id = option.settingsSwitchRenderer.enableServiceEndpoint.setSettingEndpoint.settingItemId; | ||
const set_setting = await this.actions.account('account/set_setting', { new_value: type == 'account_privacy' ? !new_value : new_value, setting_item_id }); | ||
const set_setting = await this.actions.account('account/set_setting', { | ||
new_value: type == 'SPaccount_privacy' ? !values[new_value] : values[new_value], | ||
setting_item_id | ||
}); | ||
return { | ||
success: set_setting.success, | ||
status_code: set_setting.status_code, | ||
} | ||
return set_setting; | ||
} | ||
@@ -445,4 +451,2 @@ | ||
const response = await this.actions.account('account/account_menu'); | ||
if (!response.success) throw new Utils.InnertubeError('Could not get account info', response); | ||
const menu = Utils.findNode(response, 'actions', 'multiPageMenuRenderer', 6, false); | ||
@@ -464,4 +468,4 @@ | ||
* @param {string} options.client - client used to perform the search, can be: `YTMUSIC` or `YOUTUBE`. | ||
* @param {string} options.order - filter results by order, can be: relevance | rating | age | views | ||
* @param {string} options.period - filter videos uploaded within a period, can be: any | hour | day | week | month | year | ||
* @param {string} options.order - filter results by order, can be: relevance | rating | age | views | ||
* @param {string} options.duration - filter video results by duration, can be: any | short | long | ||
@@ -472,8 +476,11 @@ * | ||
*/ | ||
async search(query, options = { client: 'YOUTUBE', period: 'any', order: 'relevance', duration: 'any' }) { | ||
const response = await this.actions.search({ query, options, is_ytm: options.client == 'YTMUSIC' }); | ||
async search(query, options) { | ||
Utils.throwIfMissing({ query }); | ||
const final_options = Object.assign({ client: 'YOUTUBE', period: 'any', duration: 'any', order: 'relevance' }, options); | ||
const response = await this.actions.search({ query, options: final_options, is_ytm: final_options.client == 'YTMUSIC' }); | ||
const results = new Parser(this, response.data, { | ||
query, | ||
client: options.client, | ||
client: final_options.client, | ||
data_type: 'SEARCH' | ||
@@ -495,4 +502,5 @@ }).parse(); | ||
async getSearchSuggestions(input, options = { client: 'YOUTUBE' }) { | ||
Utils.throwIfMissing({ input }); | ||
const response = await this.actions.getSearchSuggestions(options.client, input); | ||
if (!response.success) throw new Utils.InnertubeError('Could not get search suggestions', response); | ||
if (options.client === 'YTMUSIC' && !response.data.contents) return []; | ||
@@ -516,8 +524,8 @@ | ||
async getDetails(video_id) { | ||
if (!video_id) throw new Utils.MissingParamError('Video id is missing'); | ||
Utils.throwIfMissing({ video_id }); | ||
const response = await this.actions.getVideoInfo(video_id); | ||
const continuation = await this.actions.next({ video_id }); | ||
continuation.success && (response.continuation = continuation.data); | ||
response.continuation = continuation.data; | ||
const details = new Parser(this, response, { | ||
@@ -553,2 +561,4 @@ client: 'YOUTUBE', | ||
async getComments(video_id, sort_by) { | ||
Utils.throwIfMissing({ video_id }); | ||
const payload = Proto.encodeCommentsSectionParams(video_id, { | ||
@@ -559,4 +569,2 @@ sort_by: sort_by || 'TOP_COMMENTS' | ||
const response = await this.actions.next({ ctoken: payload }); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve comments', response); | ||
const comments = new Parser(this, response.data, { | ||
@@ -578,5 +586,6 @@ video_id, | ||
async getChannel(id) { | ||
Utils.throwIfMissing({ id }); | ||
const response = await this.actions.browse(id); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve channel info.', response); | ||
const channel_info = new Parser(this, response.data, { | ||
@@ -596,4 +605,3 @@ client: 'YOUTUBE', | ||
const response = await this.actions.browse('FEhistory'); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve watch history', response); | ||
const history = new Parser(this, response, { | ||
@@ -613,4 +621,3 @@ client: 'YOUTUBE', | ||
const response = await this.actions.browse('FEwhat_to_watch'); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve home feed', response); | ||
const homefeed = new Parser(this, response, { | ||
@@ -633,4 +640,3 @@ client: 'YOUTUBE', | ||
const response = await this.actions.browse('FEtrending'); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve trending content', response); | ||
const trending = new Parser(this, response, { | ||
@@ -649,4 +655,3 @@ client: 'YOUTUBE', | ||
const response = await this.actions.browse('FElibrary'); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve library', response); | ||
const library = new Parser(this, response.data, { | ||
@@ -666,4 +671,3 @@ client: 'YOUTUBE', | ||
const response = this.actions.browse('FEsubscriptions'); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve subscriptions feed', response); | ||
const subsfeed = new Parser(this, response, { | ||
@@ -683,4 +687,3 @@ client: 'YOUTUBE', | ||
const response = await this.actions.notifications('get_notification_menu'); | ||
if (!response.success) throw new Utils.InnertubeError('Could not fetch notifications', response); | ||
const notifications = new Parser(this, response.data, { | ||
@@ -700,3 +703,2 @@ client: 'YOUTUBE', | ||
const response = await this.actions.notifications('get_unseen_count'); | ||
if (!response.success) throw new Utils.InnertubeError('Could not get unseen notifications count', response); | ||
return response.data.unseenCount; | ||
@@ -712,9 +714,9 @@ } | ||
async getLyrics(video_id) { | ||
Utils.throwIfMissing({ video_id }); | ||
const continuation = await this.actions.next({ video_id: video_id, is_ytm: true }); | ||
if (!continuation.success) throw new Utils.InnertubeError('Could not retrieve lyrics', continuation); | ||
const lyrics_tab = Utils.findNode(continuation, 'contents', 'Lyrics', 8, false); | ||
const response = await this.actions.browse(lyrics_tab.endpoint?.browseEndpoint.browseId, { is_ytm: true }); | ||
if (!response.success || !response.data?.contents?.sectionListRenderer) throw new Utils.UnavailableContentError('Lyrics not available', { video_id }); | ||
if (!response.data?.contents?.sectionListRenderer) throw new Utils.UnavailableContentError('Lyrics not available', { video_id }); | ||
@@ -736,5 +738,5 @@ const lyrics = Utils.findNode(response.data, 'contents', 'runs', 6, false); | ||
async getPlaylist(playlist_id, options = { client: 'YOUTUBE' }) { | ||
Utils.throwIfMissing({ playlist_id }); | ||
const response = await this.actions.browse(`VL${playlist_id}`, { is_ytm: options.client == 'YTMUSIC' }); | ||
if (!response.success) throw new Utils.InnertubeError('Could not get playlist', response); | ||
const playlist = new Parser(this, response.data, { | ||
@@ -814,3 +816,3 @@ client: options.client, | ||
* | ||
* @param {string} id - video id | ||
* @param {string} video_id - video id | ||
* @param {object} options - download options. | ||
@@ -823,3 +825,5 @@ * @param {string} options.quality - video quality; 360p, 720p, 1080p, etc... | ||
*/ | ||
async getStreamingData(id, options = {}) { | ||
async getStreamingData(video_id, options = {}) { | ||
Utils.throwIfMissing({ video_id }); | ||
options.quality = options.quality || '360p'; | ||
@@ -829,3 +833,3 @@ options.type = options.type || 'videoandaudio'; | ||
const data = await this.actions.getVideoInfo(id); | ||
const data = await this.actions.getVideoInfo(video_id); | ||
const streaming_data = this.#chooseFormat(options, data); | ||
@@ -842,3 +846,3 @@ | ||
* | ||
* @param {string} id - video id | ||
* @param {string} video_id - video id | ||
* @param {object} options - download options. | ||
@@ -851,5 +855,5 @@ * @param {string} [options.quality] - video quality; 360p, 720p, 1080p, etc... | ||
*/ | ||
download(id, options = {}) { | ||
if (!id) throw new Utils.MissingParamError('Video id is missing'); | ||
download(video_id, options = {}) { | ||
Utils.throwIfMissing({ video_id }); | ||
options.quality = options.quality || '360p'; | ||
@@ -865,3 +869,3 @@ options.type = options.type || 'videoandaudio'; | ||
const stream = new Stream.PassThrough(); | ||
this.actions.getVideoInfo(id, cpn).then(async (video_data) => { | ||
this.actions.getVideoInfo(video_id, cpn).then(async (video_data) => { | ||
if (video_data.playabilityStatus.status === 'LOGIN_REQUIRED') | ||
@@ -868,0 +872,0 @@ return stream.emit('error', { message: 'You must login to download age-restricted videos.', error_type: 'LOGIN_REQUIRED', playability_status: video_data.playabilityStatus.status }); |
@@ -72,4 +72,3 @@ 'use strict'; | ||
const response = await this.session.actions.search({ ctoken, is_ytm: false }); | ||
if (!response.success) throw new Utils.InnertubeError('Could not get continuation', response); | ||
const continuation_items = Utils.findNode(response.data, 'onResponseReceivedCommands', 'itemSectionRenderer', 4, false); | ||
@@ -367,4 +366,3 @@ return parseItems(continuation_items); | ||
const response = await this.session.actions.browse(ctoken, { is_ctoken: true }); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve continuation', response); | ||
return parseItems(response.data.onResponseReceivedActions[0].appendContinuationItemsAction.continuationItems); | ||
@@ -432,4 +430,3 @@ } | ||
const response = await this.session.actions.browse(ctoken, { is_ctoken: true }); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve continuation', response); | ||
const ccontents = Utils.findNode(response.data, 'onResponseReceivedActions', 'itemSectionRenderer', 4, false); | ||
@@ -508,4 +505,3 @@ subsfeed.items = []; | ||
const response = await this.session.actions.notifications('get_notification_menu', { ctoken }); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve continuation', response); | ||
return parseItems(response.data.actions[0].appendContinuationItemsAction.continuationItems); | ||
@@ -543,3 +539,2 @@ } | ||
const response = await this.session.actions.browse('FEtrending', { params }); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve category videos', response); | ||
@@ -586,4 +581,3 @@ const tabs = Utils.findNode(response, 'contents', 'tabRenderer', 4, false); | ||
const response = await this.session.actions.browse(ctoken, { is_ctoken: true }); | ||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve continuation', response); | ||
history.items = []; | ||
@@ -590,0 +584,0 @@ |
@@ -27,7 +27,9 @@ 'use strict'; | ||
* | ||
* @param {object} obj - The object. | ||
* @param {string} key - Key of the property being accessed. | ||
* @param {string} target - Anything that might be inside of the property. | ||
* @param {number} depth - Maximum number of nested objects to flatten. | ||
* @param {boolean} safe - If set to true arrays will be preserved. | ||
* @param {object} obj - the object. | ||
* @param {string} key - key of the property being accessed. | ||
* @param {string} target - anything that might be inside of the property. | ||
* @param {number} depth - maximum number of nested objects to flatten. | ||
* @param {boolean} safe - if set to true arrays will be preserved. | ||
* | ||
* @returns {object|Array.<any>} | ||
*/ | ||
@@ -42,7 +44,9 @@ function findNode(obj, key, target, depth, safe = true) { | ||
/** | ||
* Gets a string between two delimiters. | ||
* Finds a string between two delimiters. | ||
* | ||
* @param {string} data - The data. | ||
* @param {string} start_string - Start string. | ||
* @param {string} end_string - End string. | ||
* @param {string} data - the data. | ||
* @param {string} start_string - start string. | ||
* @param {string} end_string - end string. | ||
* | ||
* @returns {string} | ||
*/ | ||
@@ -93,2 +97,8 @@ function getStringBetweenStrings(data, start_string, end_string) { | ||
/** | ||
* Generates a random string with a given length. | ||
* | ||
* @param {number} length | ||
* @returns {string} | ||
*/ | ||
function generateRandomString(length) { | ||
@@ -131,2 +141,25 @@ const result = []; | ||
/** | ||
* Checks if a given client is valid. | ||
* | ||
* @param {string} client | ||
* @returns {boolean} | ||
*/ | ||
function isValidClient(client) { | ||
return [ 'YOUTUBE', 'YTMUSIC' ].includes(client); | ||
} | ||
/** | ||
* Throws an error if given parameters are undefined. | ||
* | ||
* @param {object} params | ||
* @returns | ||
*/ | ||
function throwIfMissing(params) { | ||
for (const [key, value] of Object.entries(params)) { | ||
if (!value) | ||
throw new MissingParamError(`${key} is missing`); | ||
} | ||
} | ||
/** | ||
* Turns the ntoken transform data into a valid json array | ||
@@ -148,4 +181,4 @@ * | ||
const errors = { InnertubeError, UnavailableContentError, ParsingError, DownloadError, MissingParamError, NoStreamingDataError }; | ||
const functions = { findNode, getRandomUserAgent, generateSidAuth, generateRandomString, getStringBetweenStrings, camelToSnake, timeToSeconds, refineNTokenData }; | ||
const functions = { findNode, getRandomUserAgent, generateSidAuth, generateRandomString, getStringBetweenStrings, camelToSnake, isValidClient, throwIfMissing, timeToSeconds, refineNTokenData }; | ||
module.exports = { ...functions, ...errors }; |
{ | ||
"name": "youtubei.js", | ||
"version": "1.4.2-d.1", | ||
"version": "1.4.2-d.2", | ||
"description": "A full-featured library that allows you to get detailed info about any video, subscribe, unsubscribe, like, dislike, comment, search, download videos/music and much more!", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
114
README.md
@@ -50,3 +50,3 @@ <h1 align=center>YouTube.js</h1> | ||
<li><a href="#live-chats">Livechats</a></li> | ||
<li><a href="#download-videos">Download videos</a></li> | ||
<li><a href="#downloading-videos">Downloading videos</a></li> | ||
<li><a href="#signing-in">Signing in</a></li> | ||
@@ -120,16 +120,9 @@ </ul> | ||
To improve performance, the Innertube instance should be initialized once and then reused throughout your program. | ||
### A simple search: | ||
YouTube: | ||
Client: `YOUTUBE` | `YTMUSIC` | ||
```js | ||
const search = await youtube.search('Looking for life on Mars - Documentary'); | ||
const search = await youtube.search('Looking for life on Mars - Documentary', { client: 'YOUTUBE' }); | ||
``` | ||
YTMusic: | ||
```js | ||
const search = await youtube.search('Interstellar Main Theme', { client: 'YTMUSIC' }); | ||
``` | ||
<details> | ||
@@ -259,5 +252,3 @@ <summary>YouTube Output</summary> | ||
```js | ||
const suggestions = await youtube.getSearchSuggestions('QUERY', { | ||
client: 'YOUTUBE' // Use YTMUSIC if you want music search suggestions | ||
}) | ||
const suggestions = await youtube.getSearchSuggestions('QUERY', { client: 'YOUTUBE' }) | ||
``` | ||
@@ -341,9 +332,10 @@ | ||
### Comments | ||
### Comments: | ||
Sorting options: `TOP_COMMENTS` | `NEWEST_FIRST` | ||
```js | ||
// Sorting options: `TOP_COMMENTS` and `NEWEST_FIRST` | ||
const comments = await youtube.getComments('VIDEO_ID', 'TOP_COMMENTS'); | ||
``` | ||
Alternatively you can use: | ||
Alternatively, you can use: | ||
@@ -686,12 +678,6 @@ ```js | ||
### Get playlist: | ||
YouTube (default): | ||
```js | ||
const playlist = await youtube.getPlaylist('PLAYLIST_ID'); | ||
``` | ||
Client: `YOUTUBE` | `YTMUSIC` | ||
YouTube Music: | ||
```js | ||
const playlist = await youtube.getPlaylist('PLAYLIST_ID', { | ||
client: 'YTMUSIC' | ||
}); | ||
const playlist = await youtube.getPlaylist('PLAYLIST_ID', { client: 'YOUTUBE' }); | ||
``` | ||
@@ -762,3 +748,3 @@ | ||
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! | ||
Don't forget that you must be signed in to use some of the following methods! | ||
@@ -807,4 +793,6 @@ * Subscribe/Unsubscribe: | ||
* Change notification preferences: | ||
Options: `ALL` | `NONE` | `PERSONALIZED` | ||
```js | ||
// Options: ALL | NONE | PERSONALIZED | ||
await youtube.interact.setNotificationPreferences('CHANNEL_ID', 'ALL'); | ||
@@ -825,3 +813,3 @@ ``` | ||
### Account Settings | ||
It is also possible to manage an account's settings: | ||
It is also possible to manage account settings: | ||
@@ -858,5 +846,7 @@ * Get account info: | ||
Options: `ON` | `OFF` | ||
* Subscription notifications: | ||
```js | ||
await youtube.account.settings.notifications.setSubscriptions(true); | ||
await youtube.account.settings.notifications.setSubscriptions('ON'); | ||
``` | ||
@@ -866,3 +856,3 @@ | ||
```js | ||
await youtube.account.settings.notifications.setRecommendedVideos(true); | ||
await youtube.account.settings.notifications.setRecommendedVideos('ON'); | ||
``` | ||
@@ -872,3 +862,3 @@ | ||
```js | ||
await youtube.account.settings.notifications.setChannelActivity(true); | ||
await youtube.account.settings.notifications.setChannelActivity('ON'); | ||
``` | ||
@@ -878,3 +868,3 @@ | ||
```js | ||
await youtube.account.settings.notifications.setCommentReplies(true); | ||
await youtube.account.settings.notifications.setCommentReplies('ON'); | ||
``` | ||
@@ -884,3 +874,3 @@ | ||
```js | ||
await youtube.account.settings.notifications.setSharedContent(true); | ||
await youtube.account.settings.notifications.setSharedContent('ON'); | ||
``` | ||
@@ -890,5 +880,7 @@ | ||
Options: `ON` | `OFF` | ||
* Subscriptions privacy: | ||
```js | ||
await youtube.account.settings.privacy.setSubscriptionsPrivate(true); | ||
await youtube.account.settings.privacy.setSubscriptionsPrivate('ON'); | ||
``` | ||
@@ -898,3 +890,3 @@ | ||
```js | ||
await youtube.account.settings.privacy.setSavedPlaylistsPrivate(true); | ||
await youtube.account.settings.privacy.setSavedPlaylistsPrivate('ON'); | ||
``` | ||
@@ -946,8 +938,37 @@ | ||
### Download videos: | ||
### Downloading videos: | ||
--- | ||
```js | ||
const options = { | ||
format: string, | ||
quality: string, | ||
type: string, | ||
range: { start: number, end: number } | ||
}; | ||
YouTube.js provides an easy-to-use and simple downloader: | ||
const stream = youtube.download('VIDEO_ID', options); | ||
``` | ||
Options: | ||
* format: | ||
`mp4` | `webm` etc.. (note: only formats provided by YouTube are available) | ||
* quality: | ||
`144p`, `240p`, `360p`, `480p`, `720p`, `1080p` etc.. | ||
* type: | ||
`video` | `audio` | `videoandaudio` | ||
* range: indicates which bytes should be downloaded | ||
* start: an integer indicating the beginning of the range | ||
* end: an integer indicating the end of the range | ||
Cancel a download: | ||
```js | ||
stream.cancel(); | ||
``` | ||
Example: | ||
```js | ||
const fs = require('fs'); | ||
@@ -996,25 +1017,6 @@ const Innertube = require('youtubei.js'); | ||
You can also specify a range: | ||
```js | ||
const stream = youtube.download(VIDEO_ID, { | ||
//... | ||
type: 'videoandaudio', | ||
range: { start: 0, end: 1048576 * 5 } | ||
}); | ||
``` | ||
Cancel a download: | ||
```js | ||
stream.cancel(); | ||
``` | ||
Alternatively, you can get the deciphered streaming data and handle the download yourself: | ||
```js | ||
const streaming_data = await youtube.getStreamingData(search.videos[0].id, { | ||
format: 'mp4', | ||
quality: '360p', | ||
type: 'videoandaudio' | ||
}); | ||
const streaming_data = await youtube.getStreamingData('VIDEO_ID', options); | ||
``` | ||
@@ -1021,0 +1023,0 @@ |
export = Livechat; | ||
declare class Livechat { | ||
declare class Livechat extends EventEmitter { | ||
constructor(session: any, token: any, channel_id: any, video_id: any); | ||
@@ -13,3 +13,3 @@ ctoken: any; | ||
metadata_ctoken: any; | ||
livechat_poller: any; | ||
livechat_poller: NodeJS.Timeout; | ||
sendMessage(text: any): Promise<any>; | ||
@@ -25,1 +25,2 @@ /** | ||
} | ||
import EventEmitter = require("events"); |
@@ -11,3 +11,2 @@ export = OAuth; | ||
client_secret: string; | ||
refresh_interval: any; | ||
/** | ||
@@ -14,0 +13,0 @@ * Refreshes the access token if necessary. |
@@ -9,4 +9,4 @@ export = Player; | ||
get signature_decipher(): any; | ||
isCached(): any; | ||
isCached(): boolean; | ||
#private; | ||
} |
@@ -55,8 +55,9 @@ export = Innertube; | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
setSubscriptions: (new_value: boolean) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -66,8 +67,9 @@ /** | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
setRecommendedVideos: (new_value: boolean) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -77,8 +79,9 @@ /** | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
setChannelActivity: (new_value: boolean) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -88,8 +91,9 @@ /** | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
setCommentReplies: (new_value: boolean) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -99,8 +103,9 @@ /** | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
setMentions: (new_value: boolean) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -110,8 +115,9 @@ /** | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
setSharedContent: (new_value: boolean) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -123,8 +129,9 @@ }; | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
setSubscriptionsPrivate: (new_value: boolean) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -134,8 +141,9 @@ /** | ||
* | ||
* @param {boolean} new_value | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @param {boolean} new_value - ON | OFF | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
setSavedPlaylistsPrivate: (new_value: boolean) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -150,7 +158,8 @@ }; | ||
* @param {string} video_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
like: (video_id: string) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -161,7 +170,8 @@ /** | ||
* @param {string} video_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
dislike: (video_id: string) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -172,7 +182,8 @@ /** | ||
* @param {string} video_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
removeLike: (video_id: string) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -184,7 +195,8 @@ /** | ||
* @param {string} text | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
comment: (video_id: string, text: string) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -200,3 +212,3 @@ /** | ||
* | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; translated_content: string; data: object; }>} | ||
*/ | ||
@@ -208,3 +220,5 @@ translate: (text: string, target_language: string, args?: { | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
translated_content: string; | ||
data: object; | ||
}>; | ||
@@ -215,7 +229,8 @@ /** | ||
* @param {string} channel_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
subscribe: (channel_id: string) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -226,7 +241,8 @@ /** | ||
* @param {string} channel_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
unsubscribe: (channel_id: string) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -239,7 +255,8 @@ /** | ||
* @param {string} type PERSONALIZED | ALL | NONE | ||
* @returns {Promise.<{ success: boolean; status_code: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>} | ||
*/ | ||
setNotificationPreferences: (channel_id: string, type: string) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
data: object; | ||
}>; | ||
@@ -252,10 +269,11 @@ }; | ||
* @param {string} title | ||
* @param {string} video_ids | ||
* @param {Array.<string>} video_ids | ||
* | ||
* @returns {Promise.<{ success: boolean; status_code: string; playlist_id: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>} | ||
*/ | ||
create: (title: string, video_ids: string) => Promise<{ | ||
create: (title: string, video_ids: Array<string>) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
playlist_id: string; | ||
data: object; | ||
}>; | ||
@@ -266,8 +284,9 @@ /** | ||
* @param {string} playlist_id | ||
* @returns {Promise.<{ success: boolean; status_code: string; playlist_id: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>} | ||
*/ | ||
delete: (playlist_id: string) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
playlist_id: string; | ||
data: object; | ||
}>; | ||
@@ -279,8 +298,9 @@ /** | ||
* @param {Array.<string>} video_ids | ||
* @returns {Promise.<{ success: boolean; status_code: string; playlist_id: string; }>} | ||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>} | ||
*/ | ||
addVideos: (playlist_id: string, video_ids: Array<string>) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
playlist_id: string; | ||
data: object; | ||
}>; | ||
@@ -292,8 +312,10 @@ /** | ||
* @param {Array.<string>} video_ids | ||
* @returns {Promise.<{ success: boolean; status_code: string; playlist_id: string; }>} | ||
* | ||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>} | ||
*/ | ||
removeVideos: (playlist_id: string, video_ids: Array<string>) => Promise<{ | ||
success: boolean; | ||
status_code: string; | ||
status_code: number; | ||
playlist_id: string; | ||
data: object; | ||
}>; | ||
@@ -341,4 +363,4 @@ }; | ||
* @param {string} options.client - client used to perform the search, can be: `YTMUSIC` or `YOUTUBE`. | ||
* @param {string} options.order - filter results by order, can be: relevance | rating | age | views | ||
* @param {string} options.period - filter videos uploaded within a period, can be: any | hour | day | week | month | year | ||
* @param {string} options.order - filter results by order, can be: relevance | rating | age | views | ||
* @param {string} options.duration - filter video results by duration, can be: any | short | long | ||
@@ -349,6 +371,6 @@ * | ||
*/ | ||
search(query: string, options?: { | ||
search(query: string, options: { | ||
client: string; | ||
order: string; | ||
period: string; | ||
order: string; | ||
duration: string; | ||
@@ -539,3 +561,3 @@ }): Promise<{ | ||
* | ||
* @param {string} id - video id | ||
* @param {string} video_id - video id | ||
* @param {object} options - download options. | ||
@@ -548,3 +570,3 @@ * @param {string} options.quality - video quality; 360p, 720p, 1080p, etc... | ||
*/ | ||
getStreamingData(id: string, options?: { | ||
getStreamingData(video_id: string, options?: { | ||
quality: string; | ||
@@ -560,3 +582,3 @@ type: string; | ||
* | ||
* @param {string} id - video id | ||
* @param {string} video_id - video id | ||
* @param {object} options - download options. | ||
@@ -569,3 +591,3 @@ * @param {string} [options.quality] - video quality; 360p, 720p, 1080p, etc... | ||
*/ | ||
download(id: string, options?: { | ||
download(video_id: string, options?: { | ||
quality?: string; | ||
@@ -577,3 +599,5 @@ type?: string; | ||
} | ||
import EventEmitter = require("events"); | ||
import Request = require("./utils/Request"); | ||
import Actions = require("./core/Actions"); | ||
import Stream = require("stream"); |
@@ -20,9 +20,11 @@ export class InnertubeError extends Error { | ||
* | ||
* @param {object} obj - The object. | ||
* @param {string} key - Key of the property being accessed. | ||
* @param {string} target - Anything that might be inside of the property. | ||
* @param {number} depth - Maximum number of nested objects to flatten. | ||
* @param {boolean} safe - If set to true arrays will be preserved. | ||
* @param {object} obj - the object. | ||
* @param {string} key - key of the property being accessed. | ||
* @param {string} target - anything that might be inside of the property. | ||
* @param {number} depth - maximum number of nested objects to flatten. | ||
* @param {boolean} safe - if set to true arrays will be preserved. | ||
* | ||
* @returns {object|Array.<any>} | ||
*/ | ||
export function findNode(obj: object, key: string, target: string, depth: number, safe?: boolean): any; | ||
export function findNode(obj: object, key: string, target: string, depth: number, safe?: boolean): object | Array<any>; | ||
/** | ||
@@ -42,10 +44,18 @@ * Returns a random user agent. | ||
export function generateSidAuth(sid: string): string; | ||
export function generateRandomString(length: any): string; | ||
/** | ||
* Gets a string between two delimiters. | ||
* Generates a random string with a given length. | ||
* | ||
* @param {string} data - The data. | ||
* @param {string} start_string - Start string. | ||
* @param {string} end_string - End string. | ||
* @param {number} length | ||
* @returns {string} | ||
*/ | ||
export function generateRandomString(length: number): string; | ||
/** | ||
* Finds a string between two delimiters. | ||
* | ||
* @param {string} data - the data. | ||
* @param {string} start_string - start string. | ||
* @param {string} end_string - end string. | ||
* | ||
* @returns {string} | ||
*/ | ||
export function getStringBetweenStrings(data: string, start_string: string, end_string: string): string; | ||
@@ -60,2 +70,16 @@ /** | ||
/** | ||
* Checks if a given client is valid. | ||
* | ||
* @param {string} client | ||
* @returns {boolean} | ||
*/ | ||
export function isValidClient(client: string): boolean; | ||
/** | ||
* Throws an error if given parameters are undefined. | ||
* | ||
* @param {object} params | ||
* @returns | ||
*/ | ||
export function throwIfMissing(params: object): void; | ||
/** | ||
* Converts time (h:m:s) to seconds. | ||
@@ -62,0 +86,0 @@ * |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
299489
7522
1154
0