diff --git a/play-dl/Deezer/index.ts b/play-dl/Deezer/index.ts index 39dccdc..d827a12 100644 --- a/play-dl/Deezer/index.ts +++ b/play-dl/Deezer/index.ts @@ -255,3 +255,5 @@ export async function dz_advanced_track_search(options: DeezerAdvancedSearchOpti return results; } + +export { DeezerTrack, DeezerAlbum, DeezerPlaylist } \ No newline at end of file diff --git a/play-dl/SoundCloud/classes.ts b/play-dl/SoundCloud/classes.ts index 816d13b..a7b3c63 100644 --- a/play-dl/SoundCloud/classes.ts +++ b/play-dl/SoundCloud/classes.ts @@ -3,7 +3,7 @@ import { Readable } from 'node:stream'; import { IncomingMessage } from 'node:http'; import { StreamType } from '../YouTube/stream'; import { Timer } from '../YouTube/classes/LiveStream'; -import { SoundTrackJSON } from './constants'; +import { PlaylistJSON, SoundTrackJSON } from './constants'; export interface SoundCloudUser { /** @@ -178,7 +178,10 @@ export class SoundCloudTrack { }; this.thumbnail = data.artwork_url; } - + /** + * Converts class to JSON + * @returns JSON parsed Data + */ toJSON() : SoundTrackJSON { return { name: this.name, @@ -198,17 +201,56 @@ export class SoundCloudTrack { * SoundCloud Playlist Class */ export class SoundCloudPlaylist { + /** + * SoundCloud Playlist Name + */ name: string; + /** + * SoundCloud Playlist ID + */ id: number; + /** + * SoundCloud Playlist URL + */ url: string; + /** + * SoundCloud Class type. == "playlist" + */ type: 'track' | 'playlist' | 'user'; + /** + * SoundCloud Playlist Sub type. == "album" for soundcloud albums + */ sub_type: string; + /** + * SoundCloud Playlist Total Duration in seconds + */ durationInSec: number; + /** + * SoundCloud Playlist Total Duration in milli seconds + */ durationInMs: number; - client_id: string; + /** + * SoundCloud Playlist user data + */ user: SoundCloudUser; + /** + * SoundCloud Playlist tracks [ It can be fetched or not fetched ] + */ tracks: SoundCloudTrack[] | SoundCloudTrackDeprecated[]; + /** + * SoundCloud Playlist tracks number + */ tracksCount: number; + /** + * SoundCloud Client ID provided by user + * @private + */ + private client_id: string; + /** + * Constructor for SoundCloud Playlist + * @param data JSON parsed SoundCloud playlist data + * @param client_id Provided SoundCloud Client ID + */ constructor(data: any, client_id: string) { this.name = data.title; this.id = data.id; @@ -244,8 +286,13 @@ export class SoundCloudPlaylist { }); this.tracks = tracks; } - - async fetch(): Promise { + /** + * Fetches all unfetched songs in a playlist. + * + * For fetching songs and getting all songs, see `fetched_tracks` property. + * @returns playlist class + */ + async fetch(): Promise { const work: any[] = []; for (let i = 0; i < this.tracks.length; i++) { if (!this.tracks[i].fetched) { @@ -263,9 +310,12 @@ export class SoundCloudPlaylist { } } await Promise.allSettled(work); + return this; } - - get total_tracks() { + /** + * Get total no. of fetched tracks + */ + get total_tracks(): number { let count = 0; this.tracks.forEach((track) => { if (track instanceof SoundCloudTrack) count++; @@ -273,12 +323,35 @@ export class SoundCloudPlaylist { }); return count; } - - toJSON() { + /** + * Get all fetched tracks as a array. + * + * For getting all feetched tracks + * + * ```ts + * const playlist = await play.soundcloud("playlist url") + * + * await playlist.fetch() + * + * const result = playlist.fetched_tracks + * ``` + */ + get fetched_tracks(): SoundCloudTrack[] { + let result: SoundCloudTrack[] = []; + this.tracks.forEach((track) => { + if (track instanceof SoundCloudTrack) result.push(track); + else return; + }); + return result; + } + /** + * Converts Class to JSON data + * @returns JSON parsed data + */ + toJSON(): PlaylistJSON { return { name: this.name, id: this.id, - type: this.type, sub_type: this.sub_type, url: this.url, durationInMs: this.durationInMs, @@ -293,17 +366,57 @@ export class SoundCloudPlaylist { * SoundCloud Stream class */ export class SoundCloudStream { + /** + * Readable Stream through which data passes + */ stream: Readable; + /** + * Type of audio data that we recieved from normal youtube url. + */ type: StreamType; + /** + * Dash Url containing segment urls. + * @private + */ private url: string; + /** + * Total time of downloaded segments data. + * @private + */ private downloaded_time: number; + /** + * Timer for looping code every 5 minutes + * @private + */ private timer: Timer; + /** + * Total segments Downloaded so far + * @private + */ private downloaded_segments: number; + /** + * 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 + */ private request: IncomingMessage | null; + /** + * Array of segment time. Useful for calculating downloaded_time. + */ private time: number[]; + /** + * Array of segment_urls in dash file. + */ private segment_urls: string[]; + /** + * Constructor for SoundCloud Stream + * @param url Dash url containing dash file. + * @param type Stream Type + */ constructor(url: string, type: StreamType = StreamType.Arbitrary) { - this.stream = new Readable({ highWaterMark: 10 * 1000 * 1000, read() {} }); + this.stream = new Readable({ highWaterMark: 5 * 1000 * 1000, read() {} }); this.type = type; this.url = url; this.downloaded_time = 0; @@ -320,7 +433,10 @@ export class SoundCloudStream { }); this.start(); } - + /** + * Parses SoundCloud dash file. + * @private + */ private async parser() { const response = await request(this.url).catch((err: Error) => { return err; @@ -336,7 +452,9 @@ export class SoundCloudStream { }); return; } - + /** + * Starts looping of code for getting all segments urls data + */ private async start() { if (this.stream.destroyed) { this.cleanup(); @@ -344,12 +462,14 @@ export class SoundCloudStream { } this.time = []; this.segment_urls = []; - await this.parser(); this.downloaded_time = 0; + await this.parser(); this.segment_urls.splice(0, this.downloaded_segments); this.loop(); } - + /** + * Main Loop function for getting all segments urls data + */ private async loop() { if (this.stream.destroyed) { this.cleanup(); @@ -381,7 +501,11 @@ export class SoundCloudStream { this.stream.emit('error', err); }); } - + /** + * 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.request?.destroy(); @@ -392,11 +516,19 @@ export class SoundCloudStream { this.time = []; this.segment_urls = []; } - + /** + * 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(); } diff --git a/play-dl/SoundCloud/constants.ts b/play-dl/SoundCloud/constants.ts index be8bdf8..440b125 100644 --- a/play-dl/SoundCloud/constants.ts +++ b/play-dl/SoundCloud/constants.ts @@ -1,4 +1,4 @@ -import { SoundCloudTrackFormat, SoundCloudUser } from "./classes"; +import { SoundCloudTrack, SoundCloudTrackDeprecated, SoundCloudTrackFormat, SoundCloudUser } from "./classes"; export interface SoundTrackJSON{ /** @@ -47,8 +47,47 @@ export interface SoundTrackJSON{ * SoundCloud Track user data */ user: SoundCloudUser; +} + +export interface PlaylistJSON{ + /** + * SoundCloud Playlist Name + */ + name: string; /** - * Constructor for SoundCloud Track Class - * @param data JSON parsed track html data + * SoundCloud Playlist ID + */ + id: number; + /** + * SoundCloud Playlist URL + */ + url: string; + /** + * SoundCloud Playlist Sub type. == "album" for soundcloud albums + */ + sub_type: string; + /** + * SoundCloud Playlist Total Duration in seconds + */ + durationInSec: number; + /** + * SoundCloud Playlist Total Duration in milli seconds + */ + durationInMs: number; + /** + * SoundCloud Playlist user data + */ + user: SoundCloudUser; + /** + * SoundCloud Playlist tracks [ It can be fetched or not fetched ] + */ + tracks: SoundCloudTrack[] | SoundCloudTrackDeprecated[]; + /** + * SoundCloud Playlist tracks number + */ + tracksCount: number; + /** + * SoundCloud Client ID provided by user + * @private */ } \ No newline at end of file diff --git a/play-dl/SoundCloud/index.ts b/play-dl/SoundCloud/index.ts index 3930b9d..4b284c5 100644 --- a/play-dl/SoundCloud/index.ts +++ b/play-dl/SoundCloud/index.ts @@ -2,7 +2,6 @@ import fs from 'node:fs'; import { StreamType } from '../YouTube/stream'; import { request } from '../Request'; import { SoundCloudPlaylist, SoundCloudTrack, SoundCloudTrackFormat, SoundCloudStream } from './classes'; -export { SoundCloudStream } let soundData: SoundDataOptions; if (fs.existsSync('.data/soundcloud.data')) { soundData = JSON.parse(fs.readFileSync('.data/soundcloud.data').toString()); @@ -167,3 +166,5 @@ function parseHlsFormats(data: SoundCloudTrackFormat[]) { export function setSoundCloudToken(options: SoundDataOptions) { soundData = options; } + +export { SoundCloudTrack, SoundCloudPlaylist, SoundCloudStream } \ No newline at end of file diff --git a/play-dl/Spotify/index.ts b/play-dl/Spotify/index.ts index ef3cc00..c8d1df3 100644 --- a/play-dl/Spotify/index.ts +++ b/play-dl/Spotify/index.ts @@ -214,3 +214,5 @@ export function setSpotifyToken(options: SpotifyDataOptions) { spotifyData.file = false; refreshToken(); } + +export { SpotifyTrack, SpotifyAlbum, SpotifyPlaylist } \ No newline at end of file diff --git a/play-dl/YouTube/classes/LiveStream.ts b/play-dl/YouTube/classes/LiveStream.ts index e1267d3..25defbf 100644 --- a/play-dl/YouTube/classes/LiveStream.ts +++ b/play-dl/YouTube/classes/LiveStream.ts @@ -71,7 +71,7 @@ export class LiveStream { * @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.stream = new Readable({ highWaterMark: 5 * 1000 * 1000, read() {} }); this.type = StreamType.Arbitrary; this.url = dash_url; this.base_url = ''; @@ -258,7 +258,7 @@ export class Stream { video_url: string, options: StreamOptions ) { - this.stream = new Readable({ highWaterMark: 10 * 1000 * 1000, read() {} }); + this.stream = new Readable({ highWaterMark: 5 * 1000 * 1000, read() {} }); this.url = url; this.quality = options.quality as number; this.proxy = options.proxy || undefined; diff --git a/play-dl/YouTube/classes/Playlist.ts b/play-dl/YouTube/classes/Playlist.ts index 1d28a75..e599bc3 100644 --- a/play-dl/YouTube/classes/Playlist.ts +++ b/play-dl/YouTube/classes/Playlist.ts @@ -157,6 +157,8 @@ export class YouTubePlayList { } /** * Fetches remaining data from playlist + * + * For fetching and getting all songs data, see `total_pages` property. * @param max Max no of videos to fetch * * Default = Infinity @@ -183,7 +185,7 @@ export class YouTubePlayList { * For example, if you want to get 101 - 200 songs * * ```ts - * const playlist = play.playlist_info('playlist url') + * const playlist = await play.playlist_info('playlist url') * * await playlist.fetch() * @@ -203,13 +205,13 @@ export class YouTubePlayList { * For getting all songs in a playlist * * ```ts - * const playlist = play.playlist_info('playlist url'); + * const playlist = await play.playlist_info('playlist url'); * * await playlist.fetch(); * * let result = []; * - * for (let i = 0; i <= playlist.total_pages;i++) { + * for (let i = 0; i <= playlist.total_pages; i++) { * result.push(playlist.page(i)); * } * ``` diff --git a/play-dl/YouTube/index.ts b/play-dl/YouTube/index.ts index 30b8e7d..586baaf 100644 --- a/play-dl/YouTube/index.ts +++ b/play-dl/YouTube/index.ts @@ -2,3 +2,6 @@ export { stream, stream_from_info, YouTubeStream } from './stream'; export * from './utils'; export { YouTube } from './search'; export { cookieHeaders } from './utils/cookie'; +export { YouTubeVideo } from './classes/Video' +export { YouTubePlayList } from './classes/Playlist' +export { YouTubeChannel } from './classes/Channel' diff --git a/play-dl/index.ts b/play-dl/index.ts index c5187b7..15ecf62 100644 --- a/play-dl/index.ts +++ b/play-dl/index.ts @@ -7,11 +7,14 @@ export { extractID, YouTube, YouTubeStream, - cookieHeaders + cookieHeaders, + YouTubeChannel, + YouTubePlayList, + YouTubeVideo } from './YouTube'; -export { spotify, sp_validate, refreshToken, is_expired, Spotify } from './Spotify'; -export { soundcloud, so_validate, SoundCloud, SoundCloudStream, getFreeClientID } from './SoundCloud'; -export { deezer, dz_validate, dz_advanced_track_search, Deezer } from './Deezer'; +export { spotify, sp_validate, refreshToken, is_expired, SpotifyAlbum, SpotifyPlaylist, SpotifyTrack, Spotify } from './Spotify'; +export { soundcloud, so_validate, SoundCloud, SoundCloudStream, getFreeClientID, SoundCloudPlaylist, SoundCloudTrack } from './SoundCloud'; +export { deezer, dz_validate, dz_advanced_track_search, Deezer, DeezerTrack, DeezerPlaylist, DeezerAlbum } from './Deezer'; export { setToken } from './token'; enum AudioPlayerStatus {