From a7bb98173198932de33f08a2097788d67d1a8fd3 Mon Sep 17 00:00:00 2001 From: Patrick Kan <55383971+patrickkfkan@users.noreply.github.com> Date: Fri, 29 Nov 2024 04:44:30 +0800 Subject: [PATCH] refactor: Cookie-based session fixes and minor additions (#821) * (fix) `on_behalf_of_user` arg not applied * (feat) `AccountManager#getInfo()`: Add option to fetch full accounts list --- src/core/Session.ts | 4 ++++ src/core/managers/AccountManager.ts | 24 +++++++++++++++++-- src/parser/classes/ChannelSwitcherHeader.ts | 20 ++++++++++++++++ src/parser/classes/ChannelSwitcherPage.ts | 16 +++++++++++++ .../UpdateChannelSwitcherPageAction.ts | 20 ++++++++++++++++ src/parser/nodes.ts | 3 +++ src/utils/HTTPClient.ts | 2 ++ 7 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/parser/classes/ChannelSwitcherHeader.ts create mode 100644 src/parser/classes/ChannelSwitcherPage.ts create mode 100644 src/parser/classes/actions/UpdateChannelSwitcherPageAction.ts diff --git a/src/core/Session.ts b/src/core/Session.ts index a401279c9..69e1746f9 100644 --- a/src/core/Session.ts +++ b/src/core/Session.ts @@ -386,6 +386,10 @@ export default class Session extends EventEmitter { } } + if (on_behalf_of_user) { + context_data.on_behalf_of_user = on_behalf_of_user; + } + session_data = { api_key, api_version, diff --git a/src/core/managers/AccountManager.ts b/src/core/managers/AccountManager.ts index 2edeb471a..7ec9a141f 100644 --- a/src/core/managers/AccountManager.ts +++ b/src/core/managers/AccountManager.ts @@ -5,6 +5,7 @@ import Settings from '../../parser/youtube/Settings.js'; import NavigationEndpoint from '../../parser/classes/NavigationEndpoint.js'; import { InnertubeError } from '../../utils/Utils.js'; +import { AccountItem } from '../../parser/nodes.js'; export default class AccountManager { readonly #actions: Actions; @@ -14,11 +15,30 @@ export default class AccountManager { } /** - * Retrieves channel info. + * Retrieves the list of channels belonging to the signed-in account. Only useful when signed in through cookie. If signed in through OAuth, you will get the active channel only. */ - async getInfo(): Promise { + async getInfo(all: true): Promise; + /** + * Retrieves the active channel info for the signed-in account. Throws error if `on_behalf_of_user` was used to create the Innertube instance; use `getInfo(true)` instead. + */ + async getInfo(all?: false): Promise; + async getInfo(all = false): Promise { if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); + + if (!all && !!this.#actions.session.context.user.onBehalfOfUser) { + throw new InnertubeError('Boolean argument must be true when "on_behalf_of_user" is specified.'); + } + + if (all) { + const get_accounts_list_endpoint = new NavigationEndpoint({ getAccountsListInnertubeEndpoint: { + requestType: 'ACCOUNTS_LIST_REQUEST_TYPE_CHANNEL_SWITCHER', + callCircumstance: 'SWITCHING_USERS_FULL' + } }); + const response = await get_accounts_list_endpoint.call(this.#actions, { client: 'WEB', parse: true }); + return response.actions_memo?.getType(AccountItem) || []; + } + const get_accounts_list_endpoint = new NavigationEndpoint({ getAccountsListInnertubeEndpoint: {} }); const response = await get_accounts_list_endpoint.call(this.#actions, { client: 'TV' }); return new AccountInfo(response); diff --git a/src/parser/classes/ChannelSwitcherHeader.ts b/src/parser/classes/ChannelSwitcherHeader.ts new file mode 100644 index 000000000..e908cbd30 --- /dev/null +++ b/src/parser/classes/ChannelSwitcherHeader.ts @@ -0,0 +1,20 @@ +import { YTNode } from '../helpers.js'; +import { Parser, type RawNode } from '../index.js'; +import { Text } from '../misc.js'; +import Button from './Button.js'; + +export default class ChannelSwitcherHeader extends YTNode { + static type = 'ChannelSwitcherHeader'; + + title: string; + button?: Button | null; + + constructor(data: RawNode) { + super(); + this.title = new Text(data.title).toString(); + + if (Reflect.has(data, 'button')) { + this.button = Parser.parseItem(data.button, Button); + } + } +} \ No newline at end of file diff --git a/src/parser/classes/ChannelSwitcherPage.ts b/src/parser/classes/ChannelSwitcherPage.ts new file mode 100644 index 000000000..87657ddbf --- /dev/null +++ b/src/parser/classes/ChannelSwitcherPage.ts @@ -0,0 +1,16 @@ +import type { ObservedArray } from '../helpers.js'; +import { YTNode } from '../helpers.js'; +import { Parser, type RawNode } from '../index.js'; + +export default class ChannelSwitcherPage extends YTNode { + static type = 'ChannelSwitcherPage'; + + header: YTNode; + contents: ObservedArray | null; + + constructor(data: RawNode) { + super(); + this.header = Parser.parseItem(data.header); + this.contents = Parser.parse(data.contents, true); + } +} \ No newline at end of file diff --git a/src/parser/classes/actions/UpdateChannelSwitcherPageAction.ts b/src/parser/classes/actions/UpdateChannelSwitcherPageAction.ts new file mode 100644 index 000000000..447a575ea --- /dev/null +++ b/src/parser/classes/actions/UpdateChannelSwitcherPageAction.ts @@ -0,0 +1,20 @@ +import type { ObservedArray } from '../../helpers.js'; +import { YTNode } from '../../helpers.js'; +import { Parser, type RawNode } from '../../index.js'; +import ChannelSwitcherPage from '../ChannelSwitcherPage.js'; + +export default class UpdateChannelSwitcherPageAction extends YTNode { + static type = 'UpdateChannelSwitcherPageAction'; + + public header?: YTNode; + public contents?: ObservedArray | null; + + constructor(data: RawNode) { + super(); + const page = Parser.parseItem(data.page, ChannelSwitcherPage); + if (page) { + this.header = page.header; + this.contents = page.contents; + } + } +} diff --git a/src/parser/nodes.ts b/src/parser/nodes.ts index f42fc78dc..91328f9f7 100644 --- a/src/parser/nodes.ts +++ b/src/parser/nodes.ts @@ -14,6 +14,7 @@ export { default as GetMultiPageMenuAction } from './classes/actions/GetMultiPag export { default as OpenPopupAction } from './classes/actions/OpenPopupAction.js'; export { default as SendFeedbackAction } from './classes/actions/SendFeedbackAction.js'; export { default as SignalAction } from './classes/actions/SignalAction.js'; +export { default as UpdateChannelSwitcherPageAction } from './classes/actions/UpdateChannelSwitcherPageAction.js'; export { default as UpdateEngagementPanelAction } from './classes/actions/UpdateEngagementPanelAction.js'; export { default as UpdateSubscribeButtonAction } from './classes/actions/UpdateSubscribeButtonAction.js'; export { default as AddToPlaylist } from './classes/AddToPlaylist.js'; @@ -50,6 +51,8 @@ export { default as ChannelMobileHeader } from './classes/ChannelMobileHeader.js export { default as ChannelOptions } from './classes/ChannelOptions.js'; export { default as ChannelOwnerEmptyState } from './classes/ChannelOwnerEmptyState.js'; export { default as ChannelSubMenu } from './classes/ChannelSubMenu.js'; +export { default as ChannelSwitcherHeader } from './classes/ChannelSwitcherHeader.js'; +export { default as ChannelSwitcherPage } from './classes/ChannelSwitcherPage.js'; export { default as ChannelTagline } from './classes/ChannelTagline.js'; export { default as ChannelThumbnailWithLink } from './classes/ChannelThumbnailWithLink.js'; export { default as ChannelVideoPlayer } from './classes/ChannelVideoPlayer.js'; diff --git a/src/utils/HTTPClient.ts b/src/utils/HTTPClient.ts index db332dfcb..38db55e76 100644 --- a/src/utils/HTTPClient.ts +++ b/src/utils/HTTPClient.ts @@ -143,6 +143,8 @@ export default class HTTPClient { if (sapisid) { request_headers.set('Authorization', await generateSidAuth(sapisid)); request_headers.set('X-Goog-Authuser', this.#session.account_index.toString()); + if (this.#session.context.user.onBehalfOfUser) + request_headers.set('X-Goog-PageId', this.#session.context.user.onBehalfOfUser); } request_headers.set('Cookie', this.#cookie);