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:
LuanRT
2022-05-27 08:17:16 -03:00
parent 865b6870a1
commit a85e9ef667
25 changed files with 1275 additions and 754 deletions

View File

@@ -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
View 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;

View 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;

View File

@@ -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() {

View File

@@ -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
View 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;

View File

@@ -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;
}

View File

@@ -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() {

View File

@@ -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: [

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);
});
});

View File

@@ -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
View 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;
}

View File

@@ -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
View 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;
}

View File

@@ -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;

View File

@@ -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
View 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;
}

View File

@@ -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;
}

View File

@@ -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;
};
}

View File

@@ -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;
}

View File

@@ -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[];

View File

@@ -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;
}

View File

@@ -1,3 +1,4 @@
/** @namespace */
export class InnertubeError extends Error {
constructor(message: any, info: any);
info: any;