mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-22 05:58:14 +00:00
feat: add support for switching accounts (cookie based auth only) (#236)
* feat: add support for switching accounts * style: lint
This commit is contained in:
@@ -21,7 +21,7 @@ class AccountManager {
|
||||
*/
|
||||
editName: (new_name: string) => {
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
return this.#actions.execute('/channel/edit_name', {
|
||||
givenName: new_name,
|
||||
@@ -34,7 +34,7 @@ class AccountManager {
|
||||
*/
|
||||
editDescription: (new_description: string) => {
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
return this.#actions.execute('/channel/edit_description', {
|
||||
givenDescription: new_description,
|
||||
@@ -53,7 +53,7 @@ class AccountManager {
|
||||
*/
|
||||
async getInfo() {
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
const response = await this.#actions.execute('/account/accounts_list', { client: 'ANDROID' });
|
||||
return new AccountInfo(response);
|
||||
|
||||
@@ -122,7 +122,7 @@ class Actions {
|
||||
|
||||
if (Reflect.has(data, 'browseId')) {
|
||||
if (this.#needsLogin(data.browseId) && !this.#session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
}
|
||||
|
||||
if (Reflect.has(data, 'override_endpoint'))
|
||||
|
||||
@@ -17,7 +17,7 @@ class InteractionManager {
|
||||
throwIfMissing({ video_id });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new Error('You are not signed in');
|
||||
throw new Error('You must be signed in to perform this operation.');
|
||||
|
||||
const action = await this.#actions.execute('/like/like', {
|
||||
client: 'ANDROID',
|
||||
@@ -37,7 +37,7 @@ class InteractionManager {
|
||||
throwIfMissing({ video_id });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new Error('You are not signed in');
|
||||
throw new Error('You must be signed in to perform this operation.');
|
||||
|
||||
const action = await this.#actions.execute('/like/dislike', {
|
||||
client: 'ANDROID',
|
||||
@@ -57,7 +57,7 @@ class InteractionManager {
|
||||
throwIfMissing({ video_id });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new Error('You are not signed in');
|
||||
throw new Error('You must be signed in to perform this operation.');
|
||||
|
||||
const action = await this.#actions.execute('/like/removelike', {
|
||||
client: 'ANDROID',
|
||||
@@ -77,7 +77,7 @@ class InteractionManager {
|
||||
throwIfMissing({ channel_id });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new Error('You are not signed in');
|
||||
throw new Error('You must be signed in to perform this operation.');
|
||||
|
||||
const action = await this.#actions.execute('/subscription/subscribe', {
|
||||
client: 'ANDROID',
|
||||
@@ -96,7 +96,7 @@ class InteractionManager {
|
||||
throwIfMissing({ channel_id });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new Error('You are not signed in');
|
||||
throw new Error('You must be signed in to perform this operation.');
|
||||
|
||||
const action = await this.#actions.execute('/subscription/unsubscribe', {
|
||||
client: 'ANDROID',
|
||||
@@ -116,7 +116,7 @@ class InteractionManager {
|
||||
throwIfMissing({ video_id, text });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new Error('You are not signed in');
|
||||
throw new Error('You must be signed in to perform this operation.');
|
||||
|
||||
const action = await this.#actions.execute('/comment/create_comment', {
|
||||
client: 'ANDROID',
|
||||
@@ -163,7 +163,7 @@ class InteractionManager {
|
||||
throwIfMissing({ channel_id, type });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new Error('You are not signed in');
|
||||
throw new Error('You must be signed in to perform this operation.');
|
||||
|
||||
const pref_types = {
|
||||
PERSONALIZED: 1,
|
||||
|
||||
@@ -20,7 +20,7 @@ class PlaylistManager {
|
||||
throwIfMissing({ title, video_ids });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
const response = await this.#actions.execute('/playlist/create', {
|
||||
title,
|
||||
@@ -44,7 +44,7 @@ class PlaylistManager {
|
||||
throwIfMissing({ playlist_id });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
const response = await this.#actions.execute('playlist/delete', { playlistId: playlist_id });
|
||||
|
||||
@@ -65,7 +65,7 @@ class PlaylistManager {
|
||||
throwIfMissing({ playlist_id, video_ids });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
const response = await this.#actions.execute('/browse/edit_playlist', {
|
||||
playlistId: playlist_id,
|
||||
@@ -91,7 +91,7 @@ class PlaylistManager {
|
||||
throwIfMissing({ playlist_id, video_ids });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
const info = await this.#actions.execute('/browse', {
|
||||
browseId: `VL${playlist_id}`,
|
||||
@@ -150,7 +150,7 @@ class PlaylistManager {
|
||||
throwIfMissing({ playlist_id, moved_video_id, predecessor_video_id });
|
||||
|
||||
if (!this.#actions.session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
const info = await this.#actions.execute('/browse', {
|
||||
browseId: `VL${playlist_id}`,
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface Context {
|
||||
|
||||
export interface SessionOptions {
|
||||
lang?: string;
|
||||
account_index?: number;
|
||||
device_category?: DeviceCategory;
|
||||
client_type?: ClientType;
|
||||
timezone?: string;
|
||||
@@ -68,6 +69,7 @@ export default class Session extends EventEmitterLike {
|
||||
#api_version;
|
||||
#key;
|
||||
#context;
|
||||
#account_index;
|
||||
#player;
|
||||
|
||||
oauth;
|
||||
@@ -76,9 +78,10 @@ export default class Session extends EventEmitterLike {
|
||||
actions;
|
||||
cache;
|
||||
|
||||
constructor(context: Context, api_key: string, api_version: string, player: Player, cookie?: string, fetch?: FetchFunction, cache?: UniversalCache) {
|
||||
constructor(context: Context, api_key: string, api_version: string, account_index: number, player: Player, cookie?: string, fetch?: FetchFunction, cache?: UniversalCache) {
|
||||
super();
|
||||
this.#context = context;
|
||||
this.#account_index = account_index;
|
||||
this.#key = api_key;
|
||||
this.#api_version = api_version;
|
||||
this.#player = player;
|
||||
@@ -107,12 +110,20 @@ export default class Session extends EventEmitterLike {
|
||||
}
|
||||
|
||||
static async create(options: SessionOptions = {}) {
|
||||
const { context, api_key, api_version } = await Session.getSessionData(options.lang, options.device_category, options.client_type, options.timezone, options.fetch);
|
||||
return new Session(context, api_key, api_version, await Player.create(options.cache, options.fetch), options.cookie, options.fetch, options.cache);
|
||||
const { context, api_key, api_version, account_index } = await Session.getSessionData(
|
||||
options.lang,
|
||||
options.account_index,
|
||||
options.device_category,
|
||||
options.client_type,
|
||||
options.timezone,
|
||||
options.fetch
|
||||
);
|
||||
return new Session(context, api_key, api_version, account_index, await Player.create(options.cache, options.fetch), options.cookie, options.fetch, options.cache);
|
||||
}
|
||||
|
||||
static async getSessionData(
|
||||
lang = 'en-US',
|
||||
account_index = 0,
|
||||
device_category: DeviceCategory = 'desktop',
|
||||
client_name: ClientType = ClientType.WEB,
|
||||
tz: string = Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
@@ -177,7 +188,7 @@ export default class Session extends EventEmitterLike {
|
||||
}
|
||||
};
|
||||
|
||||
return { context, api_key, api_version };
|
||||
return { context, api_key, api_version, account_index };
|
||||
}
|
||||
|
||||
async signIn(credentials?: Credentials): Promise<void> {
|
||||
@@ -213,7 +224,7 @@ export default class Session extends EventEmitterLike {
|
||||
|
||||
async signOut() {
|
||||
if (!this.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
const response = await this.oauth.revokeCredentials();
|
||||
this.logged_in = false;
|
||||
@@ -237,6 +248,10 @@ export default class Session extends EventEmitterLike {
|
||||
return this.#context.client.clientName;
|
||||
}
|
||||
|
||||
get account_index() {
|
||||
return this.#account_index;
|
||||
}
|
||||
|
||||
get context() {
|
||||
return this.#context;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class Studio {
|
||||
*/
|
||||
async setThumbnail(video_id: string, buffer: Uint8Array): Promise<ApiResponse> {
|
||||
if (!this.#session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
if (!video_id || !buffer)
|
||||
throw new MissingParamError('One or more parameters are missing.');
|
||||
@@ -83,7 +83,7 @@ class Studio {
|
||||
*/
|
||||
async updateVideoMetadata(video_id: string, metadata: VideoMetadata) {
|
||||
if (!this.#session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
const payload = Proto.encodeVideoMetadataPayload(video_id, metadata);
|
||||
|
||||
@@ -105,7 +105,7 @@ class Studio {
|
||||
*/
|
||||
async upload(file: BodyInit, metadata: UploadedVideoMetadata = {}): Promise<ApiResponse> {
|
||||
if (!this.#session.logged_in)
|
||||
throw new InnertubeError('You are not signed in');
|
||||
throw new InnertubeError('You must be signed in to perform this operation.');
|
||||
|
||||
const initial_data = await this.#getInitialUploadData();
|
||||
const upload_result = await this.#uploadVideo(initial_data.upload_url, file);
|
||||
|
||||
@@ -103,9 +103,12 @@ export default class HTTPClient {
|
||||
|
||||
if (this.#cookie) {
|
||||
const papisid = getStringBetweenStrings(this.#cookie, 'PAPISID=', ';');
|
||||
|
||||
if (papisid) {
|
||||
request_headers.set('authorization', await generateSidAuth(papisid));
|
||||
request_headers.set('x-goog-authuser', this.#session.account_index.toString());
|
||||
}
|
||||
|
||||
request_headers.set('cookie', this.#cookie);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user