Some more updates

This commit is contained in:
killer069 2021-11-18 13:06:55 +05:30
parent 4285f1a733
commit e53e892606
48 changed files with 569 additions and 135 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,17 @@
<!DOCTYPE html><html class="default"><head><meta charSet="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>ChannelJSON | play-dl</title><meta name="description" content="Documentation for play-dl"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script async src="../assets/search.js" id="search-script"></script></head><body><script>document.body.classList.add(localStorage.getItem("tsd-theme") || "os")</script><header><div class="tsd-page-toolbar"><div class="container"><div class="table-wrap"><div class="table-cell" id="tsd-search" data-base=".."><div class="field"><label for="tsd-search-field" class="tsd-widget search no-caption">Search</label><input type="text" id="tsd-search-field"/></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="../index.html" class="title">play-dl</a></div><div class="table-cell" id="tsd-widgets"><div id="tsd-filter"><a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a><div class="tsd-filter-group"><div class="tsd-select" id="tsd-filter-visibility"><span class="tsd-select-label">All</span><ul class="tsd-select-list"><li data-value="public">Public</li><li data-value="protected">Public/Protected</li><li data-value="private" class="selected">All</li></ul></div> <input type="checkbox" id="tsd-filter-inherited" checked/><label class="tsd-widget" for="tsd-filter-inherited">Inherited</label><input type="checkbox" id="tsd-filter-externals" checked/><label class="tsd-widget" for="tsd-filter-externals">Externals</label></div></div><a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a></div></div></div></div><div class="tsd-page-title"><div class="container"><ul class="tsd-breadcrumb"><li><a href="../modules.html">play-dl</a></li><li><a href="../modules/_internal_.html">&lt;internal&gt;</a></li><li><a href="_internal_.ChannelJSON.html">ChannelJSON</a></li></ul><h1>Interface ChannelJSON</h1></div></div></header><div class="container container-main"><div class="row"><div class="col-8 col-content"><section class="tsd-panel tsd-hierarchy"><h3>Hierarchy</h3><ul class="tsd-hierarchy"><li><span class="target">ChannelJSON</span></li></ul></section><section class="tsd-panel-group tsd-index-group"><h2>Index</h2><section class="tsd-panel tsd-index-panel"><div class="tsd-index-content"><section class="tsd-index-section "><h3>Properties</h3><ul class="tsd-index-list"><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#artist" class="tsd-kind-icon">artist</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#icon" class="tsd-kind-icon">icon</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#id" class="tsd-kind-icon">id</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#name" class="tsd-kind-icon">name</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#subscribers" class="tsd-kind-icon">subscribers</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#type" class="tsd-kind-icon">type</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#url" class="tsd-kind-icon">url</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#verified" class="tsd-kind-icon">verified</a></li></ul></section></div></section></section><section class="tsd-panel-group tsd-member-group "><h2>Properties</h2><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a id="artist" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> artist</h3><div class="tsd-signature tsd-kind-icon">artist<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">boolean</span></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/play-dl/play-dl/blob/4285f1a/play-dl/YouTube/classes/Channel.ts#L115">play-dl/YouTube/classes/Channel.ts:115</a></li></ul></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>YouTube Channel artist if any.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a id="icon" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> icon</h3><div class="tsd-signature tsd-kind-icon">icon<span class="tsd-signature-symbol">?:</span> <a href="_internal_.ChannelIconInterface.html" class="tsd-signature-type" data-tsd-kind="Interface">ChannelIconInterface</a></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/play-dl/play-dl/blob/4285f1a/play-dl/YouTube/classes/Channel.ts#L131">play-dl/YouTube/classes/Channel.ts:131</a></li></ul></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>YouTube Channel Icon data.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a id="id" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> id</h3><div class="tsd-signature tsd-kind-icon">id<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/play-dl/play-dl/blob/4285f1a/play-dl/YouTube/classes/Channel.ts#L119">play-dl/YouTube/classes/Channel.ts:119</a></li></ul></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>YouTube Channel ID.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a id="name" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> name</h3><div class="tsd-signature tsd-kind-icon">name<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/play-dl/play-dl/blob/4285f1a/play-dl/YouTube/classes/Channel.ts#L107">play-dl/YouTube/classes/Channel.ts:107</a></li></ul></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>YouTube Channel Title</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a id="subscribers" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> subscribers</h3><div class="tsd-signature tsd-kind-icon">subscribers<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/play-dl/play-dl/blob/4285f1a/play-dl/YouTube/classes/Channel.ts#L135">play-dl/YouTube/classes/Channel.ts:135</a></li></ul></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>YouTube Channel subscribers count.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a id="type" class="tsd-anchor"></a><h3>type</h3><div class="tsd-signature tsd-kind-icon">type<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">&quot;video&quot;</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">&quot;playlist&quot;</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">&quot;channel&quot;</span></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/play-dl/play-dl/blob/4285f1a/play-dl/YouTube/classes/Channel.ts#L123">play-dl/YouTube/classes/Channel.ts:123</a></li></ul></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>Type of Class [ Channel ]</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a id="url" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> url</h3><div class="tsd-signature tsd-kind-icon">url<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/play-dl/play-dl/blob/4285f1a/play-dl/YouTube/classes/Channel.ts#L127">play-dl/YouTube/classes/Channel.ts:127</a></li></ul></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>YouTube Channel Url</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a id="verified" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> verified</h3><div class="tsd-signature tsd-kind-icon">verified<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">boolean</span></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/play-dl/play-dl/blob/4285f1a/play-dl/YouTube/classes/Channel.ts#L111">play-dl/YouTube/classes/Channel.ts:111</a></li></ul></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>YouTube Channel Verified status.</p>
</div></div></section></section></div><div class="col-4 col-menu menu-sticky-wrap menu-highlight"><nav class="tsd-navigation primary"><ul><li class=""><a href="../modules.html">Exports</a></li><li class="current tsd-kind-namespace"><a href="../modules/_internal_.html">&lt;internal&gt;</a><ul><li class=" tsd-kind-namespace tsd-parent-kind-namespace tsd-is-external"><a href="../modules/_internal_._node_stream_consumers_.html">&quot;node:stream/consumers&quot;</a></li><li class=" tsd-kind-namespace tsd-parent-kind-namespace tsd-is-external"><a href="../modules/_internal_._node_stream_promises_.html">&quot;node:stream/promises&quot;</a></li><li class=" tsd-kind-namespace tsd-parent-kind-namespace tsd-is-external"><a href="../modules/_internal_.EventEmitter.html">Event<wbr/>Emitter</a></li><li class=" tsd-kind-namespace tsd-parent-kind-namespace tsd-is-external"><a href="../modules/_internal_.internal.html">internal</a></li></ul></li></ul></nav><nav class="tsd-navigation secondary menu-sticky"><ul><li class="current tsd-kind-interface tsd-parent-kind-namespace"><a href="_internal_.ChannelJSON.html" class="tsd-kind-icon">ChannelJSON</a><ul><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#artist" class="tsd-kind-icon">artist</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#icon" class="tsd-kind-icon">icon</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#id" class="tsd-kind-icon">id</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#name" class="tsd-kind-icon">name</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#subscribers" class="tsd-kind-icon">subscribers</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#type" class="tsd-kind-icon">type</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#url" class="tsd-kind-icon">url</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_internal_.ChannelJSON.html#verified" class="tsd-kind-icon">verified</a></li></ul></li></ul></nav></div></div></div><footer class="with-border-bottom"><div class="container"><h2>Legend</h2><div class="tsd-legend-group"><ul class="tsd-legend"><li class="tsd-kind-property tsd-parent-kind-interface"><span class="tsd-kind-icon">Property</span></li></ul></div><h2>Settings</h2><p>Theme <select id="theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></p></div></footer><div class="container tsd-generator"><p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></div><div class="overlay"></div><script src="../assets/main.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,30 +1,60 @@
export interface ChannelIconInterface {
url?: string;
/**
* YouTube Channel Icon URL
*/
url: string;
/**
* YouTube Channel Icon Width
*/
width: number;
/**
* YouTube Channel Icon Height
*/
height: number;
}
/**
* Class for YouTube Channel url
* YouTube Channel Class
*/
export class YouTubeChannel {
/**
* YouTube Channel Title
*/
name?: string;
/**
* YouTube Channel Verified status.
*/
verified?: boolean;
/**
* YouTube Channel artist if any.
*/
artist?: boolean;
/**
* YouTube Channel ID.
*/
id?: string;
/**
* YouTube Class type. == "channel"
*/
type: 'video' | 'playlist' | 'channel';
/**
* YouTube Channel Url
*/
url?: string;
/**
* YouTube Channel Icon data.
*/
icon?: ChannelIconInterface;
/**
* YouTube Channel subscribers count.
*/
subscribers?: string;
constructor(data: any) {
/**
* YouTube Channel Constructor
* @param data YouTube Channel data that we recieve from basic info or from search
*/
constructor(data: any = {}) {
if (!data) throw new Error(`Cannot instantiate the ${this.constructor.name} class without data!`);
this.type = 'channel';
this._patch(data);
}
private _patch(data: any): void {
if (!data) data = {};
this.name = data.name || null;
this.verified = !!data.verified || false;
this.artist = !!data.artist || false;
@ -45,21 +75,62 @@ export class YouTubeChannel {
const def = this.icon.url.split('=s')[1].split('-c')[0];
return this.icon.url.replace(`=s${def}-c`, `=s${options.size}-c`);
}
/**
* Converts Channel Class to channel name.
* @returns name of channel
*/
toString(): string {
return this.name || '';
}
toJSON() {
/**
* Converts Channel Class to JSON format
* @returns json data of the channel
*/
toJSON() : ChannelJSON {
return {
name: this.name,
verified: this.verified,
artist: this.artist,
id: this.id,
url: this.url,
iconURL: this.iconURL(),
icon: this.icon,
type: this.type,
subscribers: this.subscribers
};
}
}
interface ChannelJSON{
/**
* YouTube Channel Title
*/
name?: string;
/**
* YouTube Channel Verified status.
*/
verified?: boolean;
/**
* YouTube Channel artist if any.
*/
artist?: boolean;
/**
* YouTube Channel ID.
*/
id?: string;
/**
* Type of Class [ Channel ]
*/
type: 'video' | 'playlist' | 'channel';
/**
* YouTube Channel Url
*/
url?: string;
/**
* YouTube Channel Icon data.
*/
icon?: ChannelIconInterface;
/**
* YouTube Channel subscribers count.
*/
subscribers?: string;
}

View File

@ -9,19 +9,67 @@ export interface FormatInterface {
targetDurationSec: number;
maxDvrDurationSec: number;
}
export class LiveStreaming {
/**
* YouTube Live Stream class for playing audio from Live Stream videos.
*/
export class LiveStream {
/**
* Readable Stream through which data passes
*/
stream: Readable;
/**
* Type of audio data that we recieved from live stream youtube url.
*/
type: StreamType;
/**
* Base URL in dash manifest file.
*/
private base_url: string;
/**
* Given Dash URL.
*/
private url: string;
/**
* Interval to fetch data again to dash url.
*/
private interval: number;
/**
* Sequence count of urls in dash file.
*/
private packet_count: number;
/**
* Timer that creates loop from interval time provided.
*/
private timer: Timer;
/**
* Live Stream Video url.
*/
private video_url: string;
/**
* Timer used to update dash url so as to avoid 404 errors after long hours of streaming.
*
* It updates dash_url every 30 minutes.
*/
private dash_timer: Timer;
/**
* Segments of url that we recieve in dash file.
*
* base_url + segment_urls[0] = One complete url for one segment.
*/
private segments_urls: string[];
/**
* Incoming message that we recieve.
*
* Storing this is essential.
* This helps to destroy the TCP connection completely if you stopped player in between the stream
*/
private request: IncomingMessage | null;
/**
* Live Stream Class Constructor
* @param dash_url dash manifest URL
* @param target_interval interval time for fetching dash data again
* @param video_url Live Stream video url.
*/
constructor(dash_url: string, target_interval: number, video_url: string) {
this.stream = new Readable({ highWaterMark: 10 * 1000 * 1000, read() {} });
this.type = StreamType.Arbitrary;
@ -44,7 +92,11 @@ export class LiveStreaming {
});
this.start();
}
/**
* Updates dash url.
*
* Used by dash_timer for updating dash_url every 30 minutes.
*/
private async dash_updater() {
const info = await video_info(this.video_url);
if (
@ -55,7 +107,11 @@ export class LiveStreaming {
this.url = info.LiveStreamData.dashManifestUrl;
}
}
/**
* Parses data recieved from dash_url.
*
* Updates base_url , segments_urls array.
*/
private async dash_getter() {
const response = await request(this.url);
const audioFormat = response
@ -68,7 +124,11 @@ export class LiveStreaming {
this.segments_urls = list.replace(new RegExp('<SegmentURL media="', 'g'), '').split('"/>');
if (this.segments_urls[this.segments_urls.length - 1] === '') this.segments_urls.pop();
}
/**
* This cleans every used variable in class.
*
* This is used to prevent re-use of this class and helping garbage collector to collect it.
*/
private cleanup() {
this.timer.destroy();
this.dash_timer.destroy();
@ -81,7 +141,12 @@ export class LiveStreaming {
this.packet_count = 0;
this.interval = 0;
}
/**
* This starts function in Live Stream Class.
*
* Gets data from dash url and pass it to dash getter function.
* Get data from complete segment url and pass data to Stream.
*/
private async start() {
if (this.stream.destroyed) {
this.cleanup();
@ -116,25 +181,75 @@ export class LiveStreaming {
this.timer.reuse();
}
/**
* Deprecated Functions
*/
pause() {}
/**
* Deprecated Functions
*/
resume() {}
}
/**
* Class for YouTube Stream
* YouTube Stream Class for playing audio from normal videos.
*/
export class Stream {
stream: Readable;
type: StreamType;
/**
* Readable Stream through which data passes
*/
stream: Readable;
/**
* Type of audio data that we recieved from normal youtube url.
*/
type: StreamType;
/**
* Audio Endpoint Format Url to get data from.
*/
private url: string;
/**
* Used to calculate no of bytes data that we have recieved
*/
private bytes_count: number;
/**
* Calculate per second bytes by using contentLength (Total bytes) / Duration (in seconds)
*/
private per_sec_bytes: number;
/**
* Total length of audio file in bytes
*/
private content_length: number;
/**
* YouTube video url. [ Used only for retrying purposes only. ]
*/
private video_url: string;
/**
* Timer for looping data every 265 seconds.
*/
private timer: Timer;
/**
* Quality given by user. [ Used only for retrying purposes only. ]
*/
private quality: number;
/**
* Proxy config given by user. [ Used only for retrying purposes only. ]
*/
private proxy: Proxy[] | undefined;
/**
* Incoming message that we recieve.
*
* Storing this is essential.
* This helps to destroy the TCP connection completely if you stopped player in between the stream
*/
private request: IncomingMessage | null;
/**
* YouTube Stream Class constructor
* @param url Audio Endpoint url.
* @param type Type of Stream
* @param duration Duration of audio playback [ in seconds ]
* @param contentLength Total length of Audio file in bytes.
* @param video_url YouTube video url.
* @param options Options provided to stream function.
*/
constructor(
url: string,
type: StreamType,
@ -163,19 +278,29 @@ export class Stream {
});
this.loop();
}
/**
* Retry if we get 404 or 403 Errors.
*/
private async retry() {
const info = await video_info(this.video_url, { proxy: this.proxy });
const audioFormat = parseAudioFormats(info.format);
this.url = audioFormat[this.quality].url;
}
/**
* This cleans every used variable in class.
*
* This is used to prevent re-use of this class and helping garbage collector to collect it.
*/
private cleanup() {
this.request?.destroy();
this.request = null;
this.url = '';
}
/**
* Getting data from audio endpoint url and passing it to stream.
*
* If 404 or 403 occurs, it will retry again.
*/
private async loop() {
if (this.stream.destroyed) {
this.timer.destroy();
@ -226,24 +351,62 @@ export class Stream {
}
});
}
/**
* Pauses timer.
* Stops running of loop.
*
* Useful if you don't want to get excess data to be stored in stream.
*/
pause() {
this.timer.pause();
}
/**
* Resumes timer.
* Starts running of loop.
*/
resume() {
this.timer.resume();
}
}
/**
* Timer Class.
*
* setTimeout + extra features ( re-starting, pausing, resuming ).
*/
export class Timer {
/**
* Boolean for checking if Timer is destroyed or not.
*/
private destroyed: boolean;
/**
* Boolean for checking if Timer is paused or not.
*/
private paused: boolean;
/**
* setTimeout function
*/
private timer: NodeJS.Timer;
/**
* Callback to be executed once timer finishes.
*/
private callback: () => void;
/**
* Seconds time when it is started.
*/
private time_start: number;
/**
* Total time left.
*/
private time_left: number;
/**
* Total time given by user [ Used only for re-using timer. ]
*/
private time_total: number;
/**
* Constructor for Timer Class
* @param callback Function to execute when timer is up.
* @param time Total time to wait before execution.
*/
constructor(callback: () => void, time: number) {
this.callback = callback;
this.time_total = time;
@ -253,7 +416,10 @@ export class Timer {
this.time_start = process.hrtime()[0];
this.timer = setTimeout(this.callback, this.time_total * 1000);
}
/**
* Pauses Timer
* @returns Boolean to tell that if it is paused or not.
*/
pause() {
if (!this.paused && !this.destroyed) {
this.paused = true;
@ -262,7 +428,10 @@ export class Timer {
return true;
} else return false;
}
/**
* Resumes Timer
* @returns Boolean to tell that if it is resumed or not.
*/
resume() {
if (this.paused && !this.destroyed) {
this.paused = false;
@ -271,7 +440,10 @@ export class Timer {
return true;
} else return false;
}
/**
* Reusing of timer
* @returns Boolean to tell if it is re-used or not.
*/
reuse() {
if (!this.destroyed) {
clearTimeout(this.timer);
@ -282,7 +454,11 @@ export class Timer {
return true;
} else return false;
}
/**
* Destroy timer.
*
* It can't be used again.
*/
destroy() {
clearTimeout(this.timer);
this.destroyed = true;

View File

@ -4,33 +4,79 @@ import { YouTubeChannel } from './Channel';
import { YouTubeVideo } from './Video';
const BASE_API = 'https://www.youtube.com/youtubei/v1/browse?key=';
/**
* Class for YouTube Playlist url
* YouTube Playlist Class containing vital informations about playlist.
*/
export class YouTubePlayList {
/**
* YouTube Playlist ID
*/
id?: string;
/**
* YouTube Playlist Name
*/
title?: string;
/**
* YouTube Class type. == "playlist"
*/
type: 'video' | 'playlist' | 'channel';
/**
* Total no of videos in that playlist
*/
videoCount?: number;
/**
* Time when playlist was last updated
*/
lastUpdate?: string;
/**
* Total views of that playlist
*/
views?: number;
/**
* YouTube Playlist url
*/
url?: string;
/**
* YouTube Playlist url with starting video url.
*/
link?: string;
/**
* YouTube Playlist channel data
*/
channel?: YouTubeChannel;
/**
* YouTube Playlist thumbnail Data
*/
thumbnail?: {
id: string | undefined;
width: number | undefined;
height: number | undefined;
url: string | undefined;
};
private videos?: [];
/**
* Videos array containing data of first 100 videos
*/
private videos?: YouTubeVideo[];
/**
* Map contaning data of all fetched videos
*/
private fetched_videos: Map<string, YouTubeVideo[]>;
/**
* Token containing API key, Token, ClientVersion.
*/
private _continuation: {
api?: string;
token?: string;
clientVersion?: string;
} = {};
/**
* Total no of pages count.
*/
private __count: number;
/**
* Constructor for YouTube Playlist Class
* @param data Json Parsed YouTube Playlist data
* @param searchResult If the data is from search or not
*/
constructor(data: any, searchResult = false) {
if (!data) throw new Error(`Cannot instantiate the ${this.constructor.name} class without data!`);
this.__count = 0;
@ -39,7 +85,10 @@ export class YouTubePlayList {
if (searchResult) this.__patchSearch(data);
else this.__patch(data);
}
/**
* Updates variable according to a normal data.
* @param data Json Parsed YouTube Playlist data
*/
private __patch(data: any) {
this.id = data.id || undefined;
this.url = data.url || undefined;
@ -57,7 +106,10 @@ export class YouTubePlayList {
this._continuation.token = data.continuation?.token ?? undefined;
this._continuation.clientVersion = data.continuation?.clientVersion ?? '<important data>';
}
/**
* Updates variable according to a searched data.
* @param data Json Parsed YouTube Playlist data
*/
private __patchSearch(data: any) {
this.id = data.id || undefined;
this.url = this.id ? `https://www.youtube.com/playlist?list=${this.id}` : undefined;
@ -70,7 +122,13 @@ export class YouTubePlayList {
this.lastUpdate = undefined;
this.views = 0;
}
/**
* Parses next segment of videos from playlist and returns parsed data.
* @param limit Total no of videos to parse.
*
* Default = Infinity
* @returns Array of YouTube Video Class
*/
async next(limit = Infinity): Promise<YouTubeVideo[]> {
if (!this._continuation || !this._continuation.token) return [];
@ -101,49 +159,137 @@ export class YouTubePlayList {
this._continuation.token = getContinuationToken(contents);
return playlist_videos;
}
async fetch(max = Infinity) {
/**
* Fetches remaining data from playlist
* @param max Max no of videos to fetch
*
* Default = Infinity
* @returns
*/
async fetch(max = Infinity): Promise<YouTubePlayList> {
const continuation = this._continuation.token;
if (!continuation) return this;
if (max < 1) max = Infinity;
while (typeof this._continuation.token === 'string' && this._continuation.token.length) {
if ((this.videos?.length as number) >= max) break;
this.__count++;
const res = await this.next();
max -= res.length;
if(max <= 0) break;
if (!res.length) break;
}
return this;
}
/**
* YouTube Playlist is divided into pages.
*
* For example, if you want to get 101 - 200 songs
*
* ```ts
* const playlist = play.playlist_info('playlist url')
*
* await playlist.fetch()
*
* const result = playlist.page(2)
* ```
* @param number Page number
* @returns Array of YouTube Video Class
*/
page(number: number): YouTubeVideo[] {
if (!number) throw new Error('Page number is not provided');
if (!this.fetched_videos.has(`${number}`)) throw new Error('Given Page number is invalid');
return this.fetched_videos.get(`${number}`) as YouTubeVideo[];
}
/**
* Gets total no of pages in that playlist class.
*
* For getting all songs in a playlist
*
* ```ts
* const playlist = play.playlist_info('playlist url');
*
* await playlist.fetch();
*
* let result = [];
*
* for (let i = 0; i <= playlist.total_pages;i++) {
* result.push(playlist.page(i));
* }
* ```
*/
get total_pages() {
return this.fetched_videos.size;
}
/**
* This tells total no of videos that have been fetched so far.
*
* This can be equal to videosCount if all videos in playlist have been fetched and they are not hidden.
*/
get total_videos() {
const page_number: number = this.total_pages;
return (page_number - 1) * 100 + (this.fetched_videos.get(`${page_number}`) as YouTubeVideo[]).length;
}
toJSON() {
/**
* Converts Playlist Class to a json parsed data.
* @returns
*/
toJSON() : PlaylistJSON {
return {
id: this.id,
title: this.title,
thumbnail: this.thumbnail,
channel: {
name: this.channel?.name,
id: this.channel?.id,
icon: this.channel?.iconURL()
},
channel: this.channel,
url: this.url,
videos: this.videos
};
}
}
interface PlaylistJSON{
/**
* YouTube Playlist ID
*/
id?: string;
/**
* YouTube Playlist Name
*/
title?: string;
/**
* Total no of videos in that playlist
*/
videoCount?: number;
/**
* Time when playlist was last updated
*/
lastUpdate?: string;
/**
* Total views of that playlist
*/
views?: number;
/**
* YouTube Playlist url
*/
url?: string;
/**
* YouTube Playlist url with starting video url.
*/
link?: string;
/**
* YouTube Playlist channel data
*/
channel?: YouTubeChannel;
/**
* YouTube Playlist thumbnail Data
*/
thumbnail?: {
id: string | undefined;
width: number | undefined;
height: number | undefined;
url: string | undefined;
};
/**
* first 100 videos in that playlist
*/
videos? : YouTubeVideo[]
}

View File

@ -1,5 +1,5 @@
import { video_info } from '.';
import { LiveStreaming, Stream } from './classes/LiveStream';
import { LiveStream, Stream } from './classes/LiveStream';
import { ProxyOptions as Proxy } from './../Request';
export enum StreamType {
@ -46,7 +46,7 @@ export function parseAudioFormats(formats: any[]) {
/**
* Type for YouTube Stream
*/
export type YouTubeStream = Stream | LiveStreaming;
export type YouTubeStream = Stream | LiveStream;
/**
* Stream command for YouTube
* @param url YouTube URL
@ -61,7 +61,7 @@ export async function stream(url: string, options: StreamOptions = {}): Promise<
info.LiveStreamData.hlsManifestUrl !== null &&
info.video_details.durationInSec === 0
) {
return new LiveStreaming(
return new LiveStream(
info.LiveStreamData.dashManifestUrl,
info.format[info.format.length - 1].targetDurationSec,
info.video_details.url
@ -98,7 +98,7 @@ export async function stream_from_info(info: InfoData, options: StreamOptions =
info.LiveStreamData.hlsManifestUrl !== null &&
info.video_details.durationInSec === 0
) {
return new LiveStreaming(
return new LiveStream(
info.LiveStreamData.dashManifestUrl,
info.format[info.format.length - 1].targetDurationSec,
info.video_details.url