From 7ca2a0c3e43ebd4b9443e69b7432f302b09e9c7a Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Thu, 29 Feb 2024 17:29:53 +0100 Subject: [PATCH] feat(parser): Support LockupView and it's child nodes (#609) --- src/core/mixins/Feed.ts | 14 +++++++++- src/parser/classes/CollectionThumbnailView.ts | 23 ++++++++++++++++ src/parser/classes/ContentMetadataView.ts | 4 +-- src/parser/classes/LockupMetadataView.ts | 18 +++++++++++++ src/parser/classes/LockupView.ts | 27 +++++++++++++++++++ src/parser/classes/ThumbnailBadgeView.ts | 26 ++++++++++++++++++ .../classes/ThumbnailHoverOverlayView.ts | 19 +++++++++++++ .../classes/ThumbnailOverlayBadgeView.ts | 17 ++++++++++++ src/parser/classes/ThumbnailView.ts | 27 +++++++++++++++++++ src/parser/nodes.ts | 7 +++++ 10 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 src/parser/classes/CollectionThumbnailView.ts create mode 100644 src/parser/classes/LockupMetadataView.ts create mode 100644 src/parser/classes/LockupView.ts create mode 100644 src/parser/classes/ThumbnailBadgeView.ts create mode 100644 src/parser/classes/ThumbnailHoverOverlayView.ts create mode 100644 src/parser/classes/ThumbnailOverlayBadgeView.ts create mode 100644 src/parser/classes/ThumbnailView.ts diff --git a/src/core/mixins/Feed.ts b/src/core/mixins/Feed.ts index 5c490652..ca0a889f 100644 --- a/src/core/mixins/Feed.ts +++ b/src/core/mixins/Feed.ts @@ -8,6 +8,7 @@ import CompactVideo from '../../parser/classes/CompactVideo.js'; import GridChannel from '../../parser/classes/GridChannel.js'; import GridPlaylist from '../../parser/classes/GridPlaylist.js'; import GridVideo from '../../parser/classes/GridVideo.js'; +import LockupView from '../../parser/classes/LockupView.js'; import Playlist from '../../parser/classes/Playlist.js'; import PlaylistPanelVideo from '../../parser/classes/PlaylistPanelVideo.js'; import PlaylistVideo from '../../parser/classes/PlaylistVideo.js'; @@ -88,7 +89,18 @@ export default class Feed { * Get all playlists on a given page via memo */ static getPlaylistsFromMemo(memo: Memo) { - return memo.getType(Playlist, GridPlaylist); + const playlists: ObservedArray = memo.getType(Playlist, GridPlaylist); + + const lockup_views = memo.getType(LockupView) + .filter((lockup) => { + return [ 'PLAYLIST', 'ALBUM', 'PODCAST' ].includes(lockup.content_type); + }); + + if (lockup_views.length > 0) { + playlists.push(...lockup_views); + } + + return playlists; } /** diff --git a/src/parser/classes/CollectionThumbnailView.ts b/src/parser/classes/CollectionThumbnailView.ts new file mode 100644 index 00000000..41783701 --- /dev/null +++ b/src/parser/classes/CollectionThumbnailView.ts @@ -0,0 +1,23 @@ +import { YTNode } from '../helpers.js'; +import { Parser, type RawNode } from '../index.js'; +import ThumbnailView from './ThumbnailView.js'; + +export default class CollectionThumbnailView extends YTNode { + static type = 'CollectionThumbnailView'; + + primary_thumbnail: ThumbnailView | null; + stack_color: { + light_theme: number; + dark_theme: number; + }; + + constructor(data: RawNode) { + super(); + + this.primary_thumbnail = Parser.parseItem(data.primaryThumbnail, ThumbnailView); + this.stack_color = { + light_theme: data.stackColor.lightTheme, + dark_theme: data.stackColor.darkTheme + }; + } +} \ No newline at end of file diff --git a/src/parser/classes/ContentMetadataView.ts b/src/parser/classes/ContentMetadataView.ts index c1da74ac..054156de 100644 --- a/src/parser/classes/ContentMetadataView.ts +++ b/src/parser/classes/ContentMetadataView.ts @@ -3,7 +3,7 @@ import type { RawNode } from '../index.js'; import { Text } from '../misc.js'; export type MetadataRow = { - metadata_parts: { + metadata_parts?: { text: Text; }[]; }; @@ -17,7 +17,7 @@ export default class ContentMetadataView extends YTNode { constructor(data: RawNode) { super(); this.metadata_rows = data.metadataRows.map((row: RawNode) => ({ - metadata_parts: row.metadataParts.map((part: RawNode) => ({ + metadata_parts: row.metadataParts?.map((part: RawNode) => ({ text: Text.fromAttributed(part.text) })) })); diff --git a/src/parser/classes/LockupMetadataView.ts b/src/parser/classes/LockupMetadataView.ts new file mode 100644 index 00000000..d3ed30dd --- /dev/null +++ b/src/parser/classes/LockupMetadataView.ts @@ -0,0 +1,18 @@ +import { YTNode } from '../helpers.js'; +import { Parser, type RawNode } from '../index.js'; +import ContentMetadataView from './ContentMetadataView.js'; +import Text from './misc/Text.js'; + +export default class LockupMetadataView extends YTNode { + static type = 'LockupMetadataView'; + + title: Text; + metadata: ContentMetadataView | null; + + constructor(data: RawNode) { + super(); + + this.title = Text.fromAttributed(data.title); + this.metadata = Parser.parseItem(data.metadata, ContentMetadataView); + } +} \ No newline at end of file diff --git a/src/parser/classes/LockupView.ts b/src/parser/classes/LockupView.ts new file mode 100644 index 00000000..93d7156c --- /dev/null +++ b/src/parser/classes/LockupView.ts @@ -0,0 +1,27 @@ +import { YTNode } from '../helpers.js'; +import { Parser, type RawNode } from '../index.js'; +import CollectionThumbnailView from './CollectionThumbnailView.js'; +import LockupMetadataView from './LockupMetadataView.js'; +import NavigationEndpoint from './NavigationEndpoint.js'; + +export default class LockupView extends YTNode { + static type = 'LockupView'; + + content_image: CollectionThumbnailView | null; + metadata: LockupMetadataView | null; + content_id: string; + content_type: 'SOURCE' | 'PLAYLIST' | 'ALBUM' | 'PODCAST' | 'SHOPPING_COLLECTION' | 'SHORT' | 'GAME' | 'PRODUCT'; + on_tap_endpoint: NavigationEndpoint; + + constructor(data: RawNode) { + super(); + + this.content_image = Parser.parseItem(data.contentImage, CollectionThumbnailView); + this.metadata = Parser.parseItem(data.metadata, LockupMetadataView); + this.content_id = data.contentId; + this.content_type = data.contentType.replace('LOCKUP_CONTENT_TYPE_', ''); + this.on_tap_endpoint = new NavigationEndpoint(data.rendererContext.commandContext.onTap); + console.log(data); + console.log(this); + } +} \ No newline at end of file diff --git a/src/parser/classes/ThumbnailBadgeView.ts b/src/parser/classes/ThumbnailBadgeView.ts new file mode 100644 index 00000000..2a5fb9c3 --- /dev/null +++ b/src/parser/classes/ThumbnailBadgeView.ts @@ -0,0 +1,26 @@ +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; + +export default class ThumbnailBadgeView extends YTNode { + static type = 'ThumbnailBadgeView'; + + icon_name: string; + text: string; + badge_style: string; + background_color: { + light_theme: number; + dark_theme: number; + }; + + constructor(data: RawNode) { + super(); + + this.icon_name = data.icon.sources[0].clientResource.imageName; + this.text = data.text; + this.badge_style = data.badgeStyle; + this.background_color = { + light_theme: data.backgroundColor.lightTheme, + dark_theme: data.backgroundColor.darkTheme + }; + } +} \ No newline at end of file diff --git a/src/parser/classes/ThumbnailHoverOverlayView.ts b/src/parser/classes/ThumbnailHoverOverlayView.ts new file mode 100644 index 00000000..257b208a --- /dev/null +++ b/src/parser/classes/ThumbnailHoverOverlayView.ts @@ -0,0 +1,19 @@ +import { YTNode } from '../helpers.js'; +import type { RawNode } from '../index.js'; +import Text from './misc/Text.js'; + +export default class ThumbnailHoverOverlayView extends YTNode { + static type = 'ThumbnailHoverOverlayView'; + + icon_name: string; + text: Text; + style: string; + + constructor(data: RawNode) { + super(); + + this.icon_name = data.icon.sources[0].clientResource.imageName; + this.text = Text.fromAttributed(data.text); + this.style = data.style; + } +} \ No newline at end of file diff --git a/src/parser/classes/ThumbnailOverlayBadgeView.ts b/src/parser/classes/ThumbnailOverlayBadgeView.ts new file mode 100644 index 00000000..80ac6167 --- /dev/null +++ b/src/parser/classes/ThumbnailOverlayBadgeView.ts @@ -0,0 +1,17 @@ +import { YTNode } from '../helpers.js'; +import { Parser, type RawNode } from '../index.js'; +import ThumbnailBadgeView from './ThumbnailBadgeView.js'; + +export default class ThumbnailOverlayBadgeView extends YTNode { + static type = 'ThumbnailOverlayBadgeView'; + + badges: ThumbnailBadgeView[]; + position: string; + + constructor(data: RawNode) { + super(); + + this.badges = Parser.parseArray(data.thumbnailBadges, ThumbnailBadgeView); + this.position = data.position; + } +} \ No newline at end of file diff --git a/src/parser/classes/ThumbnailView.ts b/src/parser/classes/ThumbnailView.ts new file mode 100644 index 00000000..b768f499 --- /dev/null +++ b/src/parser/classes/ThumbnailView.ts @@ -0,0 +1,27 @@ +import { YTNode } from '../helpers.js'; +import { Parser, type RawNode } from '../index.js'; +import ThumbnailHoverOverlayView from './ThumbnailHoverOverlayView.js'; +import ThumbnailOverlayBadgeView from './ThumbnailOverlayBadgeView.js'; +import Thumbnail from './misc/Thumbnail.js'; + +export default class ThumbnailView extends YTNode { + static type = 'ThumbnailView'; + + image: Thumbnail[]; + overlays: (ThumbnailOverlayBadgeView | ThumbnailHoverOverlayView)[]; + background_color: { + light_theme: number; + dark_theme: number; + }; + + constructor(data: RawNode) { + super(); + + this.image = Thumbnail.fromResponse(data.image); + this.overlays = Parser.parseArray(data.overlays, [ ThumbnailOverlayBadgeView, ThumbnailHoverOverlayView ]); + this.background_color = { + light_theme: data.backgroundColor.lightTheme, + dark_theme: data.backgroundColor.darkTheme + }; + } +} \ No newline at end of file diff --git a/src/parser/nodes.ts b/src/parser/nodes.ts index 7dac3c79..aefe60ea 100644 --- a/src/parser/nodes.ts +++ b/src/parser/nodes.ts @@ -64,6 +64,7 @@ export { default as ClipCreationTextInput } from './classes/ClipCreationTextInpu export { default as ClipSection } from './classes/ClipSection.js'; export { default as CollaboratorInfoCardContent } from './classes/CollaboratorInfoCardContent.js'; export { default as CollageHeroImage } from './classes/CollageHeroImage.js'; +export { default as CollectionThumbnailView } from './classes/CollectionThumbnailView.js'; export { default as Command } from './classes/Command.js'; export { default as AuthorCommentBadge } from './classes/comments/AuthorCommentBadge.js'; export { default as Comment } from './classes/comments/Comment.js'; @@ -210,6 +211,8 @@ export { default as LiveChatItemList } from './classes/LiveChatItemList.js'; export { default as LiveChatMessageInput } from './classes/LiveChatMessageInput.js'; export { default as LiveChatParticipant } from './classes/LiveChatParticipant.js'; export { default as LiveChatParticipantsList } from './classes/LiveChatParticipantsList.js'; +export { default as LockupMetadataView } from './classes/LockupMetadataView.js'; +export { default as LockupView } from './classes/LockupView.js'; export { default as MacroMarkersInfoItem } from './classes/MacroMarkersInfoItem.js'; export { default as MacroMarkersList } from './classes/MacroMarkersList.js'; export { default as MacroMarkersListItem } from './classes/MacroMarkersListItem.js'; @@ -367,7 +370,10 @@ export { default as Tab } from './classes/Tab.js'; export { default as Tabbed } from './classes/Tabbed.js'; export { default as TabbedSearchResults } from './classes/TabbedSearchResults.js'; export { default as TextHeader } from './classes/TextHeader.js'; +export { default as ThumbnailBadgeView } from './classes/ThumbnailBadgeView.js'; +export { default as ThumbnailHoverOverlayView } from './classes/ThumbnailHoverOverlayView.js'; export { default as ThumbnailLandscapePortrait } from './classes/ThumbnailLandscapePortrait.js'; +export { default as ThumbnailOverlayBadgeView } from './classes/ThumbnailOverlayBadgeView.js'; export { default as ThumbnailOverlayBottomPanel } from './classes/ThumbnailOverlayBottomPanel.js'; export { default as ThumbnailOverlayEndorsement } from './classes/ThumbnailOverlayEndorsement.js'; export { default as ThumbnailOverlayHoverText } from './classes/ThumbnailOverlayHoverText.js'; @@ -380,6 +386,7 @@ export { default as ThumbnailOverlayResumePlayback } from './classes/ThumbnailOv export { default as ThumbnailOverlaySidePanel } from './classes/ThumbnailOverlaySidePanel.js'; export { default as ThumbnailOverlayTimeStatus } from './classes/ThumbnailOverlayTimeStatus.js'; export { default as ThumbnailOverlayToggleButton } from './classes/ThumbnailOverlayToggleButton.js'; +export { default as ThumbnailView } from './classes/ThumbnailView.js'; export { default as TimedMarkerDecoration } from './classes/TimedMarkerDecoration.js'; export { default as TitleAndButtonListHeader } from './classes/TitleAndButtonListHeader.js'; export { default as ToggleButton } from './classes/ToggleButton.js';