Skip to content

Commit

Permalink
feat: add support for chapters & video heatmap (#263)
Browse files Browse the repository at this point in the history
* feat: add support for chapters & video heatmap

* chore: add tests
  • Loading branch information
LuanRT authored Dec 27, 2022
1 parent 2b3642b commit 6a4b4f3
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 4 deletions.
21 changes: 21 additions & 0 deletions src/parser/classes/Chapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Text from './misc/Text';
import Thumbnail from './misc/Thumbnail';

import { YTNode } from '../helpers';

class Chapter extends YTNode {
static type = 'Chapter';

title: Text;
time_range_start_millis: number;
thumbnail: Thumbnail[];

constructor(data: any) {
super();
this.title = new Text(data.title);
this.time_range_start_millis = data.timeRangeStartMillis;
this.thumbnail = Thumbnail.fromResponse(data.thumbnail);
}
}

export default Chapter;
19 changes: 19 additions & 0 deletions src/parser/classes/DecoratedPlayerBar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Parser from '..';
import { YTNode } from '../helpers';
import type Button from './Button';
import type MultiMarkersPlayerBar from './MultiMarkersPlayerBar';

class DecoratedPlayerBar extends YTNode {
static type = 'DecoratedPlayerBar';

player_bar: MultiMarkersPlayerBar | null;
player_bar_action_button: Button | null;

constructor(data: any) {
super();
this.player_bar = Parser.parseItem<MultiMarkersPlayerBar>(data.playerBar);
this.player_bar_action_button = Parser.parseItem<Button>(data.playerBarActionButton);
}
}

export default DecoratedPlayerBar;
18 changes: 18 additions & 0 deletions src/parser/classes/HeatMarker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { YTNode } from '../helpers';

class HeatMarker extends YTNode {
static type = 'HeatMarker';

time_range_start_millis: number;
marker_duration_millis: number;
heat_marker_intensity_score_normalized: number;

constructor(data: any) {
super();
this.time_range_start_millis = data.timeRangeStartMillis;
this.marker_duration_millis = data.markerDurationMillis;
this.heat_marker_intensity_score_normalized = data.heatMarkerIntensityScoreNormalized;
}
}

export default HeatMarker;
25 changes: 25 additions & 0 deletions src/parser/classes/Heatmap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Parser from '..';
import type HeatMarker from './HeatMarker';

import { YTNode } from '../helpers';

class Heatmap extends YTNode {
static type = 'Heatmap';

max_height_dp: number;
min_height_dp: number;
show_hide_animation_duration_millis: number;
heat_markers: HeatMarker[];
heat_markers_decorations: any;

constructor(data: any) {
super();
this.max_height_dp = data.maxHeightDp;
this.min_height_dp = data.minHeightDp;
this.show_hide_animation_duration_millis = data.showHideAnimationDurationMillis;
this.heat_markers = Parser.parseArray<HeatMarker>(data.heatMarkers);
this.heat_markers_decorations = Parser.parseArray(data.heatMarkersDecorations);
}
}

export default Heatmap;
44 changes: 44 additions & 0 deletions src/parser/classes/MultiMarkersPlayerBar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Parser from '..';
import type Chapter from './Chapter';
import type Heatmap from './Heatmap';

import { observe, ObservedArray, YTNode } from '../helpers';

class Marker extends YTNode {
static type = 'Marker';

marker_key: string;
value: {
heatmap?: Heatmap | null;
chapters?: Chapter[];
};

constructor (data: any) {
super();
this.marker_key = data.key;

this.value = {};

if (data.value.heatmap) {
this.value.heatmap = Parser.parseItem<Heatmap>(data.value.heatmap);
}

if (data.value.chapters) {
this.value.chapters = Parser.parseArray<Chapter>(data.value.chapters);
}
}
}

class MultiMarkersPlayerBar extends YTNode {
static type = 'MultiMarkersPlayerBar';

markers_map: ObservedArray<Marker>;

constructor(data: any) {
super();
this.markers_map = observe(data.markersMap?.map((marker: { key: string; value: { [key: string ]: any }}) => new Marker(marker)));
}
}

export { Marker };
export default MultiMarkersPlayerBar;
3 changes: 3 additions & 0 deletions src/parser/classes/PlayerOverlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Menu from './menus/Menu';
import Button from './Button';
import WatchNextEndScreen from './WatchNextEndScreen';
import PlayerOverlayAutoplay from './PlayerOverlayAutoplay';
import type DecoratedPlayerBar from './DecoratedPlayerBar';

import { YTNode } from '../helpers';

Expand All @@ -16,6 +17,7 @@ class PlayerOverlay extends YTNode {
fullscreen_engagement;
actions;
browser_media_session;
decorated_player_bar;

constructor(data: any) {
super();
Expand All @@ -26,6 +28,7 @@ class PlayerOverlay extends YTNode {
this.fullscreen_engagement = Parser.parse(data.fullscreenEngagement);
this.actions = Parser.parseArray(data.actions);
this.browser_media_session = Parser.parseItem(data.browserMediaSession);
this.decorated_player_bar = Parser.parseItem<DecoratedPlayerBar>(data.decoratedPlayerBarRenderer);
}
}

Expand Down
23 changes: 23 additions & 0 deletions src/parser/classes/TimedMarkerDecoration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Text from './misc/Text';
import { YTNode } from '../helpers';

class TimedMarkerDecoration extends YTNode {
static type = 'TimedMarkerDecoration';

visible_time_range_start_millis: number;
visible_time_range_end_millis: number;
decoration_time_millis: number;
label: Text;
icon: string;

constructor(data: any) {
super();
this.visible_time_range_start_millis = data.visibleTimeRangeStartMillis;
this.visible_time_range_end_millis = data.visibleTimeRangeEndMillis;
this.decoration_time_millis = data.decorationTimeMillis;
this.label = new Text(data.label);
this.icon = data.icon;
}
}

export default TimedMarkerDecoration;
12 changes: 12 additions & 0 deletions src/parser/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { default as ChannelMobileHeader } from './classes/ChannelMobileHeader';
import { default as ChannelOptions } from './classes/ChannelOptions';
import { default as ChannelThumbnailWithLink } from './classes/ChannelThumbnailWithLink';
import { default as ChannelVideoPlayer } from './classes/ChannelVideoPlayer';
import { default as Chapter } from './classes/Chapter';
import { default as ChildVideo } from './classes/ChildVideo';
import { default as ChipCloud } from './classes/ChipCloud';
import { default as ChipCloudChip } from './classes/ChipCloudChip';
Expand All @@ -62,6 +63,7 @@ import { default as ConfirmDialog } from './classes/ConfirmDialog';
import { default as ContinuationItem } from './classes/ContinuationItem';
import { default as CopyLink } from './classes/CopyLink';
import { default as CreatePlaylistDialog } from './classes/CreatePlaylistDialog';
import { default as DecoratedPlayerBar } from './classes/DecoratedPlayerBar';
import { default as DefaultPromoPanel } from './classes/DefaultPromoPanel';
import { default as DidYouMean } from './classes/DidYouMean';
import { default as DownloadButton } from './classes/DownloadButton';
Expand All @@ -85,6 +87,8 @@ import { default as GridChannel } from './classes/GridChannel';
import { default as GridHeader } from './classes/GridHeader';
import { default as GridPlaylist } from './classes/GridPlaylist';
import { default as GridVideo } from './classes/GridVideo';
import { default as Heatmap } from './classes/Heatmap';
import { default as HeatMarker } from './classes/HeatMarker';
import { default as HighlightsCarousel } from './classes/HighlightsCarousel';
import { default as HistorySuggestion } from './classes/HistorySuggestion';
import { default as HorizontalCardList } from './classes/HorizontalCardList';
Expand Down Expand Up @@ -161,6 +165,7 @@ import { default as MicroformatData } from './classes/MicroformatData';
import { default as Mix } from './classes/Mix';
import { default as Movie } from './classes/Movie';
import { default as MovingThumbnail } from './classes/MovingThumbnail';
import { default as MultiMarkersPlayerBar } from './classes/MultiMarkersPlayerBar';
import { default as MusicCarouselShelf } from './classes/MusicCarouselShelf';
import { default as MusicCarouselShelfBasicHeader } from './classes/MusicCarouselShelfBasicHeader';
import { default as MusicDescriptionShelf } from './classes/MusicDescriptionShelf';
Expand Down Expand Up @@ -269,6 +274,7 @@ import { default as ThumbnailOverlayResumePlayback } from './classes/ThumbnailOv
import { default as ThumbnailOverlaySidePanel } from './classes/ThumbnailOverlaySidePanel';
import { default as ThumbnailOverlayTimeStatus } from './classes/ThumbnailOverlayTimeStatus';
import { default as ThumbnailOverlayToggleButton } from './classes/ThumbnailOverlayToggleButton';
import { default as TimedMarkerDecoration } from './classes/TimedMarkerDecoration';
import { default as TitleAndButtonListHeader } from './classes/TitleAndButtonListHeader';
import { default as ToggleButton } from './classes/ToggleButton';
import { default as ToggleMenuServiceItem } from './classes/ToggleMenuServiceItem';
Expand Down Expand Up @@ -331,6 +337,7 @@ export const YTNodes = {
ChannelOptions,
ChannelThumbnailWithLink,
ChannelVideoPlayer,
Chapter,
ChildVideo,
ChipCloud,
ChipCloudChip,
Expand All @@ -354,6 +361,7 @@ export const YTNodes = {
ContinuationItem,
CopyLink,
CreatePlaylistDialog,
DecoratedPlayerBar,
DefaultPromoPanel,
DidYouMean,
DownloadButton,
Expand All @@ -377,6 +385,8 @@ export const YTNodes = {
GridHeader,
GridPlaylist,
GridVideo,
Heatmap,
HeatMarker,
HighlightsCarousel,
HistorySuggestion,
HorizontalCardList,
Expand Down Expand Up @@ -453,6 +463,7 @@ export const YTNodes = {
Mix,
Movie,
MovingThumbnail,
MultiMarkersPlayerBar,
MusicCarouselShelf,
MusicCarouselShelfBasicHeader,
MusicDescriptionShelf,
Expand Down Expand Up @@ -561,6 +572,7 @@ export const YTNodes = {
ThumbnailOverlaySidePanel,
ThumbnailOverlayTimeStatus,
ThumbnailOverlayToggleButton,
TimedMarkerDecoration,
TitleAndButtonListHeader,
ToggleButton,
ToggleMenuServiceItem,
Expand Down
22 changes: 18 additions & 4 deletions test/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,27 @@ describe('YouTube.js Tests', () => {
expect(info.basic_info.id).toBe(VIDEOS[0].ID);
});

it('should have captions on full video info', async () => {
it('should have captions', () => {
expect(info.captions?.caption_tracks.length).toBeGreaterThan(0);
});

it('should have chapters', () => {
const markers_map = info.player_overlays?.decorated_player_bar?.player_bar?.markers_map;

const chapters = (
markers_map?.get({ marker_key: 'AUTO_CHAPTERS' }) ||
markers_map?.get({ marker_key: 'DESCRIPTION_CHAPTERS' })
)?.value?.chapters;

expect(chapters).toBeDefined();
});

it('should have heatmap', () => {
const markers_map = info.player_overlays?.decorated_player_bar?.player_bar?.markers_map;
const heatmap = markers_map?.get({ marker_key: 'HEATSEEKER' })?.value?.heatmap;
expect(heatmap).toBeDefined();
});

it('should retrieve basic video info', async () => {
const b_info = await yt.getBasicInfo(VIDEOS[0].ID);
expect(b_info.basic_info.id).toBe(VIDEOS[0].ID);
Expand Down Expand Up @@ -114,9 +131,6 @@ describe('YouTube.js Tests', () => {

const filtered_list = await videos_tab.applyFilter('Popular');
expect(filtered_list.videos.length).toBeGreaterThan(0);

const search = await channel.search('e-ink');
expect(search.videos.length).toBeGreaterThan(0);
});

it('should retrieve home feed', async () => {
Expand Down

0 comments on commit 6a4b4f3

Please sign in to comment.