From 20984ce9e97f48b2197513b152567f92a7581cb5 Mon Sep 17 00:00:00 2001 From: killer069 <65385476+killer069@users.noreply.github.com> Date: Tue, 28 Sep 2021 21:05:43 +0530 Subject: [PATCH] pretty codes and LiveStream issues fixed 100% --- play-dl/YouTube/classes/LiveStream.ts | 16 ++- play-dl/YouTube/stream.ts | 12 +- play-dl/YouTube/utils/extractor.ts | 16 +-- play-dl/YouTube/utils/request.ts | 152 +++++++++++++------------- play-dl/index.ts | 20 +++- 5 files changed, 120 insertions(+), 96 deletions(-) diff --git a/play-dl/YouTube/classes/LiveStream.ts b/play-dl/YouTube/classes/LiveStream.ts index 914bd7d..534182e 100644 --- a/play-dl/YouTube/classes/LiveStream.ts +++ b/play-dl/YouTube/classes/LiveStream.ts @@ -1,6 +1,6 @@ import { PassThrough } from 'stream'; import { IncomingMessage } from 'http'; -import { StreamType } from '../stream'; +import { parseAudioFormats, StreamType } from '../stream'; import { request, request_stream } from '../utils/request'; import { video_info } from '..'; @@ -132,6 +132,7 @@ export class Stream { private cookie: string; private data_ended: boolean; private playing_count: number; + private quality: number; private request: IncomingMessage | null; constructor( url: string, @@ -139,10 +140,12 @@ export class Stream { duration: number, contentLength: number, video_url: string, - cookie: string + cookie: string, + quality: number ) { this.stream = new PassThrough({ highWaterMark: 10 * 1000 * 1000 }); this.url = url; + this.quality = quality; this.type = type; this.bytes_count = 0; this.video_url = video_url; @@ -174,8 +177,9 @@ export class Stream { } private async retry() { - const info = await video_info(this.video_url, { cookie : this.cookie }); - this.url = info.format[info.format.length - 1].url; + const info = await video_info(this.video_url, { cookie: this.cookie }); + const audioFormat = parseAudioFormats(info.format); + this.url = audioFormat[this.quality].url; } private cleanup() { @@ -220,8 +224,8 @@ export class Stream { this.request = stream; stream.pipe(this.stream, { end: false }); - stream.once('error', async(err) => { - this.cleanup() + stream.once('error', async (err) => { + this.cleanup(); await this.retry(); this.loop(); if (!this.timer) { diff --git a/play-dl/YouTube/stream.ts b/play-dl/YouTube/stream.ts index 96a0d7a..6953d26 100644 --- a/play-dl/YouTube/stream.ts +++ b/play-dl/YouTube/stream.ts @@ -1,5 +1,6 @@ import { video_info } from '.'; import { LiveStreaming, Stream } from './classes/LiveStream'; +import { Proxy } from './utils/request'; export enum StreamType { Arbitrary = 'arbitrary', @@ -12,6 +13,7 @@ export enum StreamType { export interface StreamOptions { quality?: number; cookie?: string; + proxy?: Proxy[]; } export interface InfoData { @@ -25,7 +27,7 @@ export interface InfoData { video_details: any; } -function parseAudioFormats(formats: any[]) { +export function parseAudioFormats(formats: any[]) { const result: any[] = []; formats.forEach((format) => { const type = format.mimeType as string; @@ -41,7 +43,7 @@ function parseAudioFormats(formats: any[]) { export type YouTubeStream = Stream | LiveStreaming; export async function stream(url: string, options: StreamOptions = {}): Promise { - const info = await video_info(url, { cookie : options.cookie }); + const info = await video_info(url, { cookie: options.cookie, proxy: options.proxy }); const final: any[] = []; if ( info.LiveStreamData.isLive === true && @@ -70,7 +72,8 @@ export async function stream(url: string, options: StreamOptions = {}): Promise< info.video_details.durationInSec, Number(final[0].contentLength), info.video_details.url, - options.cookie as string + options.cookie as string, + options.quality ); } @@ -103,6 +106,7 @@ export async function stream_from_info(info: InfoData, options: StreamOptions = info.video_details.durationInSec, Number(final[0].contentLength), info.video_details.url, - options.cookie as string + options.cookie as string, + options.quality ); } diff --git a/play-dl/YouTube/utils/extractor.ts b/play-dl/YouTube/utils/extractor.ts index 4293009..ed72797 100644 --- a/play-dl/YouTube/utils/extractor.ts +++ b/play-dl/YouTube/utils/extractor.ts @@ -3,14 +3,14 @@ import { format_decipher } from './cipher'; import { YouTubeVideo } from '../classes/Video'; import { YouTubePlayList } from '../classes/Playlist'; -interface InfoOptions{ - cookie? : string; - proxy? : Proxy[] +interface InfoOptions { + cookie?: string; + proxy?: Proxy[]; } interface PlaylistOptions { - incomplete? : boolean; - proxy? : Proxy[] + incomplete?: boolean; + proxy?: Proxy[]; } const DEFAULT_API_KEY = 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'; @@ -54,7 +54,7 @@ export async function video_basic_info(url: string, options: InfoOptions = {}) { } else video_id = url; const new_url = `https://www.youtube.com/watch?v=${video_id}`; const body = await request(new_url, { - proxies : options.proxy ?? [], + proxies: options.proxy ?? [], headers: options.cookie ? { 'cookie': options.cookie, @@ -148,7 +148,7 @@ export async function video_info(url: string, options: InfoOptions = {}) { } } -export async function playlist_info(url: string, options : PlaylistOptions = {}) { +export async function playlist_info(url: string, options: PlaylistOptions = {}) { if (!url || typeof url !== 'string') throw new Error(`Expected playlist url, received ${typeof url}!`); let Playlist_id: string; if (url.startsWith('https')) { @@ -158,7 +158,7 @@ export async function playlist_info(url: string, options : PlaylistOptions = {}) const new_url = `https://www.youtube.com/playlist?list=${Playlist_id}`; const body = await request(new_url, { - proxies : options.proxy ?? [], + proxies: options.proxy ?? [], headers: { 'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7' } }); const response = JSON.parse(body.split('var ytInitialData = ')[1].split(';')[0]); diff --git a/play-dl/YouTube/utils/request.ts b/play-dl/YouTube/utils/request.ts index 1d065b3..8a17852 100644 --- a/play-dl/YouTube/utils/request.ts +++ b/play-dl/YouTube/utils/request.ts @@ -1,29 +1,29 @@ import https, { RequestOptions } from 'https'; import tls from 'tls'; -import http , { ClientRequest, IncomingMessage } from 'http'; +import http, { ClientRequest, IncomingMessage } from 'http'; import { URL } from 'url'; -export type Proxy = ProxyOpts | string +export type Proxy = ProxyOpts | string; interface ProxyOpts { - host : string, - port : number, - authentication? : { - username : string; - password : string; - } + host: string; + port: number; + authentication?: { + username: string; + password: string; + }; } interface ProxyOutput { - statusCode : number; - head : string; - body : string; + statusCode: number; + head: string; + body: string; } interface RequestOpts extends RequestOptions { body?: string; method?: 'GET' | 'POST'; - proxies? : Proxy[] + proxies?: Proxy[]; } function https_getter(req_url: string, options: RequestOpts = {}): Promise { @@ -46,86 +46,91 @@ function https_getter(req_url: string, options: RequestOpts = {}): Promise{ +async function proxy_getter(req_url: string, req_proxy: Proxy[]): Promise { return new Promise((resolve, reject) => { - const proxy : string | ProxyOpts = req_proxy[randomIntFromInterval(0, req_proxy.length)] - const parsed_url = new URL(req_url) - let opts : ProxyOpts - if(typeof proxy === 'string'){ - const parsed = new URL(proxy) + const proxy: string | ProxyOpts = req_proxy[randomIntFromInterval(0, req_proxy.length)]; + const parsed_url = new URL(req_url); + let opts: ProxyOpts; + if (typeof proxy === 'string') { + const parsed = new URL(proxy); opts = { - host : parsed.hostname, - port : Number(parsed.port), - authentication : { - username : parsed.username, - password : parsed.password + host: parsed.hostname, + port: Number(parsed.port), + authentication: { + username: parsed.username, + password: parsed.password } - } - } - else opts = proxy - let req : ClientRequest - if(opts.authentication?.username.length === 0){ + }; + } else opts = proxy; + let req: ClientRequest; + if (opts.authentication?.username.length === 0) { req = http.request({ host: opts.host, port: opts.port, method: 'CONNECT', - path: `${parsed_url.host}:443`, + path: `${parsed_url.host}:443` }); - } - else { + } else { req = http.request({ host: opts.host, port: opts.port, method: 'CONNECT', path: `${parsed_url.host}:443`, - headers : { - "Proxy-Authorization" : `Basic ${Buffer.from(`${opts.authentication?.username}:${opts.authentication?.password}`).toString('base64')}` + headers: { + 'Proxy-Authorization': `Basic ${Buffer.from( + `${opts.authentication?.username}:${opts.authentication?.password}` + ).toString('base64')}` } }); } req.on('connect', function (res, socket, head) { - console.log('Connected') - const tlsConnection = tls.connect({ - host : parsed_url.hostname, - port : 443, - socket : socket, - rejectUnauthorized : false - }, function() { - tlsConnection.write(`GET ${parsed_url.pathname}${parsed_url.search} HTTP/1.1\r\n` - + `Host : ${parsed_url.hostname}\r\n` - + 'Connection: close\r\n' - + '\r\n') - }) + console.log('Connected'); + const tlsConnection = tls.connect( + { + host: parsed_url.hostname, + port: 443, + socket: socket, + rejectUnauthorized: false + }, + function () { + tlsConnection.write( + `GET ${parsed_url.pathname}${parsed_url.search} HTTP/1.1\r\n` + + `Host : ${parsed_url.hostname}\r\n` + + 'Connection: close\r\n' + + '\r\n' + ); + } + ); - tlsConnection.setEncoding('utf-8') - let data = '' - tlsConnection.once('error', (e) => reject(e)) - tlsConnection.on('data', (c) => data+=c) + tlsConnection.setEncoding('utf-8'); + let data = ''; + tlsConnection.once('error', (e) => reject(e)); + tlsConnection.on('data', (c) => (data += c)); tlsConnection.on('end', () => { - const y = data.split('\r\n\r\n') - const head = y.shift() as string + const y = data.split('\r\n\r\n'); + const head = y.shift() as string; resolve({ - statusCode : Number(head.split('\n')[0].split(' ')[1]), - head : head, - body : y.join('\n') - }) - }) - }) - req.on('error', (e : Error) => reject(e)) - req.end() - }) + statusCode: Number(head.split('\n')[0].split(' ')[1]), + head: head, + body: y.join('\n') + }); + }); + }); + req.on('error', (e: Error) => reject(e)); + req.end(); + }); } export async function request(url: string, options?: RequestOpts): Promise { return new Promise(async (resolve, reject) => { - if(!options?.proxies){ + if (!options?.proxies) { let data = ''; let res = await https_getter(url, options).catch((err: Error) => err); if (res instanceof Error) { @@ -140,19 +145,18 @@ export async function request(url: string, options?: RequestOpts): Promise (data += c)); res.on('end', () => resolve(data)); - } - else { - let res = await proxy_getter(url, options.proxies).catch((e : Error) => e) + } else { + let res = await proxy_getter(url, options.proxies).catch((e: Error) => e); if (res instanceof Error) { reject(res); return; } - if(res.statusCode >= 300 && res.statusCode < 400){ - res = await proxy_getter(res.head.split('Location: ')[1].split('\n')[0], options.proxies) - } else if (res.statusCode > 400){ - reject(new Error(`GOT ${res.statusCode} from proxy request`)) + if (res.statusCode >= 300 && res.statusCode < 400) { + res = await proxy_getter(res.head.split('Location: ')[1].split('\n')[0], options.proxies); + } else if (res.statusCode > 400) { + reject(new Error(`GOT ${res.statusCode} from proxy request`)); } - resolve(res.body) + resolve(res.body); } }); } diff --git a/play-dl/index.ts b/play-dl/index.ts index e7ffa77..32f7fa5 100644 --- a/play-dl/index.ts +++ b/play-dl/index.ts @@ -13,7 +13,16 @@ interface SearchOptions { import readline from 'readline'; import fs from 'fs'; -import { sp_validate, yt_validate, so_validate, YouTubeStream, SoundCloudStream, YouTube, SoundCloud, Spotify } from '.'; +import { + sp_validate, + yt_validate, + so_validate, + YouTubeStream, + SoundCloudStream, + YouTube, + SoundCloud, + Spotify +} from '.'; import { SpotifyAuthorize, sp_search } from './Spotify'; import { check_id, so_search, stream as so_stream, stream_from_info as so_stream_info } from './SoundCloud'; import { InfoData, stream as yt_stream, StreamOptions, stream_from_info as yt_stream_info } from './YouTube/stream'; @@ -39,17 +48,20 @@ export async function stream(url: string, options: StreamOptions = {}): Promise< * @param options contains limit and source to choose. * @returns */ -export async function search(query: string, options: SearchOptions = {}): Promise { +export async function search( + query: string, + options: SearchOptions = {} +): Promise { if (!options.source) options.source = { youtube: 'video' }; if (options.source.youtube) return await yt_search(query, { limit: options.limit, type: options.source.youtube }); else if (options.source.spotify) return await sp_search(query, options.source.spotify, options.limit); else if (options.source.soundcloud) return await so_search(query, options.source.soundcloud, options.limit); - else throw new Error('Not possible to reach Here LOL. Easter Egg of play-dl if someone get this.') + else throw new Error('Not possible to reach Here LOL. Easter Egg of play-dl if someone get this.'); } /** - * Command to be used + * Command to be used * @param info * @param options * @returns