Optimise YouTube stream function
This commit is contained in:
parent
5400d90221
commit
ebff7a8578
@ -101,7 +101,7 @@ function deciper_signature(tokens: string[], signature: string) {
|
||||
switch (token.slice(0, 2)) {
|
||||
case 'sw':
|
||||
pos = parseInt(token.slice(2));
|
||||
sig = swappositions(sig, pos);
|
||||
swappositions(sig, pos);
|
||||
break;
|
||||
case 'rv':
|
||||
sig.reverse();
|
||||
@ -122,13 +122,11 @@ function deciper_signature(tokens: string[], signature: string) {
|
||||
* Function to swap positions in a array
|
||||
* @param array array
|
||||
* @param position position to switch with first element
|
||||
* @returns new array with swapped positions.
|
||||
*/
|
||||
function swappositions(array: string[], position: number) {
|
||||
const first = array[0];
|
||||
array[0] = array[position];
|
||||
array[position] = first;
|
||||
return array;
|
||||
}
|
||||
/**
|
||||
* Sets Download url with some extra parameter
|
||||
|
||||
@ -37,3 +37,10 @@ export interface InfoData {
|
||||
video_details: YouTubeVideo;
|
||||
related_videos: string[];
|
||||
}
|
||||
|
||||
export interface StreamInfoData {
|
||||
LiveStreamData: LiveStreamData;
|
||||
html5player: string;
|
||||
format: Partial<formatData>[];
|
||||
video_details: Pick<YouTubeVideo, 'url' | 'durationInSec'>;
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { ProxyOptions as Proxy, request } from './../../Request/index';
|
||||
import { format_decipher } from './cipher';
|
||||
import { YouTubeVideo } from '../classes/Video';
|
||||
import { YouTubePlayList } from '../classes/Playlist';
|
||||
import { InfoData } from './constants';
|
||||
import { InfoData, StreamInfoData } from './constants';
|
||||
|
||||
interface InfoOptions {
|
||||
proxy?: Proxy[];
|
||||
@ -219,6 +219,69 @@ export async function video_basic_info(url: string, options: InfoOptions = {}):
|
||||
related_videos: related
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Gets the data required for streaming from YouTube url, ID or html body data and deciphers it.
|
||||
*
|
||||
* Internal function used by {@link stream} instead of {@link video_info}
|
||||
* because it only extracts the information required for streaming.
|
||||
*
|
||||
* @param url YouTube url or ID or html body data
|
||||
* @param options Video Info Options
|
||||
* - `Proxy[]` proxy : sends data through a proxy
|
||||
* - `boolean` htmldata : given data is html data or not
|
||||
* @returns Deciphered Video Info {@link StreamInfoData}.
|
||||
*/
|
||||
export async function video_stream_info(url: string, options: InfoOptions = {}): Promise<StreamInfoData> {
|
||||
let body: string;
|
||||
if (options.htmldata) {
|
||||
body = url;
|
||||
} else {
|
||||
if (yt_validate(url) !== 'video') throw new Error('This is not a YouTube Watch URL');
|
||||
const video_id: string = extractID(url);
|
||||
const new_url = `https://www.youtube.com/watch?v=${video_id}&has_verified=1`;
|
||||
body = await request(new_url, {
|
||||
proxies: options.proxy ?? [],
|
||||
headers: { 'accept-language': 'en-US,en;q=0.9' },
|
||||
cookies: true
|
||||
});
|
||||
}
|
||||
if (body.indexOf('Our systems have detected unusual traffic from your computer network.') !== -1)
|
||||
throw new Error('Captcha page: YouTube has detected that you are a bot!');
|
||||
const player_data = body
|
||||
.split('var ytInitialPlayerResponse = ')?.[1]
|
||||
?.split(';</script>')[0]
|
||||
.split(/;\s*(var|const|let)/)[0];
|
||||
if (!player_data) throw new Error('Initial Player Response Data is undefined.');
|
||||
const player_response = JSON.parse(player_data);
|
||||
if (player_response.playabilityStatus.status !== 'OK')
|
||||
throw new Error(
|
||||
`While getting info from url\n${
|
||||
player_response.playabilityStatus.errorScreen.playerErrorMessageRenderer?.reason.simpleText ??
|
||||
player_response.playabilityStatus.errorScreen.playerKavRenderer?.reason.simpleText
|
||||
}`
|
||||
);
|
||||
const html5player = `https://www.youtube.com${body.split('"jsUrl":"')[1].split('"')[0]}`;
|
||||
const duration = Number(player_response.videoDetails.lengthSeconds);
|
||||
const video_details = {
|
||||
url: `https://www.youtube.com/watch?v=${player_response.videoDetails.videoId}`,
|
||||
durationInSec: (duration < 0 ? 0 : duration) || 0
|
||||
};
|
||||
const format = [];
|
||||
format.push(...(player_response.streamingData.formats ?? []));
|
||||
format.push(...(player_response.streamingData.adaptiveFormats ?? []));
|
||||
|
||||
const LiveStreamData = {
|
||||
isLive: player_response.videoDetails.isLiveContent,
|
||||
dashManifestUrl: player_response.streamingData?.dashManifestUrl ?? null,
|
||||
hlsManifestUrl: player_response.streamingData?.hlsManifestUrl ?? null
|
||||
};
|
||||
return await decipher_info({
|
||||
LiveStreamData,
|
||||
html5player,
|
||||
format,
|
||||
video_details
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Function to convert seconds to [hour : minutes : seconds] format
|
||||
* @param seconds seconds to convert
|
||||
@ -276,7 +339,7 @@ export async function video_info(url: string, options: InfoOptions = {}): Promis
|
||||
* @param data Data - {@link InfoData}
|
||||
* @returns Deciphered Video Info {@link InfoData}
|
||||
*/
|
||||
export async function decipher_info(data: InfoData) {
|
||||
export async function decipher_info<T extends InfoData | StreamInfoData>(data: T): Promise<T> {
|
||||
if (data.LiveStreamData.isLive === true && data.LiveStreamData.dashManifestUrl !== null) {
|
||||
return data;
|
||||
} else if (data.format[0].signatureCipher || data.format[0].cipher) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user