refactor(Parser)!: general refactoring of parsers (#344)

* refactor: move common info into MediaInfo

* refactor: better inference on Memo

* refactor: improved typesafety in parser methods

* refactor: remove PlaylistAuthor in favor of Author

* refactor: cleanup live chat parsers

- Replace non standard author type with Author class
- Remove redundant code

* fix: new errors due to changes

* fix: pass actions to FormatUtils#toDash

* refactor!: merge NavigatableText and Text into single class
This commit is contained in:
Daniel Wykerd
2023-03-15 23:25:12 +02:00
committed by GitHub
parent 3d3436472f
commit b13bf6e992
78 changed files with 400 additions and 737 deletions

View File

@@ -1,15 +1,15 @@
import type AudioOnlyPlayability from './classes/AudioOnlyPlayability.js';
import type CardCollection from './classes/CardCollection.js';
import type Endscreen from './classes/Endscreen.js';
import type PlayerAnnotationsExpanded from './classes/PlayerAnnotationsExpanded.js';
import type PlayerCaptionsTracklist from './classes/PlayerCaptionsTracklist.js';
import type PlayerLiveStoryboardSpec from './classes/PlayerLiveStoryboardSpec.js';
import type PlayerStoryboardSpec from './classes/PlayerStoryboardSpec.js';
import type Message from './classes/Message.js';
import type LiveChatParticipantsList from './classes/LiveChatParticipantsList.js';
import type LiveChatHeader from './classes/LiveChatHeader.js';
import type LiveChatItemList from './classes/LiveChatItemList.js';
import type Alert from './classes/Alert.js';
import AudioOnlyPlayability from './classes/AudioOnlyPlayability.js';
import CardCollection from './classes/CardCollection.js';
import Endscreen from './classes/Endscreen.js';
import PlayerAnnotationsExpanded from './classes/PlayerAnnotationsExpanded.js';
import PlayerCaptionsTracklist from './classes/PlayerCaptionsTracklist.js';
import PlayerLiveStoryboardSpec from './classes/PlayerLiveStoryboardSpec.js';
import PlayerStoryboardSpec from './classes/PlayerStoryboardSpec.js';
import Message from './classes/Message.js';
import LiveChatParticipantsList from './classes/LiveChatParticipantsList.js';
import LiveChatHeader from './classes/LiveChatHeader.js';
import LiveChatItemList from './classes/LiveChatItemList.js';
import Alert from './classes/Alert.js';
import type { IParsedResponse, IRawResponse, RawData, RawNode } from './types/index.js';
@@ -170,7 +170,7 @@ export default class Parser {
parsed_data.overlay = overlay;
}
const alerts = this.parseArray<Alert>(data.alerts);
const alerts = this.parseArray(data.alerts, Alert);
if (alerts.length) {
parsed_data.alerts = alerts;
}
@@ -203,7 +203,7 @@ export default class Parser {
status: data.playabilityStatus.status,
reason: data.playabilityStatus.reason || '',
embeddable: !!data.playabilityStatus.playableInEmbed || false,
audio_only_playablility: this.parseItem<AudioOnlyPlayability>(data.playabilityStatus.audioOnlyPlayability),
audio_only_playablility: this.parseItem(data.playabilityStatus.audioOnlyPlayability, AudioOnlyPlayability),
error_screen: this.parseItem(data.playabilityStatus.errorScreen)
} : null;
@@ -233,7 +233,7 @@ export default class Parser {
parsed_data.endpoint = endpoint;
}
const captions = this.parseItem<PlayerCaptionsTracklist>(data.captions);
const captions = this.parseItem(data.captions, PlayerCaptionsTracklist);
if (captions) {
parsed_data.captions = captions;
}
@@ -243,22 +243,22 @@ export default class Parser {
parsed_data.video_details = video_details;
}
const annotations = this.parseArray<PlayerAnnotationsExpanded>(data.annotations);
const annotations = this.parseArray(data.annotations, PlayerAnnotationsExpanded);
if (annotations.length) {
parsed_data.annotations = annotations;
}
const storyboards = this.parseItem<PlayerStoryboardSpec | PlayerLiveStoryboardSpec>(data.storyboards);
const storyboards = this.parseItem(data.storyboards, [ PlayerStoryboardSpec, PlayerLiveStoryboardSpec ]);
if (storyboards) {
parsed_data.storyboards = storyboards;
}
const endscreen = this.parseItem<Endscreen>(data.endscreen);
const endscreen = this.parseItem(data.endscreen, Endscreen);
if (endscreen) {
parsed_data.endscreen = endscreen;
}
const cards = this.parseItem<CardCollection>(data.cards);
const cards = this.parseItem(data.cards, CardCollection);
if (cards) {
parsed_data.cards = cards;
}
@@ -279,7 +279,10 @@ export default class Parser {
* @param data - The data to parse.
* @param validTypes - YTNode types that are allowed to be parsed.
*/
static parseItem<T extends YTNode = YTNode>(data?: RawNode, validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]) {
static parseItem<T extends YTNode, K extends YTNodeConstructor<T>[]>(data: RawNode | undefined, validTypes: K): InstanceType<K[number]> | null;
static parseItem<T extends YTNode>(data: RawNode | undefined, validTypes: YTNodeConstructor<T>): T | null;
static parseItem(data?: RawNode) : YTNode;
static parseItem(data?: RawNode, validTypes?: YTNodeConstructor | YTNodeConstructor[]) {
if (!data) return null;
const keys = Object.keys(data);
@@ -306,7 +309,7 @@ export default class Parser {
const result = new TargetClass(data[keys[0]]);
this.#addToMemo(classname, result);
return result as T;
return result;
} catch (err) {
this.#errorHandler({ classname, classdata: data[keys[0]], err });
return null;
@@ -316,17 +319,24 @@ export default class Parser {
return null;
}
extra() {
Parser.parseItem({}, [ MusicMultiSelectMenuItem, MusicMultiSelectMenuItem ]);
}
/**
* Parses an array of items.
* @param data - The data to parse.
* @param validTypes - YTNode types that are allowed to be parsed.
*/
static parseArray<T extends YTNode = YTNode>(data?: RawNode[], validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]) {
static parseArray<T extends YTNode, K extends YTNodeConstructor<T>[]>(data: RawNode[] | undefined, validTypes: K): ObservedArray<InstanceType<K[number]>>;
static parseArray<T extends YTNode = YTNode>(data: RawNode[] | undefined, validType: YTNodeConstructor<T>): ObservedArray<T>;
static parseArray(data: RawNode[] | undefined): ObservedArray<YTNode>;
static parseArray(data?: RawNode[], validTypes?: YTNodeConstructor | YTNodeConstructor[]) {
if (Array.isArray(data)) {
const results: T[] = [];
const results: YTNode[] = [];
for (const item of data) {
const result = this.parseItem(item, validTypes);
const result = this.parseItem(item, validTypes as YTNodeConstructor);
if (result) {
results.push(result);
}
@@ -334,7 +344,7 @@ export default class Parser {
return observe(results);
} else if (!data) {
return observe([] as T[]);
return observe([] as YTNode[]);
}
throw new ParsingError('Expected array but got a single item');
}
@@ -345,7 +355,7 @@ export default class Parser {
* @param requireArray - Whether the data should be parsed as an array.
* @param validTypes - YTNode types that are allowed to be parsed.
*/
static parse<T extends YTNode = YTNode>(data: RawData, requireArray: true, validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]): ObservedArray<T> | null;
static parse<T extends YTNode, K extends YTNodeConstructor<T>[]>(data: RawData, requireArray: true, validTypes?: K): ObservedArray<InstanceType<K[number]>> | null;
static parse<T extends YTNode = YTNode>(data?: RawData, requireArray?: false | undefined, validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]): SuperParsedResult<T>;
static parse<T extends YTNode = YTNode>(data?: RawData, requireArray?: boolean, validTypes?: YTNodeConstructor<T> | YTNodeConstructor<T>[]) {
if (!data) return null;
@@ -354,7 +364,7 @@ export default class Parser {
const results: T[] = [];
for (const item of data) {
const result = this.parseItem(item, validTypes);
const result = this.parseItem(item, validTypes as YTNodeConstructor<T>);
if (result) {
results.push(result);
}
@@ -367,7 +377,7 @@ export default class Parser {
throw new ParsingError('Expected array but got a single item');
}
return new SuperParsedResult(this.parseItem(data, validTypes));
return new SuperParsedResult(this.parseItem(data, validTypes as YTNodeConstructor<T>));
}
static parseC(data: RawNode) {
@@ -699,10 +709,10 @@ export class LiveChatContinuation extends YTNode {
}), true) || observe<YTNode>([]);
this.action_panel = Parser.parseItem(data.actionPanel);
this.item_list = Parser.parseItem<LiveChatItemList>(data.itemList);
this.header = Parser.parseItem<LiveChatHeader>(data.header);
this.participants_list = Parser.parseItem<LiveChatParticipantsList>(data.participantsList);
this.popout_message = Parser.parseItem<Message>(data.popoutMessage);
this.item_list = Parser.parseItem(data.itemList, LiveChatItemList);
this.header = Parser.parseItem(data.header, LiveChatHeader);
this.participants_list = Parser.parseItem(data.participantsList, LiveChatParticipantsList);
this.popout_message = Parser.parseItem(data.popoutMessage, Message);
this.emojis = data.emojis?.map((emoji: any) => ({
emoji_id: emoji.emojiId,