Skip to content

Commit

Permalink
feat: Add AccessibilityContext and CommandContext classes + improve t…
Browse files Browse the repository at this point in the history
…ype definitions and parsing logic across multiple nodes
  • Loading branch information
LuanRT committed Mar 3, 2025
1 parent 5ef7ea8 commit 923e9c2
Show file tree
Hide file tree
Showing 27 changed files with 474 additions and 202 deletions.
11 changes: 7 additions & 4 deletions src/core/mixins/Feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ import TwoColumnSearchResults from '../../parser/classes/TwoColumnSearchResults.
import WatchCardCompactVideo from '../../parser/classes/WatchCardCompactVideo.js';

import type { Actions, ApiResponse } from '../index.js';
import type { Memo, ObservedArray, SuperParsedResult, YTNode } from '../../parser/helpers.js';
import type { Memo, ObservedArray } from '../../parser/helpers.js';
import type MusicQueue from '../../parser/classes/MusicQueue.js';
import type RichGrid from '../../parser/classes/RichGrid.js';
import type SectionList from '../../parser/classes/SectionList.js';
import type SecondarySearchContainer from '../../parser/classes/SecondarySearchContainer.js';
import type BrowseFeedActions from '../../parser/classes/BrowseFeedActions.js';
import type ProfileColumn from '../../parser/classes/ProfileColumn.js';

