feat: Add support for retrieving transcripts (#500)

* feat: Add support for retrieving transcripts

* chore: lint

* chore: update docs

* chore: Do not include nodes in errors thrown

* chore: Improve error messages

* fix(ExpandableMetadata): `expanded_content` type mismatch

* chore: lint
This commit is contained in:
Luan
2023-09-10 01:50:30 -03:00
committed by GitHub
parent 86fb33ed03
commit f94ea6cf91
24 changed files with 387 additions and 25 deletions

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,16 @@
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';
dialog_message: Text;
confirm_label: Text;
constructor(data: RawNode) {
super();
this.dialog_message = new Text(data.dialogMessage);
this.confirm_label = new Text(data.confirmLabel);
}
}

View File

@@ -0,0 +1,15 @@
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';
contents: ObservedArray<YTNode>;
constructor(data: RawNode) {
super();
this.contents = Parser.parseArray(data.contents);
}
}

View File

@@ -0,0 +1,16 @@
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';
title: Text;
suppress_padding_disclaimer: boolean;
constructor(data: RawNode) {
super();
this.title = new Text(data.title);
this.suppress_padding_disclaimer = !!data.suppressPaddingDisclaimer;
}
}

View File

@@ -0,0 +1,31 @@
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';
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);
}
}

View File

@@ -5,11 +5,15 @@ import HorizontalCardList from './HorizontalCardList.js';
import VideoDescriptionHeader from './VideoDescriptionHeader.js';
import VideoDescriptionInfocardsSection from './VideoDescriptionInfocardsSection.js';
import VideoDescriptionMusicSection from './VideoDescriptionMusicSection.js';
import type VideoDescriptionTranscriptSection from './VideoDescriptionTranscriptSection.js';
export default class StructuredDescriptionContent extends YTNode {
static type = 'StructuredDescriptionContent';
items: ObservedArray<VideoDescriptionHeader | ExpandableVideoDescriptionBody | VideoDescriptionMusicSection | VideoDescriptionInfocardsSection | HorizontalCardList>;
items: ObservedArray<
VideoDescriptionHeader | ExpandableVideoDescriptionBody | VideoDescriptionMusicSection |
VideoDescriptionInfocardsSection | VideoDescriptionTranscriptSection | HorizontalCardList
>;
constructor(data: RawNode) {
super();

View File

@@ -0,0 +1,15 @@
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';
content: TranscriptSearchPanel | null;
constructor(data: RawNode) {
super();
this.content = Parser.parseItem(data.content, TranscriptSearchPanel);
}
}

View File

@@ -0,0 +1,15 @@
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';
language_menu: SortFilterSubMenu | null;
constructor(data: RawNode) {
super();
this.language_menu = Parser.parseItem(data.languageMenu, SortFilterSubMenu);
}
}

View File

@@ -0,0 +1,23 @@
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';
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);
}
}

View File

@@ -0,0 +1,23 @@
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';
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;
}
}

View File

@@ -0,0 +1,22 @@
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';
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;
}
}

View File

@@ -0,0 +1,23 @@
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';
initial_segments: ObservedArray<TranscriptSegment>;
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;
}
}

View File

@@ -0,0 +1,15 @@
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';
factoid: Factoid | null;
constructor(data: RawNode) {
super();
this.factoid = Parser.parseItem(data.factoid, Factoid);
}
}

View File

@@ -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<Factoid>;
factoids: ObservedArray<Factoid | ViewCountFactoid | UploadTimeFactoid>;
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 ]);
}
}

View File

@@ -0,0 +1,20 @@
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';
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);
}
}

View File

@@ -0,0 +1,19 @@
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';
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;
}
}

View File

@@ -0,0 +1,17 @@
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';
target_id: string;
content: Transcript | null;
constructor(data: RawNode) {
super();
this.target_id = data.targetId;
this.content = Parser.parseItem(data.content, Transcript);
}
}

View File

@@ -0,0 +1,17 @@
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<MenuNavigationItem | MenuServiceItem>;
constructor(data: RawNode) {
super();
this.items = Parser.parseArray(data.items, [ MenuNavigationItem, MenuServiceItem ]);
}
}