mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-21 13:31:36 +00:00
refactor!: welp, a lot of stuff
- Use the OS temp folder to cache the player, closes #57. - Added support for editing channel name, closes #40. - Added support for editing channel description. - Added support for retrieving basic channel analytics, closes #54. - Moved `Innertube#getAccountInfo()` to `Innertube#account`, and renamed it to `getInfo()`. - `getInfo()` is now able to return email, channel id, etc. - Improved jsdoc.
This commit is contained in:
414
lib/Innertube.js
414
lib/Innertube.js
@@ -10,6 +10,9 @@ const OAuth = require('./core/OAuth');
|
||||
const Actions = require('./core/Actions');
|
||||
const Livechat = require('./core/Livechat');
|
||||
const SessionBuilder = require('./core/SessionBuilder');
|
||||
const AccountManager = require('./core/AccountManager');
|
||||
const PlaylistManager = require('./core/PlaylistManager');
|
||||
const InteractionManager = require('./core/InteractionManager');
|
||||
|
||||
const Utils = require('./utils/Utils');
|
||||
const Request = require('./utils/Request');
|
||||
@@ -19,11 +22,15 @@ const Proto = require('./proto');
|
||||
const NToken = require('./deciphers/NToken');
|
||||
const Signature = require('./deciphers/Signature');
|
||||
|
||||
/**
|
||||
* Innertube instance.
|
||||
* @namespace
|
||||
*/
|
||||
class Innertube {
|
||||
#oauth;
|
||||
#player;
|
||||
|
||||
/**
|
||||
* @example
|
||||
* ```js
|
||||
* const Innertube = require('youtubei.js');
|
||||
* const youtube = await new Innertube();
|
||||
@@ -61,7 +68,7 @@ class Innertube {
|
||||
* @type {EventEmitter}
|
||||
*/
|
||||
this.ev = new EventEmitter();
|
||||
this.#oauth = new OAuth(this.ev);
|
||||
this.oauth = new OAuth(this.ev);
|
||||
|
||||
if (this.config.cookie) {
|
||||
this.auth_apisid = Utils.getStringBetweenStrings(this.config.cookie, 'PAPISID=', ';');
|
||||
@@ -71,319 +78,29 @@ class Innertube {
|
||||
this.request = new Request(this);
|
||||
this.actions = new Actions(this);
|
||||
|
||||
this.#initMethods();
|
||||
this.account = new AccountManager(this.actions);
|
||||
this.playlist = new PlaylistManager(this.actions);
|
||||
this.interact = new InteractionManager(this.actions);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#initMethods() {
|
||||
this.account = {
|
||||
info: () => this.getAccountInfo(),
|
||||
getTimeWatched: () => { /* TODO: Implement this */ },
|
||||
settings: {
|
||||
notifications: {
|
||||
/**
|
||||
* Notify about activity from the channels you're subscribed to.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSubscriptions: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.SUBSCRIPTIONS, 'SPaccount_notifications', new_value),
|
||||
|
||||
/**
|
||||
* Recommended content notifications.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setRecommendedVideos: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.RECOMMENDED_VIDEOS, 'SPaccount_notifications', new_value),
|
||||
|
||||
/**
|
||||
* Notify about activity on your channel.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setChannelActivity: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.CHANNEL_ACTIVITY, 'SPaccount_notifications', new_value),
|
||||
|
||||
/**
|
||||
* Notify about replies to your comments.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setCommentReplies: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.COMMENT_REPLIES, 'SPaccount_notifications', new_value),
|
||||
|
||||
/**
|
||||
* Notify when others mention your channel.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setMentions: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.USER_MENTION, 'SPaccount_notifications', new_value),
|
||||
|
||||
/**
|
||||
* Notify when others share your content on their channels.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSharedContent: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.SHARED_CONTENT, 'SPaccount_notifications', new_value)
|
||||
},
|
||||
privacy: {
|
||||
/**
|
||||
* If set to true, your subscriptions won't be visible to others.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSubscriptionsPrivate: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.SUBSCRIPTIONS_PRIVACY, 'SPaccount_privacy', new_value),
|
||||
|
||||
/**
|
||||
* If set to true, saved playlists won't appear on your channel.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSavedPlaylistsPrivate: (new_value) => this.#setSetting(Constants.ACCOUNT_SETTINGS.PLAYLISTS_PRIVACY, 'SPaccount_privacy', new_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.interact = {
|
||||
/**
|
||||
* Likes a given video.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
like: (video_id) => this.actions.engage('like/like', { video_id }),
|
||||
|
||||
/**
|
||||
* Diskes a given video.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
dislike: (video_id) => this.actions.engage('like/dislike', { video_id }),
|
||||
|
||||
/**
|
||||
* Removes a like/dislike.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
removeLike: (video_id) => this.actions.engage('like/removelike', { video_id }),
|
||||
|
||||
/**
|
||||
* Posts a comment on a given video.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @param {string} text
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
comment: (video_id, text) => this.actions.engage('comment/create_comment', { video_id, text }),
|
||||
|
||||
/**
|
||||
* Translates a given text using YouTube's comment translate feature.
|
||||
*
|
||||
* @param {string} text
|
||||
* @param {string} target_language
|
||||
* @param {object} [args] - optional arguments
|
||||
* @param {string} [args.video_id]
|
||||
* @param {string} [args.comment_id]
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; translated_content: string; data: object; }>}
|
||||
*/
|
||||
translate: async (text, target_language, args = {}) => {
|
||||
const response = await this.actions.engage('comment/perform_comment_action', {
|
||||
text,
|
||||
video_id: args.video_id,
|
||||
comment_id: args.comment_id,
|
||||
target_language: target_language,
|
||||
comment_action: 'translate'
|
||||
});
|
||||
|
||||
const translated_content = Utils.findNode(response.data, 'frameworkUpdates', 'content', 7, false);
|
||||
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
translated_content: translated_content.content,
|
||||
data: response.data
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribes to a given channel.
|
||||
*
|
||||
* @param {string} channel_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
subscribe: (channel_id) => this.actions.engage('subscription/subscribe', { channel_id }),
|
||||
|
||||
/**
|
||||
* Unsubscribes from a given channel.
|
||||
*
|
||||
* @param {string} channel_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
unsubscribe: (channel_id) => this.actions.engage('subscription/unsubscribe', { channel_id }),
|
||||
|
||||
/**
|
||||
* Changes notification preferences for a given channel.
|
||||
* Only works with channels you are subscribed to.
|
||||
*
|
||||
* @param {string} channel_id
|
||||
* @param {string} type PERSONALIZED | ALL | NONE
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setNotificationPreferences: (channel_id, type) => this.actions.notifications('modify_channel_preference', { channel_id, pref: type || 'NONE' }),
|
||||
};
|
||||
|
||||
this.playlist = {
|
||||
/**
|
||||
* Creates a playlist.
|
||||
*
|
||||
* @param {string} title
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @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 });
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
playlist_id: response.data.playlistId,
|
||||
data: response.data
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @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 });
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
playlist_id,
|
||||
data: response.data
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an array of videos to a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @param {Array.<string>} video_ids
|
||||
* @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', {
|
||||
action: 'ACTION_ADD_VIDEO',
|
||||
playlist_id,
|
||||
ids: video_ids
|
||||
});
|
||||
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
playlist_id,
|
||||
data: response.data
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes videos from a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @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}`);
|
||||
|
||||
const list = Utils.findNode(plinfo.data, 'contents', 'contents', 13, false);
|
||||
if (!list.isEditable) throw new Utils.InnertubeError('This playlist cannot be edited.', playlist_id);
|
||||
|
||||
const videos = list.contents.filter((item) => video_ids.includes(item.playlistVideoRenderer.videoId));
|
||||
const set_video_ids = videos.map((video) => video.playlistVideoRenderer.setVideoId);
|
||||
|
||||
const response = await this.actions.playlist('browse/edit_playlist', {
|
||||
action: 'ACTION_REMOVE_VIDEO',
|
||||
playlist_id,
|
||||
ids: set_video_ids
|
||||
});
|
||||
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
playlist_id,
|
||||
data: response.data
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to perform changes on an account's settings.
|
||||
*
|
||||
* @param {string} setting_id
|
||||
* @param {string} type
|
||||
* @param {string} new_value
|
||||
* @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);
|
||||
|
||||
const contents = ({
|
||||
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()]();
|
||||
|
||||
const option = contents.find((option) => option.settingsSwitchRenderer.enableServiceEndpoint.setSettingEndpoint.settingItemIdForClient == setting_id);
|
||||
|
||||
const setting_item_id = option.settingsSwitchRenderer.enableServiceEndpoint.setSettingEndpoint.settingItemId;
|
||||
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 set_setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs-in to a google account.
|
||||
* Signs in to a google account.
|
||||
*
|
||||
* @param {object} auth_info
|
||||
* @param {string} auth_info.access_token - Token used to sign in.
|
||||
* @param {string} auth_info.refresh_token - Token used to get a new access token.
|
||||
* @param {Date} auth_info.expires - Access token's expiration date, which is usually 24hrs-ish
|
||||
* @param {Date} auth_info.expires - Access token's expiration date, which is usually 24hrs-ish.
|
||||
*
|
||||
* @returns {Promise.<void>}
|
||||
*/
|
||||
signIn(auth_info = {}) {
|
||||
return new Promise(async (resolve) => {
|
||||
this.#oauth.init(auth_info);
|
||||
this.oauth.init(auth_info);
|
||||
|
||||
if (this.#oauth.isValidAuthInfo()) {
|
||||
await this.#oauth.checkTokenValidity();
|
||||
if (this.oauth.isValidAuthInfo()) {
|
||||
await this.oauth.checkTokenValidity();
|
||||
this.#updateCredentials();
|
||||
return resolve();
|
||||
}
|
||||
@@ -398,8 +115,8 @@ class Innertube {
|
||||
}
|
||||
|
||||
#updateCredentials() {
|
||||
this.access_token = this.#oauth.getAccessToken();
|
||||
this.refresh_token = this.#oauth.getRefreshToken();
|
||||
this.access_token = this.oauth.getAccessToken();
|
||||
this.refresh_token = this.oauth.getRefreshToken();
|
||||
this.logged_in = true;
|
||||
}
|
||||
|
||||
@@ -409,38 +126,13 @@ class Innertube {
|
||||
*/
|
||||
async signOut() {
|
||||
if (!this.logged_in) throw new Utils.InnertubeError('You are not signed in');
|
||||
const response = await this.#oauth.revokeAccessToken();
|
||||
const response = await this.oauth.revokeAccessToken();
|
||||
response.success && (this.logged_in = false);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves account details.
|
||||
* @returns {Promise.<{ name: string; photo: Array<object>; country: string; language: string; }>}
|
||||
*/
|
||||
async getAccountInfo() {
|
||||
const response = await this.actions.account('account/account_menu');
|
||||
const menu = Utils.findNode(response, 'actions', 'multiPageMenuRenderer', 6, false);
|
||||
|
||||
return {
|
||||
name: menu.header.activeAccountHeaderRenderer.accountName.simpleText,
|
||||
photo: menu.header.activeAccountHeaderRenderer.accountPhoto.thumbnails,
|
||||
country: menu.sections[1].multiPageMenuSectionRenderer.items[2].compactLinkRenderer.subtitle.simpleText,
|
||||
language: menu.sections[1].multiPageMenuSectionRenderer.items[1].compactLinkRenderer.subtitle.simpleText
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TEMPORARY: This is only here for testing purposes
|
||||
// eslint-disable-next-line no-unused-private-class-members
|
||||
async #getBasicChannelAnalytics() {
|
||||
const params = Proto.encodeChannelAnalyticsParams('UCM9VCokI0KanBqPwq6QBv3g');
|
||||
const action = await this.actions.browse('FEanalytics_screen', { params, client: 'ANDROID' });
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches on YouTube.
|
||||
* Searches a given query.
|
||||
*
|
||||
* @param {string} query - search query.
|
||||
* @param {object} [options] - search options.
|
||||
@@ -451,8 +143,8 @@ class Innertube {
|
||||
* @param {string} [options.filters.duration] - filter videos by duration, can be: any | short | medium | long
|
||||
* @param {string} [options.filters.sort_by] - filter video results by order, can be: relevance | rating | upload_date | view_count
|
||||
*
|
||||
* @returns {Promise.<{ query: string; corrected_query: string; estimated_results: number; videos: [] } |
|
||||
* { results: { songs: []; videos: []; albums: []; community_playlists: [] } }>}
|
||||
* @returns {Promise.<{ query: string; corrected_query: string; estimated_results: number; videos: object[] } |
|
||||
* { results: { songs: object[]; videos: object[]; albums: object[]; community_playlists: object[] } }>}
|
||||
*/
|
||||
async search(query, options = { client: 'YOUTUBE' }) {
|
||||
Utils.throwIfMissing({ query });
|
||||
@@ -469,22 +161,21 @@ class Innertube {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves search suggestions.
|
||||
* Retrieves search suggestions for a given query.
|
||||
*
|
||||
* @param {string} input - the search query.
|
||||
* @param {string} query - the search query.
|
||||
* @param {object} [options] - search options.
|
||||
* @param {string} [options.client='YOUTUBE'] - client used to retrieve search suggestions, can be: `YOUTUBE` or `YTMUSIC`.
|
||||
*
|
||||
* @returns {Promise.<[{ text: string; bold_text: string }]>}
|
||||
* @returns {Promise.<{ query: string; results: string[] }>}
|
||||
*/
|
||||
async getSearchSuggestions(input, options = { client: 'YOUTUBE' }) {
|
||||
Utils.throwIfMissing({ input });
|
||||
async getSearchSuggestions(query, options = { client: 'YOUTUBE' }) {
|
||||
Utils.throwIfMissing({ query });
|
||||
|
||||
const response = await this.actions.getSearchSuggestions(options.client, input);
|
||||
const response = await this.actions.getSearchSuggestions(options.client, query);
|
||||
if (options.client === 'YTMUSIC' && !response.data.contents) return [];
|
||||
|
||||
const suggestions = new Parser(this, response.data, {
|
||||
input,
|
||||
client: options.client,
|
||||
data_type: 'SEARCH_SUGGESTIONS'
|
||||
}).parse();
|
||||
@@ -494,8 +185,7 @@ class Innertube {
|
||||
|
||||
/**
|
||||
* Retrieves video info.
|
||||
*
|
||||
* @param {string} video_id - video id
|
||||
* @param {string} video_id - the video id.
|
||||
* @return {Promise.<{ title: string; description: string; thumbnail: []; metadata: object }>}
|
||||
*/
|
||||
async getDetails(video_id) {
|
||||
@@ -528,11 +218,11 @@ class Innertube {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves comments for a video.
|
||||
* Retrieves comments for a given video.
|
||||
*
|
||||
* @param {string} video_id - video id
|
||||
* @param {string} video_id - the video id.
|
||||
* @param {string} [sort_by] - can be: `TOP_COMMENTS` or `NEWEST_FIRST`.
|
||||
* @return {Promise.<{ page_count: number; comment_count: number; items: []; }>}
|
||||
* @return {Promise.<{ page_count: number; comment_count: number; items: object[]; }>}
|
||||
*/
|
||||
async getComments(video_id, sort_by) {
|
||||
Utils.throwIfMissing({ video_id });
|
||||
@@ -553,7 +243,6 @@ class Innertube {
|
||||
|
||||
/**
|
||||
* Retrieves contents for a given channel. (WIP)
|
||||
*
|
||||
* @param {string} id - channel id
|
||||
* @return {Promise.<{ title: string; description: string; metadata: object; content: object }>}
|
||||
*/
|
||||
@@ -572,7 +261,7 @@ class Innertube {
|
||||
|
||||
/**
|
||||
* Retrieves watch history.
|
||||
* @returns {Promise.<{ items: [{ date: string; videos: [] }] }>}
|
||||
* @returns {Promise.<{ items: { date: string; videos: object[] }[] }>}
|
||||
*/
|
||||
async getHistory() {
|
||||
const response = await this.actions.browse('FEhistory');
|
||||
@@ -586,8 +275,8 @@ class Innertube {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves YouTube's home feed (aka recommendations).
|
||||
* @returns {Promise.<{ videos: [{ id: string; title: string; description: string; channel: string; metadata: object }] }>}
|
||||
* Retrieves home feed (aka recommendations).
|
||||
* @returns {Promise.<{ videos: { id: string; title: string; description: string; channel: string; metadata: object }[] }>}
|
||||
*/
|
||||
async getHomeFeed() {
|
||||
const response = await this.actions.browse('FEwhat_to_watch');
|
||||
@@ -602,10 +291,9 @@ class Innertube {
|
||||
|
||||
/**
|
||||
* Retrieves trending content.
|
||||
*
|
||||
* @returns {Promise.<{ now: { content: [{ title: string; videos: []; }] };
|
||||
* music: { getVideos: Promise.<Array>; }; gaming: { getVideos: Promise.<Array>; };
|
||||
* movies: { getVideos: Promise.<Array>; } }>}
|
||||
* @returns {Promise.<{ now: { content: { title: string; videos: object[]; }[] };
|
||||
* music: { getVideos: Promise.<Array.<object>>; }; gaming: { getVideos: Promise.<Array.<object>>; };
|
||||
* movies: { getVideos: Promise.<Array.<object>>; } }>}
|
||||
*/
|
||||
async getTrending() {
|
||||
const response = await this.actions.browse('FEtrending');
|
||||
@@ -619,6 +307,7 @@ class Innertube {
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo finish this
|
||||
* WIP
|
||||
*/
|
||||
async getLibrary() {
|
||||
@@ -634,7 +323,7 @@ class Innertube {
|
||||
|
||||
/**
|
||||
* Retrieves subscriptions feed.
|
||||
* @returns {Promise.<{ items: [{ date: string; videos: [] }] }>}
|
||||
* @returns {Promise.<{ items: { date: string; videos: object[] }[] }>}
|
||||
*/
|
||||
async getSubscriptionsFeed() {
|
||||
const response = await this.actions.browse('FEsubscriptions');
|
||||
@@ -648,8 +337,8 @@ class Innertube {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves your notifications.
|
||||
* @returns {Promise.<{ items: [{ title: string; sent_time: string; channel_name: string; channel_thumbnail: {}; video_thumbnail: {}; video_url: string; read: boolean; notification_id: string }] }>}
|
||||
* Retrieves notifications.
|
||||
* @returns {Promise.<{ items: { title: string; sent_time: string; channel_name: string; channel_thumbnail: object; video_thumbnail: object; video_url: string; read: boolean; notification_id: string }[] }>}
|
||||
*/
|
||||
async getNotifications() {
|
||||
const response = await this.actions.notifications('get_notification_menu');
|
||||
@@ -691,11 +380,12 @@ class Innertube {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a given playlist.
|
||||
* Retrieves the contents of a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id - playlist id.
|
||||
* @param {object} options - { client: YOUTUBE | YTMUSIC }
|
||||
* @param {string} playlist_id - the id of the playlist.
|
||||
* @param {object} options - `YOUTUBE` | `YTMUSIC`
|
||||
* @param {string} options.client - client used to parse the playlist, can be: `YTMUSIC` | `YOUTUBE`
|
||||
*
|
||||
* @returns {Promise.<
|
||||
* { title: string; description: string; total_items: string; last_updated: string; views: string; items: [] } |
|
||||
* { title: string; description: string; total_items: number; duration: string; year: string; items: [] }>}
|
||||
@@ -717,6 +407,7 @@ class Innertube {
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {object} video_data
|
||||
*
|
||||
* @returns {object.<{ selected_format: {}; formats: [] }>}
|
||||
*/
|
||||
#chooseFormat(options, video_data) {
|
||||
@@ -784,7 +475,7 @@ class Innertube {
|
||||
* @param {string} options.type - download type, can be: video, audio or videoandaudio
|
||||
* @param {string} options.format - file format
|
||||
*
|
||||
* @returns {Promise.<{ selected_format: {}; formats: [] }>}
|
||||
* @returns {Promise.<{ selected_format: object; formats: object[] }>}
|
||||
*/
|
||||
async getStreamingData(video_id, options = {}) {
|
||||
Utils.throwIfMissing({ video_id });
|
||||
@@ -810,6 +501,9 @@ class Innertube {
|
||||
* @param {string} [options.quality] - video quality; 360p, 720p, 1080p, etc...
|
||||
* @param {string} [options.type] - download type, can be: video, audio or videoandaudio
|
||||
* @param {string} [options.format] - file format
|
||||
* @param {object} [options.range] - download range, indicates which bytes should be downloaded.
|
||||
* @param {number} options.range.start - the beginning of the range.
|
||||
* @param {number} options.range.end - the end of the range.
|
||||
*
|
||||
* @return {Stream.PassThrough}
|
||||
*/
|
||||
@@ -890,7 +584,7 @@ class Innertube {
|
||||
let must_end = false;
|
||||
|
||||
stream.emit('start');
|
||||
|
||||
|
||||
const downloadChunk = async () => {
|
||||
(chunk_end >= format.contentLength || options.range) && (must_end = true);
|
||||
options.range && (format.contentLength = options.range.end);
|
||||
|
||||
251
lib/core/AccountManager.js
Normal file
251
lib/core/AccountManager.js
Normal file
@@ -0,0 +1,251 @@
|
||||
'use strict';
|
||||
|
||||
const Utils = require('../utils/Utils');
|
||||
const Constants = require('../utils/Constants');
|
||||
const Proto = require('../proto');
|
||||
|
||||
/** @namespace */
|
||||
class AccountManager {
|
||||
#actions;
|
||||
|
||||
/**
|
||||
* @param {Actions} actions
|
||||
* @constructor
|
||||
*/
|
||||
constructor (actions) {
|
||||
this.#actions = actions;
|
||||
|
||||
/** @namespace */
|
||||
this.channel = {
|
||||
/**
|
||||
* Edits channel name.
|
||||
*
|
||||
* @param {string} new_name
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
editName: (new_name) => this.#actions.channel('channel/edit_name', { new_name }),
|
||||
|
||||
/**
|
||||
* Edits channel description.
|
||||
*
|
||||
* @param {string} new_description
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
editDescription: (new_description) => this.#actions.channel('channel/edit_description', { new_description }),
|
||||
|
||||
/**
|
||||
* Retrieves basic channel analytics.
|
||||
* @borrows AccountManager#getAnalytics as getBasicAnalytics
|
||||
*/
|
||||
getBasicAnalytics: () => this.getAnalytics()
|
||||
}
|
||||
|
||||
/** @namespace */
|
||||
this.settings = {
|
||||
notifications: {
|
||||
/**
|
||||
* Notify about activity from the channels you're subscribed to.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSubscriptions: (option) => this.#setSetting(Constants.ACCOUNT_SETTINGS.SUBSCRIPTIONS, 'SPaccount_notifications', option),
|
||||
|
||||
/**
|
||||
* Recommended content notifications.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setRecommendedVideos: (option) => this.#setSetting(Constants.ACCOUNT_SETTINGS.RECOMMENDED_VIDEOS, 'SPaccount_notifications', option),
|
||||
|
||||
/**
|
||||
* Notify about activity on your channel.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setChannelActivity: (option) => this.#setSetting(Constants.ACCOUNT_SETTINGS.CHANNEL_ACTIVITY, 'SPaccount_notifications', option),
|
||||
|
||||
/**
|
||||
* Notify about replies to your comments.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setCommentReplies: (option) => this.#setSetting(Constants.ACCOUNT_SETTINGS.COMMENT_REPLIES, 'SPaccount_notifications', option),
|
||||
|
||||
/**
|
||||
* Notify when others mention your channel.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setMentions: (option) => this.#setSetting(Constants.ACCOUNT_SETTINGS.USER_MENTION, 'SPaccount_notifications', option),
|
||||
|
||||
/**
|
||||
* Notify when others share your content on their channels.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSharedContent: (option) => this.#setSetting(Constants.ACCOUNT_SETTINGS.SHARED_CONTENT, 'SPaccount_notifications', option)
|
||||
},
|
||||
privacy: {
|
||||
/**
|
||||
* If set to true, your subscriptions won't be visible to others.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSubscriptionsPrivate: (option) => this.#setSetting(Constants.ACCOUNT_SETTINGS.SUBSCRIPTIONS_PRIVACY, 'SPaccount_privacy', option),
|
||||
|
||||
/**
|
||||
* If set to true, saved playlists won't appear on your channel.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSavedPlaylistsPrivate: (option) => this.#setSetting(Constants.ACCOUNT_SETTINGS.PLAYLISTS_PRIVACY, 'SPaccount_privacy', option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to perform changes on an account's settings.
|
||||
*
|
||||
* @param {string} setting_id
|
||||
* @param {string} type
|
||||
* @param {string} new_value
|
||||
* @private
|
||||
*
|
||||
* @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);
|
||||
|
||||
const contents = ({
|
||||
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()]();
|
||||
|
||||
const option = contents.find((option) => option.settingsSwitchRenderer.enableServiceEndpoint.setSettingEndpoint.settingItemIdForClient == setting_id);
|
||||
|
||||
const setting_item_id = option.settingsSwitchRenderer.enableServiceEndpoint.setSettingEndpoint.settingItemId;
|
||||
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 set_setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves channel info.
|
||||
* @returns {Promise.<{ name: string; email: string; channel_id: string; subscriber_count: string; photo: object[]; }>}
|
||||
*/
|
||||
async getInfo() {
|
||||
const response = await this.#actions.account('account/accounts_list', { client: 'ANDROID' });
|
||||
|
||||
const account_item_section_renderer = Utils.findNode(response.data, 'contents', 'accountItem', 8, false);
|
||||
const profile = account_item_section_renderer.accountItem.serviceEndpoint.signInEndpoint.directSigninUserProfile;
|
||||
|
||||
const name = profile.accountName;
|
||||
const email = profile.email;
|
||||
const photo = profile.accountPhoto.thumbnails;
|
||||
const subscriber_count = account_item_section_renderer.accountItem.accountByline.runs.map((run) => run.text).join('');
|
||||
const channel_id = response.data.contents[0].accountSectionListRenderer.footers[0].accountChannelRenderer.navigationEndpoint.browseEndpoint.browseId;
|
||||
|
||||
return { name, email, channel_id, subscriber_count, photo };
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves time watched statistics.
|
||||
* @returns {Promise.<[{ title: string; time: string; }]>}
|
||||
*/
|
||||
async getTimeWatched() {
|
||||
const response = await this.#actions.browse('SPtime_watched', { client: 'ANDROID' });
|
||||
|
||||
const rows = Utils.findNode(response.data, 'contents', 'statRowRenderer', 11, false);
|
||||
|
||||
const stats = rows.map((row) => {
|
||||
const renderer = row.statRowRenderer;
|
||||
if (renderer) {
|
||||
return {
|
||||
title: renderer.title.runs.map((run) => run.text).join(''),
|
||||
time: renderer.contents.runs.map((run) => run.text).join('')
|
||||
}
|
||||
}
|
||||
}).filter((stat) => stat);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves basic channel analytics.
|
||||
*
|
||||
* @returns {Promise.<{ metrics: { title: string; subtitle: string; metric_value: string;
|
||||
* comparison_indicator: object; series_configuration: object; }[]; top_content: { views: string;
|
||||
* published: string; thumbnails: object[]; duration: string; is_short: boolean }[]; }>}
|
||||
*/
|
||||
async getAnalytics() {
|
||||
const info = await this.getInfo();
|
||||
|
||||
const params = Proto.encodeChannelAnalyticsParams(info.channel_id);
|
||||
const action = await this.#actions.browse('FEanalytics_screen', { params, client: 'ANDROID' });
|
||||
|
||||
const contents = Utils.findNode(action.data, 'contents', 'elementRenderer', 11, false);
|
||||
|
||||
const analytics = {
|
||||
metrics: {},
|
||||
top_content: {}
|
||||
}
|
||||
|
||||
contents.forEach((el) => {
|
||||
const element = el.elementRenderer.newElement;
|
||||
const model = element.type.componentType.model;
|
||||
const key = Object.keys(model)[0];
|
||||
|
||||
switch (key) {
|
||||
case 'analyticsRootModel':
|
||||
const sections = model.analyticsRootModel.analyticsKeyMetricsData.dataModel.sections;
|
||||
|
||||
analytics.metrics = sections.map((section) => ({
|
||||
title: section.title,
|
||||
subtitle: section.subtitle,
|
||||
metric_value: section.metricValue,
|
||||
comparison_indicator: section.comparisonIndicator,
|
||||
series_configuration: section.seriesConfiguration
|
||||
}));
|
||||
break;
|
||||
case 'analyticsVodCarouselCardModel':
|
||||
const video_carousel = model.analyticsVodCarouselCardModel.videoCarouselData;
|
||||
|
||||
analytics.top_content = video_carousel?.videos.map((video) => ({
|
||||
title: video.videoTitle,
|
||||
metadata: {
|
||||
views: video.videoDescription.split('·')[0].trim(),
|
||||
published: video.videoDescription.split('·')[1].trim(),
|
||||
thumbnails: video.thumbnailDetails.thumbnails,
|
||||
duration: video.formattedLength,
|
||||
is_short: video.isShort
|
||||
}
|
||||
})) || [];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return analytics;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AccountManager;
|
||||
134
lib/core/InteractionManager.js
Normal file
134
lib/core/InteractionManager.js
Normal file
@@ -0,0 +1,134 @@
|
||||
'use strict';
|
||||
|
||||
const Utils = require('../utils/Utils');
|
||||
|
||||
/** @namespace */
|
||||
class InteractionManager {
|
||||
#actions;
|
||||
|
||||
/**
|
||||
* @param {Actions} actions
|
||||
* @constructor
|
||||
*/
|
||||
constructor(actions) {
|
||||
this.#actions = actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Likes a given video.
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
async like(video_id) {
|
||||
Utils.throwIfMissing({ video_id });
|
||||
const action = await this.#actions.engage('like/like', { video_id });
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dislikes a given video.
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
async dislike(video_id) {
|
||||
Utils.throwIfMissing({ video_id });
|
||||
const action = await this.#actions.engage('like/dislike', { video_id });
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a like/dislike.
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
async removeLike(video_id) {
|
||||
Utils.throwIfMissing({ video_id });
|
||||
const action = await this.actions.engage('like/removelike', { video_id });
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to a given channel.
|
||||
* @param {string} channel_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
async subscribe(channel_id) {
|
||||
Utils.throwIfMissing({ channel_id });
|
||||
const action = await this.#actions.engage('subscription/subscribe', { channel_id });
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from a given channel.
|
||||
* @param {string} channel_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
async unsubscribe(channel_id) {
|
||||
Utils.throwIfMissing({ channel_id });
|
||||
const action = await this.#actions.engage('subscription/unsubscribe', { channel_id });
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts a comment on a given video.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @param {string} text
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
async comment(video_id, text) {
|
||||
Utils.throwIfMissing({ video_id, text });
|
||||
const action = await this.#actions.engage('comment/create_comment', { video_id, text });
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a given text using YouTube's comment translate feature.
|
||||
*
|
||||
* @param {string} text
|
||||
* @param {string} target_language - an ISO language code
|
||||
* @param {object} [args] - optional arguments
|
||||
* @param {string} [args.video_id]
|
||||
* @param {string} [args.comment_id]
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; translated_content: string; data: object; }>}
|
||||
*/
|
||||
async translate(text, target_language, args = {}) {
|
||||
Utils.throwIfMissing({ text, target_language });
|
||||
|
||||
const response = await await this.#actions.engage('comment/perform_comment_action', {
|
||||
video_id: args.video_id,
|
||||
comment_id: args.comment_id,
|
||||
target_language: target_language,
|
||||
comment_action: 'translate',
|
||||
text
|
||||
});
|
||||
|
||||
const translated_content = Utils.findNode(response.data, 'frameworkUpdates', 'content', 7, false);
|
||||
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
translated_content: translated_content.content,
|
||||
data: response.data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes notification preferences for a given channel.
|
||||
* Only works with channels you are subscribed to.
|
||||
*
|
||||
* @param {string} channel_id
|
||||
* @param {string} type - `PERSONALIZED` | `ALL` | `NONE`
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
async setNotificationPreferences(channel_id, type) {
|
||||
Utils.throwIfMissing({ channel_id, type });
|
||||
const action = await this.#actions.notifications('modify_channel_preference', { channel_id, pref: type || 'NONE' });
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = InteractionManager;
|
||||
@@ -4,11 +4,8 @@ const Axios = require('axios');
|
||||
const Constants = require('../utils/Constants');
|
||||
const Uuid = require('uuid');
|
||||
|
||||
/** @namespace */
|
||||
class OAuth {
|
||||
#scope = Constants.OAUTH.SCOPE;
|
||||
#model_name = Constants.OAUTH.MODEL_NAME;
|
||||
#grant_type = Constants.OAUTH.GRANT_TYPE;
|
||||
|
||||
#oauth_code_url = `${Constants.URLS.YT_BASE}/o/oauth2/device/code`;
|
||||
#oauth_token_url = `${Constants.URLS.YT_BASE}/o/oauth2/token`;
|
||||
#oauth_revoke_url = `${Constants.URLS.YT_BASE}/o/oauth2/revoke`;
|
||||
@@ -17,6 +14,10 @@ class OAuth {
|
||||
#polling_interval = 5;
|
||||
#ev = null;
|
||||
|
||||
/**
|
||||
* @param {EventEmitter} ev
|
||||
* @constructor
|
||||
*/
|
||||
constructor(ev) {
|
||||
this.#ev = ev;
|
||||
}
|
||||
@@ -46,9 +47,9 @@ class OAuth {
|
||||
|
||||
const data = {
|
||||
client_id: this.client_id,
|
||||
scope: this.#scope,
|
||||
scope: Constants.OAUTH.SCOPE,
|
||||
device_id: Uuid.v4(),
|
||||
model_name: this.#model_name
|
||||
model_name: Constants.OAUTH.MODEL_NAME
|
||||
};
|
||||
|
||||
const response = await Axios.post(this.#oauth_code_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error);
|
||||
@@ -77,7 +78,7 @@ class OAuth {
|
||||
client_id: this.client_id,
|
||||
client_secret: this.client_secret,
|
||||
code: device_code,
|
||||
grant_type: this.#grant_type
|
||||
grant_type: Constants.OAUTH.GRANT_TYPE
|
||||
};
|
||||
|
||||
setTimeout(async () => {
|
||||
@@ -204,16 +205,24 @@ class OAuth {
|
||||
return client_identity.groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the access token.
|
||||
* @returns {string}
|
||||
*/
|
||||
getAccessToken() {
|
||||
return this.#auth_info.access_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the refresh token.
|
||||
* @returns {string}
|
||||
*/
|
||||
getRefreshToken() {
|
||||
return this.#auth_info.refresh_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the auth info is valid.
|
||||
* Checks if the auth info format is valid.
|
||||
* @returns {boolean} true | false
|
||||
*/
|
||||
isValidAuthInfo() {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const os = require('os');
|
||||
const Fs = require('fs');
|
||||
const Axios = require('axios');
|
||||
const Utils = require('../utils/Utils');
|
||||
const Constants = require('../utils/Constants');
|
||||
|
||||
/** @namespace */
|
||||
class Player {
|
||||
#player_id;
|
||||
#player_url;
|
||||
@@ -13,12 +15,16 @@ class Player {
|
||||
#ntoken_decipher_sc;
|
||||
#signature_decipher_sc;
|
||||
#signature_timestamp;
|
||||
|
||||
#cache_dir;
|
||||
|
||||
/**
|
||||
* Represents the YouTube Web player script.
|
||||
* @param {string} id - the id of the player.
|
||||
* @constructor
|
||||
*/
|
||||
constructor(id) {
|
||||
this.#player_id = id;
|
||||
this.#cache_dir = __dirname.slice(0, -8) + 'cache';
|
||||
this.#cache_dir = `${os.tmpdir()}/cache`;
|
||||
this.#player_url = Constants.URLS.YT_BASE + '/s/player/' + this.#player_id + '/player_ias.vflset/en_US/base.js';
|
||||
this.#player_path = `${this.#cache_dir}/${this.#player_id}.js`;
|
||||
}
|
||||
@@ -26,7 +32,6 @@ class Player {
|
||||
async init() {
|
||||
if (this.isCached()) {
|
||||
const player_data = Fs.readFileSync(this.#player_path).toString();
|
||||
|
||||
this.#signature_timestamp = this.#extractSigTimestamp(player_data);
|
||||
this.#signature_decipher_sc = this.#extractSigDecipherSc(player_data);
|
||||
this.#ntoken_decipher_sc = this.#extractNTokenSc(player_data);
|
||||
@@ -52,32 +57,64 @@ class Player {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current player's url.
|
||||
* @readonly
|
||||
* @returns {string}
|
||||
*/
|
||||
get url() {
|
||||
return this.#player_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signature timestamp.
|
||||
* @readonly
|
||||
* @returns {string}
|
||||
*/
|
||||
get sts() {
|
||||
return this.#signature_timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the n-token decipher algorithm.
|
||||
* @readonly
|
||||
* @returns {string}
|
||||
*/
|
||||
get ntoken_decipher() {
|
||||
return this.#ntoken_decipher_sc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signature decipher algorithm.
|
||||
* @readonly
|
||||
* @returns {string}
|
||||
*/
|
||||
get signature_decipher() {
|
||||
return this.#signature_decipher_sc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the signature timestamp from the player source code.
|
||||
* @returns {number}
|
||||
*/
|
||||
#extractSigTimestamp(data) {
|
||||
return parseInt(Utils.getStringBetweenStrings(data, 'signatureTimestamp:', ','));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the signature decipher algorithm.
|
||||
* @returns {string}
|
||||
*/
|
||||
#extractSigDecipherSc(data) {
|
||||
const sig_alg_sc = Utils.getStringBetweenStrings(data, 'this.audioTracks};var', '};');
|
||||
const sig_data = Utils.getStringBetweenStrings(data, 'function(a){a=a.split("")', 'return a.join("")}');
|
||||
return sig_alg_sc + sig_data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts the n-token decipher algorithm.
|
||||
* @returns {string}
|
||||
*/
|
||||
#extractNTokenSc(data) {
|
||||
return `var b=a.split("")${Utils.getStringBetweenStrings(data, 'b=a.split("")', '}return b.join("")}')}} return b.join("");`;
|
||||
}
|
||||
|
||||
115
lib/core/PlaylistManager.js
Normal file
115
lib/core/PlaylistManager.js
Normal file
@@ -0,0 +1,115 @@
|
||||
'use strict';
|
||||
|
||||
const Utils = require('../utils/Utils');
|
||||
|
||||
/** @namespace */
|
||||
class PlaylistManager {
|
||||
#actions;
|
||||
|
||||
/**
|
||||
* @param {Actions} actions
|
||||
* @constructor
|
||||
*/
|
||||
constructor (actions) {
|
||||
this.#actions = actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a playlist.
|
||||
*
|
||||
* @param {string} title
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>}
|
||||
*/
|
||||
async create(title, video_ids) {
|
||||
Utils.throwIfMissing({ title, video_ids });
|
||||
|
||||
const response = await this.#actions.playlist('playlist/create', { title, ids: video_ids });
|
||||
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
playlist_id: response.data.playlistId,
|
||||
data: response.data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a given playlist.
|
||||
* @param {string} playlist_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>}
|
||||
*/
|
||||
async delete(playlist_id) {
|
||||
Utils.throwIfMissing({ playlist_id });
|
||||
|
||||
const response = await this.#actions.playlist('playlist/delete', { playlist_id });
|
||||
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
playlist_id,
|
||||
data: response.data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds videos to a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>}
|
||||
*/
|
||||
async addVideos(playlist_id, video_ids) {
|
||||
Utils.throwIfMissing({ playlist_id, video_ids });
|
||||
|
||||
const response = await this.#actions.playlist('browse/edit_playlist', {
|
||||
ids: video_ids,
|
||||
action: 'ACTION_ADD_VIDEO',
|
||||
playlist_id
|
||||
});
|
||||
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
playlist_id,
|
||||
data: response.data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes videos from a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>}
|
||||
*/
|
||||
async removeVideos(playlist_id, video_ids) {
|
||||
Utils.throwIfMissing({ playlist_id, video_ids });
|
||||
|
||||
const plinfo = await this.#actions.browse(`VL${playlist_id}`);
|
||||
|
||||
const list = Utils.findNode(plinfo.data, 'contents', 'contents', 13, false);
|
||||
if (!list.isEditable) throw new Utils.InnertubeError('This playlist cannot be edited.', playlist_id);
|
||||
|
||||
const videos = list.contents.filter((item) => video_ids.includes(item.playlistVideoRenderer.videoId));
|
||||
const set_video_ids = videos.map((video) => video.playlistVideoRenderer.setVideoId);
|
||||
|
||||
const response = await this.#actions.playlist('browse/edit_playlist', {
|
||||
ids: set_video_ids,
|
||||
action: 'ACTION_REMOVE_VIDEO',
|
||||
playlist_id
|
||||
});
|
||||
|
||||
return {
|
||||
success: response.success,
|
||||
status_code: response.status_code,
|
||||
playlist_id,
|
||||
data: response.data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PlaylistManager;
|
||||
@@ -7,6 +7,7 @@ const Utils = require('../utils/Utils');
|
||||
const Constants = require('../utils/Constants');
|
||||
const UserAgent = require('user-agents');
|
||||
|
||||
/** @namespace */
|
||||
class SessionBuilder {
|
||||
#config;
|
||||
|
||||
@@ -18,6 +19,10 @@ class SessionBuilder {
|
||||
#context;
|
||||
#player;
|
||||
|
||||
/**
|
||||
* @param {string} config
|
||||
* @constructor
|
||||
*/
|
||||
constructor(config) {
|
||||
this.#config = config;
|
||||
}
|
||||
@@ -42,6 +47,10 @@ class SessionBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a valid context object.
|
||||
* @returns
|
||||
*/
|
||||
#buildContext() {
|
||||
const user_agent = new UserAgent({ deviceCategory: 'desktop' });
|
||||
|
||||
@@ -70,6 +79,11 @@ class SessionBuilder {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves initial configuration such as keys,
|
||||
* client data, etc.
|
||||
* @returns Promise.<object>
|
||||
*/
|
||||
async #getYtConfig() {
|
||||
const response = await Axios.get(`${Constants.URLS.YT_BASE}/sw.js_data`).catch((err) => err);
|
||||
|
||||
@@ -82,6 +96,10 @@ class SessionBuilder {
|
||||
return JSON.parse(response.data.replace(')]}\'', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrives the YouTube player id.
|
||||
* @returns {Promise.<string>
|
||||
*/
|
||||
async #getPlayerId() {
|
||||
const response = await Axios.get(`${Constants.URLS.YT_BASE}/iframe_api`).catch((err) => err);
|
||||
|
||||
@@ -94,26 +112,32 @@ class SessionBuilder {
|
||||
return Utils.getStringBetweenStrings(response.data, 'player\\/', '\\/');
|
||||
}
|
||||
|
||||
/** @readonly */
|
||||
get key() {
|
||||
return this.#key;
|
||||
}
|
||||
|
||||
/** @readonly */
|
||||
get context() {
|
||||
return this.#context;
|
||||
}
|
||||
|
||||
/** @readonly */
|
||||
get api_version() {
|
||||
return this.#api_version;
|
||||
}
|
||||
|
||||
/** @readonly */
|
||||
get client_version() {
|
||||
return this.#client_version;
|
||||
}
|
||||
|
||||
/** @readonly */
|
||||
get client_name() {
|
||||
return this.#client_name;
|
||||
}
|
||||
|
||||
/** @readonly */
|
||||
get player() {
|
||||
return this.#player;
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class Parser {
|
||||
}
|
||||
|
||||
#processSearchSuggestions() {
|
||||
return YTDataItems.SearchSuggestionItem.parse(this.data[1], this.data[0]);
|
||||
return YTDataItems.SearchSuggestionItem.parse(JSON.parse(this.data.replace(')]}\'', '')));
|
||||
}
|
||||
|
||||
#processMusicSearchSuggestions() {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const Utils = require('./Utils');
|
||||
|
||||
module.exports = {
|
||||
URLS: {
|
||||
YT_BASE: 'https://www.youtube.com',
|
||||
@@ -44,15 +42,15 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
STREAM_HEADERS: {
|
||||
'Accept': '*/*',
|
||||
'User-Agent': Utils.getRandomUserAgent('desktop').userAgent,
|
||||
'Connection': 'keep-alive',
|
||||
'Origin': 'https://www.youtube.com',
|
||||
'Referer': 'https://www.youtube.com',
|
||||
'accept': '*/*',
|
||||
'connection': 'keep-alive',
|
||||
'origin': 'https://www.youtube.com',
|
||||
'referer': 'https://www.youtube.com',
|
||||
'DNT': '?1'
|
||||
},
|
||||
INNERTUBE_HEADERS_BASE: {
|
||||
'accept': '*/*',
|
||||
'accept-encoding': 'gzip, deflate',
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
METADATA_KEYS: [
|
||||
|
||||
@@ -4,7 +4,12 @@ const Axios = require('axios');
|
||||
const Utils = require('./Utils');
|
||||
const Constants = require('./Constants');
|
||||
|
||||
/** @namespace */
|
||||
class Request {
|
||||
/**
|
||||
* @param {Innertube} session
|
||||
* @constructor
|
||||
*/
|
||||
constructor(session) {
|
||||
this.session = session;
|
||||
|
||||
@@ -68,14 +73,17 @@ class Request {
|
||||
}, (error) => {
|
||||
throw new Utils.InnertubeError(error.message, error);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Standardizes the API response and catches all errors.
|
||||
*/
|
||||
this.instance.interceptors.response.use((res) => {
|
||||
const response = {
|
||||
success: res.status === 200,
|
||||
status_code: res.status,
|
||||
data: res.data
|
||||
};
|
||||
|
||||
|
||||
if (res.status !== 200)
|
||||
throw new Utils.InnertubeError(`Request to ${res.config.url} failed with status code ${res.status} ${res.statusText}`, response);
|
||||
|
||||
@@ -89,7 +97,9 @@ class Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the context according to the given client.
|
||||
* @todo refactor this?
|
||||
* @returns
|
||||
*/
|
||||
#adjustContext(ctx, client) {
|
||||
switch (client) {
|
||||
|
||||
@@ -4,6 +4,7 @@ const Crypto = require('crypto');
|
||||
const UserAgent = require('user-agents');
|
||||
const Flatten = require('flat');
|
||||
|
||||
/** @namespace */
|
||||
class InnertubeError extends Error {
|
||||
constructor (message, info) {
|
||||
super(message);
|
||||
|
||||
@@ -24,12 +24,12 @@ describe('YouTube.js Tests', () => {
|
||||
|
||||
it('Should retrieve YouTube search suggestions', async () => {
|
||||
const suggestions = await this.session.getSearchSuggestions(Constants.VIDEOS[0].QUERY, { client: 'YOUTUBE' });
|
||||
expect(suggestions.length).toBeLessThanOrEqual(10);
|
||||
expect(suggestions.results.length).toBeLessThanOrEqual(10);
|
||||
});
|
||||
|
||||
it('Should retrieve YouTube Music search suggestions', async () => {
|
||||
const suggestions = await this.session.getSearchSuggestions(Constants.VIDEOS[1].QUERY, { client: 'YTMUSIC' });
|
||||
expect(suggestions.length).toBeLessThanOrEqual(10);
|
||||
expect(suggestions.results.length).toBeLessThanOrEqual(10);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
412
typings/lib/Innertube.d.ts
vendored
412
typings/lib/Innertube.d.ts
vendored
@@ -1,6 +1,11 @@
|
||||
export = Innertube;
|
||||
/**
|
||||
* Innertube instance.
|
||||
* @namespace
|
||||
*/
|
||||
declare class Innertube {
|
||||
/**
|
||||
* @example
|
||||
* ```js
|
||||
* const Innertube = require('youtubei.js');
|
||||
* const youtube = await new Innertube();
|
||||
@@ -36,276 +41,21 @@ declare class Innertube {
|
||||
* @type {EventEmitter}
|
||||
*/
|
||||
ev: EventEmitter;
|
||||
oauth: OAuth;
|
||||
auth_apisid: any;
|
||||
request: Request;
|
||||
actions: Actions;
|
||||
account: {
|
||||
info: () => Promise<{
|
||||
name: string;
|
||||
photo: Array<object>;
|
||||
country: string;
|
||||
language: string;
|
||||
}>;
|
||||
getTimeWatched: () => void;
|
||||
settings: {
|
||||
notifications: {
|
||||
/**
|
||||
* Notify about activity from the channels you're subscribed to.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSubscriptions: (new_value: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Recommended content notifications.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setRecommendedVideos: (new_value: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Notify about activity on your channel.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setChannelActivity: (new_value: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Notify about replies to your comments.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setCommentReplies: (new_value: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Notify when others mention your channel.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setMentions: (new_value: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Notify when others share your content on their channels.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSharedContent: (new_value: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
};
|
||||
privacy: {
|
||||
/**
|
||||
* If set to true, your subscriptions won't be visible to others.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSubscriptionsPrivate: (new_value: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* If set to true, saved playlists won't appear on your channel.
|
||||
*
|
||||
* @param {boolean} new_value - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSavedPlaylistsPrivate: (new_value: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
};
|
||||
interact: {
|
||||
/**
|
||||
* Likes a given video.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
like: (video_id: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Diskes a given video.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
dislike: (video_id: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Removes a like/dislike.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
removeLike: (video_id: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Posts a comment on a given video.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @param {string} text
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
comment: (video_id: string, text: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Translates a given text using YouTube's comment translate feature.
|
||||
*
|
||||
* @param {string} text
|
||||
* @param {string} target_language
|
||||
* @param {object} [args] - optional arguments
|
||||
* @param {string} [args.video_id]
|
||||
* @param {string} [args.comment_id]
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; translated_content: string; data: object; }>}
|
||||
*/
|
||||
translate: (text: string, target_language: string, args?: {
|
||||
video_id?: string;
|
||||
comment_id?: string;
|
||||
}) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
translated_content: string;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Subscribes to a given channel.
|
||||
*
|
||||
* @param {string} channel_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
subscribe: (channel_id: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Unsubscribes from a given channel.
|
||||
*
|
||||
* @param {string} channel_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
unsubscribe: (channel_id: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Changes notification preferences for a given channel.
|
||||
* Only works with channels you are subscribed to.
|
||||
*
|
||||
* @param {string} channel_id
|
||||
* @param {string} type PERSONALIZED | ALL | NONE
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setNotificationPreferences: (channel_id: string, type: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
};
|
||||
playlist: {
|
||||
/**
|
||||
* Creates a playlist.
|
||||
*
|
||||
* @param {string} title
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>}
|
||||
*/
|
||||
create: (title: string, video_ids: Array<string>) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
playlist_id: string;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Deletes a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>}
|
||||
*/
|
||||
delete: (playlist_id: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
playlist_id: string;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Adds an array of videos to a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @param {Array.<string>} video_ids
|
||||
* @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: number;
|
||||
playlist_id: string;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Removes videos from a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @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: number;
|
||||
playlist_id: string;
|
||||
data: object;
|
||||
}>;
|
||||
};
|
||||
account: AccountManager;
|
||||
playlist: PlaylistManager;
|
||||
interact: InteractionManager;
|
||||
/**
|
||||
* Signs-in to a google account.
|
||||
* Signs in to a google account.
|
||||
*
|
||||
* @param {object} auth_info
|
||||
* @param {string} auth_info.access_token - Token used to sign in.
|
||||
* @param {string} auth_info.refresh_token - Token used to get a new access token.
|
||||
* @param {Date} auth_info.expires - Access token's expiration date, which is usually 24hrs-ish
|
||||
* @param {Date} auth_info.expires - Access token's expiration date, which is usually 24hrs-ish.
|
||||
*
|
||||
* @returns {Promise.<void>}
|
||||
*/
|
||||
signIn(auth_info?: {
|
||||
@@ -313,8 +63,8 @@ declare class Innertube {
|
||||
refresh_token: string;
|
||||
expires: Date;
|
||||
}): Promise<void>;
|
||||
access_token: any;
|
||||
refresh_token: any;
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
/**
|
||||
* Signs out of an account.
|
||||
* @returns {Promise.<{ success: boolean; status_code: number }>}
|
||||
@@ -324,17 +74,7 @@ declare class Innertube {
|
||||
status_code: number;
|
||||
}>;
|
||||
/**
|
||||
* Retrieves account details.
|
||||
* @returns {Promise.<{ name: string; photo: Array<object>; country: string; language: string; }>}
|
||||
*/
|
||||
getAccountInfo(): Promise<{
|
||||
name: string;
|
||||
photo: Array<object>;
|
||||
country: string;
|
||||
language: string;
|
||||
}>;
|
||||
/**
|
||||
* Searches on YouTube.
|
||||
* Searches a given query.
|
||||
*
|
||||
* @param {string} query - search query.
|
||||
* @param {object} [options] - search options.
|
||||
@@ -345,8 +85,8 @@ declare class Innertube {
|
||||
* @param {string} [options.filters.duration] - filter videos by duration, can be: any | short | medium | long
|
||||
* @param {string} [options.filters.sort_by] - filter video results by order, can be: relevance | rating | upload_date | view_count
|
||||
*
|
||||
* @returns {Promise.<{ query: string; corrected_query: string; estimated_results: number; videos: [] } |
|
||||
* { results: { songs: []; videos: []; albums: []; community_playlists: [] } }>}
|
||||
* @returns {Promise.<{ query: string; corrected_query: string; estimated_results: number; videos: object[] } |
|
||||
* { results: { songs: object[]; videos: object[]; albums: object[]; community_playlists: object[] } }>}
|
||||
*/
|
||||
search(query: string, options?: {
|
||||
client?: string;
|
||||
@@ -360,34 +100,33 @@ declare class Innertube {
|
||||
query: string;
|
||||
corrected_query: string;
|
||||
estimated_results: number;
|
||||
videos: [];
|
||||
videos: object[];
|
||||
} | {
|
||||
results: {
|
||||
songs: [];
|
||||
videos: [];
|
||||
albums: [];
|
||||
community_playlists: [];
|
||||
songs: object[];
|
||||
videos: object[];
|
||||
albums: object[];
|
||||
community_playlists: object[];
|
||||
};
|
||||
}>;
|
||||
/**
|
||||
* Retrieves search suggestions.
|
||||
* Retrieves search suggestions for a given query.
|
||||
*
|
||||
* @param {string} input - the search query.
|
||||
* @param {string} query - the search query.
|
||||
* @param {object} [options] - search options.
|
||||
* @param {string} [options.client='YOUTUBE'] - client used to retrieve search suggestions, can be: `YOUTUBE` or `YTMUSIC`.
|
||||
*
|
||||
* @returns {Promise.<[{ text: string; bold_text: string }]>}
|
||||
* @returns {Promise.<{ query: string; results: string[] }>}
|
||||
*/
|
||||
getSearchSuggestions(input: string, options?: {
|
||||
getSearchSuggestions(query: string, options?: {
|
||||
client?: string;
|
||||
}): Promise<[{
|
||||
text: string;
|
||||
bold_text: string;
|
||||
}]>;
|
||||
}): Promise<{
|
||||
query: string;
|
||||
results: string[];
|
||||
}>;
|
||||
/**
|
||||
* Retrieves video info.
|
||||
*
|
||||
* @param {string} video_id - video id
|
||||
* @param {string} video_id - the video id.
|
||||
* @return {Promise.<{ title: string; description: string; thumbnail: []; metadata: object }>}
|
||||
*/
|
||||
getDetails(video_id: string): Promise<{
|
||||
@@ -397,20 +136,19 @@ declare class Innertube {
|
||||
metadata: object;
|
||||
}>;
|
||||
/**
|
||||
* Retrieves comments for a video.
|
||||
* Retrieves comments for a given video.
|
||||
*
|
||||
* @param {string} video_id - video id
|
||||
* @param {string} video_id - the video id.
|
||||
* @param {string} [sort_by] - can be: `TOP_COMMENTS` or `NEWEST_FIRST`.
|
||||
* @return {Promise.<{ page_count: number; comment_count: number; items: []; }>}
|
||||
* @return {Promise.<{ page_count: number; comment_count: number; items: object[]; }>}
|
||||
*/
|
||||
getComments(video_id: string, sort_by?: string): Promise<{
|
||||
page_count: number;
|
||||
comment_count: number;
|
||||
items: [];
|
||||
items: object[];
|
||||
}>;
|
||||
/**
|
||||
* Retrieves contents for a given channel. (WIP)
|
||||
*
|
||||
* @param {string} id - channel id
|
||||
* @return {Promise.<{ title: string; description: string; metadata: object; content: object }>}
|
||||
*/
|
||||
@@ -422,80 +160,80 @@ declare class Innertube {
|
||||
}>;
|
||||
/**
|
||||
* Retrieves watch history.
|
||||
* @returns {Promise.<{ items: [{ date: string; videos: [] }] }>}
|
||||
* @returns {Promise.<{ items: { date: string; videos: object[] }[] }>}
|
||||
*/
|
||||
getHistory(): Promise<{
|
||||
items: [{
|
||||
items: {
|
||||
date: string;
|
||||
videos: [];
|
||||
}];
|
||||
videos: object[];
|
||||
}[];
|
||||
}>;
|
||||
/**
|
||||
* Retrieves YouTube's home feed (aka recommendations).
|
||||
* @returns {Promise.<{ videos: [{ id: string; title: string; description: string; channel: string; metadata: object }] }>}
|
||||
* Retrieves home feed (aka recommendations).
|
||||
* @returns {Promise.<{ videos: { id: string; title: string; description: string; channel: string; metadata: object }[] }>}
|
||||
*/
|
||||
getHomeFeed(): Promise<{
|
||||
videos: [{
|
||||
videos: {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
channel: string;
|
||||
metadata: object;
|
||||
}];
|
||||
}[];
|
||||
}>;
|
||||
/**
|
||||
* Retrieves trending content.
|
||||
*
|
||||
* @returns {Promise.<{ now: { content: [{ title: string; videos: []; }] };
|
||||
* music: { getVideos: Promise.<Array>; }; gaming: { getVideos: Promise.<Array>; };
|
||||
* movies: { getVideos: Promise.<Array>; } }>}
|
||||
* @returns {Promise.<{ now: { content: { title: string; videos: object[]; }[] };
|
||||
* music: { getVideos: Promise.<Array.<object>>; }; gaming: { getVideos: Promise.<Array.<object>>; };
|
||||
* movies: { getVideos: Promise.<Array.<object>>; } }>}
|
||||
*/
|
||||
getTrending(): Promise<{
|
||||
now: {
|
||||
content: [{
|
||||
content: {
|
||||
title: string;
|
||||
videos: [];
|
||||
}];
|
||||
videos: object[];
|
||||
}[];
|
||||
};
|
||||
music: {
|
||||
getVideos: Promise<any[]>;
|
||||
getVideos: Promise<Array<object>>;
|
||||
};
|
||||
gaming: {
|
||||
getVideos: Promise<any[]>;
|
||||
getVideos: Promise<Array<object>>;
|
||||
};
|
||||
movies: {
|
||||
getVideos: Promise<any[]>;
|
||||
getVideos: Promise<Array<object>>;
|
||||
};
|
||||
}>;
|
||||
/**
|
||||
* @todo finish this
|
||||
* WIP
|
||||
*/
|
||||
getLibrary(): Promise<any>;
|
||||
/**
|
||||
* Retrieves subscriptions feed.
|
||||
* @returns {Promise.<{ items: [{ date: string; videos: [] }] }>}
|
||||
* @returns {Promise.<{ items: { date: string; videos: object[] }[] }>}
|
||||
*/
|
||||
getSubscriptionsFeed(): Promise<{
|
||||
items: [{
|
||||
items: {
|
||||
date: string;
|
||||
videos: [];
|
||||
}];
|
||||
videos: object[];
|
||||
}[];
|
||||
}>;
|
||||
/**
|
||||
* Retrieves your notifications.
|
||||
* @returns {Promise.<{ items: [{ title: string; sent_time: string; channel_name: string; channel_thumbnail: {}; video_thumbnail: {}; video_url: string; read: boolean; notification_id: string }] }>}
|
||||
* Retrieves notifications.
|
||||
* @returns {Promise.<{ items: { title: string; sent_time: string; channel_name: string; channel_thumbnail: object; video_thumbnail: object; video_url: string; read: boolean; notification_id: string }[] }>}
|
||||
*/
|
||||
getNotifications(): Promise<{
|
||||
items: [{
|
||||
items: {
|
||||
title: string;
|
||||
sent_time: string;
|
||||
channel_name: string;
|
||||
channel_thumbnail: {};
|
||||
video_thumbnail: {};
|
||||
channel_thumbnail: object;
|
||||
video_thumbnail: object;
|
||||
video_url: string;
|
||||
read: boolean;
|
||||
notification_id: string;
|
||||
}];
|
||||
}[];
|
||||
}>;
|
||||
/**
|
||||
* Retrieves unseen notifications count.
|
||||
@@ -510,11 +248,12 @@ declare class Innertube {
|
||||
*/
|
||||
getLyrics(video_id: string): Promise<string>;
|
||||
/**
|
||||
* Retrieves a given playlist.
|
||||
* Retrieves the contents of a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id - playlist id.
|
||||
* @param {object} options - { client: YOUTUBE | YTMUSIC }
|
||||
* @param {string} playlist_id - the id of the playlist.
|
||||
* @param {object} options - `YOUTUBE` | `YTMUSIC`
|
||||
* @param {string} options.client - client used to parse the playlist, can be: `YTMUSIC` | `YOUTUBE`
|
||||
*
|
||||
* @returns {Promise.<
|
||||
* { title: string; description: string; total_items: string; last_updated: string; views: string; items: [] } |
|
||||
* { title: string; description: string; total_items: number; duration: string; year: string; items: [] }>}
|
||||
@@ -546,15 +285,15 @@ declare class Innertube {
|
||||
* @param {string} options.type - download type, can be: video, audio or videoandaudio
|
||||
* @param {string} options.format - file format
|
||||
*
|
||||
* @returns {Promise.<{ selected_format: {}; formats: [] }>}
|
||||
* @returns {Promise.<{ selected_format: object; formats: object[] }>}
|
||||
*/
|
||||
getStreamingData(video_id: string, options?: {
|
||||
quality: string;
|
||||
type: string;
|
||||
format: string;
|
||||
}): Promise<{
|
||||
selected_format: {};
|
||||
formats: [];
|
||||
selected_format: object;
|
||||
formats: object[];
|
||||
}>;
|
||||
/**
|
||||
* Downloads a given video. If you only need the direct download link take a look at {@link getStreamingData}.
|
||||
@@ -564,6 +303,9 @@ declare class Innertube {
|
||||
* @param {string} [options.quality] - video quality; 360p, 720p, 1080p, etc...
|
||||
* @param {string} [options.type] - download type, can be: video, audio or videoandaudio
|
||||
* @param {string} [options.format] - file format
|
||||
* @param {object} [options.range] - download range, indicates which bytes should be downloaded.
|
||||
* @param {number} options.range.start - the beginning of the range.
|
||||
* @param {number} options.range.end - the end of the range.
|
||||
*
|
||||
* @return {Stream.PassThrough}
|
||||
*/
|
||||
@@ -571,10 +313,18 @@ declare class Innertube {
|
||||
quality?: string;
|
||||
type?: string;
|
||||
format?: string;
|
||||
range?: {
|
||||
start: number;
|
||||
end: number;
|
||||
};
|
||||
}): Stream.PassThrough;
|
||||
#private;
|
||||
}
|
||||
import EventEmitter = require("events");
|
||||
import OAuth = require("./core/OAuth");
|
||||
import Request = require("./utils/Request");
|
||||
import Actions = require("./core/Actions");
|
||||
import AccountManager = require("./core/AccountManager");
|
||||
import PlaylistManager = require("./core/PlaylistManager");
|
||||
import InteractionManager = require("./core/InteractionManager");
|
||||
import Stream = require("stream");
|
||||
|
||||
192
typings/lib/core/AccountManager.d.ts
vendored
Normal file
192
typings/lib/core/AccountManager.d.ts
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
export = AccountManager;
|
||||
/** @namespace */
|
||||
declare class AccountManager {
|
||||
/**
|
||||
* @param {Actions} actions
|
||||
* @constructor
|
||||
*/
|
||||
constructor(actions: Actions);
|
||||
/** @namespace */
|
||||
channel: {
|
||||
/**
|
||||
* Edits channel name.
|
||||
*
|
||||
* @param {string} new_name
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
editName: (new_name: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Edits channel description.
|
||||
*
|
||||
* @param {string} new_description
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
editDescription: (new_description: string) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Retrieves basic channel analytics.
|
||||
* @borrows AccountManager#getAnalytics as getBasicAnalytics
|
||||
*/
|
||||
getBasicAnalytics: () => Promise<{
|
||||
metrics: {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
metric_value: string;
|
||||
comparison_indicator: object;
|
||||
series_configuration: object;
|
||||
}[];
|
||||
top_content: {
|
||||
views: string;
|
||||
published: string;
|
||||
thumbnails: object[];
|
||||
duration: string;
|
||||
is_short: boolean;
|
||||
}[];
|
||||
}>;
|
||||
};
|
||||
/** @namespace */
|
||||
settings: {
|
||||
notifications: {
|
||||
/**
|
||||
* Notify about activity from the channels you're subscribed to.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSubscriptions: (option: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Recommended content notifications.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setRecommendedVideos: (option: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Notify about activity on your channel.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setChannelActivity: (option: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Notify about replies to your comments.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setCommentReplies: (option: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Notify when others mention your channel.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setMentions: (option: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Notify when others share your content on their channels.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSharedContent: (option: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
};
|
||||
privacy: {
|
||||
/**
|
||||
* If set to true, your subscriptions won't be visible to others.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSubscriptionsPrivate: (option: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* If set to true, saved playlists won't appear on your channel.
|
||||
*
|
||||
* @param {boolean} option - ON | OFF
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setSavedPlaylistsPrivate: (option: boolean) => Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Retrieves channel info.
|
||||
* @returns {Promise.<{ name: string; email: string; channel_id: string; subscriber_count: string; photo: object[]; }>}
|
||||
*/
|
||||
getInfo(): Promise<{
|
||||
name: string;
|
||||
email: string;
|
||||
channel_id: string;
|
||||
subscriber_count: string;
|
||||
photo: object[];
|
||||
}>;
|
||||
/**
|
||||
* Retrieves time watched statistics.
|
||||
* @returns {Promise.<[{ title: string; time: string; }]>}
|
||||
*/
|
||||
getTimeWatched(): Promise<[{
|
||||
title: string;
|
||||
time: string;
|
||||
}]>;
|
||||
/**
|
||||
* Retrieves basic channel analytics.
|
||||
*
|
||||
* @returns {Promise.<{ metrics: { title: string; subtitle: string; metric_value: string;
|
||||
* comparison_indicator: object; series_configuration: object; }[]; top_content: { views: string;
|
||||
* published: string; thumbnails: object[]; duration: string; is_short: boolean }[]; }>}
|
||||
*/
|
||||
getAnalytics(): Promise<{
|
||||
metrics: {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
metric_value: string;
|
||||
comparison_indicator: object;
|
||||
series_configuration: object;
|
||||
}[];
|
||||
top_content: {
|
||||
views: string;
|
||||
published: string;
|
||||
thumbnails: object[];
|
||||
duration: string;
|
||||
is_short: boolean;
|
||||
}[];
|
||||
}>;
|
||||
#private;
|
||||
}
|
||||
93
typings/lib/core/Actions.d.ts
vendored
93
typings/lib/core/Actions.d.ts
vendored
@@ -1,6 +1,11 @@
|
||||
export = Actions;
|
||||
/** namespace **/
|
||||
declare class Actions {
|
||||
constructor(session: any);
|
||||
/**
|
||||
* @param {Innertube} session
|
||||
* @constructor
|
||||
*/
|
||||
constructor(session: Innertube);
|
||||
/**
|
||||
* Covers `/browse` endpoint, mostly used to access
|
||||
* YouTube's sections such as the home feed, etc
|
||||
@@ -53,21 +58,21 @@ declare class Actions {
|
||||
*
|
||||
* @param {string} action
|
||||
* @param {object} args
|
||||
* @param {string} args.new_value
|
||||
* @param {string} args.setting_item_id
|
||||
* @param {string} [args.new_value]
|
||||
* @param {string} [args.setting_item_id]
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object }>}
|
||||
*/
|
||||
account(action: string, args?: {
|
||||
new_value: string;
|
||||
setting_item_id: string;
|
||||
new_value?: string;
|
||||
setting_item_id?: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Covers endpoint used for search.
|
||||
* Endpoint used for search.
|
||||
*
|
||||
* @param {object} args
|
||||
* @param {string} [args.query]
|
||||
@@ -109,6 +114,24 @@ declare class Actions {
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Channel management endpoints.
|
||||
*
|
||||
* @param {string} action
|
||||
* @param {object} args
|
||||
* @param {string} [args.new_name]
|
||||
* @param {string} [args.new_description]
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
channel(action: string, args?: {
|
||||
new_name?: string;
|
||||
new_description?: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Covers endpoints used for playlist management.
|
||||
*
|
||||
@@ -175,6 +198,44 @@ declare class Actions {
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Endpoint used to retrieve video thumbnails.
|
||||
*
|
||||
* @param {object} args
|
||||
* @param {string} args.video_id
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
thumbnails(args?: {
|
||||
video_id: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Place Autocomplete endpoint, found it in the APK but
|
||||
* doesn't seem to be used anywhere on YouTube (maybe for ads?).
|
||||
*
|
||||
* Ex:
|
||||
* ```js
|
||||
* const places = await session.actions.geo('place_autocomplete', { input: 'San diego cafe' });
|
||||
* console.info(places.data);
|
||||
* ```
|
||||
*
|
||||
* @param {string} action
|
||||
* @param {object} args
|
||||
* @param {string} args.input
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
geo(action: string, args?: {
|
||||
input: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Covers endpoints used to report content.
|
||||
*
|
||||
@@ -212,13 +273,14 @@ declare class Actions {
|
||||
* @param {object} args
|
||||
* @param {string} [args.video_id]
|
||||
* @param {string} [args.ctoken]
|
||||
* @param {string} [client]
|
||||
* @param {string} [args.client]
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
next(args?: {
|
||||
video_id?: string;
|
||||
ctoken?: string;
|
||||
client?: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
@@ -245,7 +307,22 @@ declare class Actions {
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
getSearchSuggestions(client: string, input: string): Promise<{
|
||||
getSearchSuggestions(client: string, query: any): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Endpoint used to retrieve user mention suggestions.
|
||||
*
|
||||
* @param {object} args
|
||||
* @param {string} args.input
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
getUserMentionSuggestions(args?: {
|
||||
input: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
|
||||
107
typings/lib/core/InteractionManager.d.ts
vendored
Normal file
107
typings/lib/core/InteractionManager.d.ts
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
export = InteractionManager;
|
||||
/** @namespace */
|
||||
declare class InteractionManager {
|
||||
/**
|
||||
* @param {Actions} actions
|
||||
* @constructor
|
||||
*/
|
||||
constructor(actions: Actions);
|
||||
/**
|
||||
* Likes a given video.
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
like(video_id: string): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Dislikes a given video.
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
dislike(video_id: string): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Removes a like/dislike.
|
||||
* @param {string} video_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
removeLike(video_id: string): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Subscribes to a given channel.
|
||||
* @param {string} channel_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
subscribe(channel_id: string): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Unsubscribes from a given channel.
|
||||
* @param {string} channel_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
unsubscribe(channel_id: string): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Posts a comment on a given video.
|
||||
*
|
||||
* @param {string} video_id
|
||||
* @param {string} text
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
comment(video_id: string, text: string): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Translates a given text using YouTube's comment translate feature.
|
||||
*
|
||||
* @param {string} text
|
||||
* @param {string} target_language - an ISO language code
|
||||
* @param {object} [args] - optional arguments
|
||||
* @param {string} [args.video_id]
|
||||
* @param {string} [args.comment_id]
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; translated_content: string; data: object; }>}
|
||||
*/
|
||||
translate(text: string, target_language: string, args?: {
|
||||
video_id?: string;
|
||||
comment_id?: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
translated_content: string;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Changes notification preferences for a given channel.
|
||||
* Only works with channels you are subscribed to.
|
||||
*
|
||||
* @param {string} channel_id
|
||||
* @param {string} type - `PERSONALIZED` | `ALL` | `NONE`
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; data: object; }>}
|
||||
*/
|
||||
setNotificationPreferences(channel_id: string, type: string): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
data: object;
|
||||
}>;
|
||||
#private;
|
||||
}
|
||||
21
typings/lib/core/OAuth.d.ts
vendored
21
typings/lib/core/OAuth.d.ts
vendored
@@ -1,6 +1,11 @@
|
||||
export = OAuth;
|
||||
/** @namespace */
|
||||
declare class OAuth {
|
||||
constructor(ev: any);
|
||||
/**
|
||||
* @param {EventEmitter} ev
|
||||
* @constructor
|
||||
*/
|
||||
constructor(ev: EventEmitter);
|
||||
/**
|
||||
* Starts the auth flow in case no valid credentials are available.
|
||||
* @returns {Promise.<void>}
|
||||
@@ -18,10 +23,18 @@ declare class OAuth {
|
||||
* @returns {Promise.<void>}
|
||||
*/
|
||||
revokeAccessToken(): Promise<void>;
|
||||
getAccessToken(): any;
|
||||
getRefreshToken(): any;
|
||||
/**
|
||||
* Checks if the auth info is valid.
|
||||
* Returns the access token.
|
||||
* @returns {string}
|
||||
*/
|
||||
getAccessToken(): string;
|
||||
/**
|
||||
* Returns the refresh token.
|
||||
* @returns {string}
|
||||
*/
|
||||
getRefreshToken(): string;
|
||||
/**
|
||||
* Checks if the auth info format is valid.
|
||||
* @returns {boolean} true | false
|
||||
*/
|
||||
isValidAuthInfo(): boolean;
|
||||
|
||||
36
typings/lib/core/Player.d.ts
vendored
36
typings/lib/core/Player.d.ts
vendored
@@ -1,11 +1,37 @@
|
||||
export = Player;
|
||||
/** @namespace */
|
||||
declare class Player {
|
||||
constructor(id: any);
|
||||
/**
|
||||
* Represents the YouTube Web player script.
|
||||
* @param {string} id - the id of the player.
|
||||
* @constructor
|
||||
*/
|
||||
constructor(id: string);
|
||||
init(): Promise<Player>;
|
||||
get url(): string;
|
||||
get sts(): any;
|
||||
get ntoken_decipher(): any;
|
||||
get signature_decipher(): any;
|
||||
/**
|
||||
* Returns the current player's url.
|
||||
* @readonly
|
||||
* @returns {string}
|
||||
*/
|
||||
readonly get url(): string;
|
||||
/**
|
||||
* Returns the signature timestamp.
|
||||
* @readonly
|
||||
* @returns {string}
|
||||
*/
|
||||
readonly get sts(): string;
|
||||
/**
|
||||
* Returns the n-token decipher algorithm.
|
||||
* @readonly
|
||||
* @returns {string}
|
||||
*/
|
||||
readonly get ntoken_decipher(): string;
|
||||
/**
|
||||
* Returns the signature decipher algorithm.
|
||||
* @readonly
|
||||
* @returns {string}
|
||||
*/
|
||||
readonly get signature_decipher(): string;
|
||||
isCached(): boolean;
|
||||
#private;
|
||||
}
|
||||
|
||||
63
typings/lib/core/PlaylistManager.d.ts
vendored
Normal file
63
typings/lib/core/PlaylistManager.d.ts
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
export = PlaylistManager;
|
||||
/** @namespace */
|
||||
declare class PlaylistManager {
|
||||
/**
|
||||
* @param {Actions} actions
|
||||
* @constructor
|
||||
*/
|
||||
constructor(actions: Actions);
|
||||
/**
|
||||
* Creates a playlist.
|
||||
*
|
||||
* @param {string} title
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>}
|
||||
*/
|
||||
create(title: string, video_ids: Array<string>): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
playlist_id: string;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Deletes a given playlist.
|
||||
* @param {string} playlist_id
|
||||
* @returns {Promise.<{ success: boolean; status_code: number; playlist_id: string; data: object; }>}
|
||||
*/
|
||||
delete(playlist_id: string): Promise<{
|
||||
success: boolean;
|
||||
status_code: number;
|
||||
playlist_id: string;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Adds videos to a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @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: number;
|
||||
playlist_id: string;
|
||||
data: object;
|
||||
}>;
|
||||
/**
|
||||
* Removes videos from a given playlist.
|
||||
*
|
||||
* @param {string} playlist_id
|
||||
* @param {Array.<string>} video_ids
|
||||
*
|
||||
* @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: number;
|
||||
playlist_id: string;
|
||||
data: object;
|
||||
}>;
|
||||
#private;
|
||||
}
|
||||
25
typings/lib/core/SessionBuilder.d.ts
vendored
25
typings/lib/core/SessionBuilder.d.ts
vendored
@@ -1,12 +1,23 @@
|
||||
export = SessionBuilder;
|
||||
/** @namespace */
|
||||
declare class SessionBuilder {
|
||||
constructor(config: any);
|
||||
/**
|
||||
* @param {string} config
|
||||
* @constructor
|
||||
*/
|
||||
constructor(config: string);
|
||||
build(): Promise<SessionBuilder>;
|
||||
get key(): any;
|
||||
get context(): any;
|
||||
get api_version(): any;
|
||||
get client_version(): any;
|
||||
get client_name(): any;
|
||||
get player(): any;
|
||||
/** @readonly */
|
||||
readonly get key(): any;
|
||||
/** @readonly */
|
||||
readonly get context(): any;
|
||||
/** @readonly */
|
||||
readonly get api_version(): any;
|
||||
/** @readonly */
|
||||
readonly get client_version(): any;
|
||||
/** @readonly */
|
||||
readonly get client_name(): any;
|
||||
/** @readonly */
|
||||
readonly get player(): any;
|
||||
#private;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export = SearchSuggestionItem;
|
||||
declare class SearchSuggestionItem {
|
||||
static parse(data: any, bold_text: any): any;
|
||||
static parse(data: any): {
|
||||
query: any;
|
||||
results: any;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export = MusicSearchSuggestionItem;
|
||||
declare class MusicSearchSuggestionItem {
|
||||
static parse(data: any): any;
|
||||
static parseItem(item: any): {
|
||||
text: any;
|
||||
bold_text: any;
|
||||
static parse(data: any): {
|
||||
query: any;
|
||||
results: any;
|
||||
};
|
||||
static parseItem(item: any): any;
|
||||
}
|
||||
|
||||
16
typings/lib/utils/Constants.d.ts
vendored
16
typings/lib/utils/Constants.d.ts
vendored
@@ -41,16 +41,16 @@ export namespace CLIENTS {
|
||||
export { VERSION_1 as VERSION };
|
||||
}
|
||||
}
|
||||
export const STREAM_HEADERS: {
|
||||
Accept: string;
|
||||
'User-Agent': any;
|
||||
Connection: string;
|
||||
Origin: string;
|
||||
Referer: string;
|
||||
DNT: string;
|
||||
};
|
||||
export namespace STREAM_HEADERS {
|
||||
const accept: string;
|
||||
const connection: string;
|
||||
const origin: string;
|
||||
const referer: string;
|
||||
const DNT: string;
|
||||
}
|
||||
export const INNERTUBE_HEADERS_BASE: {
|
||||
accept: string;
|
||||
'accept-encoding': string;
|
||||
'content-type': string;
|
||||
};
|
||||
export const METADATA_KEYS: string[];
|
||||
|
||||
9
typings/lib/utils/Request.d.ts
vendored
9
typings/lib/utils/Request.d.ts
vendored
@@ -1,7 +1,12 @@
|
||||
export = Request;
|
||||
/** @namespace */
|
||||
declare class Request {
|
||||
constructor(session: any);
|
||||
session: any;
|
||||
/**
|
||||
* @param {Innertube} session
|
||||
* @constructor
|
||||
*/
|
||||
constructor(session: Innertube);
|
||||
session: Innertube;
|
||||
instance: any;
|
||||
#private;
|
||||
}
|
||||
|
||||
1
typings/lib/utils/Utils.d.ts
vendored
1
typings/lib/utils/Utils.d.ts
vendored
@@ -1,3 +1,4 @@
|
||||
/** @namespace */
|
||||
export class InnertubeError extends Error {
|
||||
constructor(message: any, info: any);
|
||||
info: any;
|
||||
|
||||
Reference in New Issue
Block a user