Skip to content

Commit e434bb2

Browse files
fix(VideoInfo.ts): reimplement get music_tracks (#409)
* fix(VideoInfo.ts): reimplement `get music_tracks` - Add parser classes to parse needed data - Add `CarouselLockup` - Add `EngagementPanelSectionList` - Add `InfoRow` - Add `StructuredDescriptionContent` - Add `VideoDescriptionMusicSection` - Add `VideoDescriptionHeader` - Add `Factoid` - Add `ExpandableVideoDescriptionBody` - Add `AdsEngagementPanelContent` - Add `engagement_panels` to raw and parsed next responses - Add `engagement_panels` parsing code to `parser.ts` * Check for song inside of video_lockup first before checking info_rows * Add support for pulling artist ids out of music_tracks - Add support for WRITERS InfoRow - Check for video id inside of naviagation endpoint on info_row metadata * Add `AdsEngagementPanelContent` to ignore list * Switch `map => parseItem` to `parseArray` * Use `Text` && `NavigationEndpoint` * Replace `String` with `Text` in `ExpandableVideoDescriptionBody`
1 parent a11e596 commit e434bb2

13 files changed

+240
-36
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { type ObservedArray, YTNode } from '../helpers.js';
2+
import InfoRow from './InfoRow.js';
3+
import Parser, { type RawNode } from '../index.js';
4+
import CompactVideo from './CompactVideo.js';
5+
6+
export default class CarouselLockup extends YTNode {
7+
static type = 'CarouselLockup';
8+
9+
info_rows: ObservedArray<InfoRow>;
10+
video_lockup?: CompactVideo;
11+
12+
constructor(data: RawNode) {
13+
super();
14+
this.info_rows = Parser.parseArray(data.infoRows, InfoRow);
15+
const video_lockup = Parser.parseItem(data.videoLockup, CompactVideo);
16+
if (video_lockup != null) {
17+
this.video_lockup = video_lockup;
18+
}
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { YTNode } from '../helpers.js';
2+
import Parser, { type RawNode } from '../index.js';
3+
import ContinuationItem from './ContinuationItem.js';
4+
import SectionList from './SectionList.js';
5+
import StructuredDescriptionContent from './StructuredDescriptionContent.js';
6+
7+
export default class EngagementPanelSectionList extends YTNode {
8+
static type = 'EngagementPanelSectionList';
9+
10+
target_id: String;
11+
content?: SectionList|ContinuationItem|StructuredDescriptionContent;
12+
constructor(data: RawNode) {
13+
super();
14+
this.target_id = data.targetId;
15+
const content = Parser.parseItem(data.content, [ SectionList, ContinuationItem, StructuredDescriptionContent ]);
16+
if (content !== null) {
17+
this.content = content;
18+
}
19+
}
20+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { YTNode } from '../helpers.js';
2+
import { type RawNode } from '../index.js';
3+
import { Text } from '../misc.js';
4+
5+
export default class ExpandableVideoDescriptionBody extends YTNode {
6+
static type = 'ExpandableVideoDescriptionBody';
7+
8+
show_more_text: Text;
9+
show_less_text: Text;
10+
attributed_description_body_text: {
11+
content: String
12+
};
13+
14+
constructor(data: RawNode) {
15+
super();
16+
this.show_more_text = new Text(data.showMoreText);
17+
this.show_less_text = new Text(data.showLessText);
18+
this.attributed_description_body_text = {
19+
content: data.attributedDescriptionBodyText.content
20+
};
21+
}
22+
}

src/parser/classes/Factoid.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { YTNode } from '../helpers.js';
2+
import { type RawNode } from '../index.js';
3+
import { Text } from '../misc.js';
4+
5+
export default class Factoid extends YTNode {
6+
static type = 'Factoid';
7+
label: Text;
8+
value: Text;
9+
accessibility_text: String;
10+
11+
constructor(data: RawNode) {
12+
super();
13+
this.label = new Text(data.label);
14+
this.value = new Text(data.value);
15+
this.accessibility_text = data.accessibilityText;
16+
}
17+
}

src/parser/classes/InfoRow.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { YTNode } from '../helpers.js';
2+
import Parser, { type RawNode } from '../index.js';
3+
import { Text } from '../misc.js';
4+
import NavigationEndpoint from './NavigationEndpoint.js';
5+
6+
export default class InfoRow extends YTNode {
7+
static type = 'InfoRow';
8+
metadata_text?: Text;
9+
metadata_endpoint?: NavigationEndpoint;
10+
info_row_expand_status_key: String;
11+
title: Text;
12+
13+
constructor(data: RawNode) {
14+
super();
15+
if ('defaultMetadata' in data && 'runs' in data.defaultMetadata) {
16+
const runs = data.defaultMetadata.runs;
17+
if (runs.length > 0) {
18+
const run = runs[0];
19+
this.metadata_text = run?.text;
20+
if ('navigationEndpoint' in run) {
21+
this.metadata_endpoint = Parser.parseItem({ navigationEndpoint: run.navigationEndpoint }, NavigationEndpoint) || undefined;
22+
}
23+
}
24+
}
25+
if ('expandedMetadata' in data && 'runs' in data.expandedMetadata) {
26+
this.metadata_text = new Text(data.expandedMetadata);
27+
}
28+
if (this.metadata_text === undefined) {
29+
this.metadata_text = data.expandedMetadata?.simpleText
30+
? new Text(data.expandedMetadata)
31+
: data.defaultMetadata?.simpleText
32+
? new Text(data.defaultMetadata)
33+
: undefined;
34+
}
35+
this.info_row_expand_status_key = data.infoRowExpandStatusKey;
36+
this.title = new Text(data.title);
37+
}
38+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { type ObservedArray, YTNode } from '../helpers.js';
2+
import Parser, { type RawNode } from '../index.js';
3+
import ExpandableVideoDescriptionBody from './ExpandableVideoDescriptionBody.js';
4+
import VideoDescriptionHeader from './VideoDescriptionHeader.js';
5+
import VideoDescriptionMusicSection from './VideoDescriptionMusicSection.js';
6+
7+
export default class StructuredDescriptionContent extends YTNode {
8+
static type = 'StructuredDescriptionContent';
9+
10+
items: ObservedArray<VideoDescriptionHeader|ExpandableVideoDescriptionBody|VideoDescriptionMusicSection>;
11+
12+
constructor(data: RawNode) {
13+
super();
14+
this.items = Parser.parseArray(data.items, [ VideoDescriptionHeader, ExpandableVideoDescriptionBody, VideoDescriptionMusicSection ]);
15+
}
16+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { type ObservedArray, YTNode } from '../helpers.js';
2+
import Parser, { type RawNode } from '../index.js';
3+
import { Text } from '../misc.js';
4+
import type { Thumbnail } from '../misc.js';
5+
import Factoid from './Factoid.js';
6+
import NavigationEndpoint from './NavigationEndpoint.js';
7+
8+
export default class VideoDescriptionHeader extends YTNode {
9+
static type = 'VideoDescriptionHeader';
10+
11+
channel: Text;
12+
channel_navigation_endpoint?: NavigationEndpoint;
13+
channel_thumbnails: String[];
14+
factoids: ObservedArray<Factoid>;
15+
publish_date: Text;
16+
title: Text;
17+
views: Text;
18+
19+
constructor(data: RawNode) {
20+
super();
21+
this.title = new Text(data.title);
22+
this.channel = new Text(data.channel);
23+
this.channel_navigation_endpoint = Parser.parseItem(data.channelNavigationEndpoint, NavigationEndpoint) || undefined;
24+
this.channel_thumbnails = data.channelThumbnail.thumbnails.map((thumbnail: Thumbnail) => thumbnail.url);
25+
this.publish_date = new Text(data.publishDate);
26+
this.views = new Text(data.views);
27+
this.factoids = Parser.parseArray(data.factoid, Factoid);
28+
}
29+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { type ObservedArray, YTNode } from '../helpers.js';
2+
import CarouselLockup from './CarouselLockup.js';
3+
import Parser, { type RawNode } from '../index.js';
4+
import { Text } from '../misc.js';
5+
6+
export default class VideoDescriptionMusicSection extends YTNode {
7+
static type = 'VideoDescriptionMusicSection';
8+
9+
carousel_lockups: ObservedArray<CarouselLockup>;
10+
section_title: Text;
11+
constructor(data: RawNode) {
12+
super();
13+
this.carousel_lockups = Parser.parseArray(data.carouselLockups, CarouselLockup);
14+
this.section_title = new Text(data.sectionTitle);
15+
}
16+
}

src/parser/nodes.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export { default as Card } from './classes/Card.js';
3030
export { default as CardCollection } from './classes/CardCollection.js';
3131
export { default as CarouselHeader } from './classes/CarouselHeader.js';
3232
export { default as CarouselItem } from './classes/CarouselItem.js';
33+
export { default as CarouselLockup } from './classes/CarouselLockup.js';
3334
export { default as Channel } from './classes/Channel.js';
3435
export { default as ChannelAboutFullMetadata } from './classes/ChannelAboutFullMetadata.js';
3536
export { default as ChannelAgeGate } from './classes/ChannelAgeGate.js';
@@ -88,9 +89,12 @@ export { default as Endscreen } from './classes/Endscreen.js';
8889
export { default as EndscreenElement } from './classes/EndscreenElement.js';
8990
export { default as EndScreenPlaylist } from './classes/EndScreenPlaylist.js';
9091
export { default as EndScreenVideo } from './classes/EndScreenVideo.js';
92+
export { default as EngagementPanelSectionList } from './classes/EngagementPanelSectionList.js';
9193
export { default as ExpandableMetadata } from './classes/ExpandableMetadata.js';
9294
export { default as ExpandableTab } from './classes/ExpandableTab.js';
95+
export { default as ExpandableVideoDescriptionBody } from './classes/ExpandableVideoDescriptionBody.js';
9396
export { default as ExpandedShelfContents } from './classes/ExpandedShelfContents.js';
97+
export { default as Factoid } from './classes/Factoid.js';
9498
export { default as FeedFilterChipBar } from './classes/FeedFilterChipBar.js';
9599
export { default as FeedTabbedHeader } from './classes/FeedTabbedHeader.js';
96100
export { default as GameCard } from './classes/GameCard.js';
@@ -121,6 +125,7 @@ export { default as HorizontalMovieList } from './classes/HorizontalMovieList.js
121125
export { default as IconLink } from './classes/IconLink.js';
122126
export { default as InfoPanelContainer } from './classes/InfoPanelContainer.js';
123127
export { default as InfoPanelContent } from './classes/InfoPanelContent.js';
128+
export { default as InfoRow } from './classes/InfoRow.js';
124129
export { default as InteractiveTabbedHeader } from './classes/InteractiveTabbedHeader.js';
125130
export { default as ItemSection } from './classes/ItemSection.js';
126131
export { default as ItemSectionHeader } from './classes/ItemSectionHeader.js';
@@ -299,6 +304,7 @@ export { default as SingleHeroImage } from './classes/SingleHeroImage.js';
299304
export { default as SlimOwner } from './classes/SlimOwner.js';
300305
export { default as SlimVideoMetadata } from './classes/SlimVideoMetadata.js';
301306
export { default as SortFilterSubMenu } from './classes/SortFilterSubMenu.js';
307+
export { default as StructuredDescriptionContent } from './classes/StructuredDescriptionContent.js';
302308
export { default as SubFeedOption } from './classes/SubFeedOption.js';
303309
export { default as SubFeedSelector } from './classes/SubFeedSelector.js';
304310
export { default as SubscribeButton } from './classes/SubscribeButton.js';
@@ -335,6 +341,8 @@ export { default as VerticalList } from './classes/VerticalList.js';
335341
export { default as VerticalWatchCardList } from './classes/VerticalWatchCardList.js';
336342
export { default as Video } from './classes/Video.js';
337343
export { default as VideoCard } from './classes/VideoCard.js';
344+
export { default as VideoDescriptionHeader } from './classes/VideoDescriptionHeader.js';
345+
export { default as VideoDescriptionMusicSection } from './classes/VideoDescriptionMusicSection.js';
338346
export { default as VideoInfoCardContent } from './classes/VideoInfoCardContent.js';
339347
export { default as VideoOwner } from './classes/VideoOwner.js';
340348
export { default as VideoPrimaryInfo } from './classes/VideoPrimaryInfo.js';

src/parser/parser.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,13 @@ export default class Parser {
264264
parsed_data.cards = cards;
265265
}
266266

267+
const engagement_panels = data.engagementPanels?.map((e) => {
268+
const item = this.parseItem(e, YTNodes.EngagementPanelSectionList) as YTNodes.EngagementPanelSectionList;
269+
return item;
270+
});
271+
if (engagement_panels) {
272+
parsed_data.engagement_panels = engagement_panels;
273+
}
267274
this.#createMemo();
268275
const items = this.parse(data.items);
269276
if (items) {
@@ -502,7 +509,8 @@ export default class Parser {
502509
'BrandVideoShelf',
503510
'BrandVideoSingleton',
504511
'StatementBanner',
505-
'GuideSigninPromo'
512+
'GuideSigninPromo',
513+
'AdsEngagementPanelContent'
506514
]);
507515

508516
static shouldIgnore(classname: string) {
@@ -739,4 +747,4 @@ export class LiveChatContinuation extends YTNode {
739747

740748
this.viewer_name = data.viewerName;
741749
}
742-
}
750+
}

0 commit comments

Comments
 (0)