diff --git a/README.md b/README.md index 24b93d8..e1ddba9 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,12 @@ if(validate(url) || validate_playlist(url)) // This will check both and if anyon ### stream(url : `string`, cookie? : `string`) *This is basic to create a youtube stream from a url.* +<<<<<<< HEAD +**Cookies are optional and are required for playing age restricted videos.** +======= **[Cookies](https://github.com/play-dl/play-dl/discussions/34) are optional and are required for playing age restricted videos.** +>>>>>>> 4487ef38a991af12fb1660bc7ffe3cf7418433ca ```js let source = await stream("url") // This will create a stream Class which contains type and stream to be played. let resource = createAudioResource(source.stream, { @@ -98,6 +102,7 @@ console.log(results[0].url); ### video_basic_info(url : `string`, cookie? : `string`) *The basic video details `play-dl` fetches at first.* +**Cookies are optional and are required for playing age restricted videos.** **[Cookies](https://github.com/play-dl/play-dl/discussions/34) are optional and are required for playing age restricted videos.** @@ -106,6 +111,7 @@ const video = await video_basic_info(url) ``` ### video_info(url : `string`, cookie? : `string`) *This contains everything with deciphered formats along with `video_details`.* +**Cookies are optional and are required for playing age restricted videos.** **[Cookies](https://github.com/play-dl/play-dl/discussions/34) are optional and are required for playing age restricted videos.** diff --git a/play-dl/YouTube/classes/LiveStream.ts b/play-dl/YouTube/classes/LiveStream.ts index 8edfaff..f032ce2 100644 --- a/play-dl/YouTube/classes/LiveStream.ts +++ b/play-dl/YouTube/classes/LiveStream.ts @@ -2,6 +2,7 @@ import { PassThrough } from 'stream' import got from 'got' import { StreamType } from '../stream'; import Request from 'got/dist/source/core'; +import { video_info } from '..'; export interface FormatInterface{ url : string; @@ -17,9 +18,11 @@ export class LiveStreaming{ private interval : number private packet_count : number private timer : NodeJS.Timer | null + private video_url : string + private dash_timer : NodeJS.Timer | null private segments_urls : string[] private request : Request | null - constructor(dash_url : string, target_interval : number){ + constructor(dash_url : string, target_interval : number, video_url : string){ this.type = StreamType.Arbitrary this.url = dash_url this.base_url = '' @@ -28,13 +31,27 @@ export class LiveStreaming{ this.packet_count = 0 this.request = null this.timer = null + this.video_url = video_url this.interval = target_interval * 1000 || 0 + this.dash_timer = setTimeout(() => { + this.dash_updater() + }, 1800000) this.stream.on('close', () => { this.cleanup() }); this.start() } + private async dash_updater(){ + let info = await video_info(this.video_url) + if(info.LiveStreamData.isLive === true && info.LiveStreamData.hlsManifestUrl !== null && info.video_details.durationInSec === '0'){ + this.url = info.LiveStreamData.dashManifestUrl + } + this.dash_timer = setTimeout(() => { + this.dash_updater() + }, 1800000) + } + private async dash_getter(){ let response = await got(this.url) let audioFormat = response.body.split('')[0].split('') @@ -47,7 +64,10 @@ export class LiveStreaming{ private cleanup(){ clearTimeout(this.timer as NodeJS.Timer) + clearTimeout(this.dash_timer as NodeJS.Timer) this.request?.destroy() + this.dash_timer = null + this.video_url = '' this.request = null this.timer = null this.url = '' @@ -63,6 +83,7 @@ export class LiveStreaming{ return } await this.dash_getter() + if(this.segments_urls.length > 3) this.segments_urls.splice(0, this.segments_urls.length - 3) if(this.packet_count === 0) this.packet_count = Number(this.segments_urls[0].split('sq/')[1].split('/')[0]) for await (let segment of this.segments_urls){ if(Number(segment.split('sq/')[1].split('/')[0]) !== this.packet_count){ @@ -89,80 +110,6 @@ export class LiveStreaming{ } } -export class LiveEnded{ - type : StreamType - stream : PassThrough - private url : string; - private base_url : string; - private packet_count : number - private segments_urls : string[] - private request : Request | null - constructor(dash_url : string){ - this.type = StreamType.Arbitrary - this.url = dash_url - this.base_url = '' - this.stream = new PassThrough({ highWaterMark : 10 * 1000 * 1000 }) - this.segments_urls = [] - this.request = null - this.packet_count = 0 - this.stream.on('close', () => { - this.cleanup() - }) - this.start() - } - - private async dash_getter(){ - let response = await got(this.url) - let audioFormat = response.body.split('')[0].split('') - if(audioFormat[audioFormat.length - 1] === '') audioFormat.pop() - this.base_url = audioFormat[audioFormat.length - 1].split('')[1].split('')[0] - let list = audioFormat[audioFormat.length - 1].split('')[1].split('')[0] - this.segments_urls = list.replace(new RegExp('') - if(this.segments_urls[this.segments_urls.length - 1] === '') this.segments_urls.pop() - } - - private cleanup(){ - this.request?.destroy() - this.request = null - this.url = '' - this.base_url = '' - this.segments_urls = [] - this.packet_count = 0 - } - - private async start(){ - if(this.stream.destroyed){ - this.cleanup() - return - } - await this.dash_getter() - if(this.packet_count === 0) this.packet_count = Number(this.segments_urls[0].split('sq/')[1].split('/')[0]) - for await (let segment of this.segments_urls){ - if(this.stream.destroyed){ - this.cleanup() - break - } - if(Number(segment.split('sq/')[1].split('/')[0]) !== this.packet_count){ - continue - } - await (async () => { - return new Promise(async (resolve, reject) => { - let stream = got.stream(this.base_url + segment) - this.request = stream - stream.on('data', (chunk: any) => this.stream.write(chunk)) - stream.on('end', () => { - this.packet_count++ - resolve('') - }) - stream.once('error', (err) => { - this.stream.emit('error', err) - }) - }) - })() - } - } -} - export class Stream { type : StreamType stream : PassThrough @@ -183,11 +130,12 @@ export class Stream { this.stream.on('close', () => { this.cleanup() }) - this.duration = duration; - (duration > 300) ? this.loop_start() : this.normal_start() + this.duration = duration + this.loop() } private cleanup(){ + console.log('Ending Everything') clearTimeout(this.timer as NodeJS.Timer) this.request?.destroy() this.request = null @@ -197,51 +145,6 @@ export class Stream { this.per_sec_bytes = 0 } - private normal_start(){ - if(this.stream.destroyed){ - this.cleanup() - return - } - let stream = got.stream(this.url) - this.request = stream - - stream.once('error', (err) => { - this.stream.emit('error', err) - }) - - stream.pipe(this.stream) - } - - private loop_start(){ - if(this.stream.destroyed){ - this.cleanup() - return - } - let stream = got.stream(this.url) - this.request = stream - stream.once('data', () => { - this.per_sec_bytes = Math.ceil((stream.downloadProgress.total as number)/this.duration) - }) - - stream.once('error', (err) => { - this.stream.emit('error', err) - }) - - stream.on('data', (chunk: any) => { - this.bytes_count += chunk.length - this.stream.write(chunk) - }) - stream.on('data', () => { - if(this.bytes_count > (this.per_sec_bytes * 300)){ - stream.destroy() - } - }) - - this.timer = setTimeout(() => { - this.loop() - }, 280 * 1000) - } - private loop(){ if(this.stream.destroyed){ this.cleanup() @@ -254,18 +157,21 @@ export class Stream { } }) this.request = stream - stream.on('data', (chunk: any) => { - absolute_bytes += chunk.length - this.bytes_count += chunk.length - this.stream.write(chunk) + stream.once('data', () => { + if(this.per_sec_bytes === 0){ + this.per_sec_bytes = Math.ceil((stream.downloadProgress.total as number)/this.duration) + } }) stream.once('error', (err) => { this.stream.emit('error', err) }) - stream.on('data', () => { - if(absolute_bytes > (this.per_sec_bytes * 300)){ + stream.on('data', (chunk: any) => { + absolute_bytes += chunk.length + this.bytes_count += chunk.length + this.stream.write(chunk) + if(absolute_bytes > (this.per_sec_bytes * 300) && this.per_sec_bytes !== 0){ stream.destroy() } }) diff --git a/play-dl/YouTube/stream.ts b/play-dl/YouTube/stream.ts index 0ae108f..7890adf 100644 --- a/play-dl/YouTube/stream.ts +++ b/play-dl/YouTube/stream.ts @@ -1,6 +1,6 @@ import got from "got/dist/source" import { video_info } from "." -import { LiveEnded, LiveStreaming, Stream } from "./classes/LiveStream" +import { LiveStreaming, Stream } from "./classes/LiveStream" export enum StreamType{ Arbitrary = 'arbitrary', @@ -35,11 +35,11 @@ function parseAudioFormats(formats : any[]){ return result } -export async function stream(url : string, cookie? : string): Promise{ +export async function stream(url : string, cookie? : string): Promise{ let info = await video_info(url, cookie) let final: any[] = []; let type : StreamType; - if(info.LiveStreamData.isLive === true && info.LiveStreamData.hlsManifestUrl !== null) { + if(info.LiveStreamData.isLive === true && info.LiveStreamData.hlsManifestUrl !== null && info.video_details.durationInSec === '0') { return live_stream(info as InfoData) } @@ -72,10 +72,10 @@ export async function stream(url : string, cookie? : string): Promise{ +export async function stream_from_info(info : InfoData): Promise{ let final: any[] = []; let type : StreamType; - if(info.LiveStreamData.isLive === true && info.LiveStreamData.hlsManifestUrl !== null) { + if(info.LiveStreamData.isLive === true && info.LiveStreamData.hlsManifestUrl !== null && info.video_details.durationInSec === '0') { return live_stream(info as InfoData) } @@ -116,13 +116,7 @@ function filterFormat(formats : any[], codec : string){ return result } -function live_stream(info : InfoData): LiveStreaming | LiveEnded{ - let stream : LiveStreaming | LiveEnded - if(info.video_details.durationInSec === '0') { - stream = new LiveStreaming(info.LiveStreamData.dashManifestUrl, info.format[info.format.length - 1].targetDurationSec) - } - else { - stream = new LiveEnded(info.LiveStreamData.dashManifestUrl) - } +function live_stream(info : InfoData): LiveStreaming{ + let stream = new LiveStreaming(info.LiveStreamData.dashManifestUrl, info.format[info.format.length - 1].targetDurationSec, info.video_details.url) return stream } \ No newline at end of file