UI/UX: Adiciona campo opcional para colagem manual de transcrição no VideoMind
This commit is contained in:
+9
@@ -0,0 +1,9 @@
|
||||
import type { ApiResponse } from '../../core/index.js';
|
||||
import type { IParsedResponse } from '../types/index.js';
|
||||
import type AccountItemSection from '../classes/AccountItemSection.js';
|
||||
export default class AccountInfo {
|
||||
#private;
|
||||
contents: AccountItemSection | null;
|
||||
constructor(response: ApiResponse);
|
||||
get page(): IParsedResponse;
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { Parser } from '../index.js';
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import AccountSectionList from '../classes/AccountSectionList.js';
|
||||
export default class AccountInfo {
|
||||
#page;
|
||||
contents;
|
||||
constructor(response) {
|
||||
this.#page = Parser.parseResponse(response.data);
|
||||
if (!this.#page.contents)
|
||||
throw new InnertubeError('Page contents not found');
|
||||
const account_section_list = this.#page.contents.array().as(AccountSectionList)[0];
|
||||
if (!account_section_list)
|
||||
throw new InnertubeError('Account section list not found');
|
||||
this.contents = account_section_list.contents[0];
|
||||
}
|
||||
get page() {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=AccountInfo.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"AccountInfo.js","sourceRoot":"","sources":["../../../../src/parser/youtube/AccountInfo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,kBAAkB,MAAM,kCAAkC,CAAC;AAMlE,MAAM,CAAC,OAAO,OAAO,WAAW;IACrB,KAAK,CAAkB;IAEhC,QAAQ,CAA4B;IAEpC,YAAY,QAAqB;QAC/B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEjD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ;YACtB,MAAM,IAAI,cAAc,CAAC,yBAAyB,CAAC,CAAC;QAEtD,MAAM,oBAAoB,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;QAEnF,IAAI,CAAC,oBAAoB;YACvB,MAAM,IAAI,cAAc,CAAC,gCAAgC,CAAC,CAAC;QAE7D,IAAI,CAAC,QAAQ,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import FilterableFeed from '../../core/mixins/FilterableFeed.js';
|
||||
import TabbedFeed from '../../core/mixins/TabbedFeed.js';
|
||||
import C4TabbedHeader from '../classes/C4TabbedHeader.js';
|
||||
import CarouselHeader from '../classes/CarouselHeader.js';
|
||||
import ChannelAboutFullMetadata from '../classes/ChannelAboutFullMetadata.js';
|
||||
import AboutChannel from '../classes/AboutChannel.js';
|
||||
import InteractiveTabbedHeader from '../classes/InteractiveTabbedHeader.js';
|
||||
import SubscribeButton from '../classes/SubscribeButton.js';
|
||||
import ExpandableTab from '../classes/ExpandableTab.js';
|
||||
import type Tab from '../classes/Tab.js';
|
||||
import PageHeader from '../classes/PageHeader.js';
|
||||
import ChipCloudChip from '../classes/ChipCloudChip.js';
|
||||
import type { AppendContinuationItemsAction, NavigateAction, ReloadContinuationItemsCommand, ShowMiniplayerCommand } from '../index.js';
|
||||
import type { ApiResponse, Actions } from '../../core/index.js';
|
||||
import type { IBrowseResponse } from '../types/index.js';
|
||||
import type OpenPopupAction from '../classes/actions/OpenPopupAction.js';
|
||||
export default class Channel extends TabbedFeed<IBrowseResponse> {
|
||||
header?: C4TabbedHeader | CarouselHeader | InteractiveTabbedHeader | PageHeader;
|
||||
metadata: {
|
||||
url_canonical?: string | undefined;
|
||||
title?: string | undefined;
|
||||
description?: string | undefined;
|
||||
thumbnail?: import("../misc.js").Thumbnail[] | undefined;
|
||||
site_name?: string | undefined;
|
||||
app_name?: string | undefined;
|
||||
android_package?: string | undefined;
|
||||
ios_app_store_id?: string | undefined;
|
||||
ios_app_arguments?: string | undefined;
|
||||
og_type?: string | undefined;
|
||||
url_applinks_web?: string | undefined;
|
||||
url_applinks_ios?: string | undefined;
|
||||
url_applinks_android?: string | undefined;
|
||||
url_twitter_ios?: string | undefined;
|
||||
url_twitter_android?: string | undefined;
|
||||
twitter_card_type?: string | undefined;
|
||||
twitter_site_handle?: string | undefined;
|
||||
schema_dot_org_type?: string | undefined;
|
||||
noindex?: string | undefined;
|
||||
is_unlisted?: boolean | undefined;
|
||||
is_family_safe?: boolean | undefined;
|
||||
tags?: string[] | undefined;
|
||||
available_countries?: string[] | undefined;
|
||||
type?: string | undefined;
|
||||
url?: string | undefined;
|
||||
rss_url?: string | undefined;
|
||||
vanity_channel_url?: string | undefined;
|
||||
external_id?: string | undefined;
|
||||
keywords?: string[] | undefined;
|
||||
avatar?: import("../misc.js").Thumbnail[] | undefined;
|
||||
music_artist_name?: string;
|
||||
android_deep_link?: string | undefined;
|
||||
android_appindexing_link?: string | undefined;
|
||||
ios_appindexing_link?: string | undefined;
|
||||
};
|
||||
subscribe_button?: SubscribeButton;
|
||||
current_tab?: Tab | ExpandableTab;
|
||||
constructor(actions: Actions, data: ApiResponse | IBrowseResponse, already_parsed?: boolean);
|
||||
/**
|
||||
* Applies given filter to the list. Use {@link filters} to get available filters.
|
||||
* @param filter - The filter to apply
|
||||
*/
|
||||
applyFilter(filter: string | ChipCloudChip): Promise<FilteredChannelList>;
|
||||
/**
|
||||
* Applies given sort filter to the list. Use {@link sort_filters} to get available filters.
|
||||
* @param sort - The sort filter to apply
|
||||
*/
|
||||
applySort(sort: string): Promise<Channel>;
|
||||
/**
|
||||
* Applies given content type filter to the list. Use {@link content_type_filters} to get available filters.
|
||||
* @param content_type_filter - The content type filter to apply
|
||||
*/
|
||||
applyContentTypeFilter(content_type_filter: string): Promise<Channel>;
|
||||
get filters(): string[];
|
||||
get sort_filters(): string[];
|
||||
get content_type_filters(): string[];
|
||||
getHome(): Promise<Channel>;
|
||||
getVideos(): Promise<Channel>;
|
||||
getShorts(): Promise<Channel>;
|
||||
getLiveStreams(): Promise<Channel>;
|
||||
getReleases(): Promise<Channel>;
|
||||
getPodcasts(): Promise<Channel>;
|
||||
getCourses(): Promise<Channel>;
|
||||
getPlaylists(): Promise<Channel>;
|
||||
getCommunity(): Promise<Channel>;
|
||||
/**
|
||||
* Retrieves the about page.
|
||||
* Note that this does not return a new {@link Channel} object.
|
||||
*/
|
||||
getAbout(): Promise<ChannelAboutFullMetadata | AboutChannel>;
|
||||
/**
|
||||
* Searches within the channel.
|
||||
*/
|
||||
search(query: string): Promise<Channel>;
|
||||
get has_home(): boolean;
|
||||
get has_videos(): boolean;
|
||||
get has_shorts(): boolean;
|
||||
get has_live_streams(): boolean;
|
||||
get has_releases(): boolean;
|
||||
get has_podcasts(): boolean;
|
||||
get has_courses(): boolean;
|
||||
get has_playlists(): boolean;
|
||||
get has_community(): boolean;
|
||||
get has_about(): boolean;
|
||||
get has_search(): boolean;
|
||||
getContinuation(): Promise<ChannelListContinuation>;
|
||||
}
|
||||
export declare class ChannelListContinuation extends Feed<IBrowseResponse> {
|
||||
contents?: AppendContinuationItemsAction | OpenPopupAction | NavigateAction | ShowMiniplayerCommand | ReloadContinuationItemsCommand;
|
||||
constructor(actions: Actions, data: ApiResponse | IBrowseResponse, already_parsed?: boolean);
|
||||
getContinuation(): Promise<ChannelListContinuation>;
|
||||
}
|
||||
export declare class FilteredChannelList extends FilterableFeed<IBrowseResponse> {
|
||||
applied_filter?: ChipCloudChip;
|
||||
contents?: AppendContinuationItemsAction | OpenPopupAction | NavigateAction | ShowMiniplayerCommand | ReloadContinuationItemsCommand;
|
||||
constructor(actions: Actions, data: ApiResponse | IBrowseResponse, already_parsed?: boolean);
|
||||
/**
|
||||
* Applies given filter to the list.
|
||||
* @param filter - The filter to apply
|
||||
*/
|
||||
applyFilter(filter: string | ChipCloudChip): Promise<FilteredChannelList>;
|
||||
getContinuation(): Promise<FilteredChannelList>;
|
||||
}
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import FilterableFeed from '../../core/mixins/FilterableFeed.js';
|
||||
import { ChannelError, InnertubeError } from '../../utils/Utils.js';
|
||||
import TabbedFeed from '../../core/mixins/TabbedFeed.js';
|
||||
import C4TabbedHeader from '../classes/C4TabbedHeader.js';
|
||||
import CarouselHeader from '../classes/CarouselHeader.js';
|
||||
import ChannelAboutFullMetadata from '../classes/ChannelAboutFullMetadata.js';
|
||||
import AboutChannel from '../classes/AboutChannel.js';
|
||||
import ChannelMetadata from '../classes/ChannelMetadata.js';
|
||||
import InteractiveTabbedHeader from '../classes/InteractiveTabbedHeader.js';
|
||||
import MicroformatData from '../classes/MicroformatData.js';
|
||||
import SubscribeButton from '../classes/SubscribeButton.js';
|
||||
import ExpandableTab from '../classes/ExpandableTab.js';
|
||||
import SectionList from '../classes/SectionList.js';
|
||||
import PageHeader from '../classes/PageHeader.js';
|
||||
import TwoColumnBrowseResults from '../classes/TwoColumnBrowseResults.js';
|
||||
import ChipCloudChip from '../classes/ChipCloudChip.js';
|
||||
import FeedFilterChipBar from '../classes/FeedFilterChipBar.js';
|
||||
import ChannelSubMenu from '../classes/ChannelSubMenu.js';
|
||||
import SortFilterSubMenu from '../classes/SortFilterSubMenu.js';
|
||||
import ContinuationItem from '../classes/ContinuationItem.js';
|
||||
import NavigationEndpoint from '../classes/NavigationEndpoint.js';
|
||||
export default class Channel extends TabbedFeed {
|
||||
header;
|
||||
metadata;
|
||||
subscribe_button;
|
||||
current_tab;
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
super(actions, data, already_parsed);
|
||||
this.header = this.page.header?.item()?.as(C4TabbedHeader, CarouselHeader, InteractiveTabbedHeader, PageHeader);
|
||||
const metadata = this.page.metadata?.item().as(ChannelMetadata);
|
||||
const microformat = this.page.microformat?.as(MicroformatData);
|
||||
if (this.page.alerts) {
|
||||
const alert = this.page.alerts[0];
|
||||
if (alert?.alert_type === 'ERROR') {
|
||||
throw new ChannelError(alert.text.toString());
|
||||
}
|
||||
}
|
||||
if (!metadata && !this.page.contents)
|
||||
throw new InnertubeError('Invalid channel', this);
|
||||
this.metadata = { ...metadata, ...(microformat || {}) };
|
||||
this.subscribe_button = this.page.header_memo?.getType(SubscribeButton)[0];
|
||||
if (this.page.contents)
|
||||
this.current_tab = this.page.contents.item().as(TwoColumnBrowseResults).tabs.find((tab) => tab.selected);
|
||||
}
|
||||
/**
|
||||
* Applies given filter to the list. Use {@link filters} to get available filters.
|
||||
* @param filter - The filter to apply
|
||||
*/
|
||||
async applyFilter(filter) {
|
||||
let target_filter;
|
||||
const filter_chipbar = this.memo.getType(FeedFilterChipBar)[0];
|
||||
if (typeof filter === 'string') {
|
||||
target_filter = filter_chipbar?.contents.find((chip) => chip.text === filter);
|
||||
if (!target_filter)
|
||||
throw new InnertubeError(`Filter ${filter} not found`, { available_filters: this.filters });
|
||||
}
|
||||
else {
|
||||
target_filter = filter;
|
||||
}
|
||||
if (!target_filter.endpoint)
|
||||
throw new InnertubeError('Invalid filter', filter);
|
||||
const page = await target_filter.endpoint.call(this.actions, { parse: true });
|
||||
if (!page)
|
||||
throw new InnertubeError('No page returned', { filter: target_filter });
|
||||
return new FilteredChannelList(this.actions, page, true);
|
||||
}
|
||||
/**
|
||||
* Applies given sort filter to the list. Use {@link sort_filters} to get available filters.
|
||||
* @param sort - The sort filter to apply
|
||||
*/
|
||||
async applySort(sort) {
|
||||
const sort_filter_sub_menu = this.memo.getType(SortFilterSubMenu)[0];
|
||||
if (!sort_filter_sub_menu || !sort_filter_sub_menu.sub_menu_items)
|
||||
throw new InnertubeError('No sort filter sub menu found');
|
||||
const target_sort = sort_filter_sub_menu.sub_menu_items.find((item) => item.title === sort);
|
||||
if (!target_sort)
|
||||
throw new InnertubeError(`Sort filter ${sort} not found`, { available_sort_filters: this.sort_filters });
|
||||
if (target_sort.selected)
|
||||
return this;
|
||||
const page = await target_sort.endpoint.call(this.actions, { parse: true });
|
||||
return new Channel(this.actions, page, true);
|
||||
}
|
||||
/**
|
||||
* Applies given content type filter to the list. Use {@link content_type_filters} to get available filters.
|
||||
* @param content_type_filter - The content type filter to apply
|
||||
*/
|
||||
async applyContentTypeFilter(content_type_filter) {
|
||||
const sub_menu = this.current_tab?.content?.as(SectionList).sub_menu?.as(ChannelSubMenu);
|
||||
if (!sub_menu)
|
||||
throw new InnertubeError('Sub menu not found');
|
||||
const item = sub_menu.content_type_sub_menu_items.find((item) => item.title === content_type_filter);
|
||||
if (!item)
|
||||
throw new InnertubeError(`Sub menu item ${content_type_filter} not found`, { available_filters: this.content_type_filters });
|
||||
if (item.selected)
|
||||
return this;
|
||||
const page = await item.endpoint.call(this.actions, { parse: true });
|
||||
return new Channel(this.actions, page, true);
|
||||
}
|
||||
get filters() {
|
||||
return this.memo.getType(FeedFilterChipBar)?.[0]?.contents.filterType(ChipCloudChip).map((chip) => chip.text) || [];
|
||||
}
|
||||
get sort_filters() {
|
||||
const sort_filter_sub_menu = this.memo.getType(SortFilterSubMenu)[0];
|
||||
return sort_filter_sub_menu?.sub_menu_items?.map((item) => item.title) || [];
|
||||
}
|
||||
get content_type_filters() {
|
||||
const sub_menu = this.current_tab?.content?.as(SectionList).sub_menu?.as(ChannelSubMenu);
|
||||
return sub_menu?.content_type_sub_menu_items.map((item) => item.title) || [];
|
||||
}
|
||||
async getHome() {
|
||||
const tab = await this.getTabByURL('featured');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
async getVideos() {
|
||||
const tab = await this.getTabByURL('videos');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
async getShorts() {
|
||||
const tab = await this.getTabByURL('shorts');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
async getLiveStreams() {
|
||||
const tab = await this.getTabByURL('streams');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
async getReleases() {
|
||||
const tab = await this.getTabByURL('releases');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
async getPodcasts() {
|
||||
const tab = await this.getTabByURL('podcasts');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
async getCourses() {
|
||||
const tab = await this.getTabByURL('courses');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
async getPlaylists() {
|
||||
const tab = await this.getTabByURL('playlists');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
async getCommunity() {
|
||||
const tab = await this.getTabByURL('posts');
|
||||
return new Channel(this.actions, tab.page, true);
|
||||
}
|
||||
/**
|
||||
* Retrieves the about page.
|
||||
* Note that this does not return a new {@link Channel} object.
|
||||
*/
|
||||
async getAbout() {
|
||||
if (this.hasTabWithURL('about')) {
|
||||
const tab = await this.getTabByURL('about');
|
||||
return tab.memo.getType(ChannelAboutFullMetadata)[0];
|
||||
}
|
||||
const tagline = this.header?.is(C4TabbedHeader) && this.header.tagline;
|
||||
if (tagline || this.header?.is(PageHeader) && this.header.content?.description) {
|
||||
if (tagline && tagline.more_endpoint instanceof NavigationEndpoint) {
|
||||
const response = await tagline.more_endpoint.call(this.actions);
|
||||
const tab = new TabbedFeed(this.actions, response, false);
|
||||
return tab.memo.getType(ChannelAboutFullMetadata)[0];
|
||||
}
|
||||
const endpoint = this.page.header_memo?.getType(ContinuationItem)[0]?.endpoint;
|
||||
if (!endpoint) {
|
||||
throw new InnertubeError('Failed to extract continuation to get channel about');
|
||||
}
|
||||
const response = await endpoint.call(this.actions, { parse: true });
|
||||
if (!response.on_response_received_endpoints_memo) {
|
||||
throw new InnertubeError('Unexpected response while fetching channel about', { response });
|
||||
}
|
||||
return response.on_response_received_endpoints_memo.getType(AboutChannel)[0];
|
||||
}
|
||||
throw new InnertubeError('About not found');
|
||||
}
|
||||
/**
|
||||
* Searches within the channel.
|
||||
*/
|
||||
async search(query) {
|
||||
const tab = this.memo.getType(ExpandableTab)?.[0];
|
||||
if (!tab)
|
||||
throw new InnertubeError('Search tab not found', this);
|
||||
const page = await tab.endpoint.call(this.actions, { query, parse: true });
|
||||
return new Channel(this.actions, page, true);
|
||||
}
|
||||
get has_home() {
|
||||
return this.hasTabWithURL('featured');
|
||||
}
|
||||
get has_videos() {
|
||||
return this.hasTabWithURL('videos');
|
||||
}
|
||||
get has_shorts() {
|
||||
return this.hasTabWithURL('shorts');
|
||||
}
|
||||
get has_live_streams() {
|
||||
return this.hasTabWithURL('streams');
|
||||
}
|
||||
get has_releases() {
|
||||
return this.hasTabWithURL('releases');
|
||||
}
|
||||
get has_podcasts() {
|
||||
return this.hasTabWithURL('podcasts');
|
||||
}
|
||||
get has_courses() {
|
||||
return this.hasTabWithURL('courses');
|
||||
}
|
||||
get has_playlists() {
|
||||
return this.hasTabWithURL('playlists');
|
||||
}
|
||||
get has_community() {
|
||||
return this.hasTabWithURL('posts');
|
||||
}
|
||||
get has_about() {
|
||||
// Game topic channels still have an about tab, user channels have switched to the popup
|
||||
return this.hasTabWithURL('about') ||
|
||||
!!(this.header?.is(C4TabbedHeader) && this.header.tagline?.more_endpoint) ||
|
||||
!!(this.header?.is(PageHeader) && this.header.content?.description?.more_endpoint);
|
||||
}
|
||||
get has_search() {
|
||||
return this.memo.getType(ExpandableTab)?.length > 0;
|
||||
}
|
||||
async getContinuation() {
|
||||
const page = await super.getContinuationData();
|
||||
if (!page)
|
||||
throw new InnertubeError('Could not get continuation data');
|
||||
return new ChannelListContinuation(this.actions, page, true);
|
||||
}
|
||||
}
|
||||
export class ChannelListContinuation extends Feed {
|
||||
contents;
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
super(actions, data, already_parsed);
|
||||
this.contents =
|
||||
this.page.on_response_received_actions?.[0] ||
|
||||
this.page.on_response_received_endpoints?.[0];
|
||||
}
|
||||
async getContinuation() {
|
||||
const page = await super.getContinuationData();
|
||||
if (!page)
|
||||
throw new InnertubeError('Could not get continuation data');
|
||||
return new ChannelListContinuation(this.actions, page, true);
|
||||
}
|
||||
}
|
||||
export class FilteredChannelList extends FilterableFeed {
|
||||
applied_filter;
|
||||
contents;
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
super(actions, data, already_parsed);
|
||||
this.applied_filter = this.memo.getType(ChipCloudChip).find((chip) => chip.is_selected);
|
||||
// Removes the filter chipbar from the actions list
|
||||
if (this.page.on_response_received_actions &&
|
||||
this.page.on_response_received_actions.length > 1) {
|
||||
this.page.on_response_received_actions.shift();
|
||||
}
|
||||
this.contents = this.page.on_response_received_actions?.[0];
|
||||
}
|
||||
/**
|
||||
* Applies given filter to the list.
|
||||
* @param filter - The filter to apply
|
||||
*/
|
||||
async applyFilter(filter) {
|
||||
const feed = await super.getFilteredFeed(filter);
|
||||
return new FilteredChannelList(this.actions, feed.page, true);
|
||||
}
|
||||
async getContinuation() {
|
||||
const page = await super.getContinuationData();
|
||||
if (!page?.on_response_received_actions_memo)
|
||||
throw new InnertubeError('Unexpected continuation data', page);
|
||||
// Keep the filters
|
||||
page.on_response_received_actions_memo.set('FeedFilterChipBar', this.memo.getType(FeedFilterChipBar));
|
||||
page.on_response_received_actions_memo.set('ChipCloudChip', this.memo.getType(ChipCloudChip));
|
||||
return new FilteredChannelList(this.actions, page, true);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Channel.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+27
@@ -0,0 +1,27 @@
|
||||
import type { ObservedArray } from '../helpers.js';
|
||||
import CommentsHeader from '../classes/comments/CommentsHeader.js';
|
||||
import CommentThread from '../classes/comments/CommentThread.js';
|
||||
import type { Actions, ApiResponse } from '../../core/index.js';
|
||||
import type { INextResponse } from '../types/index.js';
|
||||
export default class Comments {
|
||||
#private;
|
||||
header?: CommentsHeader;
|
||||
contents: ObservedArray<CommentThread>;
|
||||
constructor(actions: Actions, data: any, already_parsed?: boolean);
|
||||
/**
|
||||
* Applies given sort option to the comments.
|
||||
* @param sort - Sort type.
|
||||
*/
|
||||
applySort(sort: 'TOP_COMMENTS' | 'NEWEST_FIRST'): Promise<Comments>;
|
||||
/**
|
||||
* Creates a top-level comment.
|
||||
* @param text - Comment text.
|
||||
*/
|
||||
createComment(text: string): Promise<ApiResponse>;
|
||||
/**
|
||||
* Retrieves next batch of comments.
|
||||
*/
|
||||
getContinuation(): Promise<Comments>;
|
||||
get has_continuation(): boolean;
|
||||
get page(): INextResponse;
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
import { Parser } from '../index.js';
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import { observe } from '../helpers.js';
|
||||
import CommentsHeader from '../classes/comments/CommentsHeader.js';
|
||||
import CommentSimplebox from '../classes/comments/CommentSimplebox.js';
|
||||
import CommentThread from '../classes/comments/CommentThread.js';
|
||||
import ContinuationItem from '../classes/ContinuationItem.js';
|
||||
import { ReloadContinuationItemsCommand } from '../index.js';
|
||||
import AppendContinuationItemsAction from '../classes/actions/AppendContinuationItemsAction.js';
|
||||
export default class Comments {
|
||||
#page;
|
||||
#actions;
|
||||
#continuation;
|
||||
header;
|
||||
contents;
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
this.#page = already_parsed ? data : Parser.parseResponse(data);
|
||||
this.#actions = actions;
|
||||
const contents = this.#page.on_response_received_endpoints;
|
||||
if (!contents)
|
||||
throw new InnertubeError('Comments page did not have any content.');
|
||||
const header_node = contents.at(0)?.as(AppendContinuationItemsAction, ReloadContinuationItemsCommand);
|
||||
const body_node = contents.at(1)?.as(AppendContinuationItemsAction, ReloadContinuationItemsCommand);
|
||||
this.header = header_node?.contents?.firstOfType(CommentsHeader);
|
||||
const threads = body_node?.contents?.filterType(CommentThread) || [];
|
||||
this.contents = observe(threads.map((thread) => {
|
||||
if (thread.comment)
|
||||
thread.comment.setActions(this.#actions);
|
||||
thread.setActions(this.#actions);
|
||||
return thread;
|
||||
}));
|
||||
this.#continuation = body_node?.contents?.firstOfType(ContinuationItem);
|
||||
}
|
||||
/**
|
||||
* Applies given sort option to the comments.
|
||||
* @param sort - Sort type.
|
||||
*/
|
||||
async applySort(sort) {
|
||||
if (!this.header)
|
||||
throw new InnertubeError('Page header is missing. Cannot apply sort option.');
|
||||
let button;
|
||||
if (sort === 'TOP_COMMENTS') {
|
||||
button = this.header.sort_menu?.sub_menu_items?.at(0);
|
||||
}
|
||||
else if (sort === 'NEWEST_FIRST') {
|
||||
button = this.header.sort_menu?.sub_menu_items?.at(1);
|
||||
}
|
||||
if (!button)
|
||||
throw new InnertubeError('Could not find target button.');
|
||||
if (button.selected)
|
||||
return this;
|
||||
const response = await button.endpoint.call(this.#actions, { parse: true });
|
||||
return new Comments(this.#actions, response, true);
|
||||
}
|
||||
/**
|
||||
* Creates a top-level comment.
|
||||
* @param text - Comment text.
|
||||
*/
|
||||
async createComment(text) {
|
||||
if (!this.header)
|
||||
throw new InnertubeError('Page header is missing. Cannot create comment.');
|
||||
const button = this.header.create_renderer?.as(CommentSimplebox).submit_button;
|
||||
if (!button)
|
||||
throw new InnertubeError('Could not find target button. You are probably not logged in.');
|
||||
if (!button.endpoint)
|
||||
throw new InnertubeError('Button does not have an endpoint.');
|
||||
return await button.endpoint.call(this.#actions, { commentText: text });
|
||||
}
|
||||
/**
|
||||
* Retrieves next batch of comments.
|
||||
*/
|
||||
async getContinuation() {
|
||||
if (!this.#continuation)
|
||||
throw new InnertubeError('Continuation not found');
|
||||
const data = await this.#continuation.endpoint.call(this.#actions, { parse: true });
|
||||
// Copy the previous page so we can keep the header.
|
||||
const page = Object.assign({}, this.#page);
|
||||
if (!page.on_response_received_endpoints || !data.on_response_received_endpoints)
|
||||
throw new InnertubeError('Invalid reponse format, missing on_response_received_endpoints.');
|
||||
// Remove previous items and append the continuation.
|
||||
page.on_response_received_endpoints.pop();
|
||||
page.on_response_received_endpoints.push(data.on_response_received_endpoints[0]);
|
||||
return new Comments(this.#actions, page, true);
|
||||
}
|
||||
get has_continuation() {
|
||||
return !!this.#continuation;
|
||||
}
|
||||
get page() {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Comments.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Comments.js","sourceRoot":"","sources":["../../../../src/parser/youtube/Comments.ts"],"names":[],"mappings":"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"}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import GuideSection from '../classes/GuideSection.js';
|
||||
import GuideSubscriptionsSection from '../classes/GuideSubscriptionsSection.js';
|
||||
import type { ObservedArray } from '../helpers.js';
|
||||
import type { IGuideResponse } from '../types/index.js';
|
||||
import type { IRawResponse } from '../index.js';
|
||||
export default class Guide {
|
||||
#private;
|
||||
contents?: ObservedArray<GuideSection | GuideSubscriptionsSection>;
|
||||
constructor(data: IRawResponse);
|
||||
get page(): IGuideResponse;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { Parser } from '../index.js';
|
||||
import GuideSection from '../classes/GuideSection.js';
|
||||
import GuideSubscriptionsSection from '../classes/GuideSubscriptionsSection.js';
|
||||
export default class Guide {
|
||||
#page;
|
||||
contents;
|
||||
constructor(data) {
|
||||
this.#page = Parser.parseResponse(data);
|
||||
if (this.#page.items)
|
||||
this.contents = this.#page.items.array().as(GuideSection, GuideSubscriptionsSection);
|
||||
}
|
||||
get page() {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Guide.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Guide.js","sourceRoot":"","sources":["../../../../src/parser/youtube/Guide.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,yBAAyB,MAAM,yCAAyC,CAAC;AAMhF,MAAM,CAAC,OAAO,OAAO,KAAK;IACf,KAAK,CAAiB;IACxB,QAAQ,CAA2D;IAE1E,YAAY,IAAkB;QAC5B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,aAAa,CAAiB,IAAI,CAAC,CAAC;QACxD,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK;YAClB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,YAAY,EAAE,yBAAyB,CAAC,CAAC;IACzF,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import FilterableFeed from '../../core/mixins/FilterableFeed.js';
|
||||
import HashtagHeader from '../classes/HashtagHeader.js';
|
||||
import RichGrid from '../classes/RichGrid.js';
|
||||
import PageHeader from '../classes/PageHeader.js';
|
||||
import type { Actions, ApiResponse } from '../../core/index.js';
|
||||
import type { IBrowseResponse } from '../index.js';
|
||||
import type ChipCloudChip from '../classes/ChipCloudChip.js';
|
||||
export default class HashtagFeed extends FilterableFeed<IBrowseResponse> {
|
||||
header?: HashtagHeader | PageHeader;
|
||||
contents: RichGrid;
|
||||
constructor(actions: Actions, response: IBrowseResponse | ApiResponse);
|
||||
/**
|
||||
* Applies given filter and returns a new {@link HashtagFeed} object. Use {@link HashtagFeed.filters} to get available filters.
|
||||
* @param filter - Filter to apply.
|
||||
*/
|
||||
applyFilter(filter: string | ChipCloudChip): Promise<HashtagFeed>;
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import FilterableFeed from '../../core/mixins/FilterableFeed.js';
|
||||
import HashtagHeader from '../classes/HashtagHeader.js';
|
||||
import RichGrid from '../classes/RichGrid.js';
|
||||
import PageHeader from '../classes/PageHeader.js';
|
||||
import Tab from '../classes/Tab.js';
|
||||
export default class HashtagFeed extends FilterableFeed {
|
||||
header;
|
||||
contents;
|
||||
constructor(actions, response) {
|
||||
super(actions, response);
|
||||
if (!this.page.contents_memo)
|
||||
throw new InnertubeError('Unexpected response', this.page);
|
||||
const tab = this.page.contents_memo.getType(Tab)[0];
|
||||
if (!tab.content)
|
||||
throw new InnertubeError('Content tab has no content', tab);
|
||||
if (this.page.header) {
|
||||
this.header = this.page.header.item().as(HashtagHeader, PageHeader);
|
||||
}
|
||||
this.contents = tab.content.as(RichGrid);
|
||||
}
|
||||
/**
|
||||
* Applies given filter and returns a new {@link HashtagFeed} object. Use {@link HashtagFeed.filters} to get available filters.
|
||||
* @param filter - Filter to apply.
|
||||
*/
|
||||
async applyFilter(filter) {
|
||||
const response = await super.getFilteredFeed(filter);
|
||||
return new HashtagFeed(this.actions, response.page);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=HashtagFeed.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"HashtagFeed.js","sourceRoot":"","sources":["../../../../src/parser/youtube/HashtagFeed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,cAAc,MAAM,qCAAqC,CAAC;AACjE,OAAO,aAAa,MAAM,6BAA6B,CAAC;AACxD,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,GAAG,MAAM,mBAAmB,CAAC;AAMpC,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,cAA+B;IAC/D,MAAM,CAA8B;IACpC,QAAQ,CAAW;IAE1B,YAAY,OAAgB,EAAE,QAAuC;QACnE,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAEzB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa;YAC1B,MAAM,IAAI,cAAc,CAAC,qBAAqB,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAE7D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAEpD,IAAI,CAAC,GAAG,CAAC,OAAO;YACd,MAAM,IAAI,cAAc,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;QAE9D,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,MAA8B;QAC9C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACrD,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC;CACF"}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import ItemSection from '../classes/ItemSection.js';
|
||||
import BrowseFeedActions from '../classes/BrowseFeedActions.js';
|
||||
import type { Actions, ApiResponse } from '../../core/index.js';
|
||||
import type { IBrowseResponse } from '../types/index.js';
|
||||
export default class History extends Feed<IBrowseResponse> {
|
||||
sections: ItemSection[];
|
||||
feed_actions: BrowseFeedActions;
|
||||
constructor(actions: Actions, data: ApiResponse | IBrowseResponse, already_parsed?: boolean);
|
||||
/**
|
||||
* Retrieves next batch of contents.
|
||||
*/
|
||||
getContinuation(): Promise<History>;
|
||||
/**
|
||||
* Removes a video from watch history.
|
||||
*/
|
||||
removeVideo(video_id: string, pages_to_load?: number): Promise<boolean>;
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import ItemSection from '../classes/ItemSection.js';
|
||||
import BrowseFeedActions from '../classes/BrowseFeedActions.js';
|
||||
import Button from '../classes/Button.js';
|
||||
import Video from '../classes/Video.js';
|
||||
import LockupView from '../classes/LockupView.js';
|
||||
// TODO: make feed actions usable
|
||||
export default class History extends Feed {
|
||||
sections;
|
||||
feed_actions;
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
super(actions, data, already_parsed);
|
||||
this.sections = this.memo.getType(ItemSection);
|
||||
this.feed_actions = this.memo.getType(BrowseFeedActions)[0];
|
||||
}
|
||||
/**
|
||||
* Retrieves next batch of contents.
|
||||
*/
|
||||
async getContinuation() {
|
||||
const response = await this.getContinuationData();
|
||||
if (!response)
|
||||
throw new Error('No continuation data found');
|
||||
return new History(this.actions, response, true);
|
||||
}
|
||||
/**
|
||||
* Removes a video from watch history.
|
||||
*/
|
||||
async removeVideo(video_id, pages_to_load = 1) {
|
||||
let pagesToLoad = pages_to_load;
|
||||
while (pagesToLoad > 0) {
|
||||
let feedbackToken;
|
||||
for (const section of this.sections) {
|
||||
for (const content of section.contents) {
|
||||
if (content.is(Video)) {
|
||||
if (content.video_id === video_id && content.menu) {
|
||||
feedbackToken = content.menu.top_level_buttons[0].as(Button).endpoint.payload.feedbackToken;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (content.is(LockupView)) {
|
||||
if (content.content_id === video_id) {
|
||||
const listItems = content.metadata?.menu_button?.on_tap?.payload.panelLoadingStrategy.inlineContent.sheetViewModel.content.listViewModel.listItems;
|
||||
const listItem = listItems.find((video) => video.listItemViewModel?.title.content === 'Remove from watch history');
|
||||
feedbackToken = listItem.listItemViewModel.rendererContext.commandContext.onTap.innertubeCommand.feedbackEndpoint.feedbackToken;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (feedbackToken) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (feedbackToken) {
|
||||
const body = { feedbackTokens: [feedbackToken] };
|
||||
const response = await this.actions.execute('/feedback', body);
|
||||
const data = response.data;
|
||||
if (!data.feedbackResponses[0].isProcessed) {
|
||||
throw new Error('Failed to remove video from watch history');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (--pagesToLoad > 0) {
|
||||
try {
|
||||
Object.assign(this, await this.getContinuation());
|
||||
}
|
||||
catch {
|
||||
throw new Error('Unable to find video in watch history');
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error('Unable to find video in watch history');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=History.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"History.js","sourceRoot":"","sources":["../../../../src/parser/youtube/History.ts"],"names":[],"mappings":"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"}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import FilterableFeed from '../../core/mixins/FilterableFeed.js';
|
||||
import FeedTabbedHeader from '../classes/FeedTabbedHeader.js';
|
||||
import RichGrid from '../classes/RichGrid.js';
|
||||
import type { IBrowseResponse } from '../types/index.js';
|
||||
import type { AppendContinuationItemsAction, ReloadContinuationItemsCommand } from '../index.js';
|
||||
import type { ApiResponse, Actions } from '../../core/index.js';
|
||||
import type ChipCloudChip from '../classes/ChipCloudChip.js';
|
||||
export default class HomeFeed extends FilterableFeed<IBrowseResponse> {
|
||||
contents?: RichGrid | AppendContinuationItemsAction | ReloadContinuationItemsCommand;
|
||||
header?: FeedTabbedHeader;
|
||||
constructor(actions: Actions, data: ApiResponse | IBrowseResponse, already_parsed?: boolean);
|
||||
/**
|
||||
* Applies given filter to the feed. Use {@link filters} to get available filters.
|
||||
* @param filter - Filter to apply.
|
||||
*/
|
||||
applyFilter(filter: string | ChipCloudChip): Promise<HomeFeed>;
|
||||
/**
|
||||
* Retrieves next batch of contents.
|
||||
*/
|
||||
getContinuation(): Promise<HomeFeed>;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import FilterableFeed from '../../core/mixins/FilterableFeed.js';
|
||||
import FeedTabbedHeader from '../classes/FeedTabbedHeader.js';
|
||||
import RichGrid from '../classes/RichGrid.js';
|
||||
export default class HomeFeed extends FilterableFeed {
|
||||
contents;
|
||||
header;
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
super(actions, data, already_parsed);
|
||||
this.header = this.memo.getType(FeedTabbedHeader)[0];
|
||||
this.contents = this.memo.getType(RichGrid)[0] || this.page.on_response_received_actions?.[0];
|
||||
}
|
||||
/**
|
||||
* Applies given filter to the feed. Use {@link filters} to get available filters.
|
||||
* @param filter - Filter to apply.
|
||||
*/
|
||||
async applyFilter(filter) {
|
||||
const feed = await super.getFilteredFeed(filter);
|
||||
return new HomeFeed(this.actions, feed.page, true);
|
||||
}
|
||||
/**
|
||||
* Retrieves next batch of contents.
|
||||
*/
|
||||
async getContinuation() {
|
||||
const feed = await super.getContinuation();
|
||||
// Keep the page header
|
||||
feed.page.header = this.page.header;
|
||||
if (this.header)
|
||||
feed.page.header_memo?.set(this.header.type, [this.header]);
|
||||
return new HomeFeed(this.actions, feed.page, true);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=HomeFeed.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"HomeFeed.js","sourceRoot":"","sources":["../../../../src/parser/youtube/HomeFeed.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,qCAAqC,CAAC;AACjE,OAAO,gBAAgB,MAAM,gCAAgC,CAAC;AAC9D,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAO9C,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,cAA+B;IAC5D,QAAQ,CAA6E;IACrF,MAAM,CAAoB;IAEjC,YAAY,OAAgB,EAAE,IAAmC,EAAE,cAAc,GAAG,KAAK;QACvF,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC,CAAC,CAAC,CAAC;IAChG,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,MAA8B;QAC9C,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACjD,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE,CAAC;QAE3C,uBAAuB;QACvB,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QAEpC,IAAI,IAAI,CAAC,MAAM;YACb,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAE,IAAI,CAAC,MAAM,CAAE,CAAC,CAAC;QAEhE,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC;CACF"}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import Button from '../classes/Button.js';
|
||||
import type { Actions } from '../../core/index.js';
|
||||
import type { ObservedArray, YTNode } from '../helpers.js';
|
||||
import type { IParsedResponse } from '../types/index.js';
|
||||
export default class ItemMenu {
|
||||
#private;
|
||||
constructor(data: IParsedResponse, actions: Actions);
|
||||
selectItem(icon_type: string): Promise<IParsedResponse>;
|
||||
selectItem(button: Button): Promise<IParsedResponse>;
|
||||
items(): ObservedArray<YTNode>;
|
||||
page(): IParsedResponse;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import Menu from '../classes/menus/Menu.js';
|
||||
import Button from '../classes/Button.js';
|
||||
import MenuServiceItem from '../classes/menus/MenuServiceItem.js';
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
export default class ItemMenu {
|
||||
#page;
|
||||
#actions;
|
||||
#items;
|
||||
constructor(data, actions) {
|
||||
this.#page = data;
|
||||
this.#actions = actions;
|
||||
const menu = data?.live_chat_item_context_menu_supported_renderers;
|
||||
if (!menu || !menu.is(Menu))
|
||||
throw new InnertubeError('Response did not have a "live_chat_item_context_menu_supported_renderers" property. The call may have failed.');
|
||||
this.#items = menu.as(Menu).items;
|
||||
}
|
||||
async selectItem(item) {
|
||||
let endpoint;
|
||||
if (item instanceof Button) {
|
||||
if (!item.endpoint)
|
||||
throw new InnertubeError('Item does not have an endpoint.');
|
||||
endpoint = item.endpoint;
|
||||
}
|
||||
else {
|
||||
const button = this.#items.find((button) => {
|
||||
if (!button.is(MenuServiceItem)) {
|
||||
return false;
|
||||
}
|
||||
const menuServiceItem = button.as(MenuServiceItem);
|
||||
return menuServiceItem.icon_type === item;
|
||||
});
|
||||
if (!button || !button.is(MenuServiceItem))
|
||||
throw new InnertubeError(`Button "${item}" not found.`);
|
||||
endpoint = button.endpoint;
|
||||
}
|
||||
if (!endpoint)
|
||||
throw new InnertubeError('Target button does not have an endpoint.');
|
||||
return await endpoint.call(this.#actions, { parse: true });
|
||||
}
|
||||
items() {
|
||||
return this.#items;
|
||||
}
|
||||
page() {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=ItemMenu.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ItemMenu.js","sourceRoot":"","sources":["../../../../src/parser/youtube/ItemMenu.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAC5C,OAAO,MAAM,MAAM,sBAAsB,CAAC;AAC1C,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAGlE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAKtD,MAAM,CAAC,OAAO,OAAO,QAAQ;IAClB,KAAK,CAAkB;IACvB,QAAQ,CAAU;IAClB,MAAM,CAAwB;IAEvC,YAAY,IAAqB,EAAE,OAAgB;QACjD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QAExB,MAAM,IAAI,GAAG,IAAI,EAAE,+CAA+C,CAAC;QAEnE,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;YACzB,MAAM,IAAI,cAAc,CAAC,+GAA+G,CAAC,CAAC;QAE5I,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;IACpC,CAAC;IAID,KAAK,CAAC,UAAU,CAAC,IAAqB;QACpC,IAAI,QAAwC,CAAC;QAE7C,IAAI,IAAI,YAAY,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAChB,MAAM,IAAI,cAAc,CAAC,iCAAiC,CAAC,CAAC;YAE9D,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBACzC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;oBAChC,OAAO,KAAK,CAAC;gBACf,CAAC;gBACD,MAAM,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC;gBACnD,OAAO,eAAe,CAAC,SAAS,KAAK,IAAI,CAAC;YAC5C,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC;gBACxC,MAAM,IAAI,cAAc,CAAC,WAAW,IAAI,cAAc,CAAC,CAAC;YAE1D,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC7B,CAAC;QAED,IAAI,CAAC,QAAQ;YACX,MAAM,IAAI,cAAc,CAAC,0CAA0C,CAAC,CAAC;QAEvE,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import History from './History.js';
|
||||
import Playlist from './Playlist.js';
|
||||
import PageHeader from '../classes/PageHeader.js';
|
||||
import type { Actions, ApiResponse } from '../../core/index.js';
|
||||
import type { IBrowseResponse } from '../types/index.js';
|
||||
export default class Library extends Feed<IBrowseResponse> {
|
||||
#private;
|
||||
header: PageHeader | null;
|
||||
sections: {
|
||||
type: string | undefined;
|
||||
title: import("../misc.js").Text;
|
||||
contents: any[];
|
||||
getAll: () => Promise<Feed<IBrowseResponse> | History | Playlist>;
|
||||
}[];
|
||||
constructor(actions: Actions, data: ApiResponse | IBrowseResponse);
|
||||
get history(): {
|
||||
type: string | undefined;
|
||||
title: import("../misc.js").Text;
|
||||
contents: any[];
|
||||
getAll: () => Promise<Feed<IBrowseResponse> | History | Playlist>;
|
||||
} | undefined;
|
||||
get watch_later(): {
|
||||
type: string | undefined;
|
||||
title: import("../misc.js").Text;
|
||||
contents: any[];
|
||||
getAll: () => Promise<Feed<IBrowseResponse> | History | Playlist>;
|
||||
} | undefined;
|
||||
get liked_videos(): {
|
||||
type: string | undefined;
|
||||
title: import("../misc.js").Text;
|
||||
contents: any[];
|
||||
getAll: () => Promise<Feed<IBrowseResponse> | History | Playlist>;
|
||||
} | undefined;
|
||||
get playlists_section(): {
|
||||
type: string | undefined;
|
||||
title: import("../misc.js").Text;
|
||||
contents: any[];
|
||||
getAll: () => Promise<Feed<IBrowseResponse> | History | Playlist>;
|
||||
} | undefined;
|
||||
get clips(): {
|
||||
type: string | undefined;
|
||||
title: import("../misc.js").Text;
|
||||
contents: any[];
|
||||
getAll: () => Promise<Feed<IBrowseResponse> | History | Playlist>;
|
||||
} | undefined;
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import History from './History.js';
|
||||
import Playlist from './Playlist.js';
|
||||
import Menu from '../classes/menus/Menu.js';
|
||||
import Shelf from '../classes/Shelf.js';
|
||||
import Button from '../classes/Button.js';
|
||||
import PageHeader from '../classes/PageHeader.js';
|
||||
export default class Library extends Feed {
|
||||
header;
|
||||
sections;
|
||||
constructor(actions, data) {
|
||||
super(actions, data);
|
||||
if (!this.page.contents_memo)
|
||||
throw new InnertubeError('Page contents not found');
|
||||
this.header = this.memo.getType(PageHeader)[0];
|
||||
const shelves = this.page.contents_memo.getType(Shelf);
|
||||
this.sections = shelves.map((shelf) => ({
|
||||
type: shelf.icon_type,
|
||||
title: shelf.title,
|
||||
contents: shelf.content?.key('items').array() || [],
|
||||
getAll: () => this.#getAll(shelf)
|
||||
}));
|
||||
}
|
||||
async #getAll(shelf) {
|
||||
if (!shelf.menu?.as(Menu).top_level_buttons)
|
||||
throw new InnertubeError(`The ${shelf.title.text} shelf doesn't have more items`);
|
||||
const button = shelf.menu.as(Menu).top_level_buttons.firstOfType(Button);
|
||||
if (!button)
|
||||
throw new InnertubeError('Did not find target button.');
|
||||
const page = await button.as(Button).endpoint.call(this.actions, { parse: true });
|
||||
switch (shelf.icon_type) {
|
||||
case 'LIKE':
|
||||
case 'WATCH_LATER':
|
||||
return new Playlist(this.actions, page, true);
|
||||
case 'WATCH_HISTORY':
|
||||
return new History(this.actions, page, true);
|
||||
case 'CONTENT_CUT':
|
||||
return new Feed(this.actions, page, true);
|
||||
default:
|
||||
throw new InnertubeError('Target shelf not implemented.');
|
||||
}
|
||||
}
|
||||
get history() {
|
||||
return this.sections.find((section) => section.type === 'WATCH_HISTORY');
|
||||
}
|
||||
get watch_later() {
|
||||
return this.sections.find((section) => section.type === 'WATCH_LATER');
|
||||
}
|
||||
get liked_videos() {
|
||||
return this.sections.find((section) => section.type === 'LIKE');
|
||||
}
|
||||
get playlists_section() {
|
||||
return this.sections.find((section) => section.type === 'PLAYLISTS');
|
||||
}
|
||||
get clips() {
|
||||
return this.sections.find((section) => section.type === 'CONTENT_CUT');
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Library.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Library.js","sourceRoot":"","sources":["../../../../src/parser/youtube/Library.ts"],"names":[],"mappings":"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"}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import { EventEmitter } from '../../utils/index.js';
|
||||
import { LiveChatContinuation } from '../index.js';
|
||||
import SmoothedQueue from './SmoothedQueue.js';
|
||||
import RunAttestationCommand from '../classes/commands/RunAttestationCommand.js';
|
||||
import AddChatItemAction from '../classes/livechat/AddChatItemAction.js';
|
||||
import UpdateDateTextAction from '../classes/livechat/UpdateDateTextAction.js';
|
||||
import UpdateDescriptionAction from '../classes/livechat/UpdateDescriptionAction.js';
|
||||
import UpdateTitleAction from '../classes/livechat/UpdateTitleAction.js';
|
||||
import UpdateToggleButtonTextAction from '../classes/livechat/UpdateToggleButtonTextAction.js';
|
||||
import UpdateViewershipAction from '../classes/livechat/UpdateViewershipAction.js';
|
||||
import ItemMenu from './ItemMenu.js';
|
||||
import type { ObservedArray } from '../helpers.js';
|
||||
import type VideoInfo from './VideoInfo.js';
|
||||
import type AddBannerToLiveChatCommand from '../classes/livechat/AddBannerToLiveChatCommand.js';
|
||||
import type RemoveBannerForLiveChatCommand from '../classes/livechat/RemoveBannerForLiveChatCommand.js';
|
||||
import type ShowLiveChatTooltipCommand from '../classes/livechat/ShowLiveChatTooltipCommand.js';
|
||||
import type LiveChatAutoModMessage from '../classes/livechat/items/LiveChatAutoModMessage.js';
|
||||
import type LiveChatMembershipItem from '../classes/livechat/items/LiveChatMembershipItem.js';
|
||||
import type LiveChatPaidMessage from '../classes/livechat/items/LiveChatPaidMessage.js';
|
||||
import type LiveChatPaidSticker from '../classes/livechat/items/LiveChatPaidSticker.js';
|
||||
import type LiveChatTextMessage from '../classes/livechat/items/LiveChatTextMessage.js';
|
||||
import type LiveChatViewerEngagementMessage from '../classes/livechat/items/LiveChatViewerEngagementMessage.js';
|
||||
import type AddLiveChatTickerItemAction from '../classes/livechat/AddLiveChatTickerItemAction.js';
|
||||
import type MarkChatItemAsDeletedAction from '../classes/livechat/MarkChatItemAsDeletedAction.js';
|
||||
import type MarkChatItemsByAuthorAsDeletedAction from '../classes/livechat/MarkChatItemsByAuthorAsDeletedAction.js';
|
||||
import type ReplaceChatItemAction from '../classes/livechat/ReplaceChatItemAction.js';
|
||||
import type ReplayChatItemAction from '../classes/livechat/ReplayChatItemAction.js';
|
||||
import type ShowLiveChatActionPanelAction from '../classes/livechat/ShowLiveChatActionPanelAction.js';
|
||||
import type Button from '../classes/Button.js';
|
||||
import type { IParsedResponse } from '../types/index.js';
|
||||
export type ChatAction = AddChatItemAction | AddBannerToLiveChatCommand | AddLiveChatTickerItemAction | MarkChatItemAsDeletedAction | MarkChatItemsByAuthorAsDeletedAction | RemoveBannerForLiveChatCommand | ReplaceChatItemAction | ReplayChatItemAction | ShowLiveChatActionPanelAction | ShowLiveChatTooltipCommand;
|
||||
export type ChatItemWithMenu = LiveChatAutoModMessage | LiveChatMembershipItem | LiveChatPaidMessage | LiveChatPaidSticker | LiveChatTextMessage | LiveChatViewerEngagementMessage;
|
||||
export type LiveMetadata = {
|
||||
title?: UpdateTitleAction;
|
||||
description?: UpdateDescriptionAction;
|
||||
views?: UpdateViewershipAction;
|
||||
likes?: UpdateToggleButtonTextAction;
|
||||
date?: UpdateDateTextAction;
|
||||
};
|
||||
export default class LiveChat extends EventEmitter {
|
||||
#private;
|
||||
smoothed_queue: SmoothedQueue;
|
||||
initial_info?: LiveChatContinuation;
|
||||
metadata?: LiveMetadata;
|
||||
running: boolean;
|
||||
is_replay: boolean;
|
||||
constructor(video_info: VideoInfo);
|
||||
on(type: 'start', listener: (initial_data: LiveChatContinuation) => void): void;
|
||||
on(type: 'chat-update', listener: (action: ChatAction) => void): void;
|
||||
on(type: 'metadata-update', listener: (metadata: LiveMetadata) => void): void;
|
||||
on(type: 'error', listener: (err: Error) => void): void;
|
||||
on(type: 'end', listener: () => void): void;
|
||||
once(type: 'start', listener: (initial_data: LiveChatContinuation) => void): void;
|
||||
once(type: 'chat-update', listener: (action: ChatAction) => void): void;
|
||||
once(type: 'metadata-update', listener: (metadata: LiveMetadata) => void): void;
|
||||
once(type: 'error', listener: (err: Error) => void): void;
|
||||
once(type: 'end', listener: () => void): void;
|
||||
start(): void;
|
||||
stop(): void;
|
||||
/**
|
||||
* Sends a message.
|
||||
* @param text - Text to send.
|
||||
*/
|
||||
sendMessage(text: string): Promise<ObservedArray<AddChatItemAction | RunAttestationCommand>>;
|
||||
/**
|
||||
* Applies given filter to the live chat.
|
||||
* @param filter - Filter to apply.
|
||||
*/
|
||||
applyFilter(filter: 'TOP_CHAT' | 'LIVE_CHAT'): void;
|
||||
/**
|
||||
* Retrieves given chat item's menu.
|
||||
*/
|
||||
getItemMenu(item: ChatItemWithMenu): Promise<ItemMenu>;
|
||||
/**
|
||||
* Equivalent to "clicking" a button.
|
||||
*/
|
||||
selectButton(button: Button): Promise<IParsedResponse>;
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
// noinspection ES6MissingAwait
|
||||
import { EventEmitter } from '../../utils/index.js';
|
||||
import { InnertubeError, Platform, u8ToBase64 } from '../../utils/Utils.js';
|
||||
import { LiveChatContinuation, Parser } from '../index.js';
|
||||
import SmoothedQueue from './SmoothedQueue.js';
|
||||
import RunAttestationCommand from '../classes/commands/RunAttestationCommand.js';
|
||||
import AddChatItemAction from '../classes/livechat/AddChatItemAction.js';
|
||||
import UpdateDateTextAction from '../classes/livechat/UpdateDateTextAction.js';
|
||||
import UpdateDescriptionAction from '../classes/livechat/UpdateDescriptionAction.js';
|
||||
import UpdateTitleAction from '../classes/livechat/UpdateTitleAction.js';
|
||||
import UpdateToggleButtonTextAction from '../classes/livechat/UpdateToggleButtonTextAction.js';
|
||||
import UpdateViewershipAction from '../classes/livechat/UpdateViewershipAction.js';
|
||||
import NavigationEndpoint from '../classes/NavigationEndpoint.js';
|
||||
import ItemMenu from './ItemMenu.js';
|
||||
import { LiveMessageParams } from '../../../protos/generated/misc/params.js';
|
||||
export default class LiveChat extends EventEmitter {
|
||||
#actions;
|
||||
#video_id;
|
||||
#channel_id;
|
||||
#continuation;
|
||||
#mcontinuation;
|
||||
#retry_count = 0;
|
||||
smoothed_queue;
|
||||
initial_info;
|
||||
metadata;
|
||||
running = false;
|
||||
is_replay = false;
|
||||
constructor(video_info) {
|
||||
super();
|
||||
this.#video_id = video_info.basic_info.id;
|
||||
this.#channel_id = video_info.basic_info.channel_id;
|
||||
this.#actions = video_info.actions;
|
||||
this.#continuation = video_info.livechat?.continuation;
|
||||
this.is_replay = video_info.livechat?.is_replay || false;
|
||||
this.smoothed_queue = new SmoothedQueue();
|
||||
this.smoothed_queue.callback = async (actions) => {
|
||||
if (!actions.length) {
|
||||
// Wait 2 seconds before requesting an incremental continuation if the action group is empty.
|
||||
await this.#wait(2000);
|
||||
}
|
||||
else if (actions.length < 10) {
|
||||
// If there are less than 10 actions, wait until all of them are emitted.
|
||||
await this.#emitSmoothedActions(actions);
|
||||
}
|
||||
else if (this.is_replay) {
|
||||
/**
|
||||
* NOTE: Live chat replays require data from the video player for actions to be emitted timely
|
||||
* and as we don't have that, this ends up being quite innacurate.
|
||||
*/
|
||||
this.#emitSmoothedActions(actions);
|
||||
await this.#wait(2000);
|
||||
}
|
||||
else {
|
||||
// There are more than 10 actions, emit them asynchronously so we can request the next incremental continuation.
|
||||
this.#emitSmoothedActions(actions);
|
||||
}
|
||||
if (this.running) {
|
||||
this.#pollLivechat();
|
||||
}
|
||||
};
|
||||
}
|
||||
on(type, listener) {
|
||||
super.on(type, listener);
|
||||
}
|
||||
once(type, listener) {
|
||||
super.once(type, listener);
|
||||
}
|
||||
start() {
|
||||
if (!this.running) {
|
||||
this.running = true;
|
||||
this.#pollLivechat();
|
||||
this.#pollMetadata();
|
||||
}
|
||||
}
|
||||
stop() {
|
||||
this.smoothed_queue.clear();
|
||||
this.running = false;
|
||||
}
|
||||
#pollLivechat() {
|
||||
(async () => {
|
||||
try {
|
||||
const response = await this.#actions.execute(this.is_replay ? 'live_chat/get_live_chat_replay' : 'live_chat/get_live_chat', { continuation: this.#continuation, parse: true });
|
||||
const contents = response.continuation_contents;
|
||||
if (!contents) {
|
||||
this.emit('error', new InnertubeError('Unexpected live chat incremental continuation response', response));
|
||||
this.emit('end');
|
||||
this.stop();
|
||||
}
|
||||
if (!(contents instanceof LiveChatContinuation)) {
|
||||
this.stop();
|
||||
this.emit('end');
|
||||
return;
|
||||
}
|
||||
this.#continuation = contents.continuation.token;
|
||||
// Header only exists in the first request
|
||||
if (contents.header) {
|
||||
this.initial_info = contents;
|
||||
this.emit('start', contents);
|
||||
if (this.running)
|
||||
this.#pollLivechat();
|
||||
}
|
||||
else {
|
||||
this.smoothed_queue.enqueueActionGroup(contents.actions);
|
||||
}
|
||||
this.#retry_count = 0;
|
||||
}
|
||||
catch (err) {
|
||||
this.emit('error', err);
|
||||
if (this.#retry_count++ < 10) {
|
||||
await this.#wait(2000);
|
||||
this.#pollLivechat();
|
||||
}
|
||||
else {
|
||||
this.emit('error', new InnertubeError('Reached retry limit for incremental continuation requests', err));
|
||||
this.emit('end');
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
/**
|
||||
* Ensures actions are emitted at the right speed.
|
||||
* This and {@link SmoothedQueue} were based off of YouTube's own implementation.
|
||||
*/
|
||||
async #emitSmoothedActions(action_queue) {
|
||||
const base = 1E4;
|
||||
let delay = action_queue.length < base / 80 ? 1 : Math.ceil(action_queue.length / (base / 80));
|
||||
const emit_delay_ms = delay == 1 ? (delay = base / action_queue.length,
|
||||
delay *= Math.random() + 0.5,
|
||||
delay = Math.min(1E3, delay),
|
||||
delay = Math.max(80, delay)) : delay = 80;
|
||||
for (const action of action_queue) {
|
||||
await this.#wait(emit_delay_ms);
|
||||
this.emit('chat-update', action);
|
||||
}
|
||||
}
|
||||
#pollMetadata() {
|
||||
(async () => {
|
||||
try {
|
||||
const payload = { videoId: this.#video_id };
|
||||
if (this.#mcontinuation) {
|
||||
payload.continuation = this.#mcontinuation;
|
||||
}
|
||||
const response = await this.#actions.execute('/updated_metadata', payload);
|
||||
const data = Parser.parseResponse(response.data);
|
||||
this.#mcontinuation = data.continuation?.token;
|
||||
this.metadata = {
|
||||
title: data.actions?.array().firstOfType(UpdateTitleAction) || this.metadata?.title,
|
||||
description: data.actions?.array().firstOfType(UpdateDescriptionAction) || this.metadata?.description,
|
||||
views: data.actions?.array().firstOfType(UpdateViewershipAction) || this.metadata?.views,
|
||||
likes: data.actions?.array().firstOfType(UpdateToggleButtonTextAction) || this.metadata?.likes,
|
||||
date: data.actions?.array().firstOfType(UpdateDateTextAction) || this.metadata?.date
|
||||
};
|
||||
this.emit('metadata-update', this.metadata);
|
||||
await this.#wait(5000);
|
||||
if (this.running)
|
||||
this.#pollMetadata();
|
||||
}
|
||||
catch {
|
||||
await this.#wait(2000);
|
||||
if (this.running)
|
||||
this.#pollMetadata();
|
||||
}
|
||||
})();
|
||||
}
|
||||
/**
|
||||
* Sends a message.
|
||||
* @param text - Text to send.
|
||||
*/
|
||||
async sendMessage(text) {
|
||||
const writer = LiveMessageParams.encode({
|
||||
params: {
|
||||
ids: {
|
||||
videoId: this.#video_id,
|
||||
channelId: this.#channel_id
|
||||
}
|
||||
},
|
||||
number0: 1,
|
||||
number1: 4
|
||||
});
|
||||
const params = btoa(encodeURIComponent(u8ToBase64(writer.finish())));
|
||||
const response = await this.#actions.execute('/live_chat/send_message', {
|
||||
richMessage: { textSegments: [{ text }] },
|
||||
clientMessageId: Platform.shim.uuidv4(),
|
||||
client: 'WEB',
|
||||
parse: true,
|
||||
params
|
||||
});
|
||||
if (!response.actions)
|
||||
throw new InnertubeError('Unexpected response from send_message', response);
|
||||
return response.actions.array().as(AddChatItemAction, RunAttestationCommand);
|
||||
}
|
||||
/**
|
||||
* Applies given filter to the live chat.
|
||||
* @param filter - Filter to apply.
|
||||
*/
|
||||
applyFilter(filter) {
|
||||
if (!this.initial_info)
|
||||
throw new InnertubeError('Cannot apply filter before initial info is retrieved.');
|
||||
const menu_items = this.initial_info?.header?.view_selector?.sub_menu_items;
|
||||
if (filter === 'TOP_CHAT') {
|
||||
if (menu_items?.at(0)?.selected)
|
||||
return;
|
||||
this.#continuation = menu_items?.at(0)?.continuation;
|
||||
}
|
||||
else {
|
||||
if (menu_items?.at(1)?.selected)
|
||||
return;
|
||||
this.#continuation = menu_items?.at(1)?.continuation;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Retrieves given chat item's menu.
|
||||
*/
|
||||
async getItemMenu(item) {
|
||||
if (!item.hasKey('menu_endpoint') || !item.key('menu_endpoint').isInstanceof(NavigationEndpoint))
|
||||
throw new InnertubeError('This item does not have a menu.', item);
|
||||
const response = await item.key('menu_endpoint').instanceof(NavigationEndpoint).call(this.#actions, { parse: true });
|
||||
if (!response)
|
||||
throw new InnertubeError('Could not retrieve item menu.', item);
|
||||
return new ItemMenu(response, this.#actions);
|
||||
}
|
||||
/**
|
||||
* Equivalent to "clicking" a button.
|
||||
*/
|
||||
async selectButton(button) {
|
||||
return await button.endpoint.call(this.#actions, { parse: true });
|
||||
}
|
||||
async #wait(ms) {
|
||||
return new Promise((resolve) => setTimeout(() => resolve(), ms));
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=LiveChat.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+12
@@ -0,0 +1,12 @@
|
||||
import SimpleMenuHeader from '../classes/menus/SimpleMenuHeader.js';
|
||||
import Notification from '../classes/Notification.js';
|
||||
import type { ApiResponse, Actions } from '../../core/index.js';
|
||||
import type { IGetNotificationsMenuResponse } from '../types/index.js';
|
||||
export default class NotificationsMenu {
|
||||
#private;
|
||||
header: SimpleMenuHeader;
|
||||
contents: Notification[];
|
||||
constructor(actions: Actions, response: ApiResponse);
|
||||
getContinuation(): Promise<NotificationsMenu>;
|
||||
get page(): IGetNotificationsMenuResponse;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { Parser } from '../index.js';
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import ContinuationItem from '../classes/ContinuationItem.js';
|
||||
import SimpleMenuHeader from '../classes/menus/SimpleMenuHeader.js';
|
||||
import Notification from '../classes/Notification.js';
|
||||
export default class NotificationsMenu {
|
||||
#page;
|
||||
#actions;
|
||||
header;
|
||||
contents;
|
||||
constructor(actions, response) {
|
||||
this.#actions = actions;
|
||||
this.#page = Parser.parseResponse(response.data);
|
||||
if (!this.#page.actions_memo)
|
||||
throw new InnertubeError('Page actions not found');
|
||||
this.header = this.#page.actions_memo.getType(SimpleMenuHeader)[0];
|
||||
this.contents = this.#page.actions_memo.getType(Notification);
|
||||
}
|
||||
async getContinuation() {
|
||||
const continuation = this.#page.actions_memo?.getType(ContinuationItem)[0];
|
||||
if (!continuation)
|
||||
throw new InnertubeError('Continuation not found');
|
||||
const response = await continuation.endpoint.call(this.#actions, { parse: false });
|
||||
return new NotificationsMenu(this.#actions, response);
|
||||
}
|
||||
get page() {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=NotificationsMenu.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"NotificationsMenu.js","sourceRoot":"","sources":["../../../../src/parser/youtube/NotificationsMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,gBAAgB,MAAM,gCAAgC,CAAC;AAC9D,OAAO,gBAAgB,MAAM,sCAAsC,CAAC;AACpE,OAAO,YAAY,MAAM,4BAA4B,CAAC;AAKtD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAC3B,KAAK,CAAgC;IACrC,QAAQ,CAAU;IAEpB,MAAM,CAAmB;IACzB,QAAQ,CAAiB;IAEhC,YAAY,OAAgB,EAAE,QAAqB;QACjD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,aAAa,CAAgC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEhF,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY;YAC1B,MAAM,IAAI,cAAc,CAAC,wBAAwB,CAAC,CAAC;QAErD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;QAE3E,IAAI,CAAC,YAAY;YACf,MAAM,IAAI,cAAc,CAAC,wBAAwB,CAAC,CAAC;QAErD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAEnF,OAAO,IAAI,iBAAiB,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import Message from '../classes/Message.js';
|
||||
import ReelItem from '../classes/ReelItem.js';
|
||||
import ShortsLockupView from '../classes/ShortsLockupView.js';
|
||||
import PlaylistVideo from '../classes/PlaylistVideo.js';
|
||||
import { type ObservedArray, type YTNode } from '../helpers.js';
|
||||
import type { Actions, ApiResponse } from '../../core/index.js';
|
||||
import type { IBrowseResponse } from '../types/index.js';
|
||||
import type Thumbnail from '../classes/misc/Thumbnail.js';
|
||||
import type NavigationEndpoint from '../classes/NavigationEndpoint.js';
|
||||
export default class Playlist extends Feed<IBrowseResponse> {
|
||||
#private;
|
||||
info: {
|
||||
subtitle: import("../misc.js").Text | null;
|
||||
author: import("../misc.js").Author;
|
||||
thumbnails: Thumbnail[];
|
||||
total_items: string;
|
||||
views: string;
|
||||
last_updated: string;
|
||||
can_share: boolean;
|
||||
can_delete: boolean;
|
||||
can_reorder: boolean;
|
||||
is_editable: boolean;
|
||||
privacy: string;
|
||||
title?: string | undefined;
|
||||
description?: string | undefined;
|
||||
type?: string | undefined;
|
||||
};
|
||||
menu: YTNode;
|
||||
endpoint?: NavigationEndpoint;
|
||||
messages: ObservedArray<Message>;
|
||||
constructor(actions: Actions, data: ApiResponse | IBrowseResponse, already_parsed?: boolean);
|
||||
get items(): ObservedArray<PlaylistVideo | ReelItem | ShortsLockupView>;
|
||||
get has_continuation(): boolean;
|
||||
getContinuationData(): Promise<IBrowseResponse | undefined>;
|
||||
getContinuation(): Promise<Playlist>;
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import Message from '../classes/Message.js';
|
||||
import PlaylistCustomThumbnail from '../classes/PlaylistCustomThumbnail.js';
|
||||
import PlaylistHeader from '../classes/PlaylistHeader.js';
|
||||
import PlaylistMetadata from '../classes/PlaylistMetadata.js';
|
||||
import PlaylistSidebarPrimaryInfo from '../classes/PlaylistSidebarPrimaryInfo.js';
|
||||
import PlaylistSidebarSecondaryInfo from '../classes/PlaylistSidebarSecondaryInfo.js';
|
||||
import PlaylistVideoList from '../classes/PlaylistVideoList.js';
|
||||
import PlaylistVideoThumbnail from '../classes/PlaylistVideoThumbnail.js';
|
||||
import ReelItem from '../classes/ReelItem.js';
|
||||
import ShortsLockupView from '../classes/ShortsLockupView.js';
|
||||
import VideoOwner from '../classes/VideoOwner.js';
|
||||
import Alert from '../classes/Alert.js';
|
||||
import ContinuationItem from '../classes/ContinuationItem.js';
|
||||
import PlaylistVideo from '../classes/PlaylistVideo.js';
|
||||
import SectionList from '../classes/SectionList.js';
|
||||
import { observe } from '../helpers.js';
|
||||
export default class Playlist extends Feed {
|
||||
info;
|
||||
menu;
|
||||
endpoint;
|
||||
messages;
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
super(actions, data, already_parsed);
|
||||
const header = this.memo.getType(PlaylistHeader)[0];
|
||||
const primary_info = this.memo.getType(PlaylistSidebarPrimaryInfo)[0];
|
||||
const secondary_info = this.memo.getType(PlaylistSidebarSecondaryInfo)[0];
|
||||
const video_list = this.memo.getType(PlaylistVideoList)[0];
|
||||
const alert = this.page.alerts?.firstOfType(Alert);
|
||||
if (alert && alert.alert_type === 'ERROR')
|
||||
throw new InnertubeError(alert.text.toString(), alert);
|
||||
if (!primary_info && !secondary_info && Object.keys(this.page).length === 0)
|
||||
throw new InnertubeError('Got empty continuation response. This is likely the end of the playlist.');
|
||||
this.info = {
|
||||
...this.page.metadata?.item().as(PlaylistMetadata),
|
||||
...{
|
||||
subtitle: header ? header.subtitle : null,
|
||||
author: secondary_info?.owner?.as(VideoOwner).author ?? header?.author,
|
||||
thumbnails: primary_info?.thumbnail_renderer?.as(PlaylistVideoThumbnail, PlaylistCustomThumbnail).thumbnail,
|
||||
total_items: this.#getStat(0, primary_info),
|
||||
views: this.#getStat(1, primary_info),
|
||||
last_updated: this.#getStat(2, primary_info),
|
||||
can_share: header?.can_share,
|
||||
can_delete: header?.can_delete,
|
||||
can_reorder: video_list?.can_reorder,
|
||||
is_editable: video_list?.is_editable,
|
||||
privacy: header?.privacy
|
||||
}
|
||||
};
|
||||
this.menu = primary_info?.menu;
|
||||
this.endpoint = primary_info?.endpoint;
|
||||
this.messages = this.memo.getType(Message);
|
||||
}
|
||||
get items() {
|
||||
return observe(this.videos.as(PlaylistVideo, ReelItem, ShortsLockupView).filter((video) => video.style !== 'PLAYLIST_VIDEO_RENDERER_STYLE_RECOMMENDED_VIDEO'));
|
||||
}
|
||||
get has_continuation() {
|
||||
const section_list = this.memo.getType(SectionList)[0];
|
||||
if (!section_list)
|
||||
return super.has_continuation;
|
||||
return !!this.memo.getType(ContinuationItem).find((node) => !section_list.contents.includes(node));
|
||||
}
|
||||
async getContinuationData() {
|
||||
const section_list = this.memo.getType(SectionList)[0];
|
||||
/**
|
||||
* No section list means there can't be additional continuation nodes here,
|
||||
* so no need to check.
|
||||
*/
|
||||
if (!section_list)
|
||||
return await super.getContinuationData();
|
||||
const playlist_contents_continuation = this.memo.getType(ContinuationItem)
|
||||
.find((node) => !section_list.contents.includes(node));
|
||||
if (!playlist_contents_continuation)
|
||||
throw new InnertubeError('There are no continuations.');
|
||||
return await playlist_contents_continuation.endpoint.call(this.actions, { parse: true });
|
||||
}
|
||||
async getContinuation() {
|
||||
const page = await this.getContinuationData();
|
||||
if (!page)
|
||||
throw new InnertubeError('Could not get continuation data');
|
||||
return new Playlist(this.actions, page, true);
|
||||
}
|
||||
#getStat(index, primary_info) {
|
||||
if (!primary_info || !primary_info.stats)
|
||||
return 'N/A';
|
||||
return primary_info.stats[index]?.toString() || 'N/A';
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Playlist.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Playlist.js","sourceRoot":"","sources":["../../../../src/parser/youtube/Playlist.ts"],"names":[],"mappings":"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"}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import HorizontalCardList from '../classes/HorizontalCardList.js';
|
||||
import SearchHeader from '../classes/SearchHeader.js';
|
||||
import SearchRefinementCard from '../classes/SearchRefinementCard.js';
|
||||
import SearchSubMenu from '../classes/SearchSubMenu.js';
|
||||
import UniversalWatchCard from '../classes/UniversalWatchCard.js';
|
||||
import type { ApiResponse, Actions } from '../../core/index.js';
|
||||
import type { ObservedArray, YTNode } from '../helpers.js';
|
||||
import type { ISearchResponse } from '../types/index.js';
|
||||
export default class Search extends Feed<ISearchResponse> {
|
||||
header?: SearchHeader;
|
||||
results: ObservedArray<YTNode>;
|
||||
refinements: string[];
|
||||
estimated_results: number;
|
||||
sub_menu?: SearchSubMenu;
|
||||
watch_card?: UniversalWatchCard;
|
||||
refinement_cards?: HorizontalCardList | null;
|
||||
constructor(actions: Actions, data: ApiResponse | ISearchResponse, already_parsed?: boolean);
|
||||
/**
|
||||
* Applies given refinement card and returns a new {@link Search} object. Use {@link refinement_card_queries} to get a list of available refinement cards.
|
||||
*/
|
||||
selectRefinementCard(card: SearchRefinementCard | string): Promise<Search>;
|
||||
/**
|
||||
* Returns a list of refinement card queries.
|
||||
*/
|
||||
get refinement_card_queries(): string[];
|
||||
/**
|
||||
* Retrieves next batch of search results.
|
||||
*/
|
||||
getContinuation(): Promise<Search>;
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import Feed from '../../core/mixins/Feed.js';
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import HorizontalCardList from '../classes/HorizontalCardList.js';
|
||||
import ItemSection from '../classes/ItemSection.js';
|
||||
import SearchHeader from '../classes/SearchHeader.js';
|
||||
import SearchRefinementCard from '../classes/SearchRefinementCard.js';
|
||||
import SearchSubMenu from '../classes/SearchSubMenu.js';
|
||||
import SectionList from '../classes/SectionList.js';
|
||||
import UniversalWatchCard from '../classes/UniversalWatchCard.js';
|
||||
import { observe } from '../helpers.js';
|
||||
import { ReloadContinuationItemsCommand } from '../index.js';
|
||||
import AppendContinuationItemsAction from '../classes/actions/AppendContinuationItemsAction.js';
|
||||
export default class Search extends Feed {
|
||||
header;
|
||||
results;
|
||||
refinements;
|
||||
estimated_results;
|
||||
sub_menu;
|
||||
watch_card;
|
||||
refinement_cards;
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
super(actions, data, already_parsed);
|
||||
const contents = this.page.contents_memo?.getType(SectionList)[0].contents ||
|
||||
this.page.on_response_received_commands?.[0].as(AppendContinuationItemsAction, ReloadContinuationItemsCommand).contents;
|
||||
if (!contents)
|
||||
throw new InnertubeError('No contents found in search response');
|
||||
if (this.page.header)
|
||||
this.header = this.page.header.item().as(SearchHeader);
|
||||
this.results = observe(contents.filterType(ItemSection).flatMap((section) => section.contents));
|
||||
this.refinements = this.page.refinements || [];
|
||||
this.estimated_results = this.page.estimated_results || 0;
|
||||
if (this.page.contents_memo) {
|
||||
this.sub_menu = this.page.contents_memo.getType(SearchSubMenu)[0];
|
||||
this.watch_card = this.page.contents_memo.getType(UniversalWatchCard)[0];
|
||||
}
|
||||
this.refinement_cards = this.results?.firstOfType(HorizontalCardList);
|
||||
}
|
||||
/**
|
||||
* Applies given refinement card and returns a new {@link Search} object. Use {@link refinement_card_queries} to get a list of available refinement cards.
|
||||
*/
|
||||
async selectRefinementCard(card) {
|
||||
let target_card;
|
||||
if (typeof card === 'string') {
|
||||
if (!this.refinement_cards)
|
||||
throw new InnertubeError('No refinement cards found.');
|
||||
target_card = this.refinement_cards?.cards.find((refinement_card) => {
|
||||
return refinement_card.is(SearchRefinementCard) && refinement_card.query === card;
|
||||
});
|
||||
if (!target_card)
|
||||
throw new InnertubeError(`Refinement card "${card}" not found`, { available_cards: this.refinement_card_queries });
|
||||
}
|
||||
else if (card.type === 'SearchRefinementCard') {
|
||||
target_card = card;
|
||||
}
|
||||
else {
|
||||
throw new InnertubeError('Invalid refinement card!');
|
||||
}
|
||||
const page = await target_card.endpoint.call(this.actions, { parse: true });
|
||||
return new Search(this.actions, page, true);
|
||||
}
|
||||
/**
|
||||
* Returns a list of refinement card queries.
|
||||
*/
|
||||
get refinement_card_queries() {
|
||||
return this.refinement_cards?.cards.as(SearchRefinementCard).map((card) => card.query) || [];
|
||||
}
|
||||
/**
|
||||
* Retrieves next batch of search results.
|
||||
*/
|
||||
async getContinuation() {
|
||||
const response = await this.getContinuationData();
|
||||
if (!response)
|
||||
throw new InnertubeError('Could not get continuation data');
|
||||
return new Search(this.actions, response, true);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Search.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Search.js","sourceRoot":"","sources":["../../../../src/parser/youtube/Search.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,2BAA2B,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,kBAAkB,MAAM,kCAAkC,CAAC;AAClE,OAAO,WAAW,MAAM,2BAA2B,CAAC;AACpD,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,oBAAoB,MAAM,oCAAoC,CAAC;AACtE,OAAO,aAAa,MAAM,6BAA6B,CAAC;AACxD,OAAO,WAAW,MAAM,2BAA2B,CAAC;AACpD,OAAO,kBAAkB,MAAM,kCAAkC,CAAC;AAElE,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAKxC,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,6BAA6B,MAAM,qDAAqD,CAAC;AAEhG,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,IAAqB;IAChD,MAAM,CAAgB;IACtB,OAAO,CAAwB;IAC/B,WAAW,CAAW;IACtB,iBAAiB,CAAS;IAC1B,QAAQ,CAAiB;IACzB,UAAU,CAAsB;IAChC,gBAAgB,CAA6B;IAEpD,YAAY,OAAgB,EAAE,IAAmC,EAAE,cAAc,GAAG,KAAK;QACvF,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QAErC,MAAM,QAAQ,GACZ,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ;YACzD,IAAI,CAAC,IAAI,CAAC,6BAA6B,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,6BAA6B,EAAE,8BAA8B,CAAC,CAAC,QAAQ,CAAC;QAE1H,IAAI,CAAC,QAAQ;YACX,MAAM,IAAI,cAAc,CAAC,sCAAsC,CAAC,CAAC;QAEnE,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM;YAClB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC;QAEzD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAEhG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;QAC/C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,CAAC;QAE1D,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3E,CAAC;QAED,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,kBAAkB,CAAC,CAAC;IACxE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,oBAAoB,CAAC,IAAmC;QAC5D,IAAI,WAA6C,CAAC;QAElD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,gBAAgB;gBAAE,MAAM,IAAI,cAAc,CAAC,4BAA4B,CAAC,CAAC;YACnF,WAAW,GAAG,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,eAAe,EAA2C,EAAE;gBAC3G,OAAO,eAAe,CAAC,EAAE,CAAC,oBAAoB,CAAC,IAAI,eAAe,CAAC,KAAK,KAAK,IAAI,CAAC;YACpF,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,WAAW;gBACd,MAAM,IAAI,cAAc,CAAC,oBAAoB,IAAI,aAAa,EAAE,EAAE,eAAe,EAAE,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC;QACvH,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;YAChD,WAAW,GAAG,IAAI,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,cAAc,CAAC,0BAA0B,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAkB,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAE7F,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,IAAI,uBAAuB;QACzB,OAAO,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC/F,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe;QACnB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAClD,IAAI,CAAC,QAAQ;YACX,MAAM,IAAI,cAAc,CAAC,iCAAiC,CAAC,CAAC;QAC9D,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;CACF"}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import CompactLink from '../classes/CompactLink.js';
|
||||
import PageIntroduction from '../classes/PageIntroduction.js';
|
||||
import SettingsSidebar from '../classes/SettingsSidebar.js';
|
||||
import SettingsSwitch from '../classes/SettingsSwitch.js';
|
||||
import type { ApiResponse, Actions } from '../../core/index.js';
|
||||
import type { IBrowseResponse } from '../types/index.js';
|
||||
export default class Settings {
|
||||
#private;
|
||||
sidebar?: SettingsSidebar;
|
||||
introduction?: PageIntroduction;
|
||||
sections: {
|
||||
title: string | null;
|
||||
contents: import("../helpers.js").ObservedArray<import("../helpers.js").YTNode>;
|
||||
}[] | undefined;
|
||||
constructor(actions: Actions, response: ApiResponse);
|
||||
/**
|
||||
* Selects an item from the sidebar menu. Use {@link sidebar_items} to see available items.
|
||||
*/
|
||||
selectSidebarItem(target_item: string | CompactLink): Promise<Settings>;
|
||||
/**
|
||||
* Finds a setting by name and returns it. Use {@link setting_options} to see available options.
|
||||
*/
|
||||
getSettingOption(name: string): SettingsSwitch;
|
||||
/**
|
||||
* Returns settings available in the page.
|
||||
*/
|
||||
get setting_options(): string[];
|
||||
/**
|
||||
* Returns options available in the sidebar.
|
||||
*/
|
||||
get sidebar_items(): string[];
|
||||
get page(): IBrowseResponse;
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
import { Parser } from '../index.js';
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import CompactLink from '../classes/CompactLink.js';
|
||||
import ItemSection from '../classes/ItemSection.js';
|
||||
import PageIntroduction from '../classes/PageIntroduction.js';
|
||||
import SectionList from '../classes/SectionList.js';
|
||||
import SettingsOptions from '../classes/SettingsOptions.js';
|
||||
import SettingsSidebar from '../classes/SettingsSidebar.js';
|
||||
import SettingsSwitch from '../classes/SettingsSwitch.js';
|
||||
import CommentsHeader from '../classes/comments/CommentsHeader.js';
|
||||
import ItemSectionHeader from '../classes/ItemSectionHeader.js';
|
||||
import ItemSectionTabbedHeader from '../classes/ItemSectionTabbedHeader.js';
|
||||
import TwoColumnBrowseResults from '../classes/TwoColumnBrowseResults.js';
|
||||
export default class Settings {
|
||||
#page;
|
||||
#actions;
|
||||
sidebar;
|
||||
introduction;
|
||||
sections;
|
||||
constructor(actions, response) {
|
||||
this.#actions = actions;
|
||||
this.#page = Parser.parseResponse(response.data);
|
||||
this.sidebar = this.#page.sidebar?.as(SettingsSidebar);
|
||||
if (!this.#page.contents)
|
||||
throw new InnertubeError('Page contents not found');
|
||||
const tab = this.#page.contents.item().as(TwoColumnBrowseResults).tabs.find((tab) => tab.selected);
|
||||
if (!tab)
|
||||
throw new InnertubeError('Target tab not found');
|
||||
const contents = tab.content?.as(SectionList).contents.as(ItemSection);
|
||||
this.introduction = contents?.shift()?.contents?.firstOfType(PageIntroduction);
|
||||
this.sections = contents?.map((el) => ({
|
||||
title: el.header?.is(CommentsHeader, ItemSectionHeader, ItemSectionTabbedHeader) ? el.header.title.toString() : null,
|
||||
contents: el.contents
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Selects an item from the sidebar menu. Use {@link sidebar_items} to see available items.
|
||||
*/
|
||||
async selectSidebarItem(target_item) {
|
||||
if (!this.sidebar)
|
||||
throw new InnertubeError('Sidebar not available');
|
||||
let item;
|
||||
if (typeof target_item === 'string') {
|
||||
item = this.sidebar.items.find((link) => link.title === target_item);
|
||||
if (!item)
|
||||
throw new InnertubeError(`Item "${target_item}" not found`, { available_items: this.sidebar_items });
|
||||
}
|
||||
else if (target_item?.is(CompactLink)) {
|
||||
item = target_item;
|
||||
}
|
||||
else {
|
||||
throw new InnertubeError('Invalid item', { target_item });
|
||||
}
|
||||
const response = await item.endpoint.call(this.#actions, { parse: false });
|
||||
return new Settings(this.#actions, response);
|
||||
}
|
||||
/**
|
||||
* Finds a setting by name and returns it. Use {@link setting_options} to see available options.
|
||||
*/
|
||||
getSettingOption(name) {
|
||||
if (!this.sections)
|
||||
throw new InnertubeError('Sections not available');
|
||||
for (const section of this.sections) {
|
||||
if (!section.contents)
|
||||
continue;
|
||||
for (const el of section.contents) {
|
||||
const options = el.as(SettingsOptions).options;
|
||||
if (options) {
|
||||
for (const option of options) {
|
||||
if (option.is(SettingsSwitch) &&
|
||||
option.title?.toString() === name)
|
||||
return option;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new InnertubeError(`Option "${name}" not found`, { available_options: this.setting_options });
|
||||
}
|
||||
/**
|
||||
* Returns settings available in the page.
|
||||
*/
|
||||
get setting_options() {
|
||||
if (!this.sections)
|
||||
throw new InnertubeError('Sections not available');
|
||||
let options = [];
|
||||
for (const section of this.sections) {
|
||||
if (!section.contents)
|
||||
continue;
|
||||
for (const el of section.contents) {
|
||||
if (el.as(SettingsOptions).options)
|
||||
options = options.concat(el.as(SettingsOptions).options);
|
||||
}
|
||||
}
|
||||
return options.map((opt) => opt.title?.toString()).filter((el) => el);
|
||||
}
|
||||
/**
|
||||
* Returns options available in the sidebar.
|
||||
*/
|
||||
get sidebar_items() {
|
||||
if (!this.sidebar)
|
||||
throw new InnertubeError('Sidebar not available');
|
||||
return this.sidebar.items.map((item) => item.title.toString());
|
||||
}
|
||||
get page() {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Settings.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Settings.js","sourceRoot":"","sources":["../../../../src/parser/youtube/Settings.ts"],"names":[],"mappings":"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"}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import type { YTNode } from '../helpers.js';
|
||||
declare class DelayQueue {
|
||||
front: number[];
|
||||
back: number[];
|
||||
constructor();
|
||||
isEmpty(): boolean;
|
||||
clear(): void;
|
||||
getValues(): number[];
|
||||
}
|
||||
export default class SmoothedQueue {
|
||||
#private;
|
||||
constructor();
|
||||
enqueueActionGroup(group: YTNode[]): void;
|
||||
emitSmoothedActions(): void;
|
||||
clear(): void;
|
||||
set callback(cb: ((actions: YTNode[]) => void) | null);
|
||||
get callback(): ((actions: YTNode[]) => void) | null;
|
||||
get action_queue(): YTNode[][];
|
||||
get estimated_update_interval(): number | null;
|
||||
get last_update_time(): number | null;
|
||||
get next_update_id(): any;
|
||||
get poll_response_delay_queue(): DelayQueue;
|
||||
}
|
||||
export {};
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Flattens the given queue.
|
||||
* @param queue - The queue to flatten.
|
||||
*/
|
||||
function flattenQueue(queue) {
|
||||
const nodes = [];
|
||||
for (const group of queue) {
|
||||
if (Array.isArray(group)) {
|
||||
for (const node of group) {
|
||||
nodes.push(node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
nodes.push(group);
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
class DelayQueue {
|
||||
front;
|
||||
back;
|
||||
constructor() {
|
||||
this.front = [];
|
||||
this.back = [];
|
||||
}
|
||||
isEmpty() {
|
||||
return !this.front.length && !this.back.length;
|
||||
}
|
||||
clear() {
|
||||
this.front = [];
|
||||
this.back = [];
|
||||
}
|
||||
getValues() {
|
||||
return this.front.concat(this.back.reverse());
|
||||
}
|
||||
}
|
||||
export default class SmoothedQueue {
|
||||
#last_update_time;
|
||||
#estimated_update_interval;
|
||||
#callback;
|
||||
#action_queue;
|
||||
#next_update_id;
|
||||
#poll_response_delay_queue;
|
||||
constructor() {
|
||||
this.#last_update_time = null;
|
||||
this.#estimated_update_interval = null;
|
||||
this.#callback = null;
|
||||
this.#action_queue = [];
|
||||
this.#next_update_id = null;
|
||||
this.#poll_response_delay_queue = new DelayQueue();
|
||||
}
|
||||
enqueueActionGroup(group) {
|
||||
if (this.#last_update_time !== null) {
|
||||
const delay = Date.now() - this.#last_update_time;
|
||||
this.#poll_response_delay_queue.back.push(delay);
|
||||
if (5 < (this.#poll_response_delay_queue.front.length + this.#poll_response_delay_queue.back.length)) {
|
||||
if (!this.#poll_response_delay_queue.front.length) {
|
||||
this.#poll_response_delay_queue.front = this.#poll_response_delay_queue.back;
|
||||
this.#poll_response_delay_queue.front.reverse();
|
||||
this.#poll_response_delay_queue.back = [];
|
||||
}
|
||||
this.#poll_response_delay_queue.front.pop();
|
||||
}
|
||||
this.#estimated_update_interval = Math.max(...this.#poll_response_delay_queue.getValues());
|
||||
}
|
||||
this.#last_update_time = Date.now();
|
||||
this.#action_queue.push(group);
|
||||
if (this.#next_update_id === null) {
|
||||
this.#next_update_id = setTimeout(this.emitSmoothedActions.bind(this));
|
||||
}
|
||||
}
|
||||
emitSmoothedActions() {
|
||||
this.#next_update_id = null;
|
||||
if (this.#action_queue.length) {
|
||||
let delay = 1E4;
|
||||
if (this.#estimated_update_interval !== null && this.#last_update_time !== null) {
|
||||
delay = this.#estimated_update_interval - Date.now() + this.#last_update_time;
|
||||
}
|
||||
delay = this.#action_queue.length < delay / 80 ? 1 : Math.ceil(this.#action_queue.length / (delay / 80));
|
||||
const actions = flattenQueue(this.#action_queue.splice(0, delay));
|
||||
if (this.#callback) {
|
||||
this.#callback(actions);
|
||||
}
|
||||
if (this.#action_queue !== null) {
|
||||
if (delay == 1) {
|
||||
delay = this.#estimated_update_interval / this.#action_queue.length;
|
||||
delay *= Math.random() + 0.5;
|
||||
delay = Math.min(1E3, delay);
|
||||
delay = Math.max(80, delay);
|
||||
}
|
||||
else {
|
||||
delay = 80;
|
||||
}
|
||||
this.#next_update_id = setTimeout(this.emitSmoothedActions.bind(this), delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
clear() {
|
||||
if (this.#next_update_id !== null) {
|
||||
clearTimeout(this.#next_update_id);
|
||||
this.#next_update_id = null;
|
||||
}
|
||||
this.#action_queue = [];
|
||||
}
|
||||
set callback(cb) {
|
||||
this.#callback = cb;
|
||||
}
|
||||
get callback() {
|
||||
return this.#callback;
|
||||
}
|
||||
get action_queue() {
|
||||
return this.#action_queue;
|
||||
}
|
||||
get estimated_update_interval() {
|
||||
return this.#estimated_update_interval;
|
||||
}
|
||||
get last_update_time() {
|
||||
return this.#last_update_time;
|
||||
}
|
||||
get next_update_id() {
|
||||
return this.#next_update_id;
|
||||
}
|
||||
get poll_response_delay_queue() {
|
||||
return this.#poll_response_delay_queue;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=SmoothedQueue.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"SmoothedQueue.js","sourceRoot":"","sources":["../../../../src/parser/youtube/SmoothedQueue.ts"],"names":[],"mappings":"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"}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import Transcript from '../classes/Transcript.js';
|
||||
import type { ApiResponse, Actions } from '../../core/index.js';
|
||||
import type { IGetTranscriptResponse } from '../index.js';
|
||||
export default class TranscriptInfo {
|
||||
#private;
|
||||
transcript: Transcript;
|
||||
constructor(actions: Actions, response: ApiResponse);
|
||||
/**
|
||||
* Selects a language from the language menu and returns the updated transcript.
|
||||
* @param language - Language to select.
|
||||
*/
|
||||
selectLanguage(language: string): Promise<TranscriptInfo>;
|
||||
/**
|
||||
* Returns available languages.
|
||||
*/
|
||||
get languages(): string[];
|
||||
/**
|
||||
* Returns the currently selected language.
|
||||
*/
|
||||
get selectedLanguage(): string;
|
||||
get page(): IGetTranscriptResponse;
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { Parser } from '../index.js';
|
||||
import Transcript from '../classes/Transcript.js';
|
||||
export default class TranscriptInfo {
|
||||
#page;
|
||||
#actions;
|
||||
transcript;
|
||||
constructor(actions, response) {
|
||||
this.#page = Parser.parseResponse(response.data);
|
||||
this.#actions = actions;
|
||||
if (!this.#page.actions_memo)
|
||||
throw new Error('Page actions not found');
|
||||
this.transcript = this.#page.actions_memo.getType(Transcript)[0];
|
||||
}
|
||||
/**
|
||||
* Selects a language from the language menu and returns the updated transcript.
|
||||
* @param language - Language to select.
|
||||
*/
|
||||
async selectLanguage(language) {
|
||||
const target_menu_item = this.transcript.content?.footer?.language_menu?.sub_menu_items?.find((item) => item.title.toString() === language);
|
||||
if (!target_menu_item)
|
||||
throw new Error(`Language not found: ${language}`);
|
||||
if (target_menu_item.selected)
|
||||
return this;
|
||||
const response = await this.#actions.execute('/get_transcript', {
|
||||
params: target_menu_item.continuation
|
||||
});
|
||||
return new TranscriptInfo(this.#actions, response);
|
||||
}
|
||||
/**
|
||||
* Returns available languages.
|
||||
*/
|
||||
get languages() {
|
||||
return this.transcript.content?.footer?.language_menu?.sub_menu_items?.map((item) => item.title.toString()) || [];
|
||||
}
|
||||
/**
|
||||
* Returns the currently selected language.
|
||||
*/
|
||||
get selectedLanguage() {
|
||||
return this.transcript.content?.footer?.language_menu?.sub_menu_items?.find((item) => item.selected)?.title.toString() || '';
|
||||
}
|
||||
get page() {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=TranscriptInfo.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TranscriptInfo.js","sourceRoot":"","sources":["../../../../src/parser/youtube/TranscriptInfo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAKlD,MAAM,CAAC,OAAO,OAAO,cAAc;IACxB,KAAK,CAAyB;IAC9B,QAAQ,CAAU;IACpB,UAAU,CAAa;IAE9B,YAAY,OAAgB,EAAE,QAAqB;QACjD,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QAExB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY;YAC1B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAE5C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc,CAAC,QAAgB;QACnC,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,QAAQ,CAAC,CAAC;QAE5I,IAAI,CAAC,gBAAgB;YACnB,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;QAErD,IAAI,gBAAgB,CAAC,QAAQ;YAC3B,OAAO,IAAI,CAAC;QAEd,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,iBAAiB,EAAE;YAC9D,MAAM,EAAE,gBAAgB,CAAC,YAAY;SACtC,CAAC,CAAC;QAEH,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACrD,CAAC;IAED;;OAEG;IACH,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;IACpH,CAAC;IAED;;OAEG;IACH,IAAI,gBAAgB;QAClB,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/H,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
import { MediaInfo } from '../../core/mixins/index.js';
|
||||
import ChipCloud from '../classes/ChipCloud.js';
|
||||
import ChipCloudChip from '../classes/ChipCloudChip.js';
|
||||
import CommentsEntryPointHeader from '../classes/comments/CommentsEntryPointHeader.js';
|
||||
import LiveChat from '../classes/LiveChat.js';
|
||||
import MerchandiseShelf from '../classes/MerchandiseShelf.js';
|
||||
import PlayerOverlay from '../classes/PlayerOverlay.js';
|
||||
import TwoColumnWatchNextResults from '../classes/TwoColumnWatchNextResults.js';
|
||||
import VideoPrimaryInfo from '../classes/VideoPrimaryInfo.js';
|
||||
import VideoSecondaryInfo from '../classes/VideoSecondaryInfo.js';
|
||||
import NavigationEndpoint from '../classes/NavigationEndpoint.js';
|
||||
import LiveChatWrap from './LiveChat.js';
|
||||
import type { Actions, ApiResponse } from '../../core/index.js';
|
||||
import type { ObservedArray, YTNode } from '../helpers.js';
|
||||
import type Heatmap from '../classes/Heatmap.js';
|
||||
export default class VideoInfo extends MediaInfo {
|
||||
#private;
|
||||
primary_info?: VideoPrimaryInfo | null;
|
||||
secondary_info?: VideoSecondaryInfo | null;
|
||||
playlist?: TwoColumnWatchNextResults['playlist'];
|
||||
game_info?: {
|
||||
title: import("../misc.js").Text | undefined;
|
||||
release_year: import("../misc.js").Text | undefined;
|
||||
} | undefined;
|
||||
merchandise?: MerchandiseShelf | null;
|
||||
related_chip_cloud?: ChipCloud | null;
|
||||
watch_next_feed?: ObservedArray<YTNode> | null;
|
||||
player_overlays?: PlayerOverlay | null;
|
||||
comments_entry_point_header?: CommentsEntryPointHeader | null;
|
||||
livechat?: LiveChat | null;
|
||||
autoplay?: TwoColumnWatchNextResults['autoplay'];
|
||||
heat_map?: Heatmap | null;
|
||||
constructor(data: [ApiResponse, ApiResponse?], actions: Actions, cpn: string);
|
||||
/**
|
||||
* Applies given filter to the watch next feed. Use {@link filters} to get available filters.
|
||||
* @param target_filter - Filter to apply.
|
||||
*/
|
||||
selectFilter(target_filter: string | ChipCloudChip | undefined): Promise<VideoInfo>;
|
||||
/**
|
||||
* Adds video to the watch history.
|
||||
*/
|
||||
addToWatchHistory(): Promise<Response>;
|
||||
/**
|
||||
* Updates watch time for the video.
|
||||
*/
|
||||
updateWatchTime(startTime: number): Promise<Response>;
|
||||
/**
|
||||
* Retrieves watch next feed continuation.
|
||||
*/
|
||||
getWatchNextContinuation(): Promise<VideoInfo>;
|
||||
/**
|
||||
* Likes the video.
|
||||
*/
|
||||
like(): Promise<ApiResponse>;
|
||||
/**
|
||||
* Dislikes the video.
|
||||
*/
|
||||
dislike(): Promise<ApiResponse>;
|
||||
/**
|
||||
* Removes like/dislike.
|
||||
*/
|
||||
removeRating(): Promise<ApiResponse>;
|
||||
/**
|
||||
* Retrieves Live Chat if available.
|
||||
*/
|
||||
getLiveChat(): LiveChatWrap;
|
||||
/**
|
||||
* Retrieves trailer info if available (typically for non-purchased movies or films).
|
||||
* @returns `VideoInfo` for the trailer, or `null` if none.
|
||||
*/
|
||||
getTrailerInfo(): VideoInfo | null;
|
||||
/**
|
||||
* Watch next feed filters.
|
||||
*/
|
||||
get filters(): string[];
|
||||
/**
|
||||
* Checks if continuation is available for the watch next feed.
|
||||
*/
|
||||
get wn_has_continuation(): boolean;
|
||||
/**
|
||||
* Gets the endpoint of the autoplay video
|
||||
*/
|
||||
get autoplay_video_endpoint(): NavigationEndpoint | null;
|
||||
/**
|
||||
* Checks if trailer is available.
|
||||
*/
|
||||
get has_trailer(): boolean;
|
||||
/**
|
||||
* Get songs used in the video.
|
||||
*/
|
||||
get music_tracks(): {
|
||||
song: string | undefined;
|
||||
artist: string | undefined;
|
||||
album: string | undefined;
|
||||
license: string | undefined;
|
||||
videoId: string | undefined;
|
||||
channelId: string | undefined;
|
||||
}[];
|
||||
}
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
import { InnertubeError } from '../../utils/Utils.js';
|
||||
import { MediaInfo } from '../../core/mixins/index.js';
|
||||
import ChipCloud from '../classes/ChipCloud.js';
|
||||
import ChipCloudChip from '../classes/ChipCloudChip.js';
|
||||
import CommentsEntryPointHeader from '../classes/comments/CommentsEntryPointHeader.js';
|
||||
import ContinuationItem from '../classes/ContinuationItem.js';
|
||||
import ItemSection from '../classes/ItemSection.js';
|
||||
import LiveChat from '../classes/LiveChat.js';
|
||||
import MerchandiseShelf from '../classes/MerchandiseShelf.js';
|
||||
import PlayerMicroformat from '../classes/PlayerMicroformat.js';
|
||||
import PlayerOverlay from '../classes/PlayerOverlay.js';
|
||||
import RelatedChipCloud from '../classes/RelatedChipCloud.js';
|
||||
import RichMetadata from '../classes/RichMetadata.js';
|
||||
import RichMetadataRow from '../classes/RichMetadataRow.js';
|
||||
import SegmentedLikeDislikeButton from '../classes/SegmentedLikeDislikeButton.js';
|
||||
import SegmentedLikeDislikeButtonView from '../classes/SegmentedLikeDislikeButtonView.js';
|
||||
import ToggleButton from '../classes/ToggleButton.js';
|
||||
import TwoColumnWatchNextResults from '../classes/TwoColumnWatchNextResults.js';
|
||||
import VideoPrimaryInfo from '../classes/VideoPrimaryInfo.js';
|
||||
import VideoSecondaryInfo from '../classes/VideoSecondaryInfo.js';
|
||||
import NavigationEndpoint from '../classes/NavigationEndpoint.js';
|
||||
import PlayerLegacyDesktopYpcTrailer from '../classes/PlayerLegacyDesktopYpcTrailer.js';
|
||||
import YpcTrailer from '../classes/YpcTrailer.js';
|
||||
import StructuredDescriptionContent from '../classes/StructuredDescriptionContent.js';
|
||||
import VideoDescriptionMusicSection from '../classes/VideoDescriptionMusicSection.js';
|
||||
import LiveChatWrap from './LiveChat.js';
|
||||
import MacroMarkersListEntity from '../classes/MacroMarkersListEntity.js';
|
||||
import { ReloadContinuationItemsCommand } from '../index.js';
|
||||
import AppendContinuationItemsAction from '../classes/actions/AppendContinuationItemsAction.js';
|
||||
export default class VideoInfo extends MediaInfo {
|
||||
primary_info;
|
||||
secondary_info;
|
||||
playlist;
|
||||
game_info;
|
||||
merchandise;
|
||||
related_chip_cloud;
|
||||
watch_next_feed;
|
||||
player_overlays;
|
||||
comments_entry_point_header;
|
||||
livechat;
|
||||
autoplay;
|
||||
heat_map;
|
||||
#watch_next_continuation;
|
||||
constructor(data, actions, cpn) {
|
||||
super(data, actions, cpn);
|
||||
const [info, next] = this.page;
|
||||
if (this.streaming_data) {
|
||||
const default_audio_track = this.streaming_data.adaptive_formats.find((format) => format.audio_track?.audio_is_default);
|
||||
if (default_audio_track) {
|
||||
// The combined formats only exist for the default language, even for videos with multiple audio tracks
|
||||
// So we can copy the language from the default audio track to the combined formats
|
||||
this.streaming_data.formats.forEach((format) => format.language = default_audio_track.language);
|
||||
}
|
||||
else if (this.captions?.caption_tracks && this.captions?.caption_tracks.length > 0) {
|
||||
// For videos with a single audio track and captions, we can use the captions to figure out the language of the audio and combined formats
|
||||
const auto_generated_caption_track = this.captions.caption_tracks.find((caption) => caption.kind === 'asr');
|
||||
const language_code = auto_generated_caption_track?.language_code;
|
||||
this.streaming_data.adaptive_formats.forEach((format) => {
|
||||
if (format.has_audio) {
|
||||
format.language = language_code;
|
||||
}
|
||||
});
|
||||
this.streaming_data.formats.forEach((format) => format.language = language_code);
|
||||
}
|
||||
}
|
||||
const two_col = next?.contents?.item().as(TwoColumnWatchNextResults);
|
||||
const results = two_col?.results;
|
||||
const secondary_results = two_col?.secondary_results;
|
||||
if (results && secondary_results) {
|
||||
if (info.microformat?.is(PlayerMicroformat) && info.microformat?.category === 'Gaming') {
|
||||
const row = results.firstOfType(VideoSecondaryInfo)?.metadata?.rows?.firstOfType(RichMetadataRow);
|
||||
if (row?.is(RichMetadataRow)) {
|
||||
this.game_info = {
|
||||
title: row?.contents?.firstOfType(RichMetadata)?.title,
|
||||
release_year: row?.contents?.firstOfType(RichMetadata)?.subtitle
|
||||
};
|
||||
}
|
||||
}
|
||||
this.primary_info = results.firstOfType(VideoPrimaryInfo);
|
||||
this.secondary_info = results.firstOfType(VideoSecondaryInfo);
|
||||
this.merchandise = results.firstOfType(MerchandiseShelf);
|
||||
this.related_chip_cloud = secondary_results.firstOfType(RelatedChipCloud)?.content.as(ChipCloud);
|
||||
if (two_col?.playlist) {
|
||||
this.playlist = two_col.playlist;
|
||||
}
|
||||
this.watch_next_feed = secondary_results.firstOfType(ItemSection)?.contents || secondary_results;
|
||||
if (this.watch_next_feed && Array.isArray(this.watch_next_feed) && this.watch_next_feed.at(-1)?.is(ContinuationItem))
|
||||
this.#watch_next_continuation = this.watch_next_feed.pop()?.as(ContinuationItem);
|
||||
this.player_overlays = next?.player_overlays?.item().as(PlayerOverlay);
|
||||
if (two_col?.autoplay) {
|
||||
this.autoplay = two_col.autoplay;
|
||||
}
|
||||
const segmented_like_dislike_button = this.primary_info?.menu?.top_level_buttons.firstOfType(SegmentedLikeDislikeButton);
|
||||
if (segmented_like_dislike_button?.like_button?.is(ToggleButton) && segmented_like_dislike_button?.dislike_button?.is(ToggleButton)) {
|
||||
this.basic_info.like_count = segmented_like_dislike_button?.like_button?.like_count;
|
||||
this.basic_info.is_liked = segmented_like_dislike_button?.like_button?.is_toggled;
|
||||
this.basic_info.is_disliked = segmented_like_dislike_button?.dislike_button?.is_toggled;
|
||||
}
|
||||
const segmented_like_dislike_button_view = this.primary_info?.menu?.top_level_buttons.firstOfType(SegmentedLikeDislikeButtonView);
|
||||
if (segmented_like_dislike_button_view) {
|
||||
this.basic_info.like_count = segmented_like_dislike_button_view.like_count;
|
||||
if (segmented_like_dislike_button_view.like_button) {
|
||||
const like_status = segmented_like_dislike_button_view.like_button.like_status_entity.like_status;
|
||||
this.basic_info.is_liked = like_status === 'LIKE';
|
||||
this.basic_info.is_disliked = like_status === 'DISLIKE';
|
||||
}
|
||||
}
|
||||
const comments_entry_point = results.find((node) => {
|
||||
return node.is(ItemSection) && node.target_id === 'comments-entry-point';
|
||||
});
|
||||
this.comments_entry_point_header = comments_entry_point?.contents?.firstOfType(CommentsEntryPointHeader);
|
||||
this.livechat = next?.contents_memo?.getType(LiveChat)[0];
|
||||
const macro_markers_list_for_heatmap = this.page[1]?.contents_memo?.getType(MacroMarkersListEntity);
|
||||
let calculated_heat_map = null;
|
||||
if (macro_markers_list_for_heatmap) {
|
||||
const heatmap_markers_entity = macro_markers_list_for_heatmap.find((markers) => markers.isHeatmap());
|
||||
if (heatmap_markers_entity) {
|
||||
try {
|
||||
calculated_heat_map = heatmap_markers_entity.toHeatmap();
|
||||
}
|
||||
catch { /** NO-OP */ }
|
||||
}
|
||||
}
|
||||
this.heat_map = calculated_heat_map;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Applies given filter to the watch next feed. Use {@link filters} to get available filters.
|
||||
* @param target_filter - Filter to apply.
|
||||
*/
|
||||
async selectFilter(target_filter) {
|
||||
if (!this.related_chip_cloud)
|
||||
throw new InnertubeError('Chip cloud not found, cannot apply filter');
|
||||
let cloud_chip;
|
||||
if (typeof target_filter === 'string') {
|
||||
const filter = this.related_chip_cloud?.chips?.find((chip) => chip.text === target_filter);
|
||||
if (!filter)
|
||||
throw new InnertubeError('Invalid filter', { available_filters: this.filters });
|
||||
cloud_chip = filter;
|
||||
}
|
||||
else if (target_filter?.is(ChipCloudChip)) {
|
||||
cloud_chip = target_filter;
|
||||
}
|
||||
else {
|
||||
throw new InnertubeError('Invalid cloud chip', target_filter);
|
||||
}
|
||||
if (cloud_chip.is_selected)
|
||||
return this;
|
||||
const response = await cloud_chip.endpoint?.call(this.actions, { parse: true });
|
||||
const data = response?.on_response_received_endpoints?.find((endpoint) => {
|
||||
return endpoint.is(ReloadContinuationItemsCommand) && endpoint.target_id === 'watch-next-feed';
|
||||
});
|
||||
this.watch_next_feed = data?.contents;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Adds video to the watch history.
|
||||
*/
|
||||
async addToWatchHistory() {
|
||||
return super.addToWatchHistory();
|
||||
}
|
||||
/**
|
||||
* Updates watch time for the video.
|
||||
*/
|
||||
async updateWatchTime(startTime) {
|
||||
return super.updateWatchTime(startTime);
|
||||
}
|
||||
/**
|
||||
* Retrieves watch next feed continuation.
|
||||
*/
|
||||
async getWatchNextContinuation() {
|
||||
if (!this.#watch_next_continuation)
|
||||
throw new InnertubeError('Watch next feed continuation not found');
|
||||
const response = await this.#watch_next_continuation?.endpoint.call(this.actions, { parse: true });
|
||||
const data = response?.on_response_received_endpoints?.firstOfType(AppendContinuationItemsAction);
|
||||
if (!data)
|
||||
throw new InnertubeError('AppendContinuationItemsAction not found');
|
||||
this.watch_next_feed = data?.contents;
|
||||
if (this.watch_next_feed?.at(-1)?.is(ContinuationItem)) {
|
||||
this.#watch_next_continuation = this.watch_next_feed.pop()?.as(ContinuationItem);
|
||||
}
|
||||
else {
|
||||
this.#watch_next_continuation = undefined;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Likes the video.
|
||||
*/
|
||||
async like() {
|
||||
const segmented_like_dislike_button_view = this.primary_info?.menu?.top_level_buttons.firstOfType(SegmentedLikeDislikeButtonView);
|
||||
if (segmented_like_dislike_button_view) {
|
||||
const button = segmented_like_dislike_button_view?.like_button?.toggle_button;
|
||||
if (!button || !button.default_button || !segmented_like_dislike_button_view.like_button)
|
||||
throw new InnertubeError('Like button not found', { video_id: this.basic_info.id });
|
||||
const like_status = segmented_like_dislike_button_view.like_button.like_status_entity.like_status;
|
||||
if (like_status === 'LIKE')
|
||||
throw new InnertubeError('This video is already liked', { video_id: this.basic_info.id });
|
||||
if (!button.default_button.on_tap)
|
||||
throw new InnertubeError('onTap command not found', { video_id: this.basic_info.id });
|
||||
const endpoint = new NavigationEndpoint(button.default_button.on_tap.payload.commands.find((cmd) => cmd.innertubeCommand));
|
||||
return await endpoint.call(this.actions);
|
||||
}
|
||||
const segmented_like_dislike_button = this.primary_info?.menu?.top_level_buttons.firstOfType(SegmentedLikeDislikeButton);
|
||||
const button = segmented_like_dislike_button?.like_button;
|
||||
if (!button)
|
||||
throw new InnertubeError('Like button not found', { video_id: this.basic_info.id });
|
||||
if (!button.is(ToggleButton))
|
||||
throw new InnertubeError('Like button is not a toggle button. This action is likely disabled for this video.', { video_id: this.basic_info.id });
|
||||
if (button.is_toggled)
|
||||
throw new InnertubeError('This video is already liked', { video_id: this.basic_info.id });
|
||||
return await button.endpoint.call(this.actions);
|
||||
}
|
||||
/**
|
||||
* Dislikes the video.
|
||||
*/
|
||||
async dislike() {
|
||||
const segmented_like_dislike_button_view = this.primary_info?.menu?.top_level_buttons.firstOfType(SegmentedLikeDislikeButtonView);
|
||||
if (segmented_like_dislike_button_view) {
|
||||
const button = segmented_like_dislike_button_view?.dislike_button?.toggle_button;
|
||||
if (!button || !button.default_button || !segmented_like_dislike_button_view.dislike_button || !segmented_like_dislike_button_view.like_button)
|
||||
throw new InnertubeError('Dislike button not found', { video_id: this.basic_info.id });
|
||||
const like_status = segmented_like_dislike_button_view.like_button.like_status_entity.like_status;
|
||||
if (like_status === 'DISLIKE')
|
||||
throw new InnertubeError('This video is already disliked', { video_id: this.basic_info.id });
|
||||
if (!button.default_button.on_tap)
|
||||
throw new InnertubeError('onTap command not found', { video_id: this.basic_info.id });
|
||||
const endpoint = new NavigationEndpoint(button.default_button.on_tap.payload.commands.find((cmd) => cmd.innertubeCommand));
|
||||
return await endpoint.call(this.actions);
|
||||
}
|
||||
const segmented_like_dislike_button = this.primary_info?.menu?.top_level_buttons.firstOfType(SegmentedLikeDislikeButton);
|
||||
const button = segmented_like_dislike_button?.dislike_button;
|
||||
if (!button)
|
||||
throw new InnertubeError('Dislike button not found', { video_id: this.basic_info.id });
|
||||
if (!button.is(ToggleButton))
|
||||
throw new InnertubeError('Dislike button is not a toggle button. This action is likely disabled for this video.', { video_id: this.basic_info.id });
|
||||
if (button.is_toggled)
|
||||
throw new InnertubeError('This video is already disliked', { video_id: this.basic_info.id });
|
||||
return await button.endpoint.call(this.actions);
|
||||
}
|
||||
/**
|
||||
* Removes like/dislike.
|
||||
*/
|
||||
async removeRating() {
|
||||
let button;
|
||||
const segmented_like_dislike_button_view = this.primary_info?.menu?.top_level_buttons.firstOfType(SegmentedLikeDislikeButtonView);
|
||||
if (segmented_like_dislike_button_view) {
|
||||
const toggle_button = segmented_like_dislike_button_view?.like_button?.toggle_button;
|
||||
if (!toggle_button || !toggle_button.default_button || !segmented_like_dislike_button_view.like_button)
|
||||
throw new InnertubeError('Like button not found', { video_id: this.basic_info.id });
|
||||
const like_status = segmented_like_dislike_button_view.like_button.like_status_entity.like_status;
|
||||
if (like_status === 'LIKE') {
|
||||
button = segmented_like_dislike_button_view?.like_button?.toggle_button;
|
||||
}
|
||||
else if (like_status === 'DISLIKE') {
|
||||
button = segmented_like_dislike_button_view?.dislike_button?.toggle_button;
|
||||
}
|
||||
else {
|
||||
throw new InnertubeError('This video is not liked/disliked', { video_id: this.basic_info.id });
|
||||
}
|
||||
if (!button || !button.toggled_button)
|
||||
throw new InnertubeError('Like/Dislike button not found', { video_id: this.basic_info.id });
|
||||
if (!button.toggled_button.on_tap)
|
||||
throw new InnertubeError('onTap command not found', { video_id: this.basic_info.id });
|
||||
const endpoint = new NavigationEndpoint(button.toggled_button.on_tap.payload.commands.find((cmd) => cmd.innertubeCommand));
|
||||
return await endpoint.call(this.actions);
|
||||
}
|
||||
const segmented_like_dislike_button = this.primary_info?.menu?.top_level_buttons.firstOfType(SegmentedLikeDislikeButton);
|
||||
const like_button = segmented_like_dislike_button?.like_button;
|
||||
const dislike_button = segmented_like_dislike_button?.dislike_button;
|
||||
if (!like_button?.is(ToggleButton) || !dislike_button?.is(ToggleButton))
|
||||
throw new InnertubeError('Like/Dislike button is not a toggle button. This action is likely disabled for this video.', { video_id: this.basic_info.id });
|
||||
if (like_button?.is_toggled) {
|
||||
button = like_button;
|
||||
}
|
||||
else if (dislike_button?.is_toggled) {
|
||||
button = dislike_button;
|
||||
}
|
||||
if (!button)
|
||||
throw new InnertubeError('This video is not liked/disliked', { video_id: this.basic_info.id });
|
||||
return await button.toggled_endpoint.call(this.actions);
|
||||
}
|
||||
/**
|
||||
* Retrieves Live Chat if available.
|
||||
*/
|
||||
getLiveChat() {
|
||||
if (!this.livechat)
|
||||
throw new InnertubeError('Live Chat is not available', { video_id: this.basic_info.id });
|
||||
return new LiveChatWrap(this);
|
||||
}
|
||||
/**
|
||||
* Retrieves trailer info if available (typically for non-purchased movies or films).
|
||||
* @returns `VideoInfo` for the trailer, or `null` if none.
|
||||
*/
|
||||
getTrailerInfo() {
|
||||
if (this.has_trailer && this.playability_status?.error_screen) {
|
||||
let player_response;
|
||||
if (this.playability_status.error_screen.is(PlayerLegacyDesktopYpcTrailer)) {
|
||||
player_response = this.playability_status.error_screen.trailer?.player_response;
|
||||
}
|
||||
else if (this.playability_status.error_screen.is(YpcTrailer)) {
|
||||
player_response = this.playability_status.error_screen.player_response;
|
||||
}
|
||||
if (player_response) {
|
||||
return new VideoInfo([{ data: player_response }], this.actions, this.cpn);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Watch next feed filters.
|
||||
*/
|
||||
get filters() {
|
||||
return this.related_chip_cloud?.chips?.map((chip) => chip.text?.toString()) || [];
|
||||
}
|
||||
/**
|
||||
* Checks if continuation is available for the watch next feed.
|
||||
*/
|
||||
get wn_has_continuation() {
|
||||
return !!this.#watch_next_continuation;
|
||||
}
|
||||
/**
|
||||
* Gets the endpoint of the autoplay video
|
||||
*/
|
||||
get autoplay_video_endpoint() {
|
||||
return this.autoplay?.sets?.[0]?.autoplay_video || null;
|
||||
}
|
||||
/**
|
||||
* Checks if trailer is available.
|
||||
*/
|
||||
get has_trailer() {
|
||||
return !!this.playability_status?.error_screen?.is(PlayerLegacyDesktopYpcTrailer, YpcTrailer);
|
||||
}
|
||||
/**
|
||||
* Get songs used in the video.
|
||||
*/
|
||||
get music_tracks() {
|
||||
// @TODO: Refactor this.
|
||||
const description_content = this.page[1]?.engagement_panels?.filter((panel) => panel.content?.is(StructuredDescriptionContent));
|
||||
if (description_content !== undefined && description_content.length > 0) {
|
||||
const music_section = description_content[0].content?.as(StructuredDescriptionContent)?.items?.filterType(VideoDescriptionMusicSection);
|
||||
if (music_section !== undefined && music_section.length > 0) {
|
||||
return music_section[0].carousel_lockups?.map((lookup) => {
|
||||
let song;
|
||||
let artist;
|
||||
let album;
|
||||
let license;
|
||||
let videoId;
|
||||
let channelId;
|
||||
// If the song isn't in the video_lockup, it should be in the info_rows
|
||||
song = lookup.video_lockup?.title?.toString();
|
||||
// If the video id isn't in the video_lockup, it should be in the info_rows
|
||||
videoId = lookup.video_lockup?.endpoint?.payload.videoId;
|
||||
for (let i = 0; i < lookup.info_rows.length; i++) {
|
||||
const info_row = lookup.info_rows[i];
|
||||
if (info_row.info_row_expand_status_key === undefined) {
|
||||
if (song === undefined) {
|
||||
song = info_row.default_metadata?.toString() || info_row.expanded_metadata?.toString();
|
||||
if (videoId === undefined) {
|
||||
const endpoint = info_row.default_metadata?.endpoint || info_row.expanded_metadata?.endpoint;
|
||||
videoId = endpoint?.payload?.videoId;
|
||||
}
|
||||
}
|
||||
else {
|
||||
album = info_row.default_metadata?.toString() || info_row.expanded_metadata?.toString();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (info_row.info_row_expand_status_key?.indexOf('structured-description-music-section-artists-row-state-id') !== -1) {
|
||||
artist = info_row.default_metadata?.toString() || info_row.expanded_metadata?.toString();
|
||||
if (channelId === undefined) {
|
||||
const endpoint = info_row.default_metadata?.endpoint || info_row.expanded_metadata?.endpoint;
|
||||
channelId = endpoint?.payload?.browseId;
|
||||
}
|
||||
}
|
||||
if (info_row.info_row_expand_status_key?.indexOf('structured-description-music-section-licenses-row-state-id') !== -1) {
|
||||
license = info_row.default_metadata?.toString() || info_row.expanded_metadata?.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return { song, artist, album, license, videoId, channelId };
|
||||
});
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=VideoInfo.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+18
@@ -0,0 +1,18 @@
|
||||
export { default as AccountInfo } from './AccountInfo.js';
|
||||
export { default as Channel } from './Channel.js';
|
||||
export * from './Channel.js';
|
||||
export { default as Comments } from './Comments.js';
|
||||
export { default as Guide } from './Guide.js';
|
||||
export { default as History } from './History.js';
|
||||
export { default as HomeFeed } from './HomeFeed.js';
|
||||
export { default as HashtagFeed } from './HashtagFeed.js';
|
||||
export { default as ItemMenu } from './ItemMenu.js';
|
||||
export { default as Library } from './Library.js';
|
||||
export { default as LiveChat } from './LiveChat.js';
|
||||
export { default as NotificationsMenu } from './NotificationsMenu.js';
|
||||
export { default as Playlist } from './Playlist.js';
|
||||
export { default as Search } from './Search.js';
|
||||
export { default as Settings } from './Settings.js';
|
||||
export { default as SmoothedQueue } from './SmoothedQueue.js';
|
||||
export { default as VideoInfo } from './VideoInfo.js';
|
||||
export { default as TranscriptInfo } from './TranscriptInfo.js';
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
export { default as AccountInfo } from './AccountInfo.js';
|
||||
export { default as Channel } from './Channel.js';
|
||||
export * from './Channel.js';
|
||||
export { default as Comments } from './Comments.js';
|
||||
export { default as Guide } from './Guide.js';
|
||||
export { default as History } from './History.js';
|
||||
export { default as HomeFeed } from './HomeFeed.js';
|
||||
export { default as HashtagFeed } from './HashtagFeed.js';
|
||||
export { default as ItemMenu } from './ItemMenu.js';
|
||||
export { default as Library } from './Library.js';
|
||||
export { default as LiveChat } from './LiveChat.js';
|
||||
export { default as NotificationsMenu } from './NotificationsMenu.js';
|
||||
export { default as Playlist } from './Playlist.js';
|
||||
export { default as Search } from './Search.js';
|
||||
export { default as Settings } from './Settings.js';
|
||||
export { default as SmoothedQueue } from './SmoothedQueue.js';
|
||||
export { default as VideoInfo } from './VideoInfo.js';
|
||||
export { default as TranscriptInfo } from './TranscriptInfo.js';
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/parser/youtube/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
||||
Reference in New Issue
Block a user