From 93d4e1d05d25ccef9cefafc44a659a805b4c0409 Mon Sep 17 00:00:00 2001 From: LuanRT Date: Sun, 10 Sep 2023 01:03:03 -0300 Subject: [PATCH 1/7] feat: Add support for retrieving transcripts --- src/Innertube.ts | 33 +++++++++++++- .../classes/EngagementPanelSectionList.ts | 7 +-- src/parser/classes/FancyDismissibleDialog.ts | 16 +++++++ src/parser/classes/ProductList.ts | 13 ++++++ src/parser/classes/ProductListHeader.ts | 16 +++++++ src/parser/classes/ProductListItem.ts | 30 +++++++++++++ .../classes/StructuredDescriptionContent.ts | 3 +- src/parser/classes/Transcript.ts | 14 ++++++ src/parser/classes/TranscriptFooter.ts | 14 ++++++ src/parser/classes/TranscriptSearchBox.ts | 22 +++++++++ src/parser/classes/TranscriptSearchPanel.ts | 22 +++++++++ src/parser/classes/TranscriptSegment.ts | 22 +++++++++ src/parser/classes/TranscriptSegmentList.ts | 21 +++++++++ src/parser/classes/UploadTimeFactoid.ts | 14 ++++++ src/parser/classes/VideoDescriptionHeader.ts | 6 ++- .../VideoDescriptionTranscriptSection.ts | 19 ++++++++ src/parser/classes/ViewCountFactoid.ts | 18 ++++++++ .../actions/UpdateEngagementPanelAction.ts | 16 +++++++ src/parser/classes/menus/MenuPopup.ts | 15 +++++++ src/parser/nodes.ts | 15 +++++++ src/parser/parser.ts | 45 ++++++++++--------- src/parser/types/ParsedResponse.ts | 10 ++++- 22 files changed, 362 insertions(+), 29 deletions(-) create mode 100644 src/parser/classes/FancyDismissibleDialog.ts create mode 100644 src/parser/classes/ProductList.ts create mode 100644 src/parser/classes/ProductListHeader.ts create mode 100644 src/parser/classes/ProductListItem.ts create mode 100644 src/parser/classes/Transcript.ts create mode 100644 src/parser/classes/TranscriptFooter.ts create mode 100644 src/parser/classes/TranscriptSearchBox.ts create mode 100644 src/parser/classes/TranscriptSearchPanel.ts create mode 100644 src/parser/classes/TranscriptSegment.ts create mode 100644 src/parser/classes/TranscriptSegmentList.ts create mode 100644 src/parser/classes/UploadTimeFactoid.ts create mode 100644 src/parser/classes/VideoDescriptionTranscriptSection.ts create mode 100644 src/parser/classes/ViewCountFactoid.ts create mode 100644 src/parser/classes/actions/UpdateEngagementPanelAction.ts create mode 100644 src/parser/classes/menus/MenuPopup.ts diff --git a/src/Innertube.ts b/src/Innertube.ts index 6cdcf9738..daa800d3f 100644 --- a/src/Innertube.ts +++ b/src/Innertube.ts @@ -14,6 +14,8 @@ import NotificationsMenu from './parser/youtube/NotificationsMenu.js'; import Playlist from './parser/youtube/Playlist.js'; import Search from './parser/youtube/Search.js'; import VideoInfo from './parser/youtube/VideoInfo.js'; +import ContinuationItem from './parser/classes/ContinuationItem.js'; +import Transcript from './parser/classes/Transcript.js'; import { Kids, Music, Studio } from './core/clients/index.js'; import { AccountManager, InteractionManager, PlaylistManager } from './core/managers/index.js'; @@ -36,7 +38,7 @@ import { import { GetUnseenCountEndpoint } from './core/endpoints/notification/index.js'; import type { ApiResponse } from './core/Actions.js'; -import type { IBrowseResponse, IParsedResponse } from './parser/types/index.js'; +import { type IGetTranscriptResponse, type IBrowseResponse, type IParsedResponse } from './parser/types/index.js'; import type { INextRequest } from './types/index.js'; import type { DownloadOptions, FormatOptions } from './types/FormatUtils.js'; @@ -332,6 +334,35 @@ export default class Innertube { return info.chooseFormat(options); } + /** + * Retrieves a video's transcript. + * @param video_id - The video id. + */ + async getTranscript(video_id: string): Promise { + throwIfMissing({ video_id }); + + const next_response = await this.actions.execute(NextEndpoint.PATH, { ...NextEndpoint.build({ video_id }), parse: true }); + + if (!next_response.engagement_panels) + throw new InnertubeError('Engagement panels not found. Video likely has no transcript.', next_response); + + const transcript_panel = next_response.engagement_panels.get({ + panel_identifier: 'engagement-panel-searchable-transcript' + }); + + if (!transcript_panel) + throw new InnertubeError('Transcript panel not found.', next_response); + + const transcript_continuation = transcript_panel.content?.as(ContinuationItem); + + if (!transcript_continuation) + throw new InnertubeError('Transcript continuation not found.', transcript_panel); + + const transcript_response = await transcript_continuation.endpoint.call(this.actions, { parse: true }); + + return transcript_response.actions_memo.getType(Transcript).first(); + } + /** * Downloads a given video. If you only need the direct download link see {@link getStreamingData}. * If you wish to retrieve the video info too, have a look at {@link getBasicInfo} or {@link getInfo}. diff --git a/src/parser/classes/EngagementPanelSectionList.ts b/src/parser/classes/EngagementPanelSectionList.ts index 61e5094a0..7d7b0c47c 100644 --- a/src/parser/classes/EngagementPanelSectionList.ts +++ b/src/parser/classes/EngagementPanelSectionList.ts @@ -3,6 +3,7 @@ import Parser, { type RawNode } from '../index.js'; import ContinuationItem from './ContinuationItem.js'; import EngagementPanelTitleHeader from './EngagementPanelTitleHeader.js'; import MacroMarkersList from './MacroMarkersList.js'; +import ProductList from './ProductList.js'; import SectionList from './SectionList.js'; import StructuredDescriptionContent from './StructuredDescriptionContent.js'; @@ -10,7 +11,7 @@ export default class EngagementPanelSectionList extends YTNode { static type = 'EngagementPanelSectionList'; header: EngagementPanelTitleHeader | null; - content: SectionList | ContinuationItem | StructuredDescriptionContent | MacroMarkersList | null; + content: SectionList | ContinuationItem | StructuredDescriptionContent | MacroMarkersList | ProductList | null; target_id?: string; panel_identifier?: string; visibility?: string; @@ -18,9 +19,9 @@ export default class EngagementPanelSectionList extends YTNode { constructor(data: RawNode) { super(); this.header = Parser.parseItem(data.header, EngagementPanelTitleHeader); - this.content = Parser.parseItem(data.content, [ SectionList, ContinuationItem, StructuredDescriptionContent, MacroMarkersList ]); + this.content = Parser.parseItem(data.content, [ SectionList, ContinuationItem, StructuredDescriptionContent, MacroMarkersList, ProductList]); this.panel_identifier = data.panelIdentifier; this.target_id = data.targetId; this.visibility = data.visibility; } -} +} \ No newline at end of file diff --git a/src/parser/classes/FancyDismissibleDialog.ts b/src/parser/classes/FancyDismissibleDialog.ts new file mode 100644 index 000000000..d0017e76b --- /dev/null +++ b/src/parser/classes/FancyDismissibleDialog.ts @@ -0,0 +1,16 @@ +import { YTNode } from "../helpers.js"; +import { RawNode } from "../index.js"; +import { Text } from "../misc.js"; + +export default class FancyDismissibleDialog extends YTNode { + static type = 'FancyDismissibleDialog'; + + dialog_message: Text; + confirm_label: Text; + + constructor(data: RawNode) { + super(); + this.dialog_message = new Text(data.dialogMessage); + this.confirm_label = new Text(data.confirmLabel); + } +} \ No newline at end of file diff --git a/src/parser/classes/ProductList.ts b/src/parser/classes/ProductList.ts new file mode 100644 index 000000000..24889eb39 --- /dev/null +++ b/src/parser/classes/ProductList.ts @@ -0,0 +1,13 @@ +import { ObservedArray, YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; + +export default class ProductList extends YTNode { + static type = 'ProductList'; + + contents: ObservedArray; + + constructor(data: RawNode) { + super(); + this.contents = Parser.parseArray(data.contents); + } +} \ No newline at end of file diff --git a/src/parser/classes/ProductListHeader.ts b/src/parser/classes/ProductListHeader.ts new file mode 100644 index 000000000..c47b1903d --- /dev/null +++ b/src/parser/classes/ProductListHeader.ts @@ -0,0 +1,16 @@ +import { YTNode } from "../helpers.js"; +import { RawNode } from "../index.js"; +import { Text } from "../misc.js"; + +export default class ProductListHeader extends YTNode { + static type = 'ProductListHeader'; + + title: Text; + suppress_padding_disclaimer: boolean; + + constructor(data: RawNode) { + super(); + this.title = new Text(data.title); + this.suppress_padding_disclaimer = !!data.suppressPaddingDisclaimer; + } +} \ No newline at end of file diff --git a/src/parser/classes/ProductListItem.ts b/src/parser/classes/ProductListItem.ts new file mode 100644 index 000000000..652ad2597 --- /dev/null +++ b/src/parser/classes/ProductListItem.ts @@ -0,0 +1,30 @@ +import { YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; +import { Text, Thumbnail } from "../misc.js"; +import Button from "./Button.js"; +import NavigationEndpoint from "./NavigationEndpoint.js"; + +export default class ProductListItem extends YTNode { + static type = 'ProductListItem'; + + title: Text; + accessibility_title: string; + thumbnail: Thumbnail[]; + price: string; + endpoint: NavigationEndpoint; + merchant_name: string; + stay_in_app: boolean; + view_button: Button | null; + + constructor(data: RawNode) { + super(); + this.title = new Text(data.title); + this.accessibility_title = data.accessibilityTitle; + this.thumbnail = Thumbnail.fromResponse(data.thumbnail); + this.price = data.price; + this.endpoint = new NavigationEndpoint(data.onClickCommand); + this.merchant_name = data.merchantName; + this.stay_in_app = !!data.stayInApp; + this.view_button = Parser.parseItem(data.viewButton, Button); + } +} \ No newline at end of file diff --git a/src/parser/classes/StructuredDescriptionContent.ts b/src/parser/classes/StructuredDescriptionContent.ts index 48dc939ec..699785c2c 100644 --- a/src/parser/classes/StructuredDescriptionContent.ts +++ b/src/parser/classes/StructuredDescriptionContent.ts @@ -5,11 +5,12 @@ import HorizontalCardList from './HorizontalCardList.js'; import VideoDescriptionHeader from './VideoDescriptionHeader.js'; import VideoDescriptionInfocardsSection from './VideoDescriptionInfocardsSection.js'; import VideoDescriptionMusicSection from './VideoDescriptionMusicSection.js'; +import VideoDescriptionTranscriptSection from './VideoDescriptionTranscriptSection.js'; export default class StructuredDescriptionContent extends YTNode { static type = 'StructuredDescriptionContent'; - items: ObservedArray; + items: ObservedArray; constructor(data: RawNode) { super(); diff --git a/src/parser/classes/Transcript.ts b/src/parser/classes/Transcript.ts new file mode 100644 index 000000000..7bc9fa143 --- /dev/null +++ b/src/parser/classes/Transcript.ts @@ -0,0 +1,14 @@ +import { YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; +import TranscriptSearchPanel from "./TranscriptSearchPanel.js"; + +export default class Transcript extends YTNode { + static type = 'Transcript'; + + content: TranscriptSearchPanel | null; + + constructor(data: RawNode) { + super(); + this.content = Parser.parseItem(data.content, TranscriptSearchPanel); + } +} \ No newline at end of file diff --git a/src/parser/classes/TranscriptFooter.ts b/src/parser/classes/TranscriptFooter.ts new file mode 100644 index 000000000..531513ed7 --- /dev/null +++ b/src/parser/classes/TranscriptFooter.ts @@ -0,0 +1,14 @@ +import { YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; +import SortFilterSubMenu from "./SortFilterSubMenu.js"; + +export default class TranscriptFooter extends YTNode { + static type = 'TranscriptFooter'; + + language_menu: SortFilterSubMenu | null; + + constructor(data: RawNode) { + super(); + this.language_menu = Parser.parseItem(data.languageMenu, SortFilterSubMenu); + } +} \ No newline at end of file diff --git a/src/parser/classes/TranscriptSearchBox.ts b/src/parser/classes/TranscriptSearchBox.ts new file mode 100644 index 000000000..5ddb8884f --- /dev/null +++ b/src/parser/classes/TranscriptSearchBox.ts @@ -0,0 +1,22 @@ +import { YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; +import Button from "./Button.js"; +import NavigationEndpoint from "./NavigationEndpoint.js"; +import { Text } from "../misc.js"; + +export default class TranscriptSearchBox extends YTNode { + static type = 'TranscriptSearchBox'; + + formatted_placeholder: Text; + clear_button: Button | null; + endpoint: NavigationEndpoint; + search_button: Button | null; + + constructor(data: RawNode) { + super(); + this.formatted_placeholder = new Text(data.formattedPlaceholder); + this.clear_button = Parser.parseItem(data.clearButton, Button); + this.endpoint = new NavigationEndpoint(data.onTextChangeCommand); + this.search_button = Parser.parseItem(data.searchButton, Button); + } +} \ No newline at end of file diff --git a/src/parser/classes/TranscriptSearchPanel.ts b/src/parser/classes/TranscriptSearchPanel.ts new file mode 100644 index 000000000..776cc6d0a --- /dev/null +++ b/src/parser/classes/TranscriptSearchPanel.ts @@ -0,0 +1,22 @@ +import { YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; +import TranscriptFooter from "./TranscriptFooter.js"; +import TranscriptSearchBox from "./TranscriptSearchBox.js"; +import TranscriptSegmentList from "./TranscriptSegmentList.js"; + +export default class TranscriptSearchPanel extends YTNode { + static type = 'TranscriptSearchPanel'; + + header: TranscriptSearchBox | null; + body: TranscriptSegmentList | null; + footer: TranscriptFooter | null; + target_id: string; + + constructor(data: RawNode) { + super(); + this.header = Parser.parseItem(data.header, TranscriptSearchBox); + this.body = Parser.parseItem(data.body, TranscriptSegmentList); + this.footer = Parser.parseItem(data.footer, TranscriptFooter); + this.target_id = data.targetId; + } +} \ No newline at end of file diff --git a/src/parser/classes/TranscriptSegment.ts b/src/parser/classes/TranscriptSegment.ts new file mode 100644 index 000000000..ec32b3341 --- /dev/null +++ b/src/parser/classes/TranscriptSegment.ts @@ -0,0 +1,22 @@ +import { YTNode } from "../helpers.js"; +import { RawNode } from "../index.js"; +import { Text } from "../misc.js"; + +export default class TranscriptSegment extends YTNode { + static type = 'TranscriptSegment'; + + start_ms: string; + end_ms: string; + snippet: Text; + start_time_text: Text; + target_id: string; + + constructor(data: RawNode) { + super(); + this.start_ms = data.startMs; + this.end_ms = data.endMs; + this.snippet = new Text(data.snippet); + this.start_time_text = new Text(data.startTimeText); + this.target_id = data.targetId; + } +} \ No newline at end of file diff --git a/src/parser/classes/TranscriptSegmentList.ts b/src/parser/classes/TranscriptSegmentList.ts new file mode 100644 index 000000000..055a6af20 --- /dev/null +++ b/src/parser/classes/TranscriptSegmentList.ts @@ -0,0 +1,21 @@ +import { ObservedArray, YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; +import { Text } from "../misc.js"; +import TranscriptSegment from "./TranscriptSegment.js"; + +export default class TranscriptSegmentList extends YTNode { + static type = 'TranscriptSegmentList'; + + initial_segments: ObservedArray; + no_result_label: Text; + retry_label: Text; + touch_captions_enabled: boolean; + + constructor(data: RawNode) { + super(); + this.initial_segments = Parser.parseArray(data.initialSegments, TranscriptSegment); + this.no_result_label = new Text(data.noResultLabel); + this.retry_label = new Text(data.retryLabel); + this.touch_captions_enabled = data.touchCaptionsEnabled; + } +} \ No newline at end of file diff --git a/src/parser/classes/UploadTimeFactoid.ts b/src/parser/classes/UploadTimeFactoid.ts new file mode 100644 index 000000000..04cb98262 --- /dev/null +++ b/src/parser/classes/UploadTimeFactoid.ts @@ -0,0 +1,14 @@ +import { YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; +import Factoid from "./Factoid.js"; + +export default class UploadTimeFactoid extends YTNode { + static type = 'UploadTimeFactoid'; + + factoid: Factoid | null; + + constructor(data: RawNode) { + super(); + this.factoid = Parser.parseItem(data.factoid, Factoid); + } +} \ No newline at end of file diff --git a/src/parser/classes/VideoDescriptionHeader.ts b/src/parser/classes/VideoDescriptionHeader.ts index 1a94c2f62..03a2277e2 100644 --- a/src/parser/classes/VideoDescriptionHeader.ts +++ b/src/parser/classes/VideoDescriptionHeader.ts @@ -3,6 +3,8 @@ import Parser, { type RawNode } from '../index.js'; import { Text, Thumbnail } from '../misc.js'; import Factoid from './Factoid.js'; import NavigationEndpoint from './NavigationEndpoint.js'; +import UploadTimeFactoid from './UploadTimeFactoid.js'; +import ViewCountFactoid from './ViewCountFactoid.js'; export default class VideoDescriptionHeader extends YTNode { static type = 'VideoDescriptionHeader'; @@ -10,7 +12,7 @@ export default class VideoDescriptionHeader extends YTNode { channel: Text; channel_navigation_endpoint?: NavigationEndpoint; channel_thumbnail: Thumbnail[]; - factoids: ObservedArray; + factoids: ObservedArray; publish_date: Text; title: Text; views: Text; @@ -23,6 +25,6 @@ export default class VideoDescriptionHeader extends YTNode { this.channel_thumbnail = Thumbnail.fromResponse(data.channelThumbnail); this.publish_date = new Text(data.publishDate); this.views = new Text(data.views); - this.factoids = Parser.parseArray(data.factoid, Factoid); + this.factoids = Parser.parseArray(data.factoid, [Factoid, ViewCountFactoid, UploadTimeFactoid]); } } diff --git a/src/parser/classes/VideoDescriptionTranscriptSection.ts b/src/parser/classes/VideoDescriptionTranscriptSection.ts new file mode 100644 index 000000000..0922d81ff --- /dev/null +++ b/src/parser/classes/VideoDescriptionTranscriptSection.ts @@ -0,0 +1,19 @@ +import { YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; +import { Text } from "../misc.js"; +import Button from "./Button.js"; + +export default class VideoDescriptionTranscriptSection extends YTNode { + static type = 'VideoDescriptionTranscriptSection'; + + section_title: Text; + sub_header_text: Text; + primary_button: Button | null; + + constructor(data: RawNode) { + super(); + this.section_title = new Text(data.sectionTitle); + this.sub_header_text = new Text(data.subHeaderText); + this.primary_button = Parser.parseItem(data.primaryButton, Button); + } +} \ No newline at end of file diff --git a/src/parser/classes/ViewCountFactoid.ts b/src/parser/classes/ViewCountFactoid.ts new file mode 100644 index 000000000..d55efaa9d --- /dev/null +++ b/src/parser/classes/ViewCountFactoid.ts @@ -0,0 +1,18 @@ +import { YTNode } from "../helpers.js"; +import Parser, { RawNode } from "../index.js"; +import Factoid from "./Factoid.js"; + +export default class ViewCountFactoid extends YTNode { + static type = 'ViewCountFactoid'; + + view_count_entity_key: string; + factoid: Factoid | null; + view_count_type: string; + + constructor(data: RawNode) { + super(); + this.view_count_entity_key = data.viewCountEntityKey; + this.factoid = Parser.parseItem(data.factoid, [Factoid]); + this.view_count_type = data.viewCountType; + } +} diff --git a/src/parser/classes/actions/UpdateEngagementPanelAction.ts b/src/parser/classes/actions/UpdateEngagementPanelAction.ts new file mode 100644 index 000000000..cb1e30af0 --- /dev/null +++ b/src/parser/classes/actions/UpdateEngagementPanelAction.ts @@ -0,0 +1,16 @@ +import { YTNode } from "../../helpers.js"; +import Parser, { RawNode } from "../../index.js"; +import Transcript from "../Transcript.js"; + +export default class UpdateEngagementPanelAction extends YTNode { + static type = 'UpdateEngagementPanelAction'; + + target_id: string; + content: Transcript | null; + + constructor(data: RawNode) { + super(); + this.target_id = data.targetId; + this.content = Parser.parseItem(data.content, Transcript); + } +} \ No newline at end of file diff --git a/src/parser/classes/menus/MenuPopup.ts b/src/parser/classes/menus/MenuPopup.ts new file mode 100644 index 000000000..6023d143a --- /dev/null +++ b/src/parser/classes/menus/MenuPopup.ts @@ -0,0 +1,15 @@ +import { ObservedArray, YTNode } from "../../helpers.js"; +import Parser, { RawNode } from "../../index.js"; +import MenuNavigationItem from "./MenuNavigationItem.js"; +import MenuServiceItem from "./MenuServiceItem.js"; + +export default class MenuPopup extends YTNode { + static type = 'MenuPopup'; + + items: ObservedArray; + + constructor(data: RawNode) { + super(); + this.items = Parser.parseArray(data.items, [MenuNavigationItem, MenuServiceItem]); + } +} \ No newline at end of file diff --git a/src/parser/nodes.ts b/src/parser/nodes.ts index 497245725..c1cc83314 100644 --- a/src/parser/nodes.ts +++ b/src/parser/nodes.ts @@ -7,6 +7,7 @@ export { default as AccountItemSectionHeader } from './classes/AccountItemSectio export { default as AccountSectionList } from './classes/AccountSectionList.js'; export { default as AppendContinuationItemsAction } from './classes/actions/AppendContinuationItemsAction.js'; export { default as OpenPopupAction } from './classes/actions/OpenPopupAction.js'; +export { default as UpdateEngagementPanelAction } from './classes/actions/UpdateEngagementPanelAction.js'; export { default as Alert } from './classes/Alert.js'; export { default as AlertWithButton } from './classes/AlertWithButton.js'; export { default as AnalyticsMainAppKeyMetrics } from './classes/analytics/AnalyticsMainAppKeyMetrics.js'; @@ -102,6 +103,7 @@ export { default as ExpandableTab } from './classes/ExpandableTab.js'; export { default as ExpandableVideoDescriptionBody } from './classes/ExpandableVideoDescriptionBody.js'; export { default as ExpandedShelfContents } from './classes/ExpandedShelfContents.js'; export { default as Factoid } from './classes/Factoid.js'; +export { default as FancyDismissibleDialog } from './classes/FancyDismissibleDialog.js'; export { default as FeedFilterChipBar } from './classes/FeedFilterChipBar.js'; export { default as FeedTabbedHeader } from './classes/FeedTabbedHeader.js'; export { default as GameCard } from './classes/GameCard.js'; @@ -191,6 +193,7 @@ export { default as MacroMarkersList } from './classes/MacroMarkersList.js'; export { default as MacroMarkersListItem } from './classes/MacroMarkersListItem.js'; export { default as Menu } from './classes/menus/Menu.js'; export { default as MenuNavigationItem } from './classes/menus/MenuNavigationItem.js'; +export { default as MenuPopup } from './classes/menus/MenuPopup.js'; export { default as MenuServiceItem } from './classes/menus/MenuServiceItem.js'; export { default as MenuServiceItemDownload } from './classes/menus/MenuServiceItemDownload.js'; export { default as MultiPageMenu } from './classes/menus/MultiPageMenu.js'; @@ -274,6 +277,9 @@ export { default as PlaylistVideoThumbnail } from './classes/PlaylistVideoThumbn export { default as Poll } from './classes/Poll.js'; export { default as Post } from './classes/Post.js'; export { default as PostMultiImage } from './classes/PostMultiImage.js'; +export { default as ProductList } from './classes/ProductList.js'; +export { default as ProductListHeader } from './classes/ProductListHeader.js'; +export { default as ProductListItem } from './classes/ProductListItem.js'; export { default as ProfileColumn } from './classes/ProfileColumn.js'; export { default as ProfileColumnStats } from './classes/ProfileColumnStats.js'; export { default as ProfileColumnStatsEntry } from './classes/ProfileColumnStatsEntry.js'; @@ -349,10 +355,17 @@ export { default as ToggleButton } from './classes/ToggleButton.js'; export { default as ToggleMenuServiceItem } from './classes/ToggleMenuServiceItem.js'; export { default as Tooltip } from './classes/Tooltip.js'; export { default as TopicChannelDetails } from './classes/TopicChannelDetails.js'; +export { default as Transcript } from './classes/Transcript.js'; +export { default as TranscriptFooter } from './classes/TranscriptFooter.js'; +export { default as TranscriptSearchBox } from './classes/TranscriptSearchBox.js'; +export { default as TranscriptSearchPanel } from './classes/TranscriptSearchPanel.js'; +export { default as TranscriptSegment } from './classes/TranscriptSegment.js'; +export { default as TranscriptSegmentList } from './classes/TranscriptSegmentList.js'; export { default as TwoColumnBrowseResults } from './classes/TwoColumnBrowseResults.js'; export { default as TwoColumnSearchResults } from './classes/TwoColumnSearchResults.js'; export { default as TwoColumnWatchNextResults } from './classes/TwoColumnWatchNextResults.js'; export { default as UniversalWatchCard } from './classes/UniversalWatchCard.js'; +export { default as UploadTimeFactoid } from './classes/UploadTimeFactoid.js'; export { default as UpsellDialog } from './classes/UpsellDialog.js'; export { default as VerticalList } from './classes/VerticalList.js'; export { default as VerticalWatchCardList } from './classes/VerticalWatchCardList.js'; @@ -361,10 +374,12 @@ export { default as VideoCard } from './classes/VideoCard.js'; export { default as VideoDescriptionHeader } from './classes/VideoDescriptionHeader.js'; export { default as VideoDescriptionInfocardsSection } from './classes/VideoDescriptionInfocardsSection.js'; export { default as VideoDescriptionMusicSection } from './classes/VideoDescriptionMusicSection.js'; +export { default as VideoDescriptionTranscriptSection } from './classes/VideoDescriptionTranscriptSection.js'; export { default as VideoInfoCardContent } from './classes/VideoInfoCardContent.js'; export { default as VideoOwner } from './classes/VideoOwner.js'; export { default as VideoPrimaryInfo } from './classes/VideoPrimaryInfo.js'; export { default as VideoSecondaryInfo } from './classes/VideoSecondaryInfo.js'; +export { default as ViewCountFactoid } from './classes/ViewCountFactoid.js'; export { default as WatchCardCompactVideo } from './classes/WatchCardCompactVideo.js'; export { default as WatchCardHeroVideo } from './classes/WatchCardHeroVideo.js'; export { default as WatchCardRichHeader } from './classes/WatchCardRichHeader.js'; diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 31612bf45..c29a41e8d 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -7,6 +7,7 @@ import PlayerLiveStoryboardSpec from './classes/PlayerLiveStoryboardSpec.js'; import PlayerStoryboardSpec from './classes/PlayerStoryboardSpec.js'; import Alert from './classes/Alert.js'; import AlertWithButton from './classes/AlertWithButton.js'; +import EngagementPanelSectionList from './classes/EngagementPanelSectionList.js'; import type { IParsedResponse, IRawResponse, RawData, RawNode } from './types/index.js'; @@ -21,7 +22,13 @@ import { Memo, observe, SuperParsedResult } from './helpers.js'; import * as YTNodes from './nodes.js'; import type { KeyInfo } from './generator.js'; import { camelToSnake, generateRuntimeClass, generateTypescriptClass } from './generator.js'; -import { Continuation, ItemSectionContinuation, SectionListContinuation, LiveChatContinuation, MusicPlaylistShelfContinuation, MusicShelfContinuation, GridContinuation, PlaylistPanelContinuation, NavigateAction, ShowMiniplayerCommand, ReloadContinuationItemsCommand } from './continuations.js'; + +import { + Continuation, ItemSectionContinuation, SectionListContinuation, + LiveChatContinuation, MusicPlaylistShelfContinuation, MusicShelfContinuation, + GridContinuation, PlaylistPanelContinuation, NavigateAction, ShowMiniplayerCommand, + ReloadContinuationItemsCommand +} from './continuations.js'; export type ParserError = { classname: string, @@ -82,8 +89,8 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError) new InnertubeError( `Something went wrong at ${classname}!\n` + `This is a bug, please report it at ${Platform.shim.info.bugs_url}`, { - stack: context.error.stack - } + stack: context.error.stack + } ) ); } @@ -125,7 +132,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError) case 'class_changed': console.warn( `${classname} changed!\n` + - `The following keys where altered: ${context.changed_keys.map(([ key ]) => camelToSnake(key)).join(', ')}\n` + + `The following keys where altered: ${context.changed_keys.map(([key]) => camelToSnake(key)).join(', ')}\n` + `The class has changed to:\n${generateTypescriptClass(classname, context.key_info)}` ); break; @@ -155,7 +162,7 @@ function _addToMemo(classname: string, result: YTNode) { const list = MEMO.get(classname); if (!list) - return MEMO.set(classname, [ result ]); + return MEMO.set(classname, [result]); list.push(result); } @@ -289,6 +296,14 @@ export function parseResponse(data: } _clearMemo(); + _createMemo(); + const items = parse(data.items); + if (items) { + parsed_data.items = items; + parsed_data.items_memo = _getMemo(); + } + _clearMemo(); + applyMutations(contents_memo, data.frameworkUpdates?.entityBatchUpdate?.mutations); const continuation = data.continuation ? parseC(data.continuation) : null; @@ -311,7 +326,7 @@ export function parseResponse(data: parsed_data.overlay = overlay; } - const alerts = parseArray(data.alerts, [ Alert, AlertWithButton ]); + const alerts = parseArray(data.alerts, [Alert, AlertWithButton]); if (alerts.length) { parsed_data.alerts = alerts; } @@ -389,7 +404,7 @@ export function parseResponse(data: parsed_data.annotations = annotations; } - const storyboards = parseItem(data.storyboards, [ PlayerStoryboardSpec, PlayerLiveStoryboardSpec ]); + const storyboards = parseItem(data.storyboards, [PlayerStoryboardSpec, PlayerLiveStoryboardSpec]); if (storyboards) { parsed_data.storyboards = storyboards; } @@ -404,20 +419,10 @@ export function parseResponse(data: parsed_data.cards = cards; } - const engagement_panels = data.engagementPanels?.map((e) => { - const item = parseItem(e, YTNodes.EngagementPanelSectionList) as YTNodes.EngagementPanelSectionList; - return item; - }); - if (engagement_panels) { + const engagement_panels = parseArray(data.engagementPanels, EngagementPanelSectionList); + if (engagement_panels.length) { parsed_data.engagement_panels = engagement_panels; } - _createMemo(); - const items = parse(data.items); - if (items) { - parsed_data.items = items; - parsed_data.items_memo = _getMemo(); - } - _clearMemo(); return parsed_data; } @@ -429,7 +434,7 @@ export function parseResponse(data: */ export function parseItem[]>(data: RawNode | undefined, validTypes: K): InstanceType | null; export function parseItem(data: RawNode | undefined, validTypes: YTNodeConstructor): T | null; -export function parseItem(data?: RawNode) : YTNode; +export function parseItem(data?: RawNode): YTNode; export function parseItem(data?: RawNode, validTypes?: YTNodeConstructor | YTNodeConstructor[]) { if (!data) return null; diff --git a/src/parser/types/ParsedResponse.ts b/src/parser/types/ParsedResponse.ts index bbc273f53..3bb82e253 100644 --- a/src/parser/types/ParsedResponse.ts +++ b/src/parser/types/ParsedResponse.ts @@ -20,6 +20,7 @@ import type NavigationEndpoint from '../classes/NavigationEndpoint.js'; import type PlayerAnnotationsExpanded from '../classes/PlayerAnnotationsExpanded.js'; import type EngagementPanelSectionList from '../classes/EngagementPanelSectionList.js'; import type { AppendContinuationItemsAction } from '../nodes.js'; + export interface IParsedResponse { actions?: SuperParsedResult; actions_memo?: Memo; @@ -63,7 +64,7 @@ export interface IParsedResponse { storyboards?: PlayerStoryboardSpec | PlayerLiveStoryboardSpec; endscreen?: Endscreen; cards?: CardCollection; - engagement_panels?: EngagementPanelSectionList[]; + engagement_panels?: ObservedArray; items?: SuperParsedResult; } @@ -106,7 +107,7 @@ export interface INextResponse { on_response_received_endpoints?: ObservedArray; on_response_received_endpoints_memo?: Memo; player_overlays?: SuperParsedResult; - engagement_panels?: EngagementPanelSectionList[]; + engagement_panels?: ObservedArray; } export interface IBrowseResponse { @@ -145,6 +146,11 @@ export interface IResolveURLResponse { endpoint: NavigationEndpoint; } +export interface IGetTranscriptResponse { + actions: SuperParsedResult; + actions_memo: Memo; +} + export interface IGetNotificationsMenuResponse { actions: SuperParsedResult; actions_memo: Memo; From 9775c44cbe151da4cbb010416fcc8d0ba14b170f Mon Sep 17 00:00:00 2001 From: LuanRT Date: Sun, 10 Sep 2023 01:05:25 -0300 Subject: [PATCH 2/7] chore: lint --- src/Innertube.ts | 4 ++-- src/parser/classes/EngagementPanelSectionList.ts | 2 +- src/parser/classes/FancyDismissibleDialog.ts | 6 +++--- src/parser/classes/ProductList.ts | 6 ++++-- src/parser/classes/ProductListHeader.ts | 6 +++--- src/parser/classes/ProductListItem.ts | 11 ++++++----- src/parser/classes/StructuredDescriptionContent.ts | 7 +++++-- src/parser/classes/Transcript.ts | 7 ++++--- src/parser/classes/TranscriptFooter.ts | 7 ++++--- src/parser/classes/TranscriptSearchBox.ts | 11 ++++++----- src/parser/classes/TranscriptSearchPanel.ts | 13 +++++++------ src/parser/classes/TranscriptSegment.ts | 6 +++--- src/parser/classes/TranscriptSegmentList.ts | 10 ++++++---- src/parser/classes/UploadTimeFactoid.ts | 7 ++++--- src/parser/classes/VideoDescriptionHeader.ts | 2 +- .../classes/VideoDescriptionTranscriptSection.ts | 9 +++++---- src/parser/classes/ViewCountFactoid.ts | 9 +++++---- .../classes/actions/UpdateEngagementPanelAction.ts | 7 ++++--- src/parser/classes/menus/MenuPopup.ts | 14 ++++++++------ src/parser/parser.ts | 12 ++++++------ 20 files changed, 87 insertions(+), 69 deletions(-) diff --git a/src/Innertube.ts b/src/Innertube.ts index daa800d3f..00b1e0db2 100644 --- a/src/Innertube.ts +++ b/src/Innertube.ts @@ -342,7 +342,7 @@ export default class Innertube { throwIfMissing({ video_id }); const next_response = await this.actions.execute(NextEndpoint.PATH, { ...NextEndpoint.build({ video_id }), parse: true }); - + if (!next_response.engagement_panels) throw new InnertubeError('Engagement panels not found. Video likely has no transcript.', next_response); @@ -357,7 +357,7 @@ export default class Innertube { if (!transcript_continuation) throw new InnertubeError('Transcript continuation not found.', transcript_panel); - + const transcript_response = await transcript_continuation.endpoint.call(this.actions, { parse: true }); return transcript_response.actions_memo.getType(Transcript).first(); diff --git a/src/parser/classes/EngagementPanelSectionList.ts b/src/parser/classes/EngagementPanelSectionList.ts index 7d7b0c47c..506fcf6ff 100644 --- a/src/parser/classes/EngagementPanelSectionList.ts +++ b/src/parser/classes/EngagementPanelSectionList.ts @@ -19,7 +19,7 @@ export default class EngagementPanelSectionList extends YTNode { constructor(data: RawNode) { super(); this.header = Parser.parseItem(data.header, EngagementPanelTitleHeader); - this.content = Parser.parseItem(data.content, [ SectionList, ContinuationItem, StructuredDescriptionContent, MacroMarkersList, ProductList]); + this.content = Parser.parseItem(data.content, [ SectionList, ContinuationItem, StructuredDescriptionContent, MacroMarkersList, ProductList ]); this.panel_identifier = data.panelIdentifier; this.target_id = data.targetId; this.visibility = data.visibility; diff --git a/src/parser/classes/FancyDismissibleDialog.ts b/src/parser/classes/FancyDismissibleDialog.ts index d0017e76b..b7eb4188b 100644 --- a/src/parser/classes/FancyDismissibleDialog.ts +++ b/src/parser/classes/FancyDismissibleDialog.ts @@ -1,6 +1,6 @@ -import { YTNode } from "../helpers.js"; -import { RawNode } from "../index.js"; -import { Text } from "../misc.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import { Text } from '../misc.js'; export default class FancyDismissibleDialog extends YTNode { static type = 'FancyDismissibleDialog'; diff --git a/src/parser/classes/ProductList.ts b/src/parser/classes/ProductList.ts index 24889eb39..d3a3bc212 100644 --- a/src/parser/classes/ProductList.ts +++ b/src/parser/classes/ProductList.ts @@ -1,5 +1,7 @@ -import { ObservedArray, YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; +import type { ObservedArray} from '../helpers.js'; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; export default class ProductList extends YTNode { static type = 'ProductList'; diff --git a/src/parser/classes/ProductListHeader.ts b/src/parser/classes/ProductListHeader.ts index c47b1903d..246d07f69 100644 --- a/src/parser/classes/ProductListHeader.ts +++ b/src/parser/classes/ProductListHeader.ts @@ -1,6 +1,6 @@ -import { YTNode } from "../helpers.js"; -import { RawNode } from "../index.js"; -import { Text } from "../misc.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import { Text } from '../misc.js'; export default class ProductListHeader extends YTNode { static type = 'ProductListHeader'; diff --git a/src/parser/classes/ProductListItem.ts b/src/parser/classes/ProductListItem.ts index 652ad2597..6a2f273db 100644 --- a/src/parser/classes/ProductListItem.ts +++ b/src/parser/classes/ProductListItem.ts @@ -1,8 +1,9 @@ -import { YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; -import { Text, Thumbnail } from "../misc.js"; -import Button from "./Button.js"; -import NavigationEndpoint from "./NavigationEndpoint.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; +import { Text, Thumbnail } from '../misc.js'; +import Button from './Button.js'; +import NavigationEndpoint from './NavigationEndpoint.js'; export default class ProductListItem extends YTNode { static type = 'ProductListItem'; diff --git a/src/parser/classes/StructuredDescriptionContent.ts b/src/parser/classes/StructuredDescriptionContent.ts index 699785c2c..3936e0679 100644 --- a/src/parser/classes/StructuredDescriptionContent.ts +++ b/src/parser/classes/StructuredDescriptionContent.ts @@ -5,12 +5,15 @@ import HorizontalCardList from './HorizontalCardList.js'; import VideoDescriptionHeader from './VideoDescriptionHeader.js'; import VideoDescriptionInfocardsSection from './VideoDescriptionInfocardsSection.js'; import VideoDescriptionMusicSection from './VideoDescriptionMusicSection.js'; -import VideoDescriptionTranscriptSection from './VideoDescriptionTranscriptSection.js'; +import type VideoDescriptionTranscriptSection from './VideoDescriptionTranscriptSection.js'; export default class StructuredDescriptionContent extends YTNode { static type = 'StructuredDescriptionContent'; - items: ObservedArray; + items: ObservedArray< + VideoDescriptionHeader | ExpandableVideoDescriptionBody | VideoDescriptionMusicSection | + VideoDescriptionInfocardsSection | VideoDescriptionTranscriptSection | HorizontalCardList + >; constructor(data: RawNode) { super(); diff --git a/src/parser/classes/Transcript.ts b/src/parser/classes/Transcript.ts index 7bc9fa143..cba9456ca 100644 --- a/src/parser/classes/Transcript.ts +++ b/src/parser/classes/Transcript.ts @@ -1,6 +1,7 @@ -import { YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; -import TranscriptSearchPanel from "./TranscriptSearchPanel.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; +import TranscriptSearchPanel from './TranscriptSearchPanel.js'; export default class Transcript extends YTNode { static type = 'Transcript'; diff --git a/src/parser/classes/TranscriptFooter.ts b/src/parser/classes/TranscriptFooter.ts index 531513ed7..2394b3705 100644 --- a/src/parser/classes/TranscriptFooter.ts +++ b/src/parser/classes/TranscriptFooter.ts @@ -1,6 +1,7 @@ -import { YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; -import SortFilterSubMenu from "./SortFilterSubMenu.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; +import SortFilterSubMenu from './SortFilterSubMenu.js'; export default class TranscriptFooter extends YTNode { static type = 'TranscriptFooter'; diff --git a/src/parser/classes/TranscriptSearchBox.ts b/src/parser/classes/TranscriptSearchBox.ts index 5ddb8884f..6e64edb62 100644 --- a/src/parser/classes/TranscriptSearchBox.ts +++ b/src/parser/classes/TranscriptSearchBox.ts @@ -1,8 +1,9 @@ -import { YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; -import Button from "./Button.js"; -import NavigationEndpoint from "./NavigationEndpoint.js"; -import { Text } from "../misc.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; +import Button from './Button.js'; +import NavigationEndpoint from './NavigationEndpoint.js'; +import { Text } from '../misc.js'; export default class TranscriptSearchBox extends YTNode { static type = 'TranscriptSearchBox'; diff --git a/src/parser/classes/TranscriptSearchPanel.ts b/src/parser/classes/TranscriptSearchPanel.ts index 776cc6d0a..bf99235d1 100644 --- a/src/parser/classes/TranscriptSearchPanel.ts +++ b/src/parser/classes/TranscriptSearchPanel.ts @@ -1,8 +1,9 @@ -import { YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; -import TranscriptFooter from "./TranscriptFooter.js"; -import TranscriptSearchBox from "./TranscriptSearchBox.js"; -import TranscriptSegmentList from "./TranscriptSegmentList.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; +import TranscriptFooter from './TranscriptFooter.js'; +import TranscriptSearchBox from './TranscriptSearchBox.js'; +import TranscriptSegmentList from './TranscriptSegmentList.js'; export default class TranscriptSearchPanel extends YTNode { static type = 'TranscriptSearchPanel'; @@ -13,7 +14,7 @@ export default class TranscriptSearchPanel extends YTNode { target_id: string; constructor(data: RawNode) { - super(); + super(); this.header = Parser.parseItem(data.header, TranscriptSearchBox); this.body = Parser.parseItem(data.body, TranscriptSegmentList); this.footer = Parser.parseItem(data.footer, TranscriptFooter); diff --git a/src/parser/classes/TranscriptSegment.ts b/src/parser/classes/TranscriptSegment.ts index ec32b3341..c29955236 100644 --- a/src/parser/classes/TranscriptSegment.ts +++ b/src/parser/classes/TranscriptSegment.ts @@ -1,6 +1,6 @@ -import { YTNode } from "../helpers.js"; -import { RawNode } from "../index.js"; -import { Text } from "../misc.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import { Text } from '../misc.js'; export default class TranscriptSegment extends YTNode { static type = 'TranscriptSegment'; diff --git a/src/parser/classes/TranscriptSegmentList.ts b/src/parser/classes/TranscriptSegmentList.ts index 055a6af20..da7090f92 100644 --- a/src/parser/classes/TranscriptSegmentList.ts +++ b/src/parser/classes/TranscriptSegmentList.ts @@ -1,7 +1,9 @@ -import { ObservedArray, YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; -import { Text } from "../misc.js"; -import TranscriptSegment from "./TranscriptSegment.js"; +import type { ObservedArray} from '../helpers.js'; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; +import { Text } from '../misc.js'; +import TranscriptSegment from './TranscriptSegment.js'; export default class TranscriptSegmentList extends YTNode { static type = 'TranscriptSegmentList'; diff --git a/src/parser/classes/UploadTimeFactoid.ts b/src/parser/classes/UploadTimeFactoid.ts index 04cb98262..6714b0073 100644 --- a/src/parser/classes/UploadTimeFactoid.ts +++ b/src/parser/classes/UploadTimeFactoid.ts @@ -1,6 +1,7 @@ -import { YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; -import Factoid from "./Factoid.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; +import Factoid from './Factoid.js'; export default class UploadTimeFactoid extends YTNode { static type = 'UploadTimeFactoid'; diff --git a/src/parser/classes/VideoDescriptionHeader.ts b/src/parser/classes/VideoDescriptionHeader.ts index 03a2277e2..fb9cd8f13 100644 --- a/src/parser/classes/VideoDescriptionHeader.ts +++ b/src/parser/classes/VideoDescriptionHeader.ts @@ -25,6 +25,6 @@ export default class VideoDescriptionHeader extends YTNode { this.channel_thumbnail = Thumbnail.fromResponse(data.channelThumbnail); this.publish_date = new Text(data.publishDate); this.views = new Text(data.views); - this.factoids = Parser.parseArray(data.factoid, [Factoid, ViewCountFactoid, UploadTimeFactoid]); + this.factoids = Parser.parseArray(data.factoid, [ Factoid, ViewCountFactoid, UploadTimeFactoid ]); } } diff --git a/src/parser/classes/VideoDescriptionTranscriptSection.ts b/src/parser/classes/VideoDescriptionTranscriptSection.ts index 0922d81ff..282196876 100644 --- a/src/parser/classes/VideoDescriptionTranscriptSection.ts +++ b/src/parser/classes/VideoDescriptionTranscriptSection.ts @@ -1,7 +1,8 @@ -import { YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; -import { Text } from "../misc.js"; -import Button from "./Button.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; +import { Text } from '../misc.js'; +import Button from './Button.js'; export default class VideoDescriptionTranscriptSection extends YTNode { static type = 'VideoDescriptionTranscriptSection'; diff --git a/src/parser/classes/ViewCountFactoid.ts b/src/parser/classes/ViewCountFactoid.ts index d55efaa9d..0feaa64df 100644 --- a/src/parser/classes/ViewCountFactoid.ts +++ b/src/parser/classes/ViewCountFactoid.ts @@ -1,6 +1,7 @@ -import { YTNode } from "../helpers.js"; -import Parser, { RawNode } from "../index.js"; -import Factoid from "./Factoid.js"; +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Parser from '../index.js'; +import Factoid from './Factoid.js'; export default class ViewCountFactoid extends YTNode { static type = 'ViewCountFactoid'; @@ -12,7 +13,7 @@ export default class ViewCountFactoid extends YTNode { constructor(data: RawNode) { super(); this.view_count_entity_key = data.viewCountEntityKey; - this.factoid = Parser.parseItem(data.factoid, [Factoid]); + this.factoid = Parser.parseItem(data.factoid, [ Factoid ]); this.view_count_type = data.viewCountType; } } diff --git a/src/parser/classes/actions/UpdateEngagementPanelAction.ts b/src/parser/classes/actions/UpdateEngagementPanelAction.ts index cb1e30af0..60cad408a 100644 --- a/src/parser/classes/actions/UpdateEngagementPanelAction.ts +++ b/src/parser/classes/actions/UpdateEngagementPanelAction.ts @@ -1,6 +1,7 @@ -import { YTNode } from "../../helpers.js"; -import Parser, { RawNode } from "../../index.js"; -import Transcript from "../Transcript.js"; +import { YTNode } from '../../helpers.js'; +import type { RawNode } from '../../index.js'; +import Parser from '../../index.js'; +import Transcript from '../Transcript.js'; export default class UpdateEngagementPanelAction extends YTNode { static type = 'UpdateEngagementPanelAction'; diff --git a/src/parser/classes/menus/MenuPopup.ts b/src/parser/classes/menus/MenuPopup.ts index 6023d143a..dbfdaa1c2 100644 --- a/src/parser/classes/menus/MenuPopup.ts +++ b/src/parser/classes/menus/MenuPopup.ts @@ -1,15 +1,17 @@ -import { ObservedArray, YTNode } from "../../helpers.js"; -import Parser, { RawNode } from "../../index.js"; -import MenuNavigationItem from "./MenuNavigationItem.js"; -import MenuServiceItem from "./MenuServiceItem.js"; +import type { ObservedArray} from '../../helpers.js'; +import { YTNode } from '../../helpers.js'; +import type { RawNode } from '../../index.js'; +import Parser from '../../index.js'; +import MenuNavigationItem from './MenuNavigationItem.js'; +import MenuServiceItem from './MenuServiceItem.js'; export default class MenuPopup extends YTNode { static type = 'MenuPopup'; items: ObservedArray; - + constructor(data: RawNode) { super(); - this.items = Parser.parseArray(data.items, [MenuNavigationItem, MenuServiceItem]); + this.items = Parser.parseArray(data.items, [ MenuNavigationItem, MenuServiceItem ]); } } \ No newline at end of file diff --git a/src/parser/parser.ts b/src/parser/parser.ts index c29a41e8d..78926087a 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -89,8 +89,8 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError) new InnertubeError( `Something went wrong at ${classname}!\n` + `This is a bug, please report it at ${Platform.shim.info.bugs_url}`, { - stack: context.error.stack - } + stack: context.error.stack + } ) ); } @@ -132,7 +132,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError) case 'class_changed': console.warn( `${classname} changed!\n` + - `The following keys where altered: ${context.changed_keys.map(([key]) => camelToSnake(key)).join(', ')}\n` + + `The following keys where altered: ${context.changed_keys.map(([ key ]) => camelToSnake(key)).join(', ')}\n` + `The class has changed to:\n${generateTypescriptClass(classname, context.key_info)}` ); break; @@ -162,7 +162,7 @@ function _addToMemo(classname: string, result: YTNode) { const list = MEMO.get(classname); if (!list) - return MEMO.set(classname, [result]); + return MEMO.set(classname, [ result ]); list.push(result); } @@ -326,7 +326,7 @@ export function parseResponse(data: parsed_data.overlay = overlay; } - const alerts = parseArray(data.alerts, [Alert, AlertWithButton]); + const alerts = parseArray(data.alerts, [ Alert, AlertWithButton ]); if (alerts.length) { parsed_data.alerts = alerts; } @@ -404,7 +404,7 @@ export function parseResponse(data: parsed_data.annotations = annotations; } - const storyboards = parseItem(data.storyboards, [PlayerStoryboardSpec, PlayerLiveStoryboardSpec]); + const storyboards = parseItem(data.storyboards, [ PlayerStoryboardSpec, PlayerLiveStoryboardSpec ]); if (storyboards) { parsed_data.storyboards = storyboards; } From 91ab8fd38eb6b84973e08684aad4d33c5d4e3385 Mon Sep 17 00:00:00 2001 From: LuanRT Date: Sun, 10 Sep 2023 01:10:04 -0300 Subject: [PATCH 3/7] chore: update docs --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index d69a705b9..79a244276 100644 --- a/README.md +++ b/README.md @@ -660,6 +660,16 @@ console.info('Playback url:', url); | video_id | `string` | Video id | | options | `FormatOptions` | Format options | + +### `getTranscript(video_id)` +Retrieves a given video's transcript. + +**Returns**: `Promise` + +| Param | Type | Description | +| --- | --- | --- | +| video_id | `string` | Video id | + ### `download(video_id, options?)` Downloads a given video. From 12167e86c3a6328b07e4847cf8a2c59671317a32 Mon Sep 17 00:00:00 2001 From: LuanRT Date: Sun, 10 Sep 2023 01:16:18 -0300 Subject: [PATCH 4/7] chore: Do not include nodes in errors thrown --- src/Innertube.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Innertube.ts b/src/Innertube.ts index 00b1e0db2..a2c028fbe 100644 --- a/src/Innertube.ts +++ b/src/Innertube.ts @@ -344,19 +344,19 @@ export default class Innertube { const next_response = await this.actions.execute(NextEndpoint.PATH, { ...NextEndpoint.build({ video_id }), parse: true }); if (!next_response.engagement_panels) - throw new InnertubeError('Engagement panels not found. Video likely has no transcript.', next_response); + throw new InnertubeError('Engagement panels not found. Video likely has no transcript.'); const transcript_panel = next_response.engagement_panels.get({ panel_identifier: 'engagement-panel-searchable-transcript' }); if (!transcript_panel) - throw new InnertubeError('Transcript panel not found.', next_response); + throw new InnertubeError('Transcript panel not found.'); const transcript_continuation = transcript_panel.content?.as(ContinuationItem); if (!transcript_continuation) - throw new InnertubeError('Transcript continuation not found.', transcript_panel); + throw new InnertubeError('Transcript continuation not found.'); const transcript_response = await transcript_continuation.endpoint.call(this.actions, { parse: true }); From 432b07976e4e80e284d582619224d9722440cc85 Mon Sep 17 00:00:00 2001 From: LuanRT Date: Sun, 10 Sep 2023 01:17:12 -0300 Subject: [PATCH 5/7] chore: Improve error messages --- src/Innertube.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Innertube.ts b/src/Innertube.ts index a2c028fbe..ce6c5257f 100644 --- a/src/Innertube.ts +++ b/src/Innertube.ts @@ -351,7 +351,7 @@ export default class Innertube { }); if (!transcript_panel) - throw new InnertubeError('Transcript panel not found.'); + throw new InnertubeError('Transcript panel not found. Video likely has no transcript.'); const transcript_continuation = transcript_panel.content?.as(ContinuationItem); From 3ba5558bfd32239ae96d7361d2e604b363f0864d Mon Sep 17 00:00:00 2001 From: LuanRT Date: Sun, 10 Sep 2023 01:43:21 -0300 Subject: [PATCH 6/7] fix(ExpandableMetadata): `expanded_content` type mismatch --- src/parser/classes/ExpandableMetadata.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/parser/classes/ExpandableMetadata.ts b/src/parser/classes/ExpandableMetadata.ts index ea5442b51..f6201d2b5 100644 --- a/src/parser/classes/ExpandableMetadata.ts +++ b/src/parser/classes/ExpandableMetadata.ts @@ -2,6 +2,7 @@ import { YTNode } from '../helpers.js'; import Parser, { type RawNode } from '../index.js'; import Button from './Button.js'; import HorizontalCardList from './HorizontalCardList.js'; +import HorizontalList from './HorizontalList.js'; import Text from './misc/Text.js'; import Thumbnail from './misc/Thumbnail.js'; @@ -15,7 +16,7 @@ export default class ExpandableMetadata extends YTNode { expanded_title: Text; }; - expanded_content: HorizontalCardList | null; + expanded_content: HorizontalCardList | HorizontalList | null; expand_button: Button | null; collapse_button: Button | null; @@ -31,7 +32,7 @@ export default class ExpandableMetadata extends YTNode { }; } - this.expanded_content = Parser.parseItem(data.expandedContent, HorizontalCardList); + this.expanded_content = Parser.parseItem(data.expandedContent, [HorizontalCardList, HorizontalList]); this.expand_button = Parser.parseItem(data.expandButton, Button); this.collapse_button = Parser.parseItem(data.collapseButton, Button); } From 0a1c3a048f489530ae21ae4c55fbed2d422c85bd Mon Sep 17 00:00:00 2001 From: LuanRT Date: Sun, 10 Sep 2023 01:44:49 -0300 Subject: [PATCH 7/7] chore: lint --- src/parser/classes/ExpandableMetadata.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/classes/ExpandableMetadata.ts b/src/parser/classes/ExpandableMetadata.ts index f6201d2b5..e7096b3be 100644 --- a/src/parser/classes/ExpandableMetadata.ts +++ b/src/parser/classes/ExpandableMetadata.ts @@ -32,7 +32,7 @@ export default class ExpandableMetadata extends YTNode { }; } - this.expanded_content = Parser.parseItem(data.expandedContent, [HorizontalCardList, HorizontalList]); + this.expanded_content = Parser.parseItem(data.expandedContent, [ HorizontalCardList, HorizontalList ]); this.expand_button = Parser.parseItem(data.expandButton, Button); this.collapse_button = Parser.parseItem(data.collapseButton, Button); }