Skip to content

Commit

Permalink
refactor: Cookie-based session fixes and minor additions (#821)
Browse files Browse the repository at this point in the history
* (fix) `on_behalf_of_user` arg not applied

* (feat) `AccountManager#getInfo()`: Add option to fetch full accounts list
  • Loading branch information
patrickkfkan authored Nov 28, 2024
1 parent 386257c commit a7bb981
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/core/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
24 changes: 22 additions & 2 deletions src/core/managers/AccountManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<AccountInfo> {
async getInfo(all: true): Promise<AccountItem[]>;
/**
* 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<AccountInfo>;
async getInfo(all = false): Promise<AccountInfo | AccountItem[]> {
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);
Expand Down
20 changes: 20 additions & 0 deletions src/parser/classes/ChannelSwitcherHeader.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
16 changes: 16 additions & 0 deletions src/parser/classes/ChannelSwitcherPage.ts
Original file line number Diff line number Diff line change
@@ -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<YTNode> | null;

constructor(data: RawNode) {
super();
this.header = Parser.parseItem(data.header);
this.contents = Parser.parse(data.contents, true);
}
}
20 changes: 20 additions & 0 deletions src/parser/classes/actions/UpdateChannelSwitcherPageAction.ts
Original file line number Diff line number Diff line change
@@ -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<YTNode> | null;

constructor(data: RawNode) {
super();
const page = Parser.parseItem(data.page, ChannelSwitcherPage);
if (page) {
this.header = page.header;
this.contents = page.contents;
}
}
}
3 changes: 3 additions & 0 deletions src/parser/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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';
Expand Down
2 changes: 2 additions & 0 deletions src/utils/HTTPClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit a7bb981

Please sign in to comment.