mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-07-02 21:52:48 +00:00
feat(yt): add support for movie items and trailers (#349)
This commit is contained in:
34
src/parser/classes/GridMovie.ts
Normal file
34
src/parser/classes/GridMovie.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import Parser from '../index.js';
|
||||
import Text from './misc/Text.js';
|
||||
import Thumbnail from './misc/Thumbnail.js';
|
||||
import NavigationEndpoint from './NavigationEndpoint.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import MetadataBadge from './MetadataBadge.js';
|
||||
|
||||
class GridMovie extends YTNode {
|
||||
static type = 'GridMovie';
|
||||
|
||||
id: string;
|
||||
title: Text;
|
||||
thumbnails: Thumbnail[];
|
||||
duration: Text | null;
|
||||
endpoint: NavigationEndpoint;
|
||||
badges: MetadataBadge[];
|
||||
metadata: Text;
|
||||
thumbnail_overlays;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
const length_alt = data.thumbnailOverlays.find((overlay: any) => overlay.hasOwnProperty('thumbnailOverlayTimeStatusRenderer'))?.thumbnailOverlayTimeStatusRenderer;
|
||||
this.id = data.videoId;
|
||||
this.title = new Text(data.title);
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail);
|
||||
this.duration = data.lengthText ? new Text(data.lengthText) : length_alt?.text ? new Text(length_alt.text) : null;
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
|
||||
this.badges = Parser.parseArray<MetadataBadge>(data.badges, MetadataBadge);
|
||||
this.metadata = new Text(data.metadata);
|
||||
this.thumbnail_overlays = Parser.parseArray(data.thumbnailOverlays);
|
||||
}
|
||||
}
|
||||
|
||||
export default GridMovie;
|
||||
25
src/parser/classes/HorizontalMovieList.ts
Normal file
25
src/parser/classes/HorizontalMovieList.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import Parser from '../index.js';
|
||||
import { YTNode } from '../helpers.js';
|
||||
import Button from './Button.js';
|
||||
|
||||
class HorizontalMovieList extends YTNode {
|
||||
static type = 'HorizontalMovieList';
|
||||
|
||||
items;
|
||||
previous_button: Button | null;
|
||||
next_button: Button | null;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.items = Parser.parseArray(data.items);
|
||||
this.previous_button = Parser.parseItem<Button>(data.previousButton, Button);
|
||||
this.next_button = Parser.parseItem<Button>(data.nextButton, Button);
|
||||
}
|
||||
|
||||
// XXX: alias for consistency
|
||||
get contents() {
|
||||
return this.items;
|
||||
}
|
||||
}
|
||||
|
||||
export default HorizontalMovieList;
|
||||
32
src/parser/classes/PlayerLegacyDesktopYpcTrailer.ts
Normal file
32
src/parser/classes/PlayerLegacyDesktopYpcTrailer.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import { Parser, RawNode } from '../index.js';
|
||||
import YpcTrailer from './YpcTrailer.js';
|
||||
|
||||
class PlayerLegacyDesktopYpcTrailer extends YTNode {
|
||||
static type = 'PlayerLegacyDesktopYpcTrailer';
|
||||
|
||||
video_id: string;
|
||||
title: string;
|
||||
thumbnail: string;
|
||||
offer_headline: string;
|
||||
offer_description: string;
|
||||
offer_id: string;
|
||||
offer_button_text: string;
|
||||
video_message: string;
|
||||
trailer: YpcTrailer | null;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.video_id = data.trailerVideoId;
|
||||
this.title = data.itemTitle;
|
||||
this.thumbnail = data.itemThumbnail;
|
||||
this.offer_headline = data.offerHeadline;
|
||||
this.offer_description = data.offerDescription;
|
||||
this.offer_id = data.offerId;
|
||||
this.offer_button_text = data.offerButtonText;
|
||||
this.video_message = data.fullVideoMessage;
|
||||
this.trailer = Parser.parseItem<YpcTrailer>(data.ypcTrailer, YpcTrailer);
|
||||
}
|
||||
}
|
||||
|
||||
export default PlayerLegacyDesktopYpcTrailer;
|
||||
17
src/parser/classes/YpcTrailer.ts
Normal file
17
src/parser/classes/YpcTrailer.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { YTNode } from '../helpers.js';
|
||||
import { RawNode } from '../index.js';
|
||||
|
||||
class YpcTrailer extends YTNode {
|
||||
static type = 'YpcTrailer';
|
||||
|
||||
video_message: string;
|
||||
player_response;
|
||||
|
||||
constructor(data: RawNode) {
|
||||
super();
|
||||
this.video_message = data.fullVideoMessage;
|
||||
this.player_response = data.unserializedPlayerResponse;
|
||||
}
|
||||
}
|
||||
|
||||
export default YpcTrailer;
|
||||
@@ -194,6 +194,8 @@ import { default as GridChannel } from './classes/GridChannel.js';
|
||||
export { GridChannel };
|
||||
import { default as GridHeader } from './classes/GridHeader.js';
|
||||
export { GridHeader };
|
||||
import { default as GridMovie } from './classes/GridMovie.js';
|
||||
export { GridMovie };
|
||||
import { default as GridPlaylist } from './classes/GridPlaylist.js';
|
||||
export { GridPlaylist };
|
||||
import { default as GridVideo } from './classes/GridVideo.js';
|
||||
@@ -226,6 +228,8 @@ import { default as HorizontalCardList } from './classes/HorizontalCardList.js';
|
||||
export { HorizontalCardList };
|
||||
import { default as HorizontalList } from './classes/HorizontalList.js';
|
||||
export { HorizontalList };
|
||||
import { default as HorizontalMovieList } from './classes/HorizontalMovieList.js';
|
||||
export { HorizontalMovieList };
|
||||
import { default as IconLink } from './classes/IconLink.js';
|
||||
export { IconLink };
|
||||
import { default as InteractiveTabbedHeader } from './classes/InteractiveTabbedHeader.js';
|
||||
@@ -452,6 +456,8 @@ import { default as PlayerErrorMessage } from './classes/PlayerErrorMessage.js';
|
||||
export { PlayerErrorMessage };
|
||||
import { default as PlayerLegacyDesktopYpcOffer } from './classes/PlayerLegacyDesktopYpcOffer.js';
|
||||
export { PlayerLegacyDesktopYpcOffer };
|
||||
import { default as PlayerLegacyDesktopYpcTrailer } from './classes/PlayerLegacyDesktopYpcTrailer.js';
|
||||
export { PlayerLegacyDesktopYpcTrailer };
|
||||
import { default as PlayerLiveStoryboardSpec } from './classes/PlayerLiveStoryboardSpec.js';
|
||||
export { PlayerLiveStoryboardSpec };
|
||||
import { default as PlayerMicroformat } from './classes/PlayerMicroformat.js';
|
||||
@@ -674,6 +680,8 @@ import { default as WatchNextEndScreen } from './classes/WatchNextEndScreen.js';
|
||||
export { WatchNextEndScreen };
|
||||
import { default as WatchNextTabbedResults } from './classes/WatchNextTabbedResults.js';
|
||||
export { WatchNextTabbedResults };
|
||||
import { default as YpcTrailer } from './classes/YpcTrailer.js';
|
||||
export { YpcTrailer };
|
||||
import { default as AnchoredSection } from './classes/ytkids/AnchoredSection.js';
|
||||
export { AnchoredSection };
|
||||
import { default as KidsCategoriesHeader } from './classes/ytkids/KidsCategoriesHeader.js';
|
||||
@@ -780,6 +788,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
Grid,
|
||||
GridChannel,
|
||||
GridHeader,
|
||||
GridMovie,
|
||||
GridPlaylist,
|
||||
GridVideo,
|
||||
GuideCollapsibleEntry,
|
||||
@@ -796,6 +805,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
HistorySuggestion,
|
||||
HorizontalCardList,
|
||||
HorizontalList,
|
||||
HorizontalMovieList,
|
||||
IconLink,
|
||||
InteractiveTabbedHeader,
|
||||
ItemSection,
|
||||
@@ -904,6 +914,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
PlayerCaptionsTracklist,
|
||||
PlayerErrorMessage,
|
||||
PlayerLegacyDesktopYpcOffer,
|
||||
PlayerLegacyDesktopYpcTrailer,
|
||||
PlayerLiveStoryboardSpec,
|
||||
PlayerMicroformat,
|
||||
PlayerOverlay,
|
||||
@@ -1015,6 +1026,7 @@ const map: Record<string, YTNodeConstructor> = {
|
||||
WatchCardSectionSequence,
|
||||
WatchNextEndScreen,
|
||||
WatchNextTabbedResults,
|
||||
YpcTrailer,
|
||||
AnchoredSection,
|
||||
KidsCategoriesHeader,
|
||||
KidsCategoryTab,
|
||||
|
||||
@@ -21,6 +21,7 @@ import VideoPrimaryInfo from '../classes/VideoPrimaryInfo.js';
|
||||
import VideoSecondaryInfo from '../classes/VideoSecondaryInfo.js';
|
||||
import LiveChatWrap from './LiveChat.js';
|
||||
import NavigationEndpoint from '../classes/NavigationEndpoint.js';
|
||||
import PlayerLegacyDesktopYpcTrailer from '../classes/PlayerLegacyDesktopYpcTrailer.js';
|
||||
|
||||
import type CardCollection from '../classes/CardCollection.js';
|
||||
import type Endscreen from '../classes/Endscreen.js';
|
||||
@@ -334,6 +335,20 @@ class VideoInfo {
|
||||
return new LiveChatWrap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves trailer info if available (typically for non-purchased movies or films).
|
||||
* @returns `VideoInfo` for the trailer, or `null` if none.
|
||||
*/
|
||||
getTrailerInfo(): VideoInfo | null {
|
||||
if (this.has_trailer) {
|
||||
const player_response = this.playability_status.error_screen?.as(PlayerLegacyDesktopYpcTrailer).trailer?.player_response;
|
||||
if (player_response) {
|
||||
return new VideoInfo([ { data: player_response } as ApiResponse ], this.#actions, this.#player, this.#cpn);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the format that best matches the given options.
|
||||
* @param options - Options
|
||||
@@ -395,6 +410,13 @@ class VideoInfo {
|
||||
return this.autoplay?.sets?.[0]?.autoplay_video || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if trailer is available.
|
||||
*/
|
||||
get has_trailer(): boolean {
|
||||
return !!this.playability_status.error_screen?.is(PlayerLegacyDesktopYpcTrailer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get songs used in the video.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user