Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 45 additions & 5 deletions src/Innertube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
HomeFeed,
Library,
NotificationsMenu,
PaidMemberships,
Playlist,
Search,
VideoInfo
Expand Down Expand Up @@ -143,7 +144,7 @@ export default class Innertube {
signatureTimestamp: session.player?.signature_timestamp
}
},
client: options?.client
client: options?.client
};

if (options?.po_token) {
Expand All @@ -155,7 +156,7 @@ export default class Innertube {
poToken: session.po_token
};
}

const watch_response = await watch_endpoint.call(session.actions, extra_payload);

const cpn = generateRandomString(16);
Expand Down Expand Up @@ -296,7 +297,7 @@ export default class Innertube {
'Cookie': session.cookie || ''
}
});

const text = await response.text();

const data = JSON.parse(text.replace('window.google.ac.h(', '').slice(0, -1));
Expand Down Expand Up @@ -553,17 +554,56 @@ export default class Innertube {
return new Comments(this.actions, response.data);
}

/**
* Gets the user's paid purchases and memberships.
*
* @see {@link https://www.youtube.com/paid_memberships}
*
* @example
* ```ts
* const page = await yt.getPaidMemberships();
*
* // Digital purchases & rentals (paginated)
* let section = page.purchases;
* while (section) {
* for (const item of section.items) {
* console.log(item.title?.text?.toString());
* }
* try {
* section = await section.getContinuation();
* } catch {
* break;
* }
* }
*
* // Active memberships
* for (const m of page.memberships) {
* console.log(m.channel_name, m.membership_level, m.price);
* }
*
* // Inactive memberships
* for (const m of page.inactive_memberships) {
* console.log(m.channel_name, m.expired_date);
* }
* ```
*/
async getPaidMemberships(): Promise<PaidMemberships> {
const browse_endpoint = new NavigationEndpoint({ browseEndpoint: { browseId: 'FEmemberships_and_purchases' } });
const response = await browse_endpoint.call(this.#session.actions);
return await PaidMemberships.create(this.actions, response);
}

/**
* Fetches an attestation challenge.
*/
async getAttestationChallenge(engagement_type: EngagementType, ids?: Record<string, any>[]) {
const payload: Record<string, any> = {
engagementType: engagement_type
};

if (ids)
payload.ids = ids;

return this.actions.execute('/att/get', { parse: true, ...payload });
}

Expand Down
29 changes: 29 additions & 0 deletions src/parser/classes/ActivityItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
import { NavigateAction, Parser } from '../index.js';
import CardItemText from './CardItemText.js';
import CardItemTextCollection from './CardItemTextCollection.js';
import ThemedImage from './ThemedImage.js';

export default class ActivityItem extends YTNode {
static type = 'ActivityItem';

image: ThemedImage | null;
title: CardItemText | null;
subtitle: CardItemText | null;
section_heading: CardItemText | null;
metadata: CardItemTextCollection | null;
on_tap: NavigateAction | null;

constructor(data: RawNode) {
super();
this.image = Parser.parseItem(data.image, ThemedImage);
this.title = Parser.parseItem(data.title, CardItemText);
this.subtitle = Parser.parseItem(data.subtitle, CardItemText);
this.section_heading = Parser.parseItem(data.sectionHeading, CardItemText);
this.metadata = Parser.parseItem(data.activityMetadata, CardItemTextCollection);
this.on_tap = data.onTap
? new NavigateAction(data.onTap.navigateAction)
: null;
}
}
19 changes: 19 additions & 0 deletions src/parser/classes/CardItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import CardItemActions from './CardItemActions.js';
import CardItemTextWithImage from './CardItemTextWithImage.js';
import CardItemTextCollection from './CardItemTextCollection.js';
import { YTNode } from '../helpers.js';
import { Parser } from '../index.js';
import type { RawNode } from '../index.js';

export default class CardItem extends YTNode {
static type = 'CardItem';

heading: CardItemTextWithImage | CardItemTextCollection | null;
additional_info: CardItemActions | null;

constructor(data: RawNode) {
super();
this.heading = Parser.parseItem(data.headingRenderer, [ CardItemTextWithImage, CardItemTextCollection ]);
this.additional_info = Parser.parseItem(data.additionalInfoRenderer, CardItemActions);
}
}
14 changes: 14 additions & 0 deletions src/parser/classes/CardItemActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { YTNode } from '../helpers.js';
import { Parser } from '../index.js';
import type { RawNode } from '../index.js';

export default class CardItemActions extends YTNode {
static type = 'CardItemActions';

primary_button: YTNode | null;

constructor(data: RawNode) {
super();
this.primary_button = Parser.parseItem(data.primaryButtonRenderer);
}
}
22 changes: 22 additions & 0 deletions src/parser/classes/CardItemContainer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { ObservedArray } from '../helpers.js';
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
import { Parser } from '../index.js';
import NavigationEndpoint from './NavigationEndpoint.js';

export default class CardItemContainer extends YTNode {
static type = 'CardItemContainer';

contents: ObservedArray<YTNode>;
base_renderer: YTNode | null;
endpoint: NavigationEndpoint | null;
target_id: string | null;

constructor(data: RawNode) {
super();
this.contents = Parser.parseArray(data.contents);
this.base_renderer = Parser.parseItem(data.baseRenderer);
this.endpoint = new NavigationEndpoint(data.onClickCommand);
this.target_id = data.targetId || null;
}
}
18 changes: 18 additions & 0 deletions src/parser/classes/CardItemText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
import Text from './misc/Text.js';

export default class CardItemText extends YTNode {
static type = 'CardItemText';

text: Text;
textColor: string;
style: string;

constructor(data: RawNode) {
super();
this.text = new Text(data.text);
this.textColor = data.textColor;
this.style = data.style;
}
}
17 changes: 17 additions & 0 deletions src/parser/classes/CardItemTextCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import CardItemText from './CardItemText.js';
import { YTNode } from '../helpers.js';
import { Parser } from '../index.js';
import type { RawNode } from '../index.js';

export default class CardItemTextCollection extends YTNode {
static type = 'CardItemTextCollection';

texts: CardItemText[];

constructor(data: RawNode) {
super();
this.texts = (data.textRenderers || []).map(
(renderer: RawNode) => Parser.parseItem(renderer, CardItemText)
);
}
}
25 changes: 25 additions & 0 deletions src/parser/classes/CardItemTextWithImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import CardItemTextCollection from './CardItemTextCollection.js';
import type Thumbnail from './misc/Thumbnail.js';
import ThemedImage from './ThemedImage.js';
import { YTNode } from '../helpers.js';
import { Parser } from '../index.js';
import type { RawNode } from '../index.js';

export default class CardItemTextWithImage extends YTNode {
static type = 'CardItemTextWithImage';

image: ThemedImage | null;
thumbnails: Thumbnail[];
text_collections: CardItemTextCollection[];

constructor(data: RawNode) {
super();

this.image = Parser.parseItem(data.imageRenderer, ThemedImage);
this.thumbnails = this.image?.thumbnails || [];

this.text_collections = (data.textCollectionRenderer || []).map(
(collection: RawNode) => Parser.parseItem(collection, CardItemTextCollection)
);
}
}
20 changes: 20 additions & 0 deletions src/parser/classes/OfferItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import CardItemTextCollection from './CardItemTextCollection.js';
import ThemedImage from './ThemedImage.js';
import { YTNode } from '../helpers.js';
import { Parser } from '../index.js';
import type { RawNode } from '../index.js';

export default class OfferItem extends YTNode {
static type = 'OfferItem';

image: ThemedImage | null;
heading: CardItemTextCollection | null;
description: CardItemTextCollection | null;

constructor(data: RawNode) {
super();
this.image = Parser.parseItem(data.imageRenderer, ThemedImage);
this.heading = Parser.parseItem(data.headingRenderer, CardItemTextCollection);
this.description = Parser.parseItem(data.descriptionRenderer, CardItemTextCollection);
}
}
20 changes: 20 additions & 0 deletions src/parser/classes/ThemedImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
import Thumbnail from './misc/Thumbnail.js';

export default class ThemedImage extends YTNode {
static type = 'ThemedImage';

thumbnails: Thumbnail[];
image_height: number;
image_width: number;
is_circular: boolean;

constructor(data: RawNode) {
super();
this.thumbnails = Thumbnail.fromResponse({ thumbnails: data.imageLight?.thumbnails || [] });
this.image_height = data.imageHeight;
this.image_width = data.imageWidth;
this.is_circular = data.isCircular;
}
}
17 changes: 17 additions & 0 deletions src/parser/classes/actions/UpdateCardItemOnClickCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import NavigationEndpoint from '../NavigationEndpoint.js';
import { YTNode } from '../../helpers.js';
import { Parser } from '../../index.js';
import type { RawNode } from '../../index.js';

export default class UpdateCardItemOnClickCommand extends YTNode {
static type = 'UpdateCardItemOnClickCommand';

endpoint: NavigationEndpoint;
target_id: string;

constructor(data: RawNode) {
super();
this.endpoint = new NavigationEndpoint(data.onClickCommand);
this.target_id = data.targetId;
}
}
1 change: 1 addition & 0 deletions src/parser/continuations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class ShowMiniplayerCommand extends YTNode {
}

export { default as AppendContinuationItemsAction } from './classes/actions/AppendContinuationItemsAction.js';
export { default as UpdateCardItemOnClickCommand } from './classes/actions/UpdateCardItemOnClickCommand.js';

export class ReloadContinuationItemsCommand extends YTNode {
static readonly type = 'reloadContinuationItemsCommand';
Expand Down
10 changes: 10 additions & 0 deletions src/parser/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ 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 UpdateCardItemOnClickCommand } from './classes/actions/UpdateCardItemOnClickCommand.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 ActiveAccountHeader } from './classes/ActiveAccountHeader.js';
export { default as ActivityItem } from './classes/ActivityItem.js';
export { default as AddToPlaylist } from './classes/AddToPlaylist.js';
export { default as Alert } from './classes/Alert.js';
export { default as AlertWithButton } from './classes/AlertWithButton.js';
Expand All @@ -41,6 +43,12 @@ export { default as C4TabbedHeader } from './classes/C4TabbedHeader.js';
export { default as CallToActionButton } from './classes/CallToActionButton.js';
export { default as Card } from './classes/Card.js';
export { default as CardCollection } from './classes/CardCollection.js';
export { default as CardItem } from './classes/CardItem.js';
export { default as CardItemActions } from './classes/CardItemActions.js';
export { default as CardItemContainer } from './classes/CardItemContainer.js';
export { default as CardItemText } from './classes/CardItemText.js';
export { default as CardItemTextCollection } from './classes/CardItemTextCollection.js';
export { default as CardItemTextWithImage } from './classes/CardItemTextWithImage.js';
export { default as CarouselHeader } from './classes/CarouselHeader.js';
export { default as CarouselItem } from './classes/CarouselItem.js';
export { default as CarouselItemView } from './classes/CarouselItemView.js';
Expand Down Expand Up @@ -361,6 +369,7 @@ export { default as TopbarMenuButton } from './classes/mweb/TopbarMenuButton.js'
export { default as NavigationEndpoint } from './classes/NavigationEndpoint.js';
export { default as Notification } from './classes/Notification.js';
export { default as NotificationAction } from './classes/NotificationAction.js';
export { default as OfferItem } from './classes/OfferItem.js';
export { default as OpenOnePickAddVideoModalCommand } from './classes/OpenOnePickAddVideoModalCommand.js';
export { default as PageHeader } from './classes/PageHeader.js';
export { default as PageHeaderView } from './classes/PageHeaderView.js';
Expand Down Expand Up @@ -476,6 +485,7 @@ export { default as TabbedSearchResults } from './classes/TabbedSearchResults.js
export { default as TextCarouselItemView } from './classes/TextCarouselItemView.js';
export { default as TextFieldView } from './classes/TextFieldView.js';
export { default as TextHeader } from './classes/TextHeader.js';
export { default as ThemedImage } from './classes/ThemedImage.js';
export { default as ThirdPartyShareTargetSection } from './classes/ThirdPartyShareTargetSection.js';
export { default as ThumbnailBadgeView } from './classes/ThumbnailBadgeView.js';
export { default as ThumbnailBottomOverlayView } from './classes/ThumbnailBottomOverlayView.js';
Expand Down
Loading