export default class Feed<T extends IParsedResponse = IParsedResponse> {
readonly #page: T;
Expand Down Expand Up @@ -163,14 +166,14 @@ export default class Feed<T extends IParsedResponse = IParsedResponse> {
/**
* Returns secondary contents from the page.
*/
get secondary_contents(): SuperParsedResult<YTNode> | undefined {
get secondary_contents(): SectionList | SecondarySearchContainer | BrowseFeedActions | ProfileColumn | null {
if (!this.#page.contents?.is_node)
return undefined;
return null;

const node = this.#page.contents?.item();

if (!node.is(TwoColumnBrowseResults, TwoColumnSearchResults))
return undefined;
return null;

return node.secondary_contents;
}
Expand Down
4 changes: 3 additions & 1 deletion src/parser/classes/Alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import Text from './misc/Text.js';
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';

export type AlertType = 'UNKNOWN' | 'WARNING' | 'ERROR' | 'SUCCESS' | 'INFO';

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

text: Text;
alert_type: string;
alert_type: AlertType;

constructor(data: RawNode) {
super();
Expand Down
2 changes: 1 addition & 1 deletion src/parser/classes/BadgeView.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { YTNode } from '../helpers.js';
import type { RawNode } from '../types/RawResponse.js';
import type { RawNode } from '../types/index.js';

export default class BadgeView extends YTNode {
text: string;
Expand Down
10 changes: 8 additions & 2 deletions src/parser/classes/BrowseFeedActions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { Parser, type RawNode } from '../index.js';
import { type ObservedArray, YTNode } from '../helpers.js';
import SubFeedSelector from './SubFeedSelector.js';
import EomSettingsDisclaimer from './EomSettingsDisclaimer.js';
import ToggleButton from './ToggleButton.js';
import CompactLink from './CompactLink.js';
import SearchBox from './SearchBox.js';
import Button from './Button.js';

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

contents: ObservedArray<YTNode>;
public contents: ObservedArray<SubFeedSelector | EomSettingsDisclaimer | ToggleButton | CompactLink | SearchBox | Button>;

constructor(data: RawNode) {
super();
this.contents = Parser.parseArray(data.contents);
this.contents = Parser.parseArray(data.contents, [ SubFeedSelector, EomSettingsDisclaimer, ToggleButton, CompactLink, SearchBox, Button ]);
}
}
128 changes: 112 additions & 16 deletions src/parser/classes/ButtonView.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,124 @@
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import Thumbnail from './misc/Thumbnail.js';

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

icon_name: string;
title: string;
accessibility_text: string;
style: string;
is_full_width: boolean;
button_type: string;
button_size: string;
on_tap: NavigationEndpoint;
public secondary_icon_image?: Thumbnail[];
public icon_name?: string;
public enable_icon_button?: boolean;
public tooltip?: string;
public icon_image_flip_for_rtl?: boolean;
public button_size?: 'BUTTON_VIEW_MODEL_SIZE_UNKNOWN' | 'BUTTON_VIEW_MODEL_SIZE_DEFAULT' | 'BUTTON_VIEW_MODEL_SIZE_COMPACT' | 'BUTTON_VIEW_MODEL_SIZE_XSMALL' | 'BUTTON_VIEW_MODEL_SIZE_LARGE' | 'BUTTON_VIEW_MODEL_SIZE_XLARGE' | 'BUTTON_VIEW_MODEL_SIZE_XXLARGE';
public icon_position?: 'BUTTON_VIEW_MODEL_ICON_POSITION_UNKNOWN' | 'BUTTON_VIEW_MODEL_ICON_POSITION_TRAILING' | 'BUTTON_VIEW_MODEL_ICON_POSITION_LEADING' | 'BUTTON_VIEW_MODEL_ICON_POSITION_ABOVE' | 'BUTTON_VIEW_MODEL_ICON_POSITION_LEADING_TRAILING';
public is_full_width?: boolean;
public state?: 'BUTTON_VIEW_MODEL_STATE_UNKNOWN' | 'BUTTON_VIEW_MODEL_STATE_ACTIVE' | 'BUTTON_VIEW_MODEL_STATE_INACTIVE' | 'BUTTON_VIEW_MODEL_STATE_DISABLED';
public on_disabled_tap?: NavigationEndpoint;
public custom_border_color?: number;
public on_tap?: NavigationEndpoint;
public style?: 'BUTTON_VIEW_MODEL_STYLE_UNKNOWN' | 'BUTTON_VIEW_MODEL_STYLE_CTA' | 'BUTTON_VIEW_MODEL_STYLE_BRAND' | 'BUTTON_VIEW_MODEL_STYLE_ADS_CTA' | 'BUTTON_VIEW_MODEL_STYLE_OVERLAY' | 'BUTTON_VIEW_MODEL_STYLE_CTA_THEMED' | 'BUTTON_VIEW_MODEL_STYLE_BLACK_CTA' | 'BUTTON_VIEW_MODEL_STYLE_CUSTOM' | 'BUTTON_VIEW_MODEL_STYLE_MONO' | 'BUTTON_VIEW_MODEL_STYLE_OVERLAY_DARK' | 'BUTTON_VIEW_MODEL_STYLE_CTA_OVERLAY' | 'BUTTON_VIEW_MODEL_STYLE_BRAND_AI' | 'BUTTON_VIEW_MODEL_STYLE_YT_GRADIENT' | 'BUTTON_VIEW_MODEL_STYLE_BRAND_GRADIENT';
public icon_image?: object;
public custom_dark_theme_border_color?: number;
public title?: string;
public target_id?: string;
public enable_full_width_margins?: boolean;
public custom_font_color?: number;
public button_type?: 'BUTTON_VIEW_MODEL_TYPE_UNKNOWN' | 'BUTTON_VIEW_MODEL_TYPE_FILLED' | 'BUTTON_VIEW_MODEL_TYPE_OUTLINE' | 'BUTTON_VIEW_MODEL_TYPE_TEXT' | 'BUTTON_VIEW_MODEL_TYPE_TONAL';
public enabled?: boolean;
public accessibility_id?: string;
public custom_background_color?: number;
public on_long_press?: NavigationEndpoint;
public title_formatted?: object;
public on_visible?: object;
public icon_trailing?: boolean;
public accessibility_text?: string;

constructor(data: RawNode) {
super();
this.icon_name = data.iconName;
this.title = data.title;
this.accessibility_text = data.accessibilityText;
this.style = data.style;
this.is_full_width = data.isFullWidth;
this.button_type = data.type;
this.button_size = data.buttonSize;
this.on_tap = new NavigationEndpoint(data.onTap);
if ('secondaryIconImage' in data)
this.secondary_icon_image = Thumbnail.fromResponse(data.secondaryIconImage);

if ('iconName' in data)
this.icon_name = data.iconName;

if ('enableIconButton' in data)
this.enable_icon_button = data.enableIconButton;

if ('tooltip' in data)
this.tooltip = data.tooltip;

if ('iconImageFlipForRtl' in data)
this.icon_image_flip_for_rtl = data.iconImageFlipForRtl;

if ('buttonSize' in data)
this.button_size = data.buttonSize;

if ('iconPosition' in data)
this.icon_position = data.iconPosition;

if ('isFullWidth' in data)
this.is_full_width = data.isFullWidth;

if ('state' in data)
this.state = data.state;

if ('onDisabledTap' in data)
this.on_disabled_tap = new NavigationEndpoint(data.onDisabledTap);

if ('customBorderColor' in data)
this.custom_border_color = data.customBorderColor;

if ('onTap' in data)
this.on_tap = new NavigationEndpoint(data.onTap);

if ('style' in data)
this.style = data.style;

if ('iconImage' in data)
this.icon_image = data.iconImage;

if ('customDarkThemeBorderColor' in data)
this.custom_dark_theme_border_color = data.customDarkThemeBorderColor;

if ('title' in data)
this.title = data.title;

if ('targetId' in data)
this.target_id = data.targetId;

if ('enableFullWidthMargins' in data)
this.enable_full_width_margins = data.enableFullWidthMargins;

if ('customFontColor' in data)
this.custom_font_color = data.customFontColor;

if ('buttonType' in data)
this.button_type = data.buttonType;

if ('enabled' in data)
this.enabled = data.enabled;

if ('accessibilityId' in data)
this.accessibility_id = data.accessibilityId;

if ('customBackgroundColor' in data)
this.custom_background_color = data.customBackgroundColor;

if ('onLongPress' in data)
this.on_long_press = new NavigationEndpoint(data.onLongPress);

if ('titleFormatted' in data)
this.title_formatted = data.titleFormatted;

if ('onVisible' in data)
this.on_visible = data.onVisible;

if ('iconTrailing' in data)
this.icon_trailing = data.iconTrailing;

if ('accessibilityText' in data)
this.accessibility_text = data.accessibilityText;
}
}
109 changes: 73 additions & 36 deletions src/parser/classes/CompactVideo.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,96 @@
import { timeToSeconds } from '../../utils/Utils.js';
import { YTNode, type ObservedArray } from '../helpers.js';
import { type ObservedArray, YTNode } from '../helpers.js';
import { Parser, type RawNode } from '../index.js';
import Menu from './menus/Menu.js';
import MetadataBadge from './MetadataBadge.js';
import Author from './misc/Author.js';
import Text from './misc/Text.js';
import Thumbnail from './misc/Thumbnail.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import ThumbnailOverlayTimeStatus from './ThumbnailOverlayTimeStatus.js';

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

id: string;
thumbnails: Thumbnail[];
rich_thumbnail?: YTNode;
title: Text;
author: Author;
view_count: Text;
short_view_count: Text;
published: Text;
badges: MetadataBadge[];

duration: {
text: string;
seconds: number;
};

thumbnail_overlays: ObservedArray<YTNode>;
endpoint: NavigationEndpoint;
menu: Menu | null;
public video_id: string;
public thumbnails: Thumbnail[];
public rich_thumbnail?: YTNode;
public title: Text;
public author: Author;
public view_count?: Text;
public short_view_count?: Text;
public short_byline_text?: Text;
public long_byline_text?: Text;
public published?: Text;
public badges: MetadataBadge[];
public thumbnail_overlays: ObservedArray<YTNode>;
public endpoint?: NavigationEndpoint;
public menu: Menu | null;
public length_text?: Text;
public is_watched: boolean;
public service_endpoints?: NavigationEndpoint[];
public service_endpoint?: NavigationEndpoint;
public style?: 'COMPACT_VIDEO_STYLE_TYPE_UNKNOWN' | 'COMPACT_VIDEO_STYLE_TYPE_NORMAL' | 'COMPACT_VIDEO_STYLE_TYPE_PROMINENT_THUMBNAIL' | 'COMPACT_VIDEO_STYLE_TYPE_HERO';

constructor(data: RawNode) {
super();
this.id = data.videoId;
this.thumbnails = Thumbnail.fromResponse(data.thumbnail) || null;

if (Reflect.has(data, 'richThumbnail')) {
this.rich_thumbnail = Parser.parseItem(data.richThumbnail);
}

this.video_id = data.videoId;
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
this.title = new Text(data.title);
this.author = new Author(data.longBylineText, data.ownerBadges, data.channelThumbnail);
this.view_count = new Text(data.viewCountText);
this.short_view_count = new Text(data.shortViewCountText);
this.published = new Text(data.publishedTimeText);
this.is_watched = !!data.isWatched;
this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
this.menu = Parser.parseItem(data.menu, Menu);
this.badges = Parser.parseArray(data.badges, MetadataBadge);

this.duration = {
text: new Text(data.lengthText).toString(),
seconds: timeToSeconds(new Text(data.lengthText).toString())
};
if ('publishedTimeText' in data)
this.published = new Text(data.publishedTimeText);

if ('shortBylineText' in data)
this.view_count = new Text(data.viewCountText);

this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
this.menu = Parser.parseItem(data.menu, Menu);
if ('shortViewCountText' in data)
this.short_view_count = new Text(data.shortViewCountText);

if ('richThumbnail' in data)
this.rich_thumbnail = Parser.parseItem(data.richThumbnail);

if ('shortBylineText' in data)
this.short_byline_text = new Text(data.shortBylineText);

if ('longBylineText' in data)
this.long_byline_text = new Text(data.longBylineText);

if ('lengthText' in data)
this.length_text = new Text(data.lengthText);

if ('serviceEndpoints' in data)
this.service_endpoints = data.serviceEndpoints.map((endpoint: RawNode) => new NavigationEndpoint(endpoint));

if ('serviceEndpoint' in data)
this.service_endpoint = new NavigationEndpoint(data.serviceEndpoint);

if ('navigationEndpoint' in data)
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);

if ('style' in data)
this.style = data.style;
}

/**
* @deprecated Use {@linkcode video_id} instead.
*/
get id(): string {
return this.video_id;
}

get duration() {
const overlay_time_status = this.thumbnail_overlays.firstOfType(ThumbnailOverlayTimeStatus);
const length_text = this.length_text?.toString() || overlay_time_status?.text.toString();
return {
text: length_text,
seconds: length_text ? timeToSeconds(length_text) : 0
};
}

get best_thumbnail() {
Expand Down
Loading

0 comments on commit 923e9c2

Please sign in to comment.