Comments are now added in functions

This commit is contained in:
killer069
2021-09-29 20:23:16 +05:30
parent 8e96e24aeb
commit 5d6b9167ed
15 changed files with 342 additions and 149 deletions

View File

@@ -9,6 +9,7 @@ interface formatOptions {
cipher?: string;
s?: string;
}
// RegExp for various js functions
const var_js = '[a-zA-Z_\\$][a-zA-Z_0-9]*';
const singlequote_js = `'[^'\\\\]*(:?\\\\[\\s\\S][^'\\\\]*)*'`;
const duoblequote_js = `"[^"\\\\]*(:?\\\\[\\s\\S][^"\\\\]*)*"`;
@@ -37,7 +38,11 @@ const reverse_regexp = new RegExp(`(?:^|,)(${key_js})${reverse_function}`, 'm');
const slice_regexp = new RegExp(`(?:^|,)(${key_js})${slice_function}`, 'm');
const splice_regexp = new RegExp(`(?:^|,)(${key_js})${splice_function}`, 'm');
const swap_regexp = new RegExp(`(?:^|,)(${key_js})${swap_function}`, 'm');
/**
* Function to get tokens from html5player body data.
* @param body body data of html5player.
* @returns Array of tokens.
*/
export function js_tokens(body: string) {
const function_action = function_regexp.exec(body);
const object_action = obj_regexp.exec(body);
@@ -82,7 +87,12 @@ export function js_tokens(body: string) {
}
return tokens;
}
/**
* Function to decipher signature
* @param tokens Tokens from js_tokens function
* @param signature Signatured format url
* @returns deciphered signature
*/
function deciper_signature(tokens: string[], signature: string) {
let sig = signature.split('');
const len = tokens.length;
@@ -109,14 +119,24 @@ function deciper_signature(tokens: string[], signature: string) {
}
return sig.join('');
}
/**
* 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
* @param format video fomat
* @param sig deciphered signature
* @returns void
*/
function download_url(format: formatOptions, sig: string) {
let decoded_url;
if (!format.url) return;
@@ -132,8 +152,13 @@ function download_url(format: formatOptions, sig: string) {
}
format.url = parsed_url.toString();
}
export async function format_decipher(formats: formatOptions[], html5player: string) {
/**
* Main function which handles all queries related to video format deciphering
* @param formats video formats
* @param html5player url of html5player
* @returns array of format.
*/
export async function format_decipher(formats: formatOptions[], html5player: string): Promise<formatOptions[]> {
const body = await request(html5player);
const tokens = js_tokens(body);
formats.forEach((format) => {

View File

@@ -17,7 +17,11 @@ const DEFAULT_API_KEY = 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8';
const video_pattern =
/^((?:https?:)?\/\/)?(?:(?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/;
const playlist_pattern = /^((?:https?:)?\/\/)?(?:(?:www|m)\.)?(youtube\.com)\/(?:(playlist|watch))(.*)?((\?|\&)list=)/;
/**
* Command to validate a YouTube url
* @param url Url for validation
* @returns type of url or false.
*/
export function yt_validate(url: string): 'playlist' | 'video' | false {
if (url.indexOf('list=') === -1) {
if (!url.match(video_pattern)) return false;
@@ -31,7 +35,11 @@ export function yt_validate(url: string): 'playlist' | 'video' | false {
return 'playlist';
}
}
/**
* Function to extract ID of YouTube url.
* @param url ID or url of YouTube
* @returns ID of video or playlist.
*/
export function extractID(url: string): string {
if (url.startsWith('https')) {
if (url.indexOf('list=') === -1) {
@@ -45,7 +53,12 @@ export function extractID(url: string): string {
}
} else return url;
}
/**
* Basic function to get data from a YouTube url or ID.
* @param url YouTube url or ID
* @param options cookie and proxy parameters to add
* @returns Data containing video_details, LiveStreamData and formats of video url.
*/
export async function video_basic_info(url: string, options: InfoOptions = {}) {
let video_id: string;
if (url.startsWith('https')) {
@@ -123,7 +136,11 @@ export async function video_basic_info(url: string, options: InfoOptions = {}) {
related_videos: related
};
}
/**
* Function to convert seconds to [hour : minutes : seconds] format
* @param seconds seconds to convert
* @returns [hour : minutes : seconds] format
*/
function parseSeconds(seconds: number): string {
const d = Number(seconds);
const h = Math.floor(d / 3600);
@@ -135,7 +152,12 @@ function parseSeconds(seconds: number): string {
const sDisplay = s > 0 ? (s < 10 ? `0${s}` : s) : '00';
return hDisplay + mDisplay + sDisplay;
}
/**
* Function which gets data from video_basic_info and deciphers it if it contains signatures.
* @param url YouTube Video URL
* @param options cookie and proxy parameters to add
* @returns Data containing video_details, LiveStreamData and formats of video url.
*/
export async function video_info(url: string, options: InfoOptions = {}) {
const data = await video_basic_info(url, options);
if (data.LiveStreamData.isLive === true && data.LiveStreamData.hlsManifestUrl !== null) {
@@ -147,8 +169,13 @@ export async function video_info(url: string, options: InfoOptions = {}) {
return data;
}
}
export async function playlist_info(url: string, options: PlaylistOptions = {}) {
/**
* Function to get YouTube playlist info from a playlist url.
* @param url Playlist URL
* @param options incomplete and proxy to add.
* @returns YouTube Playlist
*/
export async function playlist_info(url: string, options: PlaylistOptions = {}): Promise<YouTubePlayList> {
if (!url || typeof url !== 'string') throw new Error(`Expected playlist url, received ${typeof url}!`);
let Playlist_id: string;
if (url.startsWith('https')) {
@@ -184,7 +211,7 @@ export async function playlist_info(url: string, options: PlaylistOptions = {})
const videos = getPlaylistVideos(parsed, 100);
const data = playlistDetails[0].playlistSidebarPrimaryInfoRenderer;
if (!data.title.runs || !data.title.runs.length) return undefined;
if (!data.title.runs || !data.title.runs.length) throw new Error('Failed to Parse Playlist info.');
const author = playlistDetails[1]?.playlistSidebarSecondaryInfoRenderer.videoOwner;
const views = data.stats.length === 3 ? data.stats[1].simpleText.replace(/[^0-9]/g, '') : 0;
@@ -234,7 +261,12 @@ export async function playlist_info(url: string, options: PlaylistOptions = {})
});
return res;
}
/**
* Function to parse Playlist from YouTube search
* @param data html data of that request
* @param limit No. of videos to parse
* @returns Array of YouTubeVideo.
*/
export function getPlaylistVideos(data: any, limit = Infinity): YouTubeVideo[] {
const videos = [];
@@ -270,7 +302,11 @@ export function getPlaylistVideos(data: any, limit = Infinity): YouTubeVideo[] {
}
return videos;
}
/**
* Function to convert [hour : minutes : seconds] format to seconds
* @param duration hour : minutes : seconds format
* @returns seconds
*/
function parseDuration(duration: string): number {
duration ??= '0:00';
const args = duration.split(':');
@@ -289,7 +325,11 @@ function parseDuration(duration: string): number {
return dur;
}
/**
* Function to get Continuation Token
* @param data html data of playlist url
* @returns token
*/
export function getContinuationToken(data: any): string {
const continuationToken = data.find((x: any) => Object.keys(x)[0] === 'continuationItemRenderer')
?.continuationItemRenderer.continuationEndpoint?.continuationCommand?.token;

View File

@@ -1,6 +1,7 @@
import { YouTubeVideo } from '../classes/Video';
import { YouTubePlayList } from '../classes/Playlist';
import { YouTubeChannel } from '../classes/Channel';
import { YouTube } from '..';
export interface ParseSearchInterface {
type?: 'video' | 'playlist' | 'channel';
@@ -12,11 +13,13 @@ export interface thumbnail {
height: string;
url: string;
}
export function ParseSearchResult(
html: string,
options?: ParseSearchInterface
): (YouTubeVideo | YouTubePlayList | YouTubeChannel)[] {
/**
* Main command which converts html body data and returns the type of data requested.
* @param html body of that request
* @param options limit & type of YouTube search you want.
* @returns Array of one of YouTube type.
*/
export function ParseSearchResult(html: string, options?: ParseSearchInterface): YouTube[] {
if (!html) throw new Error("Can't parse Search result without data");
if (!options) options = { type: 'video', limit: 0 };
if (!options.type) options.type = 'video';
@@ -45,7 +48,11 @@ export function ParseSearchResult(
}
return results;
}
/**
* Function to convert [hour : minutes : seconds] format to seconds
* @param duration hour : minutes : seconds format
* @returns seconds
*/
function parseDuration(duration: string): number {
duration ??= '0:00';
const args = duration.split(':');
@@ -64,7 +71,11 @@ function parseDuration(duration: string): number {
return dur;
}
/**
* Function to parse Channel searches
* @param data body of that channel request.
* @returns YouTubeChannel class
*/
export function parseChannel(data?: any): YouTubeChannel {
if (!data || !data.channelRenderer) throw new Error('Failed to Parse YouTube Channel');
const badge = data.channelRenderer.ownerBadges && data.channelRenderer.ownerBadges[0];
@@ -93,7 +104,11 @@ export function parseChannel(data?: any): YouTubeChannel {
return res;
}
/**
* Function to parse Video searches
* @param data body of that video request.
* @returns YouTubeVideo class
*/
export function parseVideo(data?: any): YouTubeVideo {
if (!data || !data.videoRenderer) throw new Error('Failed to Parse YouTube Video');
@@ -133,7 +148,11 @@ export function parseVideo(data?: any): YouTubeVideo {
return res;
}
/**
* Function to parse Playlist searches
* @param data body of that playlist request.
* @returns YouTubePlaylist class
*/
export function parsePlaylist(data?: any): YouTubePlayList {
if (!data.playlistRenderer) throw new Error('Failed to Parse YouTube Playlist');

View File

@@ -2,7 +2,9 @@ import https, { RequestOptions } from 'https';
import tls from 'tls';
import http, { ClientRequest, IncomingMessage } from 'http';
import { URL } from 'url';
/**
* Types for Proxy
*/
export type Proxy = ProxyOpts | string;
interface ProxyOpts {
@@ -25,7 +27,12 @@ interface RequestOpts extends RequestOptions {
method?: 'GET' | 'POST';
proxies?: Proxy[];
}
/**
* Main module that play-dl uses for making a https request
* @param req_url URL to make https request to
* @param options Request options for https request
* @returns Incoming Message from the https request
*/
function https_getter(req_url: string, options: RequestOpts = {}): Promise<IncomingMessage> {
return new Promise((resolve, reject) => {
const s = new URL(req_url);
@@ -45,13 +52,23 @@ function https_getter(req_url: string, options: RequestOpts = {}): Promise<Incom
req.end();
});
}
/**
* Chooses one random number between max and min number.
* @param min Minimum number
* @param max Maximum number
* @returns Random Number
*/
function randomIntFromInterval(min: number, max: number): number {
let x = Math.floor(Math.random() * (max - min + 1) + min);
if (x === 0) return 0;
else return x - 1;
}
/**
* Main module that play-dl uses for proxy.
* @param req_url URL to make https request to
* @param req_proxy Proxies array
* @returns Object with statusCode, head and body
*/
async function proxy_getter(req_url: string, req_proxy: Proxy[]): Promise<ProxyOutput> {
return new Promise((resolve, reject) => {
const proxy: string | ProxyOpts = req_proxy[randomIntFromInterval(0, req_proxy.length)];
@@ -127,7 +144,12 @@ async function proxy_getter(req_url: string, req_proxy: Proxy[]): Promise<ProxyO
req.end();
});
}
/**
* Main module which play-dl uses to make a proxy or normal request
* @param url URL to make https request to
* @param options Request options for https request
* @returns body of that request
*/
export async function request(url: string, options?: RequestOpts): Promise<string> {
return new Promise(async (resolve, reject) => {
if (!options?.proxies) {
@@ -160,7 +182,12 @@ export async function request(url: string, options?: RequestOpts): Promise<strin
}
});
}
/**
* Main module which play-dl uses to make a request to stream url.
* @param url URL to make https request to
* @param options Request options for https request
* @returns IncomingMessage from the request
*/
export async function request_stream(url: string, options?: RequestOpts): Promise<IncomingMessage> {
return new Promise(async (resolve, reject) => {
let res = await https_getter(url, options).catch((err: Error) => err);