Add support for upcoming videos, adding the upcoming property to YouTubeVideo
This commit is contained in:
parent
2c7e94402c
commit
285465a1c6
@ -38,6 +38,10 @@ interface VideoOptions {
|
|||||||
* YouTube Video Uploaded Date
|
* YouTube Video Uploaded Date
|
||||||
*/
|
*/
|
||||||
uploadedAt?: string;
|
uploadedAt?: string;
|
||||||
|
/**
|
||||||
|
* If the video is upcoming or a premiere that isn't currently live, this will contain the premiere date, for watch page playlists this will be true, it defaults to undefined
|
||||||
|
*/
|
||||||
|
upcoming?: Date | true;
|
||||||
/**
|
/**
|
||||||
* YouTube Views
|
* YouTube Views
|
||||||
*/
|
*/
|
||||||
@ -115,6 +119,10 @@ export class YouTubeVideo {
|
|||||||
* YouTube Video Uploaded Date
|
* YouTube Video Uploaded Date
|
||||||
*/
|
*/
|
||||||
uploadedAt?: string;
|
uploadedAt?: string;
|
||||||
|
/**
|
||||||
|
* If the video is upcoming or a premiere that isn't currently live, this will contain the premiere date, for watch page playlists this will be true, it defaults to undefined
|
||||||
|
*/
|
||||||
|
upcoming?: Date | true;
|
||||||
/**
|
/**
|
||||||
* YouTube Views
|
* YouTube Views
|
||||||
*/
|
*/
|
||||||
@ -166,6 +174,7 @@ export class YouTubeVideo {
|
|||||||
this.durationRaw = data.duration_raw || '0:00';
|
this.durationRaw = data.duration_raw || '0:00';
|
||||||
this.durationInSec = (data.duration < 0 ? 0 : data.duration) || 0;
|
this.durationInSec = (data.duration < 0 ? 0 : data.duration) || 0;
|
||||||
this.uploadedAt = data.uploadedAt || undefined;
|
this.uploadedAt = data.uploadedAt || undefined;
|
||||||
|
this.upcoming = data.upcoming;
|
||||||
this.views = parseInt(data.views) || 0;
|
this.views = parseInt(data.views) || 0;
|
||||||
const thumbnails = [];
|
const thumbnails = [];
|
||||||
for (const thumb of data.thumbnails) {
|
for (const thumb of data.thumbnails) {
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export interface StreamOptions {
|
|||||||
language?: string;
|
language?: string;
|
||||||
htmldata?: boolean;
|
htmldata?: boolean;
|
||||||
precache?: number;
|
precache?: number;
|
||||||
discordPlayerCompatibility?: boolean
|
discordPlayerCompatibility?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,6 +63,9 @@ export async function stream_from_info(
|
|||||||
info: InfoData | StreamInfoData,
|
info: InfoData | StreamInfoData,
|
||||||
options: StreamOptions = {}
|
options: StreamOptions = {}
|
||||||
): Promise<YouTubeStream> {
|
): Promise<YouTubeStream> {
|
||||||
|
if (!info.format || info.format.length === 0)
|
||||||
|
throw new Error('Upcoming and premiere videos that are not currently live cannot be streamed.');
|
||||||
|
|
||||||
const final: any[] = [];
|
const final: any[] = [];
|
||||||
if (
|
if (
|
||||||
info.LiveStreamData.isLive === true &&
|
info.LiveStreamData.isLive === true &&
|
||||||
@ -87,8 +90,8 @@ export async function stream_from_info(
|
|||||||
final[0].codec === 'opus' && final[0].container === 'webm' ? StreamType.WebmOpus : StreamType.Arbitrary;
|
final[0].codec === 'opus' && final[0].container === 'webm' ? StreamType.WebmOpus : StreamType.Arbitrary;
|
||||||
await request_stream(`https://${new URL(final[0].url).host}/generate_204`);
|
await request_stream(`https://${new URL(final[0].url).host}/generate_204`);
|
||||||
if (type === StreamType.WebmOpus) {
|
if (type === StreamType.WebmOpus) {
|
||||||
if(!options.discordPlayerCompatibility){
|
if (!options.discordPlayerCompatibility) {
|
||||||
options.seek ??= 0
|
options.seek ??= 0;
|
||||||
if (options.seek >= info.video_details.durationInSec || options.seek < 0)
|
if (options.seek >= info.video_details.durationInSec || options.seek < 0)
|
||||||
throw new Error(`Seeking beyond limit. [ 0 - ${info.video_details.durationInSec - 1}]`);
|
throw new Error(`Seeking beyond limit. [ 0 - ${info.video_details.durationInSec - 1}]`);
|
||||||
return new SeekStream(
|
return new SeekStream(
|
||||||
@ -99,7 +102,7 @@ export async function stream_from_info(
|
|||||||
info.video_details.url,
|
info.video_details.url,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
} else if(options.seek) throw new Error("Can not seek with discordPlayerCompatibility set to true.")
|
} else if (options.seek) throw new Error('Can not seek with discordPlayerCompatibility set to true.');
|
||||||
}
|
}
|
||||||
return new Stream(
|
return new Stream(
|
||||||
final[0].url,
|
final[0].url,
|
||||||
|
|||||||
@ -159,6 +159,7 @@ export async function video_basic_info(url: string, options: InfoOptions = {}):
|
|||||||
const vid = player_response.videoDetails;
|
const vid = player_response.videoDetails;
|
||||||
|
|
||||||
let discretionAdvised = false;
|
let discretionAdvised = false;
|
||||||
|
let upcoming = false;
|
||||||
if (player_response.playabilityStatus.status !== 'OK') {
|
if (player_response.playabilityStatus.status !== 'OK') {
|
||||||
if (player_response.playabilityStatus.status === 'CONTENT_CHECK_REQUIRED') {
|
if (player_response.playabilityStatus.status === 'CONTENT_CHECK_REQUIRED') {
|
||||||
if (options.htmldata)
|
if (options.htmldata)
|
||||||
@ -179,7 +180,8 @@ export async function video_basic_info(url: string, options: InfoOptions = {}):
|
|||||||
const updatedValues = await acceptViewerDiscretion(vid.videoId, cookieJar, body, true);
|
const updatedValues = await acceptViewerDiscretion(vid.videoId, cookieJar, body, true);
|
||||||
player_response.streamingData = updatedValues.streamingData;
|
player_response.streamingData = updatedValues.streamingData;
|
||||||
initial_response.contents.twoColumnWatchNextResults.secondaryResults = updatedValues.relatedVideos;
|
initial_response.contents.twoColumnWatchNextResults.secondaryResults = updatedValues.relatedVideos;
|
||||||
} else
|
} else if (player_response.playabilityStatus.status === 'LIVE_STREAM_OFFLINE') upcoming = true;
|
||||||
|
else
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`While getting info from url\n${
|
`While getting info from url\n${
|
||||||
player_response.playabilityStatus.errorScreen.playerErrorMessageRenderer?.reason.simpleText ??
|
player_response.playabilityStatus.errorScreen.playerErrorMessageRenderer?.reason.simpleText ??
|
||||||
@ -223,6 +225,17 @@ export async function video_basic_info(url: string, options: InfoOptions = {}):
|
|||||||
x.metadataRowRenderer.contents[0].simpleText ?? x.metadataRowRenderer.contents[0]?.runs?.[0]?.text;
|
x.metadataRowRenderer.contents[0].simpleText ?? x.metadataRowRenderer.contents[0]?.runs?.[0]?.text;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let upcomingDate;
|
||||||
|
if (upcoming) {
|
||||||
|
if (microformat.liveBroadcastDetails.startTimestamp)
|
||||||
|
upcomingDate = new Date(microformat.liveBroadcastDetails.startTimestamp);
|
||||||
|
else {
|
||||||
|
const timestamp =
|
||||||
|
player_response.playabilityStatus.liveStreamability.liveStreamabilityRenderer.offlineSlate
|
||||||
|
.liveStreamOfflineSlateRenderer.scheduledStartTime;
|
||||||
|
upcomingDate = new Date(parseInt(timestamp) * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
const video_details = new YouTubeVideo({
|
const video_details = new YouTubeVideo({
|
||||||
id: vid.videoId,
|
id: vid.videoId,
|
||||||
title: vid.title,
|
title: vid.title,
|
||||||
@ -230,6 +243,7 @@ export async function video_basic_info(url: string, options: InfoOptions = {}):
|
|||||||
duration: Number(vid.lengthSeconds),
|
duration: Number(vid.lengthSeconds),
|
||||||
duration_raw: parseSeconds(vid.lengthSeconds),
|
duration_raw: parseSeconds(vid.lengthSeconds),
|
||||||
uploadedAt: microformat.publishDate,
|
uploadedAt: microformat.publishDate,
|
||||||
|
upcoming: upcomingDate,
|
||||||
thumbnails: vid.thumbnail.thumbnails,
|
thumbnails: vid.thumbnail.thumbnails,
|
||||||
channel: {
|
channel: {
|
||||||
name: vid.author,
|
name: vid.author,
|
||||||
@ -254,8 +268,11 @@ export async function video_basic_info(url: string, options: InfoOptions = {}):
|
|||||||
discretionAdvised,
|
discretionAdvised,
|
||||||
music
|
music
|
||||||
});
|
});
|
||||||
const format = player_response.streamingData.formats ?? [];
|
let format = [];
|
||||||
format.push(...(player_response.streamingData.adaptiveFormats ?? []));
|
if (!upcoming) {
|
||||||
|
format.push(...(player_response.streamingData.formats ?? []));
|
||||||
|
format.push(...(player_response.streamingData.adaptiveFormats ?? []));
|
||||||
|
}
|
||||||
const LiveStreamData = {
|
const LiveStreamData = {
|
||||||
isLive: video_details.live,
|
isLive: video_details.live,
|
||||||
dashManifestUrl: player_response.streamingData?.dashManifestUrl ?? null,
|
dashManifestUrl: player_response.streamingData?.dashManifestUrl ?? null,
|
||||||
@ -304,6 +321,7 @@ export async function video_stream_info(url: string, options: InfoOptions = {}):
|
|||||||
.split(/;\s*(var|const|let)\s/)[0];
|
.split(/;\s*(var|const|let)\s/)[0];
|
||||||
if (!player_data) throw new Error('Initial Player Response Data is undefined.');
|
if (!player_data) throw new Error('Initial Player Response Data is undefined.');
|
||||||
const player_response = JSON.parse(player_data);
|
const player_response = JSON.parse(player_data);
|
||||||
|
let upcoming = false;
|
||||||
if (player_response.playabilityStatus.status !== 'OK') {
|
if (player_response.playabilityStatus.status !== 'OK') {
|
||||||
if (player_response.playabilityStatus.status === 'CONTENT_CHECK_REQUIRED') {
|
if (player_response.playabilityStatus.status === 'CONTENT_CHECK_REQUIRED') {
|
||||||
if (options.htmldata)
|
if (options.htmldata)
|
||||||
@ -334,7 +352,8 @@ export async function video_stream_info(url: string, options: InfoOptions = {}):
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
player_response.streamingData = updatedValues.streamingData;
|
player_response.streamingData = updatedValues.streamingData;
|
||||||
} else
|
} else if (player_response.playabilityStatus.status === 'LIVE_STREAM_OFFLINE') upcoming = true;
|
||||||
|
else
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`While getting info from url\n${
|
`While getting info from url\n${
|
||||||
player_response.playabilityStatus.errorScreen.playerErrorMessageRenderer?.reason.simpleText ??
|
player_response.playabilityStatus.errorScreen.playerErrorMessageRenderer?.reason.simpleText ??
|
||||||
@ -348,8 +367,11 @@ export async function video_stream_info(url: string, options: InfoOptions = {}):
|
|||||||
url: `https://www.youtube.com/watch?v=${player_response.videoDetails.videoId}`,
|
url: `https://www.youtube.com/watch?v=${player_response.videoDetails.videoId}`,
|
||||||
durationInSec: (duration < 0 ? 0 : duration) || 0
|
durationInSec: (duration < 0 ? 0 : duration) || 0
|
||||||
};
|
};
|
||||||
const format = player_response.streamingData.formats ?? [];
|
let format = [];
|
||||||
format.push(...(player_response.streamingData.adaptiveFormats ?? []));
|
if (!upcoming) {
|
||||||
|
format.push(...(player_response.streamingData.formats ?? []));
|
||||||
|
format.push(...(player_response.streamingData.adaptiveFormats ?? []));
|
||||||
|
}
|
||||||
|
|
||||||
const LiveStreamData = {
|
const LiveStreamData = {
|
||||||
isLive: player_response.videoDetails.isLiveContent,
|
isLive: player_response.videoDetails.isLiveContent,
|
||||||
@ -414,7 +436,7 @@ export async function decipher_info<T extends InfoData | StreamInfoData>(data: T
|
|||||||
data.video_details.durationInSec === 0
|
data.video_details.durationInSec === 0
|
||||||
) {
|
) {
|
||||||
return data;
|
return data;
|
||||||
} else if (data.format[0].signatureCipher || data.format[0].cipher) {
|
} else if (data.format.length > 0 && (data.format[0].signatureCipher || data.format[0].cipher)) {
|
||||||
data.format = await format_decipher(data.format, data.html5player);
|
data.format = await format_decipher(data.format, data.html5player);
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
@ -495,6 +517,9 @@ export function getPlaylistVideos(data: any, limit = Infinity): YouTubeVideo[] {
|
|||||||
duration_raw: info.lengthText?.simpleText ?? '0:00',
|
duration_raw: info.lengthText?.simpleText ?? '0:00',
|
||||||
thumbnails: info.thumbnail.thumbnails,
|
thumbnails: info.thumbnail.thumbnails,
|
||||||
title: info.title.runs[0].text,
|
title: info.title.runs[0].text,
|
||||||
|
upcoming: info.upcomingEventData?.startTime
|
||||||
|
? new Date(parseInt(info.upcomingEventData.startTime) * 1000)
|
||||||
|
: undefined,
|
||||||
channel: {
|
channel: {
|
||||||
id: info.shortBylineText.runs[0].navigationEndpoint.browseEndpoint.browseId || undefined,
|
id: info.shortBylineText.runs[0].navigationEndpoint.browseEndpoint.browseId || undefined,
|
||||||
name: info.shortBylineText.runs[0].text || undefined,
|
name: info.shortBylineText.runs[0].text || undefined,
|
||||||
@ -723,6 +748,8 @@ function getWatchPlaylistVideos(data: any, limit = Infinity): YouTubeVideo[] {
|
|||||||
duration_raw: info.lengthText?.simpleText ?? '0:00',
|
duration_raw: info.lengthText?.simpleText ?? '0:00',
|
||||||
thumbnails: info.thumbnail.thumbnails,
|
thumbnails: info.thumbnail.thumbnails,
|
||||||
title: info.title.simpleText,
|
title: info.title.simpleText,
|
||||||
|
upcoming:
|
||||||
|
info.thumbnailOverlays[0].thumbnailOverlayTimeStatusRenderer.style === 'UPCOMING' || undefined,
|
||||||
channel: {
|
channel: {
|
||||||
id: channel_info.navigationEndpoint.browseEndpoint.browseId || undefined,
|
id: channel_info.navigationEndpoint.browseEndpoint.browseId || undefined,
|
||||||
name: channel_info.text || undefined,
|
name: channel_info.text || undefined,
|
||||||
|
|||||||
@ -146,6 +146,9 @@ export function parseVideo(data?: any): YouTubeVideo {
|
|||||||
artist: Boolean(badge?.includes('artist'))
|
artist: Boolean(badge?.includes('artist'))
|
||||||
},
|
},
|
||||||
uploadedAt: data.videoRenderer.publishedTimeText?.simpleText ?? null,
|
uploadedAt: data.videoRenderer.publishedTimeText?.simpleText ?? null,
|
||||||
|
upcoming: data.videoRenderer.upcomingEventData?.startTime
|
||||||
|
? new Date(parseInt(data.videoRenderer.upcomingEventData.startTime) * 1000)
|
||||||
|
: undefined,
|
||||||
views: data.videoRenderer.viewCountText?.simpleText?.replace(/\D/g, '') ?? 0,
|
views: data.videoRenderer.viewCountText?.simpleText?.replace(/\D/g, '') ?? 0,
|
||||||
live: durationText ? false : true
|
live: durationText ? false : true
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user