{"version":3,"sources":["../play-dl/Request/index.ts","../play-dl/YouTube/utils/cookie.ts","../play-dl/Request/useragent.ts","../play-dl/YouTube/classes/LiveStream.ts","../play-dl/YouTube/utils/cipher.ts","../play-dl/YouTube/classes/Channel.ts","../play-dl/YouTube/classes/Thumbnail.ts","../play-dl/YouTube/classes/Video.ts","../play-dl/YouTube/classes/Playlist.ts","../play-dl/YouTube/utils/extractor.ts","../play-dl/YouTube/classes/WebmSeeker.ts","../play-dl/YouTube/classes/SeekStream.ts","../play-dl/YouTube/stream.ts","../play-dl/YouTube/utils/parser.ts","../play-dl/YouTube/search.ts","../play-dl/Spotify/classes.ts","../play-dl/Spotify/index.ts","../play-dl/SoundCloud/index.ts","../play-dl/SoundCloud/classes.ts","../play-dl/Deezer/index.ts","../play-dl/Deezer/classes.ts","../play-dl/token.ts","../play-dl/index.ts"],"sourcesContent":["import { IncomingMessage } from 'node:http';\nimport { RequestOptions, request as httpsRequest } from 'node:https';\nimport { URL } from 'node:url';\nimport { BrotliDecompress, Deflate, Gunzip, createGunzip, createBrotliDecompress, createDeflate } from 'node:zlib';\nimport { cookieHeaders, getCookies } from '../YouTube/utils/cookie';\nimport { getRandomUserAgent } from './useragent';\n\ninterface RequestOpts extends RequestOptions {\n    body?: string;\n    method?: 'GET' | 'POST' | 'HEAD';\n    cookies?: boolean;\n    cookieJar?: { [key: string]: string };\n}\n\n/**\n * Main module which play-dl uses to make a request to stream url.\n * @param url URL to make https request to\n * @param options Request options for https request\n * @returns IncomingMessage from the request\n */\nexport function request_stream(req_url: string, options: RequestOpts = { method: 'GET' }): Promise<IncomingMessage> {\n    return new Promise(async (resolve, reject) => {\n        let res = await https_getter(req_url, options).catch((err: Error) => err);\n        if (res instanceof Error) {\n            reject(res);\n            return;\n        }\n        if (Number(res.statusCode) >= 300 && Number(res.statusCode) < 400) {\n            res = await request_stream(res.headers.location as string, options);\n        }\n        resolve(res);\n    });\n}\n/**\n * Makes a request and follows redirects if necessary\n * @param req_url URL to make https request to\n * @param options Request options for https request\n * @returns A promise with the final response object\n */\nfunction internalRequest(req_url: string, options: RequestOpts = { method: 'GET' }): Promise<IncomingMessage> {\n    return new Promise(async (resolve, reject) => {\n        let res = await https_getter(req_url, options).catch((err: Error) => err);\n        if (res instanceof Error) {\n            reject(res);\n            return;\n        }\n        if (Number(res.statusCode) >= 300 && Number(res.statusCode) < 400) {\n            res = await internalRequest(res.headers.location as string, options);\n        } else if (Number(res.statusCode) > 400) {\n            reject(new Error(`Got ${res.statusCode} from the request`));\n            return;\n        }\n        resolve(res);\n    });\n}\n/**\n * Main module which play-dl uses to make a request\n * @param url URL to make https request to\n * @param options Request options for https request\n * @returns body of that request\n */\nexport function request(req_url: string, options: RequestOpts = { method: 'GET' }): Promise<string> {\n    return new Promise(async (resolve, reject) => {\n        let cookies_added = false;\n        if (options.cookies) {\n            let cook = getCookies();\n            if (typeof cook === 'string' && options.headers) {\n                Object.assign(options.headers, { cookie: cook });\n                cookies_added = true;\n            }\n        }\n        if (options.cookieJar) {\n            const cookies = [];\n            for (const cookie of Object.entries(options.cookieJar)) {\n                cookies.push(cookie.join('='));\n            }\n\n            if (cookies.length !== 0) {\n                if (!options.headers) options.headers = {};\n                const existingCookies = cookies_added ? `; ${options.headers.cookie}` : '';\n                Object.assign(options.headers, { cookie: `${cookies.join('; ')}${existingCookies}` });\n            }\n        }\n        if (options.headers) {\n            options.headers = {\n                ...options.headers,\n                'accept-encoding': 'gzip, deflate, br',\n                'user-agent': getRandomUserAgent()\n            };\n        }\n        const res = await internalRequest(req_url, options).catch((err: Error) => err);\n        if (res instanceof Error) {\n            reject(res);\n            return;\n        }\n        if (res.headers && res.headers['set-cookie']) {\n            if (options.cookieJar) {\n                for (const cookie of res.headers['set-cookie']) {\n                    const parts = cookie.split(';')[0].trim().split('=');\n                    options.cookieJar[parts.shift() as string] = parts.join('=');\n                }\n            }\n            if (cookies_added) {\n                cookieHeaders(res.headers['set-cookie']);\n            }\n        }\n        const data: string[] = [];\n        let decoder: BrotliDecompress | Gunzip | Deflate | undefined = undefined;\n        const encoding = res.headers['content-encoding'];\n        if (encoding === 'gzip') decoder = createGunzip();\n        else if (encoding === 'br') decoder = createBrotliDecompress();\n        else if (encoding === 'deflate') decoder = createDeflate();\n\n        if (decoder) {\n            res.pipe(decoder);\n            decoder.setEncoding('utf-8');\n            decoder.on('data', (c) => data.push(c));\n            decoder.on('end', () => resolve(data.join('')));\n        } else {\n            res.setEncoding('utf-8');\n            res.on('data', (c) => data.push(c));\n            res.on('end', () => resolve(data.join('')));\n        }\n    });\n}\n\nexport function request_resolve_redirect(url: string): Promise<string> {\n    return new Promise(async (resolve, reject) => {\n        let res = await https_getter(url, { method: 'HEAD' }).catch((err: Error) => err);\n        if (res instanceof Error) {\n            reject(res);\n            return;\n        }\n        const statusCode = Number(res.statusCode);\n        if (statusCode < 300) {\n            resolve(url);\n        } else if (statusCode < 400) {\n            const resolved = await request_resolve_redirect(res.headers.location as string).catch((err) => err);\n            if (resolved instanceof Error) {\n                reject(resolved);\n                return;\n            }\n\n            resolve(resolved);\n        } else {\n            reject(new Error(`${res.statusCode}: ${res.statusMessage}, ${url}`));\n        }\n    });\n}\n\nexport function request_content_length(url: string): Promise<number> {\n    return new Promise(async (resolve, reject) => {\n        let res = await https_getter(url, { method: 'HEAD' }).catch((err: Error) => err);\n        if (res instanceof Error) {\n            reject(res);\n            return;\n        }\n        const statusCode = Number(res.statusCode);\n        if (statusCode < 300) {\n            resolve(Number(res.headers['content-length']));\n        } else if (statusCode < 400) {\n            const newURL = await request_resolve_redirect(res.headers.location as string).catch((err) => err);\n            if (newURL instanceof Error) {\n                reject(newURL);\n                return;\n            }\n\n            const res2 = await request_content_length(newURL).catch((err) => err);\n            if (res2 instanceof Error) {\n                reject(res2);\n                return;\n            }\n\n            resolve(res2);\n        } else {\n            reject(\n                new Error(`Failed to get content length with error: ${res.statusCode}, ${res.statusMessage}, ${url}`)\n            );\n        }\n    });\n}\n\n/**\n * Main module that play-dl uses for making a https request\n * @param req_url URL to make https request to\n * @param options Request options for https request\n * @returns Incoming Message from the https request\n */\nfunction https_getter(req_url: string, options: RequestOpts = {}): Promise<IncomingMessage> {\n    return new Promise((resolve, reject) => {\n        const s = new URL(req_url);\n        options.method ??= 'GET';\n        const req_options: RequestOptions = {\n            host: s.hostname,\n            path: s.pathname + s.search,\n            headers: options.headers ?? {},\n            method: options.method\n        };\n\n        const req = httpsRequest(req_options, resolve);\n        req.on('error', (err) => {\n            reject(err);\n        });\n        if (options.method === 'POST') req.write(options.body);\n        req.end();\n    });\n}\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs';\n\nlet youtubeData: youtubeDataOptions;\nif (existsSync('.data/youtube.data')) {\n    youtubeData = JSON.parse(readFileSync('.data/youtube.data', 'utf-8'));\n    youtubeData.file = true;\n}\n\ninterface youtubeDataOptions {\n    cookie?: Object;\n    file?: boolean;\n}\n\nexport function getCookies(): undefined | string {\n    let result = '';\n    if (!youtubeData?.cookie) return undefined;\n    for (const [key, value] of Object.entries(youtubeData.cookie)) {\n        result += `${key}=${value};`;\n    }\n    return result;\n}\n\nexport function setCookie(key: string, value: string): boolean {\n    if (!youtubeData?.cookie) return false;\n    key = key.trim();\n    value = value.trim();\n    Object.assign(youtubeData.cookie, { [key]: value });\n    return true;\n}\n\nexport function uploadCookie() {\n    if (youtubeData.cookie && youtubeData.file)\n        writeFileSync('.data/youtube.data', JSON.stringify(youtubeData, undefined, 4));\n}\n\nexport function setCookieToken(options: { cookie: string }) {\n    let cook = options.cookie;\n    let cookie: Object = {};\n    cook.split(';').forEach((x) => {\n        const arr = x.split('=');\n        if (arr.length <= 1) return;\n        const key = arr.shift()?.trim() as string;\n        const value = arr.join('=').trim();\n        Object.assign(cookie, { [key]: value });\n    });\n    youtubeData = { cookie };\n    youtubeData.file = false;\n}\n\n/**\n * Updates cookies locally either in file or in memory.\n *\n * Example\n * ```ts\n * const response = ... // Any https package get function.\n *\n * play.cookieHeaders(response.headers['set-cookie'])\n * ```\n * @param headCookie response headers['set-cookie'] array\n * @returns Nothing\n */\nexport function cookieHeaders(headCookie: string[]): void {\n    if (!youtubeData?.cookie) return;\n    headCookie.forEach((x: string) => {\n        x.split(';').forEach((z) => {\n            const arr = z.split('=');\n            if (arr.length <= 1) return;\n            const key = arr.shift()?.trim() as string;\n            const value = arr.join('=').trim();\n            setCookie(key, value);\n        });\n    });\n    uploadCookie();\n}\n","import useragents from './useragents.json';\n\nexport function setUserAgent(array: string[]): void {\n    useragents.push(...array);\n}\n\nfunction getRandomInt(min: number, max: number): number {\n    min = Math.ceil(min);\n    max = Math.floor(max);\n    return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nexport function getRandomUserAgent() {\n    const random = getRandomInt(0, useragents.length - 1);\n    return useragents[random];\n}\n","import { Readable } from 'node:stream';\nimport { IncomingMessage } from 'node:http';\nimport { parseAudioFormats, StreamOptions, StreamType } from '../stream';\nimport { request, request_stream } from '../../Request';\nimport { video_stream_info } from '../utils/extractor';\nimport { URL } from 'node:url';\n\n/**\n * YouTube Live Stream class for playing audio from Live Stream videos.\n */\nexport class LiveStream {\n    /**\n     * Readable Stream through which data passes\n     */\n    stream: Readable;\n    /**\n     * Type of audio data that we recieved from live stream youtube url.\n     */\n    type: StreamType;\n    /**\n     * Incoming message that we recieve.\n     *\n     * Storing this is essential.\n     * This helps to destroy the TCP connection completely if you stopped player in between the stream\n     */\n    private request?: IncomingMessage;\n    /**\n     * Timer that creates loop from interval time provided.\n     */\n    private normal_timer?: Timer;\n    /**\n     * Timer used to update dash url so as to avoid 404 errors after long hours of streaming.\n     *\n     * It updates dash_url every 30 minutes.\n     */\n    private dash_timer: Timer;\n    /**\n     * Given Dash URL.\n     */\n    private dash_url: string;\n    /**\n     * Base URL in dash manifest file.\n     */\n    private base_url: string;\n    /**\n     * Interval to fetch data again to dash url.\n     */\n    private interval: number;\n    /**\n     * Timer used to update dash url so as to avoid 404 errors after long hours of streaming.\n     *\n     * It updates dash_url every 30 minutes.\n     */\n    private video_url: string;\n    /**\n     * No of segments of data to add in stream before starting to loop\n     */\n    private precache: number;\n    /**\n     * Segment sequence number\n     */\n    private sequence: number;\n    /**\n     * Live Stream Class Constructor\n     * @param dash_url dash manifest URL\n     * @param target_interval interval time for fetching dash data again\n     * @param video_url Live Stream video url.\n     */\n    constructor(dash_url: string, interval: number, video_url: string, precache?: number) {\n        this.stream = new Readable({ highWaterMark: 5 * 1000 * 1000, read() {} });\n        this.type = StreamType.Arbitrary;\n        this.sequence = 0;\n        this.dash_url = dash_url;\n        this.base_url = '';\n        this.interval = interval;\n        this.video_url = video_url;\n        this.precache = precache || 3;\n        this.dash_timer = new Timer(() => {\n            this.dash_updater();\n            this.dash_timer.reuse();\n        }, 1800);\n        this.stream.on('close', () => {\n            this.cleanup();\n        });\n        this.initialize_dash();\n    }\n    /**\n     * This cleans every used variable in class.\n     *\n     * This is used to prevent re-use of this class and helping garbage collector to collect it.\n     */\n    private cleanup() {\n        this.normal_timer?.destroy();\n        this.dash_timer.destroy();\n        this.request?.destroy();\n        this.video_url = '';\n        this.request = undefined;\n        this.dash_url = '';\n        this.base_url = '';\n        this.interval = 0;\n    }\n    /**\n     * Updates dash url.\n     *\n     * Used by dash_timer for updating dash_url every 30 minutes.\n     */\n    private async dash_updater() {\n        const info = await video_stream_info(this.video_url);\n        if (info.LiveStreamData.dashManifestUrl) this.dash_url = info.LiveStreamData.dashManifestUrl;\n        return this.initialize_dash();\n    }\n    /**\n     * Initializes dash after getting dash url.\n     *\n     * Start if it is first time of initialishing dash function.\n     */\n    private async initialize_dash() {\n        const response = await request(this.dash_url);\n        const audioFormat = response\n            .split('<AdaptationSet id=\"0\"')[1]\n            .split('</AdaptationSet>')[0]\n            .split('</Representation>');\n        if (audioFormat[audioFormat.length - 1] === '') audioFormat.pop();\n        this.base_url = audioFormat[audioFormat.length - 1].split('<BaseURL>')[1].split('</BaseURL>')[0];\n        await request_stream(`https://${new URL(this.base_url).host}/generate_204`);\n        if (this.sequence === 0) {\n            const list = audioFormat[audioFormat.length - 1]\n                .split('<SegmentList>')[1]\n                .split('</SegmentList>')[0]\n                .replaceAll('<SegmentURL media=\"', '')\n                .split('\"/>');\n            if (list[list.length - 1] === '') list.pop();\n            if (list.length > this.precache) list.splice(0, list.length - this.precache);\n            this.sequence = Number(list[0].split('sq/')[1].split('/')[0]);\n            this.first_data(list.length);\n        }\n    }\n    /**\n     * Used only after initializing dash function first time.\n     * @param len Length of data that you want to\n     */\n    private async first_data(len: number) {\n        for (let i = 1; i <= len; i++) {\n            await new Promise(async (resolve) => {\n                const stream = await request_stream(this.base_url + 'sq/' + this.sequence).catch((err: Error) => err);\n                if (stream instanceof Error) {\n                    this.stream.emit('error', stream);\n                    return;\n                }\n                this.request = stream;\n                stream.on('data', (c) => {\n                    this.stream.push(c);\n                });\n                stream.on('end', () => {\n                    this.sequence++;\n                    resolve('');\n                });\n                stream.once('error', (err) => {\n                    this.stream.emit('error', err);\n                });\n            });\n        }\n        this.normal_timer = new Timer(() => {\n            this.loop();\n            this.normal_timer?.reuse();\n        }, this.interval);\n    }\n    /**\n     * This loops function in Live Stream Class.\n     *\n     * Gets next segment and push it.\n     */\n    private loop() {\n        return new Promise(async (resolve) => {\n            const stream = await request_stream(this.base_url + 'sq/' + this.sequence).catch((err: Error) => err);\n            if (stream instanceof Error) {\n                this.stream.emit('error', stream);\n                return;\n            }\n            this.request = stream;\n            stream.on('data', (c) => {\n                this.stream.push(c);\n            });\n            stream.on('end', () => {\n                this.sequence++;\n                resolve('');\n            });\n            stream.once('error', (err) => {\n                this.stream.emit('error', err);\n            });\n        });\n    }\n    /**\n     * Deprecated Functions\n     */\n    pause() {}\n    /**\n     * Deprecated Functions\n     */\n    resume() {}\n}\n/**\n * YouTube Stream Class for playing audio from normal videos.\n */\nexport class Stream {\n    /**\n     * Readable Stream through which data passes\n     */\n    stream: Readable;\n    /**\n     * Type of audio data that we recieved from normal youtube url.\n     */\n    type: StreamType;\n    /**\n     * Audio Endpoint Format Url to get data from.\n     */\n    private url: string;\n    /**\n     * Used to calculate no of bytes data that we have recieved\n     */\n    private bytes_count: number;\n    /**\n     * Calculate per second bytes by using contentLength (Total bytes) / Duration (in seconds)\n     */\n    private per_sec_bytes: number;\n    /**\n     * Total length of audio file in bytes\n     */\n    private content_length: number;\n    /**\n     * YouTube video url. [ Used only for retrying purposes only. ]\n     */\n    private video_url: string;\n    /**\n     * Timer for looping data every 265 seconds.\n     */\n    private timer: Timer;\n    /**\n     * Quality given by user. [ Used only for retrying purposes only. ]\n     */\n    private quality: number;\n    /**\n     * Incoming message that we recieve.\n     *\n     * Storing this is essential.\n     * This helps to destroy the TCP connection completely if you stopped player in between the stream\n     */\n    private request: IncomingMessage | null;\n    /**\n     * YouTube Stream Class constructor\n     * @param url Audio Endpoint url.\n     * @param type Type of Stream\n     * @param duration Duration of audio playback [ in seconds ]\n     * @param contentLength Total length of Audio file in bytes.\n     * @param video_url YouTube video url.\n     * @param options Options provided to stream function.\n     */\n    constructor(\n        url: string,\n        type: StreamType,\n        duration: number,\n        contentLength: number,\n        video_url: string,\n        options: StreamOptions\n    ) {\n        this.stream = new Readable({ highWaterMark: 5 * 1000 * 1000, read() {} });\n        this.url = url;\n        this.quality = options.quality as number;\n        this.type = type;\n        this.bytes_count = 0;\n        this.video_url = video_url;\n        this.per_sec_bytes = Math.ceil(contentLength / duration);\n        this.content_length = contentLength;\n        this.request = null;\n        this.timer = new Timer(() => {\n            this.timer.reuse();\n            this.loop();\n        }, 265);\n        this.stream.on('close', () => {\n            this.timer.destroy();\n            this.cleanup();\n        });\n        this.loop();\n    }\n    /**\n     * Retry if we get 404 or 403 Errors.\n     */\n    private async retry() {\n        const info = await video_stream_info(this.video_url);\n        const audioFormat = parseAudioFormats(info.format);\n        this.url = audioFormat[this.quality].url;\n    }\n    /**\n     * This cleans every used variable in class.\n     *\n     * This is used to prevent re-use of this class and helping garbage collector to collect it.\n     */\n    private cleanup() {\n        this.request?.destroy();\n        this.request = null;\n        this.url = '';\n    }\n    /**\n     * Getting data from audio endpoint url and passing it to stream.\n     *\n     * If 404 or 403 occurs, it will retry again.\n     */\n    private async loop() {\n        if (this.stream.destroyed) {\n            this.timer.destroy();\n            this.cleanup();\n            return;\n        }\n        const end: number = this.bytes_count + this.per_sec_bytes * 300;\n        const stream = await request_stream(this.url, {\n            headers: {\n                range: `bytes=${this.bytes_count}-${end >= this.content_length ? '' : end}`\n            }\n        }).catch((err: Error) => err);\n        if (stream instanceof Error) {\n            this.stream.emit('error', stream);\n            this.bytes_count = 0;\n            this.per_sec_bytes = 0;\n            this.cleanup();\n            return;\n        }\n        if (Number(stream.statusCode) >= 400) {\n            this.cleanup();\n            await this.retry();\n            this.timer.reuse();\n            this.loop();\n            return;\n        }\n        this.request = stream;\n        stream.on('data', (c) => {\n            this.stream.push(c);\n        });\n\n        stream.once('error', async () => {\n            this.cleanup();\n            await this.retry();\n            this.timer.reuse();\n            this.loop();\n        });\n\n        stream.on('data', (chunk: any) => {\n            this.bytes_count += chunk.length;\n        });\n\n        stream.on('end', () => {\n            if (end >= this.content_length) {\n                this.timer.destroy();\n                this.stream.push(null);\n                this.cleanup();\n            }\n        });\n    }\n    /**\n     * Pauses timer.\n     * Stops running of loop.\n     *\n     * Useful if you don't want to get excess data to be stored in stream.\n     */\n    pause() {\n        this.timer.pause();\n    }\n    /**\n     * Resumes timer.\n     * Starts running of loop.\n     */\n    resume() {\n        this.timer.resume();\n    }\n}\n/**\n * Timer Class.\n *\n * setTimeout + extra features ( re-starting, pausing, resuming ).\n */\nexport class Timer {\n    /**\n     * Boolean for checking if Timer is destroyed or not.\n     */\n    private destroyed: boolean;\n    /**\n     * Boolean for checking if Timer is paused or not.\n     */\n    private paused: boolean;\n    /**\n     * setTimeout function\n     */\n    private timer: NodeJS.Timer;\n    /**\n     * Callback to be executed once timer finishes.\n     */\n    private callback: () => void;\n    /**\n     * Seconds time when it is started.\n     */\n    private time_start: number;\n    /**\n     * Total time left.\n     */\n    private time_left: number;\n    /**\n     * Total time given by user [ Used only for re-using timer. ]\n     */\n    private time_total: number;\n    /**\n     * Constructor for Timer Class\n     * @param callback Function to execute when timer is up.\n     * @param time Total time to wait before execution.\n     */\n    constructor(callback: () => void, time: number) {\n        this.callback = callback;\n        this.time_total = time;\n        this.time_left = time;\n        this.paused = false;\n        this.destroyed = false;\n        this.time_start = process.hrtime()[0];\n        this.timer = setTimeout(this.callback, this.time_total * 1000);\n    }\n    /**\n     * Pauses Timer\n     * @returns Boolean to tell that if it is paused or not.\n     */\n    pause() {\n        if (!this.paused && !this.destroyed) {\n            this.paused = true;\n            clearTimeout(this.timer);\n            this.time_left = this.time_left - (process.hrtime()[0] - this.time_start);\n            return true;\n        } else return false;\n    }\n    /**\n     * Resumes Timer\n     * @returns Boolean to tell that if it is resumed or not.\n     */\n    resume() {\n        if (this.paused && !this.destroyed) {\n            this.paused = false;\n            this.time_start = process.hrtime()[0];\n            this.timer = setTimeout(this.callback, this.time_left * 1000);\n            return true;\n        } else return false;\n    }\n    /**\n     * Reusing of timer\n     * @returns Boolean to tell if it is re-used or not.\n     */\n    reuse() {\n        if (!this.destroyed) {\n            clearTimeout(this.timer);\n            this.time_left = this.time_total;\n            this.paused = false;\n            this.time_start = process.hrtime()[0];\n            this.timer = setTimeout(this.callback, this.time_total * 1000);\n            return true;\n        } else return false;\n    }\n    /**\n     * Destroy timer.\n     *\n     * It can't be used again.\n     */\n    destroy() {\n        clearTimeout(this.timer);\n        this.destroyed = true;\n        this.callback = () => {};\n        this.time_total = 0;\n        this.time_left = 0;\n        this.paused = false;\n        this.time_start = 0;\n    }\n}\n","import { URL, URLSearchParams } from 'node:url';\nimport { request } from './../../Request';\n\ninterface formatOptions {\n    url?: string;\n    sp?: string;\n    signatureCipher?: string;\n    cipher?: string;\n    s?: string;\n}\n// RegExp for various js functions\nconst var_js = '[a-zA-Z_\\\\$]\\\\w*';\nconst singlequote_js = `'[^'\\\\\\\\]*(:?\\\\\\\\[\\\\s\\\\S][^'\\\\\\\\]*)*'`;\nconst duoblequote_js = `\"[^\"\\\\\\\\]*(:?\\\\\\\\[\\\\s\\\\S][^\"\\\\\\\\]*)*\"`;\nconst quote_js = `(?:${singlequote_js}|${duoblequote_js})`;\nconst key_js = `(?:${var_js}|${quote_js})`;\nconst prop_js = `(?:\\\\.${var_js}|\\\\[${quote_js}\\\\])`;\nconst empty_js = `(?:''|\"\")`;\nconst reverse_function = ':function\\\\(a\\\\)\\\\{' + '(?:return )?a\\\\.reverse\\\\(\\\\)' + '\\\\}';\nconst slice_function = ':function\\\\(a,b\\\\)\\\\{' + 'return a\\\\.slice\\\\(b\\\\)' + '\\\\}';\nconst splice_function = ':function\\\\(a,b\\\\)\\\\{' + 'a\\\\.splice\\\\(0,b\\\\)' + '\\\\}';\nconst swap_function =\n    ':function\\\\(a,b\\\\)\\\\{' +\n    'var c=a\\\\[0\\\\];a\\\\[0\\\\]=a\\\\[b(?:%a\\\\.length)?\\\\];a\\\\[b(?:%a\\\\.length)?\\\\]=c(?:;return a)?' +\n    '\\\\}';\nconst obj_regexp = new RegExp(\n    `var (${var_js})=\\\\{((?:(?:${key_js}${reverse_function}|${key_js}${slice_function}|${key_js}${splice_function}|${key_js}${swap_function}),?\\\\r?\\\\n?)+)\\\\};`\n);\nconst function_regexp = new RegExp(\n    `${\n        `function(?: ${var_js})?\\\\(a\\\\)\\\\{` + `a=a\\\\.split\\\\(${empty_js}\\\\);\\\\s*` + `((?:(?:a=)?${var_js}`\n    }${prop_js}\\\\(a,\\\\d+\\\\);)+)` +\n        `return a\\\\.join\\\\(${empty_js}\\\\)` +\n        `\\\\}`\n);\nconst reverse_regexp = new RegExp(`(?:^|,)(${key_js})${reverse_function}`, 'm');\nconst slice_regexp = new RegExp(`(?:^|,)(${key_js})${slice_function}`, 'm');\nconst splice_regexp = new RegExp(`(?:^|,)(${key_js})${splice_function}`, 'm');\nconst swap_regexp = new RegExp(`(?:^|,)(${key_js})${swap_function}`, 'm');\n/**\n * Function to get tokens from html5player body data.\n * @param body body data of html5player.\n * @returns Array of tokens.\n */\nfunction js_tokens(body: string) {\n    const function_action = function_regexp.exec(body);\n    const object_action = obj_regexp.exec(body);\n    if (!function_action || !object_action) return null;\n\n    const object = object_action[1].replace(/\\$/g, '\\\\$');\n    const object_body = object_action[2].replace(/\\$/g, '\\\\$');\n    const function_body = function_action[1].replace(/\\$/g, '\\\\$');\n\n    let result = reverse_regexp.exec(object_body);\n    const reverseKey = result && result[1].replace(/\\$/g, '\\\\$').replace(/\\$|^'|^\"|'$|\"$/g, '');\n\n    result = slice_regexp.exec(object_body);\n    const sliceKey = result && result[1].replace(/\\$/g, '\\\\$').replace(/\\$|^'|^\"|'$|\"$/g, '');\n\n    result = splice_regexp.exec(object_body);\n    const spliceKey = result && result[1].replace(/\\$/g, '\\\\$').replace(/\\$|^'|^\"|'$|\"$/g, '');\n\n    result = swap_regexp.exec(object_body);\n    const swapKey = result && result[1].replace(/\\$/g, '\\\\$').replace(/\\$|^'|^\"|'$|\"$/g, '');\n\n    const keys = `(${[reverseKey, sliceKey, spliceKey, swapKey].join('|')})`;\n    const myreg = `(?:a=)?${object}(?:\\\\.${keys}|\\\\['${keys}'\\\\]|\\\\[\"${keys}\"\\\\])` + `\\\\(a,(\\\\d+)\\\\)`;\n    const tokenizeRegexp = new RegExp(myreg, 'g');\n    const tokens = [];\n    while ((result = tokenizeRegexp.exec(function_body)) !== null) {\n        const key = result[1] || result[2] || result[3];\n        switch (key) {\n            case swapKey:\n                tokens.push(`sw${result[4]}`);\n                break;\n            case reverseKey:\n                tokens.push('rv');\n                break;\n            case sliceKey:\n                tokens.push(`sl${result[4]}`);\n                break;\n            case spliceKey:\n                tokens.push(`sp${result[4]}`);\n                break;\n        }\n    }\n    return tokens;\n}\n/**\n * Function to decipher signature\n * @param tokens Tokens from js_tokens function\n * @param signature Signatured format url\n * @returns deciphered signature\n */\nfunction deciper_signature(tokens: string[], signature: string) {\n    let sig = signature.split('');\n    const len = tokens.length;\n    for (let i = 0; i < len; i++) {\n        let token = tokens[i],\n            pos;\n        switch (token.slice(0, 2)) {\n            case 'sw':\n                pos = parseInt(token.slice(2));\n                swappositions(sig, pos);\n                break;\n            case 'rv':\n                sig.reverse();\n                break;\n            case 'sl':\n                pos = parseInt(token.slice(2));\n                sig = sig.slice(pos);\n                break;\n            case 'sp':\n                pos = parseInt(token.slice(2));\n                sig.splice(0, pos);\n                break;\n        }\n    }\n    return sig.join('');\n}\n/**\n * Function to swap positions in a array\n * @param array array\n * @param position position to switch with first element\n */\nfunction swappositions(array: string[], position: number) {\n    const first = array[0];\n    array[0] = array[position];\n    array[position] = first;\n}\n/**\n * Sets Download url with some extra parameter\n * @param format video fomat\n * @param sig deciphered signature\n * @returns void\n */\nfunction download_url(format: formatOptions, sig: string) {\n    if (!format.url) return;\n\n    const decoded_url = decodeURIComponent(format.url);\n\n    const parsed_url = new URL(decoded_url);\n    parsed_url.searchParams.set('ratebypass', 'yes');\n\n    if (sig) {\n        parsed_url.searchParams.set(format.sp || 'signature', sig);\n    }\n    format.url = parsed_url.toString();\n}\n/**\n * Main function which handles all queries related to video format deciphering\n * @param formats video formats\n * @param html5player url of html5player\n * @returns array of format.\n */\nexport async function format_decipher(formats: formatOptions[], html5player: string): Promise<formatOptions[]> {\n    const body = await request(html5player);\n    const tokens = js_tokens(body);\n    formats.forEach((format) => {\n        const cipher = format.signatureCipher || format.cipher;\n        if (cipher) {\n            const params = Object.fromEntries(new URLSearchParams(cipher));\n            Object.assign(format, params);\n            delete format.signatureCipher;\n            delete format.cipher;\n        }\n        if (tokens && format.s) {\n            const sig = deciper_signature(tokens, format.s);\n            download_url(format, sig);\n            delete format.s;\n            delete format.sp;\n        }\n    });\n    return formats;\n}\n","export interface ChannelIconInterface {\n    /**\n     * YouTube Channel Icon URL\n     */\n    url: string;\n    /**\n     * YouTube Channel Icon Width\n     */\n    width: number;\n    /**\n     * YouTube Channel Icon Height\n     */\n    height: number;\n}\n/**\n * YouTube Channel Class\n */\nexport class YouTubeChannel {\n    /**\n     * YouTube Channel Title\n     */\n    name?: string;\n    /**\n     * YouTube Channel Verified status.\n     */\n    verified?: boolean;\n    /**\n     * YouTube Channel artist if any.\n     */\n    artist?: boolean;\n    /**\n     * YouTube Channel ID.\n     */\n    id?: string;\n    /**\n     * YouTube Class type. == \"channel\"\n     */\n    type: 'video' | 'playlist' | 'channel';\n    /**\n     * YouTube Channel Url\n     */\n    url?: string;\n    /**\n     * YouTube Channel Icons data.\n     */\n    icons?: ChannelIconInterface[];\n    /**\n     * YouTube Channel subscribers count.\n     */\n    subscribers?: string;\n    /**\n     * YouTube Channel Constructor\n     * @param data YouTube Channel data that we recieve from basic info or from search\n     */\n    constructor(data: any = {}) {\n        if (!data) throw new Error(`Cannot instantiate the ${this.constructor.name} class without data!`);\n        this.type = 'channel';\n        this.name = data.name || null;\n        this.verified = !!data.verified || false;\n        this.artist = !!data.artist || false;\n        this.id = data.id || null;\n        this.url = data.url || null;\n        this.icons = data.icons || [{ url: null, width: 0, height: 0 }];\n        this.subscribers = data.subscribers || null;\n    }\n\n    /**\n     * Returns channel icon url\n     * @param {object} options Icon options\n     * @param {number} [options.size=0] Icon size. **Default is 0**\n     */\n    iconURL(options = { size: 0 }): string | undefined {\n        if (typeof options.size !== 'number' || options.size < 0) throw new Error('invalid icon size');\n        if (!this.icons?.[0]?.url) return undefined;\n        const def = this.icons?.[0]?.url.split('=s')[1].split('-c')[0];\n        return this.icons?.[0]?.url.replace(`=s${def}-c`, `=s${options.size}-c`);\n    }\n    /**\n     * Converts Channel Class to channel name.\n     * @returns name of channel\n     */\n    toString(): string {\n        return this.name || '';\n    }\n    /**\n     * Converts Channel Class to JSON format\n     * @returns json data of the channel\n     */\n    toJSON(): ChannelJSON {\n        return {\n            name: this.name,\n            verified: this.verified,\n            artist: this.artist,\n            id: this.id,\n            url: this.url,\n            icons: this.icons,\n            type: this.type,\n            subscribers: this.subscribers\n        };\n    }\n}\n\ninterface ChannelJSON {\n    /**\n     * YouTube Channel Title\n     */\n    name?: string;\n    /**\n     * YouTube Channel Verified status.\n     */\n    verified?: boolean;\n    /**\n     * YouTube Channel artist if any.\n     */\n    artist?: boolean;\n    /**\n     * YouTube Channel ID.\n     */\n    id?: string;\n    /**\n     * Type of Class [ Channel ]\n     */\n    type: 'video' | 'playlist' | 'channel';\n    /**\n     * YouTube Channel Url\n     */\n    url?: string;\n    /**\n     * YouTube Channel Icon data.\n     */\n    icons?: ChannelIconInterface[];\n    /**\n     * YouTube Channel subscribers count.\n     */\n    subscribers?: string;\n}\n","export class YouTubeThumbnail {\n    url: string;\n    width: number;\n    height: number;\n\n    constructor(data: any) {\n        this.url = data.url;\n        this.width = data.width;\n        this.height = data.height;\n    }\n\n    toJSON() {\n        return {\n            url: this.url,\n            width: this.width,\n            height: this.height\n        };\n    }\n}\n","import { YouTubeChannel } from './Channel';\nimport { YouTubeThumbnail } from './Thumbnail';\n\n/**\n * Licensed music in the video\n * \n * The property names change depending on your region's language.\n */\ninterface VideoMusic {\n    song?: string;\n    url?: string | null;\n    artist?: string;\n    album?: string;\n    writers?: string;\n    licenses?: string;\n}\n\ninterface VideoOptions {\n    /**\n     * YouTube Video ID\n     */\n    id?: string;\n    /**\n     * YouTube video url\n     */\n    url: string;\n    /**\n     * YouTube Video title\n     */\n    title?: string;\n    /**\n     * YouTube Video description.\n     */\n    description?: string;\n    /**\n     * YouTube Video Duration Formatted\n     */\n    durationRaw: string;\n    /**\n     * YouTube Video Duration in seconds\n     */\n    durationInSec: number;\n    /**\n     * YouTube Video Uploaded Date\n     */\n    uploadedAt?: string;\n    /**\n     * If the video is upcoming or a premiere that isn't currently live, this will contain the premiere date, for watch page playlists this will be true, it defaults to undefined\n     */\n    upcoming?: Date | true;\n    /**\n     * YouTube Views\n     */\n    views: number;\n    /**\n     * YouTube Thumbnail Data\n     */\n    thumbnail?: {\n        width: number | undefined;\n        height: number | undefined;\n        url: string | undefined;\n    };\n    /**\n     * YouTube Video's uploader Channel Data\n     */\n    channel?: YouTubeChannel;\n    /**\n     * YouTube Video's likes\n     */\n    likes: number;\n    /**\n     * YouTube Video live status\n     */\n    live: boolean;\n    /**\n     * YouTube Video private status\n     */\n    private: boolean;\n    /**\n     * YouTube Video tags\n     */\n    tags: string[];\n    /**\n     * `true` if the video has been identified by the YouTube community as inappropriate or offensive to some audiences and viewer discretion is advised\n     */\n    discretionAdvised?: boolean;\n    /**\n     * Gives info about music content in that video.\n     * \n     * The property names of VideoMusic change depending on your region's language.\n     */\n    music?: VideoMusic[];\n    /**\n     * The chapters for this video\n     *\n     * If the video doesn't have any chapters or if the video object wasn't created by {@link video_basic_info} or {@link video_info} this will be an empty array.\n     */\n    chapters: VideoChapter[];\n}\n\nexport interface VideoChapter {\n    /**\n     * The title of the chapter\n     */\n    title: string;\n    /**\n     * The timestamp of the start of the chapter\n     */\n    timestamp: string;\n    /**\n     * The start of the chapter in seconds\n     */\n    seconds: number;\n    /**\n     * Thumbnails of the frame at the start of this chapter\n     */\n    thumbnails: YouTubeThumbnail[];\n}\n\n/**\n * Class for YouTube Video url\n */\nexport class YouTubeVideo {\n    /**\n     * YouTube Video ID\n     */\n    id?: string;\n    /**\n     * YouTube video url\n     */\n    url: string;\n    /**\n     * YouTube Class type. == \"video\"\n     */\n    type: 'video' | 'playlist' | 'channel';\n    /**\n     * YouTube Video title\n     */\n    title?: string;\n    /**\n     * YouTube Video description.\n     */\n    description?: string;\n    /**\n     * YouTube Video Duration Formatted\n     */\n    durationRaw: string;\n    /**\n     * YouTube Video Duration in seconds\n     */\n    durationInSec: number;\n    /**\n     * YouTube Video Uploaded Date\n     */\n    uploadedAt?: string;\n    /**\n     * YouTube Live Date\n     */\n    liveAt?: string;\n    /**\n     * If the video is upcoming or a premiere that isn't currently live, this will contain the premiere date, for watch page playlists this will be true, it defaults to undefined\n     */\n    upcoming?: Date | true;\n    /**\n     * YouTube Views\n     */\n    views: number;\n    /**\n     * YouTube Thumbnail Data\n     */\n    thumbnails: YouTubeThumbnail[];\n    /**\n     * YouTube Video's uploader Channel Data\n     */\n    channel?: YouTubeChannel;\n    /**\n     * YouTube Video's likes\n     */\n    likes: number;\n    /**\n     * YouTube Video live status\n     */\n    live: boolean;\n    /**\n     * YouTube Video private status\n     */\n    private: boolean;\n    /**\n     * YouTube Video tags\n     */\n    tags: string[];\n    /**\n     * `true` if the video has been identified by the YouTube community as inappropriate or offensive to some audiences and viewer discretion is advised\n     */\n    discretionAdvised?: boolean;\n    /**\n     * Gives info about music content in that video.\n     */\n    music?: VideoMusic[];\n    /**\n     * The chapters for this video\n     *\n     * If the video doesn't have any chapters or if the video object wasn't created by {@link video_basic_info} or {@link video_info} this will be an empty array.\n     */\n    chapters: VideoChapter[];\n    /**\n     * Constructor for YouTube Video Class\n     * @param data JSON parsed data.\n     */\n    constructor(data: any) {\n        if (!data) throw new Error(`Can not initiate ${this.constructor.name} without data`);\n\n        this.id = data.id || undefined;\n        this.url = `https://www.youtube.com/watch?v=${this.id}`;\n        this.type = 'video';\n        this.title = data.title || undefined;\n        this.description = data.description || undefined;\n        this.durationRaw = data.duration_raw || '0:00';\n        this.durationInSec = (data.duration < 0 ? 0 : data.duration) || 0;\n        this.uploadedAt = data.uploadedAt || undefined;\n        this.liveAt = data.liveAt || undefined;\n        this.upcoming = data.upcoming;\n        this.views = parseInt(data.views) || 0;\n        const thumbnails = [];\n        for (const thumb of data.thumbnails) {\n            thumbnails.push(new YouTubeThumbnail(thumb));\n        }\n        this.thumbnails = thumbnails || [];\n        this.channel = new YouTubeChannel(data.channel) || {};\n        this.likes = data.likes || 0;\n        this.live = !!data.live;\n        this.private = !!data.private;\n        this.tags = data.tags || [];\n        this.discretionAdvised = data.discretionAdvised ?? undefined;\n        this.music = data.music || [];\n        this.chapters = data.chapters || [];\n    }\n    /**\n     * Converts class to title name of video.\n     * @returns Title name\n     */\n    toString(): string {\n        return this.url || '';\n    }\n    /**\n     * Converts class to JSON data\n     * @returns JSON data.\n     */\n    toJSON(): VideoOptions {\n        return {\n            id: this.id,\n            url: this.url,\n            title: this.title,\n            description: this.description,\n            durationInSec: this.durationInSec,\n            durationRaw: this.durationRaw,\n            uploadedAt: this.uploadedAt,\n            thumbnail: this.thumbnails[this.thumbnails.length - 1].toJSON() || this.thumbnails,\n            channel: this.channel,\n            views: this.views,\n            tags: this.tags,\n            likes: this.likes,\n            live: this.live,\n            private: this.private,\n            discretionAdvised: this.discretionAdvised,\n            music: this.music,\n            chapters: this.chapters\n        };\n    }\n}\n","import { getPlaylistVideos, getContinuationToken } from '../utils/extractor';\nimport { request } from '../../Request';\nimport { YouTubeChannel } from './Channel';\nimport { YouTubeVideo } from './Video';\nimport { YouTubeThumbnail } from './Thumbnail';\nconst BASE_API = 'https://www.youtube.com/youtubei/v1/browse?key=';\n/**\n * YouTube Playlist Class containing vital informations about playlist.\n */\nexport class YouTubePlayList {\n    /**\n     * YouTube Playlist ID\n     */\n    id?: string;\n    /**\n     * YouTube Playlist Name\n     */\n    title?: string;\n    /**\n     * YouTube Class type. == \"playlist\"\n     */\n    type: 'video' | 'playlist' | 'channel';\n    /**\n     * Total no of videos in that playlist\n     */\n    videoCount?: number;\n    /**\n     * Time when playlist was last updated\n     */\n    lastUpdate?: string;\n    /**\n     * Total views of that playlist\n     */\n    views?: number;\n    /**\n     * YouTube Playlist url\n     */\n    url?: string;\n    /**\n     * YouTube Playlist url with starting video url.\n     */\n    link?: string;\n    /**\n     * YouTube Playlist channel data\n     */\n    channel?: YouTubeChannel;\n    /**\n     * YouTube Playlist thumbnail Data\n     */\n    thumbnail?: YouTubeThumbnail;\n    /**\n     * Videos array containing data of first 100 videos\n     */\n    private videos?: YouTubeVideo[];\n    /**\n     * Map contaning data of all fetched videos\n     */\n    private fetched_videos: Map<string, YouTubeVideo[]>;\n    /**\n     * Token containing API key, Token, ClientVersion.\n     */\n    private _continuation: {\n        api?: string;\n        token?: string;\n        clientVersion?: string;\n    } = {};\n    /**\n     * Total no of pages count.\n     */\n    private __count: number;\n    /**\n     * Constructor for YouTube Playlist Class\n     * @param data Json Parsed YouTube Playlist data\n     * @param searchResult If the data is from search or not\n     */\n    constructor(data: any, searchResult = false) {\n        if (!data) throw new Error(`Cannot instantiate the ${this.constructor.name} class without data!`);\n        this.__count = 0;\n        this.fetched_videos = new Map();\n        this.type = 'playlist';\n        if (searchResult) this.__patchSearch(data);\n        else this.__patch(data);\n    }\n    /**\n     * Updates variable according to a normal data.\n     * @param data Json Parsed YouTube Playlist data\n     */\n    private __patch(data: any) {\n        this.id = data.id || undefined;\n        this.url = data.url || undefined;\n        this.title = data.title || undefined;\n        this.videoCount = data.videoCount || 0;\n        this.lastUpdate = data.lastUpdate || undefined;\n        this.views = data.views || 0;\n        this.link = data.link || undefined;\n        this.channel = new YouTubeChannel(data.channel) || undefined;\n        this.thumbnail = data.thumbnail ? new YouTubeThumbnail(data.thumbnail) : undefined;\n        this.videos = data.videos || [];\n        this.__count++;\n        this.fetched_videos.set(`${this.__count}`, this.videos as YouTubeVideo[]);\n        this._continuation.api = data.continuation?.api ?? undefined;\n        this._continuation.token = data.continuation?.token ?? undefined;\n        this._continuation.clientVersion = data.continuation?.clientVersion ?? '<important data>';\n    }\n    /**\n     * Updates variable according to a searched data.\n     * @param data Json Parsed YouTube Playlist data\n     */\n    private __patchSearch(data: any) {\n        this.id = data.id || undefined;\n        this.url = this.id ? `https://www.youtube.com/playlist?list=${this.id}` : undefined;\n        this.title = data.title || undefined;\n        this.thumbnail = new YouTubeThumbnail(data.thumbnail) || undefined;\n        this.channel = data.channel || undefined;\n        this.videos = [];\n        this.videoCount = data.videos || 0;\n        this.link = undefined;\n        this.lastUpdate = undefined;\n        this.views = 0;\n    }\n    /**\n     * Parses next segment of videos from playlist and returns parsed data.\n     * @param limit Total no of videos to parse.\n     *\n     * Default = Infinity\n     * @returns Array of YouTube Video Class\n     */\n    async next(limit = Infinity): Promise<YouTubeVideo[]> {\n        if (!this._continuation || !this._continuation.token) return [];\n\n        const nextPage = await request(`${BASE_API}${this._continuation.api}&prettyPrint=false`, {\n            method: 'POST',\n            body: JSON.stringify({\n                continuation: this._continuation.token,\n                context: {\n                    client: {\n                        utcOffsetMinutes: 0,\n                        gl: 'US',\n                        hl: 'en',\n                        clientName: 'WEB',\n                        clientVersion: this._continuation.clientVersion\n                    },\n                    user: {},\n                    request: {}\n                }\n            })\n        });\n\n        const contents =\n            JSON.parse(nextPage)?.onResponseReceivedActions[0]?.appendContinuationItemsAction?.continuationItems;\n        if (!contents) return [];\n\n        const playlist_videos = getPlaylistVideos(contents, limit);\n        this.fetched_videos.set(`${this.__count}`, playlist_videos);\n        this._continuation.token = getContinuationToken(contents);\n        return playlist_videos;\n    }\n    /**\n     * Fetches remaining data from playlist\n     *\n     * For fetching and getting all songs data, see `total_pages` property.\n     * @param max Max no of videos to fetch\n     *\n     * Default = Infinity\n     * @returns\n     */\n    async fetch(max = Infinity): Promise<YouTubePlayList> {\n        const continuation = this._continuation.token;\n        if (!continuation) return this;\n        if (max < 1) max = Infinity;\n\n        while (typeof this._continuation.token === 'string' && this._continuation.token.length) {\n            this.__count++;\n            const res = await this.next();\n            max -= res.length;\n            if (max <= 0) break;\n            if (!res.length) break;\n        }\n\n        return this;\n    }\n    /**\n     * YouTube Playlists are divided into pages.\n     *\n     * For example, if you want to get 101 - 200 songs\n     *\n     * ```ts\n     * const playlist = await play.playlist_info('playlist url')\n     *\n     * await playlist.fetch()\n     *\n     * const result = playlist.page(2)\n     * ```\n     * @param number Page number\n     * @returns Array of YouTube Video Class\n     * @see {@link YouTubePlayList.all_videos}\n     */\n    page(number: number): YouTubeVideo[] {\n        if (!number) throw new Error('Page number is not provided');\n        if (!this.fetched_videos.has(`${number}`)) throw new Error('Given Page number is invalid');\n        return this.fetched_videos.get(`${number}`) as YouTubeVideo[];\n    }\n    /**\n     * Gets total number of pages in that playlist class.\n     * @see {@link YouTubePlayList.all_videos}\n     */\n    get total_pages() {\n        return this.fetched_videos.size;\n    }\n    /**\n     * This tells total number of videos that have been fetched so far.\n     *\n     * This can be equal to videosCount if all videos in playlist have been fetched and they are not hidden.\n     */\n    get total_videos() {\n        const page_number: number = this.total_pages;\n        return (page_number - 1) * 100 + (this.fetched_videos.get(`${page_number}`) as YouTubeVideo[]).length;\n    }\n    /**\n     * Fetches all the videos in the playlist and returns them\n     *\n     * ```ts\n     * const playlist = await play.playlist_info('playlist url')\n     *\n     * const videos = await playlist.all_videos()\n     * ```\n     * @returns An array of {@link YouTubeVideo} objects\n     * @see {@link YouTubePlayList.fetch}\n     */\n    async all_videos(): Promise<YouTubeVideo[]> {\n        await this.fetch();\n\n        const videos = [];\n\n        for (const page of this.fetched_videos.values()) videos.push(...page);\n\n        return videos;\n    }\n    /**\n     * Converts Playlist Class to a json parsed data.\n     * @returns\n     */\n    toJSON(): PlaylistJSON {\n        return {\n            id: this.id,\n            title: this.title,\n            thumbnail: this.thumbnail?.toJSON() || this.thumbnail,\n            channel: this.channel,\n            url: this.url,\n            videos: this.videos\n        };\n    }\n}\n\ninterface PlaylistJSON {\n    /**\n     * YouTube Playlist ID\n     */\n    id?: string;\n    /**\n     * YouTube Playlist Name\n     */\n    title?: string;\n    /**\n     * Total no of videos in that playlist\n     */\n    videoCount?: number;\n    /**\n     * Time when playlist was last updated\n     */\n    lastUpdate?: string;\n    /**\n     * Total views of that playlist\n     */\n    views?: number;\n    /**\n     * YouTube Playlist url\n     */\n    url?: string;\n    /**\n     * YouTube Playlist url with starting video url.\n     */\n    link?: string;\n    /**\n     * YouTube Playlist channel data\n     */\n    channel?: YouTubeChannel;\n    /**\n     * YouTube Playlist thumbnail Data\n     */\n    thumbnail?: {\n        width: number | undefined;\n        height: number | undefined;\n        url: string | undefined;\n    };\n    /**\n     * first 100 videos in that playlist\n     */\n    videos?: YouTubeVideo[];\n}\n","import { request } from './../../Request/index';\nimport { format_decipher } from './cipher';\nimport { VideoChapter, YouTubeVideo } from '../classes/Video';\nimport { YouTubePlayList } from '../classes/Playlist';\nimport { InfoData, StreamInfoData } from './constants';\nimport { URL, URLSearchParams } from 'node:url';\nimport { parseAudioFormats } from '../stream';\n\ninterface InfoOptions {\n    htmldata?: boolean;\n    language?: string;\n}\n\ninterface PlaylistOptions {\n    incomplete?: boolean;\n    language?: string;\n}\n\nconst video_id_pattern = /^[a-zA-Z\\d_-]{11,12}$/;\nconst playlist_id_pattern = /^(PL|UU|LL|RD|OL)[a-zA-Z\\d_-]{10,}$/;\nconst DEFAULT_API_KEY = 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8';\nconst video_pattern =\n    /^((?:https?:)?\\/\\/)?(?:(?:www|m|music)\\.)?((?:youtube\\.com|youtu.be))(\\/(?:[\\w\\-]+\\?v=|shorts\\/|embed\\/|live\\/|v\\/)?)([\\w\\-]+)(\\S+)?$/;\nconst playlist_pattern =\n    /^((?:https?:)?\\/\\/)?(?:(?:www|m|music)\\.)?((?:youtube\\.com|youtu.be))\\/(?:(playlist|watch))?(.*)?((\\?|\\&)list=)(PL|UU|LL|RD|OL)[a-zA-Z\\d_-]{10,}(&.*)?$/;\n/**\n * Validate YouTube URL or ID.\n *\n * **CAUTION :** If your search word is 11 or 12 characters long, you might get it validated as video ID.\n *\n * To avoid above, add one more condition to yt_validate\n * ```ts\n * if (url.startsWith('https') && yt_validate(url) === 'video') {\n *      // YouTube Video Url.\n * }\n * ```\n * @param url YouTube URL OR ID\n * @returns\n * ```\n * 'playlist' | 'video' | 'search' | false\n * ```\n */\nexport function yt_validate(url: string): 'playlist' | 'video' | 'search' | false {\n    const url_ = url.trim();\n    if (url_.indexOf('list=') === -1) {\n        if (url_.startsWith('https')) {\n            if (url_.match(video_pattern)) {\n                let id: string;\n                if (url_.includes('youtu.be/')) id = url_.split('youtu.be/')[1].split(/(\\?|\\/|&)/)[0];\n                else if (url_.includes('youtube.com/embed/'))\n                    id = url_.split('youtube.com/embed/')[1].split(/(\\?|\\/|&)/)[0];\n                else if (url_.includes('youtube.com/shorts/'))\n                    id = url_.split('youtube.com/shorts/')[1].split(/(\\?|\\/|&)/)[0];\n                else id = url_.split('watch?v=')[1]?.split(/(\\?|\\/|&)/)[0];\n                if (id?.match(video_id_pattern)) return 'video';\n                else return false;\n            } else return false;\n        } else {\n            if (url_.match(video_id_pattern)) return 'video';\n            else if (url_.match(playlist_id_pattern)) return 'playlist';\n            else return 'search';\n        }\n    } else {\n        if (!url_.match(playlist_pattern)) return yt_validate(url_.replace(/(\\?|\\&)list=[^&]*/, ''));\n        else return 'playlist';\n    }\n}\n/**\n * Extracts the video ID from a YouTube URL.\n *\n * Will return the value of `urlOrId` if it looks like a video ID.\n * @param urlOrId A YouTube URL or video ID\n * @returns the video ID or `false` if it can't find a video ID.\n */\nfunction extractVideoId(urlOrId: string): string | false {\n    if (urlOrId.startsWith('https://') && urlOrId.match(video_pattern)) {\n        let id: string;\n        if (urlOrId.includes('youtu.be/')) {\n            id = urlOrId.split('youtu.be/')[1].split(/(\\?|\\/|&)/)[0];\n        } else if (urlOrId.includes('youtube.com/embed/')) {\n            id = urlOrId.split('youtube.com/embed/')[1].split(/(\\?|\\/|&)/)[0];\n        } else if (urlOrId.includes('youtube.com/shorts/')) {\n            id = urlOrId.split('youtube.com/shorts/')[1].split(/(\\?|\\/|&)/)[0];\n        } else if (urlOrId.includes('youtube.com/live/')) {\n            id = urlOrId.split('youtube.com/live/')[1].split(/(\\?|\\/|&)/)[0];\n        } else {\n            id = (urlOrId.split('watch?v=')[1] ?? urlOrId.split('&v=')[1]).split(/(\\?|\\/|&)/)[0];\n        }\n\n        if (id.match(video_id_pattern)) return id;\n    } else if (urlOrId.match(video_id_pattern)) {\n        return urlOrId;\n    }\n\n    return false;\n}\n/**\n * Extract ID of YouTube url.\n * @param url ID or url of YouTube\n * @returns ID of video or playlist.\n */\nexport function extractID(url: string): string {\n    const check = yt_validate(url);\n    if (!check || check === 'search') throw new Error('This is not a YouTube url or videoId or PlaylistID');\n    const url_ = url.trim();\n    if (url_.startsWith('https')) {\n        if (url_.indexOf('list=') === -1) {\n            const video_id = extractVideoId(url_);\n            if (!video_id) throw new Error('This is not a YouTube url or videoId or PlaylistID');\n            return video_id;\n        } else {\n            return url_.split('list=')[1].split('&')[0];\n        }\n    } else return url_;\n}\n/**\n * Basic function to get data from a YouTube url or ID.\n *\n * Example\n * ```ts\n * const video = await play.video_basic_info('youtube video url')\n *\n * const res = ... // Any https package get function.\n *\n * const video = await play.video_basic_info(res.body, { htmldata : true })\n * ```\n * @param url YouTube url or ID or html body data\n * @param options Video Info Options\n *  - `boolean` htmldata : given data is html data or not\n * @returns Video Basic Info {@link InfoData}.\n */\nexport async function video_basic_info(url: string, options: InfoOptions = {}): Promise<InfoData> {\n    if (typeof url !== 'string') throw new Error('url parameter is not a URL string or a string of HTML');\n    const url_ = url.trim();\n    let body: string;\n    const cookieJar = {};\n    if (options.htmldata) {\n        body = url_;\n    } else {\n        const video_id = extractVideoId(url_);\n        if (!video_id) throw new Error('This is not a YouTube Watch URL');\n        const new_url = `https://www.youtube.com/watch?v=${video_id}&has_verified=1`;\n        body = await request(new_url, {\n            headers: {\n                'accept-language': options.language || 'en-US;q=0.9'\n            },\n            cookies: true,\n            cookieJar\n        });\n    }\n    if (body.indexOf('Our systems have detected unusual traffic from your computer network.') !== -1)\n        throw new Error('Captcha page: YouTube has detected that you are a bot!');\n    const player_data = body\n        .split('var ytInitialPlayerResponse = ')?.[1]\n        ?.split(';</script>')[0]\n        .split(/(?<=}}});\\s*(var|const|let)\\s/)[0];\n    if (!player_data) throw new Error('Initial Player Response Data is undefined.');\n    const initial_data = body\n        .split('var ytInitialData = ')?.[1]\n        ?.split(';</script>')[0]\n        .split(/;\\s*(var|const|let)\\s/)[0];\n    if (!initial_data) throw new Error('Initial Response Data is undefined.');\n    const player_response = JSON.parse(player_data);\n    const initial_response = JSON.parse(initial_data);\n    const vid = player_response.videoDetails;\n\n    let discretionAdvised = false;\n    let upcoming = false;\n    if (player_response.playabilityStatus.status !== 'OK') {\n        if (player_response.playabilityStatus.status === 'CONTENT_CHECK_REQUIRED') {\n            if (options.htmldata)\n                throw new Error(\n                    `Accepting the viewer discretion is not supported when using htmldata, video: ${vid.videoId}`\n                );\n            discretionAdvised = true;\n            const cookies =\n                initial_response.topbar.desktopTopbarRenderer.interstitial?.consentBumpV2Renderer.agreeButton\n                    .buttonRenderer.command.saveConsentAction;\n            if (cookies) {\n                Object.assign(cookieJar, {\n                    VISITOR_INFO1_LIVE: cookies.visitorCookie,\n                    CONSENT: cookies.consentCookie\n                });\n            }\n\n            const updatedValues = await acceptViewerDiscretion(vid.videoId, cookieJar, body, true);\n            player_response.streamingData = updatedValues.streamingData;\n            initial_response.contents.twoColumnWatchNextResults.secondaryResults = updatedValues.relatedVideos;\n        } else if (player_response.playabilityStatus.status === 'LIVE_STREAM_OFFLINE') upcoming = true;\n        else\n            throw new Error(\n                `While getting info from url\\n${\n                    player_response.playabilityStatus.errorScreen.playerErrorMessageRenderer?.reason.simpleText ??\n                    player_response.playabilityStatus.errorScreen.playerKavRenderer?.reason.simpleText ??\n                    player_response.playabilityStatus.reason\n                }`\n            );\n    }\n    const ownerInfo =\n        initial_response.contents.twoColumnWatchNextResults.results?.results?.contents[1]?.videoSecondaryInfoRenderer\n            ?.owner?.videoOwnerRenderer;\n    const badge = ownerInfo?.badges?.[0]?.metadataBadgeRenderer?.style?.toLowerCase();\n    const html5player = `https://www.youtube.com${body.split('\"jsUrl\":\"')[1].split('\"')[0]}`;\n    const related: string[] = [];\n    initial_response.contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results.forEach(\n        (res: any) => {\n            if (res.compactVideoRenderer)\n                related.push(`https://www.youtube.com/watch?v=${res.compactVideoRenderer.videoId}`);\n            if (res.itemSectionRenderer?.contents)\n                res.itemSectionRenderer.contents.forEach((x: any) => {\n                    if (x.compactVideoRenderer)\n                        related.push(`https://www.youtube.com/watch?v=${x.compactVideoRenderer.videoId}`);\n                });\n        }\n    );\n    const microformat = player_response.microformat.playerMicroformatRenderer;\n    const musicInfo = initial_response.engagementPanels.find((item: any) => item?.engagementPanelSectionListRenderer?.panelIdentifier == 'engagement-panel-structured-description')?.engagementPanelSectionListRenderer.content.structuredDescriptionContentRenderer.items\n        .find((el: any) => el.videoDescriptionMusicSectionRenderer)?.videoDescriptionMusicSectionRenderer.carouselLockups;\n\n    const music: any[] = [];\n    if (musicInfo) {\n        musicInfo.forEach((x: any) => {\n            if (!x.carouselLockupRenderer) return;\n            const row = x.carouselLockupRenderer;\n\n            const song = row.videoLockup?.compactVideoRenderer.title.simpleText ?? row.videoLockup?.compactVideoRenderer.title.runs?.find((x:any) => x.text)?.text;\n            const metadata = row.infoRows?.map((info: any) => [info.infoRowRenderer.title.simpleText.toLowerCase(), ((info.infoRowRenderer.expandedMetadata ?? info.infoRowRenderer.defaultMetadata)?.runs?.map((i:any) => i.text).join(\"\")) ?? info.infoRowRenderer.defaultMetadata?.simpleText ?? info.infoRowRenderer.expandedMetadata?.simpleText ?? \"\"]);\n            const contents = Object.fromEntries(metadata ?? {});\n            const id = row.videoLockup?.compactVideoRenderer.navigationEndpoint?.watchEndpoint.videoId\n                ?? row.infoRows?.find((x: any) => x.infoRowRenderer.title.simpleText.toLowerCase() == \"song\")?.infoRowRenderer.defaultMetadata.runs?.find((x: any) => x.navigationEndpoint)?.navigationEndpoint.watchEndpoint?.videoId;\n\n            music.push({song, url: id ? `https://www.youtube.com/watch?v=${id}` : null, ...contents})\n        });\n    }\n    const rawChapters =\n        initial_response.playerOverlays.playerOverlayRenderer.decoratedPlayerBarRenderer?.decoratedPlayerBarRenderer.playerBar?.multiMarkersPlayerBarRenderer.markersMap?.find(\n            (m: any) => m.key === 'DESCRIPTION_CHAPTERS'\n        )?.value?.chapters;\n    const chapters: VideoChapter[] = [];\n    if (rawChapters) {\n        for (const { chapterRenderer } of rawChapters) {\n            chapters.push({\n                title: chapterRenderer.title.simpleText,\n                timestamp: parseSeconds(chapterRenderer.timeRangeStartMillis / 1000),\n                seconds: chapterRenderer.timeRangeStartMillis / 1000,\n                thumbnails: chapterRenderer.thumbnail.thumbnails\n            });\n        }\n    }\n    let upcomingDate;\n    if (upcoming) {\n        if (microformat.liveBroadcastDetails.startTimestamp)\n            upcomingDate = new Date(microformat.liveBroadcastDetails.startTimestamp);\n        else {\n            const timestamp =\n                player_response.playabilityStatus.liveStreamability.liveStreamabilityRenderer.offlineSlate\n                    .liveStreamOfflineSlateRenderer.scheduledStartTime;\n            upcomingDate = new Date(parseInt(timestamp) * 1000);\n        }\n    }\n\n    const likeRenderer = initial_response.contents.twoColumnWatchNextResults.results.results.contents\n        .find((content: any) => content.videoPrimaryInfoRenderer)\n        ?.videoPrimaryInfoRenderer.videoActions.menuRenderer.topLevelButtons?.find(\n            (button: any) => button.toggleButtonRenderer?.defaultIcon.iconType === 'LIKE' || button.segmentedLikeDislikeButtonRenderer?.likeButton.toggleButtonRenderer?.defaultIcon.iconType === 'LIKE'\n        )\n\n    const video_details = new YouTubeVideo({\n        id: vid.videoId,\n        title: vid.title,\n        description: vid.shortDescription,\n        duration: Number(vid.lengthSeconds),\n        duration_raw: parseSeconds(vid.lengthSeconds),\n        uploadedAt: microformat.publishDate,\n        liveAt: microformat.liveBroadcastDetails?.startTimestamp,\n        upcoming: upcomingDate,\n        thumbnails: vid.thumbnail.thumbnails,\n        channel: {\n            name: vid.author,\n            id: vid.channelId,\n            url: `https://www.youtube.com/channel/${vid.channelId}`,\n            verified: Boolean(badge?.includes('verified')),\n            artist: Boolean(badge?.includes('artist')),\n            icons: ownerInfo?.thumbnail?.thumbnails || undefined\n        },\n        views: vid.viewCount,\n        tags: vid.keywords,\n        likes: parseInt(\n            likeRenderer?.toggleButtonRenderer?.defaultText.accessibility?.accessibilityData.label.replace(/\\D+/g, '') ?? \n            likeRenderer?.segmentedLikeDislikeButtonRenderer?.likeButton.toggleButtonRenderer?.defaultText.accessibility?.accessibilityData.label.replace(/\\D+/g, '') ?? 0\n        ),\n        live: vid.isLiveContent,\n        private: vid.isPrivate,\n        discretionAdvised,\n        music,\n        chapters\n    });\n    let format = [];\n    if (!upcoming) {\n        format.push(...(player_response.streamingData.formats ?? []));\n        format.push(...(player_response.streamingData.adaptiveFormats ?? []));\n\n        // get the formats for the android player for legacy videos\n        // fixes the stream being closed because not enough data\n        // arrived in time for ffmpeg to be able to extract audio data\n        if (parseAudioFormats(format).length === 0 && !options.htmldata) {\n            format = await getAndroidFormats(vid.videoId, cookieJar, body);\n        }\n    }\n    const LiveStreamData = {\n        isLive: video_details.live,\n        dashManifestUrl: player_response.streamingData?.dashManifestUrl ?? null,\n        hlsManifestUrl: player_response.streamingData?.hlsManifestUrl ?? null\n    };\n    return {\n        LiveStreamData,\n        html5player,\n        format,\n        video_details,\n        related_videos: related\n    };\n}\n/**\n * Gets the data required for streaming from YouTube url, ID or html body data and deciphers it.\n *\n * Internal function used by {@link stream} instead of {@link video_info}\n * because it only extracts the information required for streaming.\n *\n * @param url YouTube url or ID or html body data\n * @param options Video Info Options\n *  - `boolean` htmldata : given data is html data or not\n * @returns Deciphered Video Info {@link StreamInfoData}.\n */\nexport async function video_stream_info(url: string, options: InfoOptions = {}): Promise<StreamInfoData> {\n    if (typeof url !== 'string') throw new Error('url parameter is not a URL string or a string of HTML');\n    let body: string;\n    const cookieJar = {};\n    if (options.htmldata) {\n        body = url;\n    } else {\n        const video_id = extractVideoId(url);\n        if (!video_id) throw new Error('This is not a YouTube Watch URL');\n        const new_url = `https://www.youtube.com/watch?v=${video_id}&has_verified=1`;\n        body = await request(new_url, {\n            headers: { 'accept-language': 'en-US,en;q=0.9' },\n            cookies: true,\n            cookieJar\n        });\n    }\n    if (body.indexOf('Our systems have detected unusual traffic from your computer network.') !== -1)\n        throw new Error('Captcha page: YouTube has detected that you are a bot!');\n    const player_data = body\n        .split('var ytInitialPlayerResponse = ')?.[1]\n        ?.split(';</script>')[0]\n        .split(/(?<=}}});\\s*(var|const|let)\\s/)[0];\n    if (!player_data) throw new Error('Initial Player Response Data is undefined.');\n    const player_response = JSON.parse(player_data);\n    let upcoming = false;\n    if (player_response.playabilityStatus.status !== 'OK') {\n        if (player_response.playabilityStatus.status === 'CONTENT_CHECK_REQUIRED') {\n            if (options.htmldata)\n                throw new Error(\n                    `Accepting the viewer discretion is not supported when using htmldata, video: ${player_response.videoDetails.videoId}`\n                );\n\n            const initial_data = body\n                .split('var ytInitialData = ')?.[1]\n                ?.split(';</script>')[0]\n                .split(/;\\s*(var|const|let)\\s/)[0];\n            if (!initial_data) throw new Error('Initial Response Data is undefined.');\n\n            const cookies =\n                JSON.parse(initial_data).topbar.desktopTopbarRenderer.interstitial?.consentBumpV2Renderer.agreeButton\n                    .buttonRenderer.command.saveConsentAction;\n            if (cookies) {\n                Object.assign(cookieJar, {\n                    VISITOR_INFO1_LIVE: cookies.visitorCookie,\n                    CONSENT: cookies.consentCookie\n                });\n            }\n\n            const updatedValues = await acceptViewerDiscretion(\n                player_response.videoDetails.videoId,\n                cookieJar,\n                body,\n                false\n            );\n            player_response.streamingData = updatedValues.streamingData;\n        } else if (player_response.playabilityStatus.status === 'LIVE_STREAM_OFFLINE') upcoming = true;\n        else\n            throw new Error(\n                `While getting info from url\\n${\n                    player_response.playabilityStatus.errorScreen.playerErrorMessageRenderer?.reason.simpleText ??\n                    player_response.playabilityStatus.errorScreen.playerKavRenderer?.reason.simpleText ??\n                    player_response.playabilityStatus.reason\n                }`\n            );\n    }\n    const html5player = `https://www.youtube.com${body.split('\"jsUrl\":\"')[1].split('\"')[0]}`;\n    const duration = Number(player_response.videoDetails.lengthSeconds);\n    const video_details = {\n        url: `https://www.youtube.com/watch?v=${player_response.videoDetails.videoId}`,\n        durationInSec: (duration < 0 ? 0 : duration) || 0\n    };\n    let format = [];\n    if (!upcoming) {\n        format.push(...(player_response.streamingData.formats ?? []));\n        format.push(...(player_response.streamingData.adaptiveFormats ?? []));\n\n        // get the formats for the android player for legacy videos\n        // fixes the stream being closed because not enough data\n        // arrived in time for ffmpeg to be able to extract audio data\n        if (parseAudioFormats(format).length === 0 && !options.htmldata) {\n            format = await getAndroidFormats(player_response.videoDetails.videoId, cookieJar, body);\n        }\n    }\n\n    const LiveStreamData = {\n        isLive: player_response.videoDetails.isLiveContent,\n        dashManifestUrl: player_response.streamingData?.dashManifestUrl ?? null,\n        hlsManifestUrl: player_response.streamingData?.hlsManifestUrl ?? null\n    };\n    return await decipher_info(\n        {\n            LiveStreamData,\n            html5player,\n            format,\n            video_details\n        },\n        true\n    );\n}\n/**\n * Function to convert seconds to [hour : minutes : seconds] format\n * @param seconds seconds to convert\n * @returns [hour : minutes : seconds] format\n */\nfunction parseSeconds(seconds: number): string {\n    const d = Number(seconds);\n    const h = Math.floor(d / 3600);\n    const m = Math.floor((d % 3600) / 60);\n    const s = Math.floor((d % 3600) % 60);\n\n    const hDisplay = h > 0 ? (h < 10 ? `0${h}` : h) + ':' : '';\n    const mDisplay = m > 0 ? (m < 10 ? `0${m}` : m) + ':' : '00:';\n    const sDisplay = s > 0 ? (s < 10 ? `0${s}` : s) : '00';\n    return hDisplay + mDisplay + sDisplay;\n}\n/**\n * Gets data from YouTube url or ID or html body data and deciphers it.\n * ```\n * video_basic_info + decipher_info = video_info\n * ```\n *\n * Example\n * ```ts\n * const video = await play.video_info('youtube video url')\n *\n * const res = ... // Any https package get function.\n *\n * const video = await play.video_info(res.body, { htmldata : true })\n * ```\n * @param url YouTube url or ID or html body data\n * @param options Video Info Options\n *  - `boolean` htmldata : given data is html data or not\n * @returns Deciphered Video Info {@link InfoData}.\n */\nexport async function video_info(url: string, options: InfoOptions = {}): Promise<InfoData> {\n    const data = await video_basic_info(url.trim(), options);\n    return await decipher_info(data);\n}\n/**\n * Function uses data from video_basic_info and deciphers it if it contains signatures.\n * @param data Data - {@link InfoData}\n * @param audio_only `boolean` - To decipher only audio formats only.\n * @returns Deciphered Video Info {@link InfoData}\n */\nexport async function decipher_info<T extends InfoData | StreamInfoData>(\n    data: T,\n    audio_only: boolean = false\n): Promise<T> {\n    if (\n        data.LiveStreamData.isLive === true &&\n        data.LiveStreamData.dashManifestUrl !== null &&\n        data.video_details.durationInSec === 0\n    ) {\n        return data;\n    } else if (data.format.length > 0 && (data.format[0].signatureCipher || data.format[0].cipher)) {\n        if (audio_only) data.format = parseAudioFormats(data.format);\n        data.format = await format_decipher(data.format, data.html5player);\n        return data;\n    } else return data;\n}\n/**\n * Gets YouTube playlist info from a playlist url.\n *\n * Example\n * ```ts\n * const playlist = await play.playlist_info('youtube playlist url')\n *\n * const playlist = await play.playlist_info('youtube playlist url', { incomplete : true })\n * ```\n * @param url Playlist URL\n * @param options Playlist Info Options\n * - `boolean` incomplete : When this is set to `false` (default) this function will throw an error\n *                          if the playlist contains hidden videos.\n *                          If it is set to `true`, it parses the playlist skipping the hidden videos,\n *                          only visible videos are included in the resulting {@link YouTubePlaylist}.\n *\n * @returns YouTube Playlist\n */\nexport async function playlist_info(url: string, options: PlaylistOptions = {}): Promise<YouTubePlayList> {\n    if (!url || typeof url !== 'string') throw new Error(`Expected playlist url, received ${typeof url}!`);\n    let url_ = url.trim();\n    if (!url_.startsWith('https')) url_ = `https://www.youtube.com/playlist?list=${url_}`;\n    if (url_.indexOf('list=') === -1) throw new Error('This is not a Playlist URL');\n\n    if (url_.includes('music.youtube.com')) {\n        const urlObj = new URL(url_);\n        urlObj.hostname = 'www.youtube.com';\n        url_ = urlObj.toString();\n    }\n\n    const body = await request(url_, {\n        headers: {\n            'accept-language': options.language || 'en-US;q=0.9'\n        }\n    });\n    if (body.indexOf('Our systems have detected unusual traffic from your computer network.') !== -1)\n        throw new Error('Captcha page: YouTube has detected that you are a bot!');\n    const response = JSON.parse(\n        body\n            .split('var ytInitialData = ')[1]\n            .split(';</script>')[0]\n            .split(/;\\s*(var|const|let)\\s/)[0]\n    );\n    if (response.alerts) {\n        if (response.alerts[0].alertWithButtonRenderer?.type === 'INFO') {\n            if (!options.incomplete)\n                throw new Error(\n                    `While parsing playlist url\\n${response.alerts[0].alertWithButtonRenderer.text.simpleText}`\n                );\n        } else if (response.alerts[0].alertRenderer?.type === 'ERROR')\n            throw new Error(`While parsing playlist url\\n${response.alerts[0].alertRenderer.text.runs[0].text}`);\n        else throw new Error('While parsing playlist url\\nUnknown Playlist Error');\n    }\n    if (response.currentVideoEndpoint) {\n        return getWatchPlaylist(response, body, url_);\n    } else return getNormalPlaylist(response, body);\n}\n/**\n * Function to parse Playlist from YouTube search\n * @param data html data of that request\n * @param limit No. of videos to parse\n * @returns Array of YouTubeVideo.\n */\nexport function getPlaylistVideos(data: any, limit = Infinity): YouTubeVideo[] {\n    const videos = [];\n\n    for (let i = 0; i < data.length; i++) {\n        if (limit === videos.length) break;\n        const info = data[i].playlistVideoRenderer;\n        if (!info || !info.shortBylineText) continue;\n\n        videos.push(\n            new YouTubeVideo({\n                id: info.videoId,\n                duration: parseInt(info.lengthSeconds) || 0,\n                duration_raw: info.lengthText?.simpleText ?? '0:00',\n                thumbnails: info.thumbnail.thumbnails,\n                title: info.title.runs[0].text,\n                upcoming: info.upcomingEventData?.startTime\n                    ? new Date(parseInt(info.upcomingEventData.startTime) * 1000)\n                    : undefined,\n                channel: {\n                    id: info.shortBylineText.runs[0].navigationEndpoint.browseEndpoint.browseId || undefined,\n                    name: info.shortBylineText.runs[0].text || undefined,\n                    url: `https://www.youtube.com${\n                        info.shortBylineText.runs[0].navigationEndpoint.browseEndpoint.canonicalBaseUrl ||\n                        info.shortBylineText.runs[0].navigationEndpoint.commandMetadata.webCommandMetadata.url\n                    }`,\n                    icon: undefined\n                }\n            })\n        );\n    }\n    return videos;\n}\n/**\n * Function to get Continuation Token\n * @param data html data of playlist url\n * @returns token\n */\nexport function getContinuationToken(data: any): string {\n    return data.find((x: any) => Object.keys(x)[0] === 'continuationItemRenderer')?.continuationItemRenderer\n        .continuationEndpoint?.continuationCommand?.token;\n}\n\nasync function acceptViewerDiscretion(\n    videoId: string,\n    cookieJar: { [key: string]: string },\n    body: string,\n    extractRelated: boolean\n): Promise<{ streamingData: any; relatedVideos?: any }> {\n    const apiKey =\n        body.split('INNERTUBE_API_KEY\":\"')[1]?.split('\"')[0] ??\n        body.split('innertubeApiKey\":\"')[1]?.split('\"')[0] ??\n        DEFAULT_API_KEY;\n    const sessionToken =\n        body.split('\"XSRF_TOKEN\":\"')[1]?.split('\"')[0].replaceAll('\\\\u003d', '=') ??\n        body.split('\"xsrf_token\":\"')[1]?.split('\"')[0].replaceAll('\\\\u003d', '=');\n    if (!sessionToken)\n        throw new Error(`Unable to extract XSRF_TOKEN to accept the viewer discretion popup for video: ${videoId}.`);\n\n    const verificationResponse = await request(`https://www.youtube.com/youtubei/v1/verify_age?key=${apiKey}&prettyPrint=false`, {\n        method: 'POST',\n        body: JSON.stringify({\n            context: {\n                client: {\n                    utcOffsetMinutes: 0,\n                    gl: 'US',\n                    hl: 'en',\n                    clientName: 'WEB',\n                    clientVersion:\n                        body.split('\"INNERTUBE_CONTEXT_CLIENT_VERSION\":\"')[1]?.split('\"')[0] ??\n                        body.split('\"innertube_context_client_version\":\"')[1]?.split('\"')[0] ??\n                        '<some version>'\n                },\n                user: {},\n                request: {}\n            },\n            nextEndpoint: {\n                urlEndpoint: {\n                    url: `/watch?v=${videoId}&has_verified=1`\n                }\n            },\n            setControvercy: true\n        }),\n        cookies: true,\n        cookieJar\n    });\n\n    const endpoint = JSON.parse(verificationResponse).actions[0].navigateAction.endpoint;\n\n    const videoPage = await request(`https://www.youtube.com/${endpoint.urlEndpoint.url}&pbj=1`, {\n        method: 'POST',\n        headers: {\n            'Content-Type': 'application/x-www-form-urlencoded'\n        },\n        body: new URLSearchParams([\n            ['command', JSON.stringify(endpoint)],\n            ['session_token', sessionToken]\n        ]).toString(),\n        cookies: true,\n        cookieJar\n    });\n\n    if (videoPage.includes('<h1>Something went wrong</h1>'))\n        throw new Error(`Unable to accept the viewer discretion popup for video: ${videoId}`);\n\n    const videoPageData = JSON.parse(videoPage);\n\n    if (videoPageData[2].playerResponse.playabilityStatus.status !== 'OK')\n        throw new Error(\n            `While getting info from url after trying to accept the discretion popup for video ${videoId}\\n${\n                videoPageData[2].playerResponse.playabilityStatus.errorScreen.playerErrorMessageRenderer?.reason\n                    .simpleText ??\n                videoPageData[2].playerResponse.playabilityStatus.errorScreen.playerKavRenderer?.reason.simpleText\n            }`\n        );\n\n    const streamingData = videoPageData[2].playerResponse.streamingData;\n\n    if (extractRelated)\n        return {\n            streamingData,\n            relatedVideos: videoPageData[3].response.contents.twoColumnWatchNextResults.secondaryResults\n        };\n\n    return { streamingData };\n}\n\nasync function getAndroidFormats(videoId: string, cookieJar: { [key: string]: string }, body: string): Promise<any[]> {\n    const apiKey =\n        body.split('INNERTUBE_API_KEY\":\"')[1]?.split('\"')[0] ??\n        body.split('innertubeApiKey\":\"')[1]?.split('\"')[0] ??\n        DEFAULT_API_KEY;\n\n    const response = await request(`https://www.youtube.com/youtubei/v1/player?key=${apiKey}&prettyPrint=false`, {\n        method: 'POST',\n        body: JSON.stringify({\n            context: {\n                client: {\n                    clientName: 'ANDROID',\n                    clientVersion: '16.49',\n                    hl: 'en',\n                    timeZone: 'UTC',\n                    utcOffsetMinutes: 0\n                }\n            },\n            videoId: videoId,\n            playbackContext: { contentPlaybackContext: { html5Preference: 'HTML5_PREF_WANTS' } },\n            contentCheckOk: true,\n            racyCheckOk: true\n        }),\n        cookies: true,\n        cookieJar\n    });\n\n    return JSON.parse(response).streamingData.formats;\n}\n\nfunction getWatchPlaylist(response: any, body: any, url: string): YouTubePlayList {\n    const playlist_details = response.contents.twoColumnWatchNextResults.playlist?.playlist;\n    if (!playlist_details)\n        throw new Error(\"Watch playlist unavailable due to YouTube layout changes.\")\n\n    const videos = getWatchPlaylistVideos(playlist_details.contents);\n    const API_KEY =\n        body.split('INNERTUBE_API_KEY\":\"')[1]?.split('\"')[0] ??\n        body.split('innertubeApiKey\":\"')[1]?.split('\"')[0] ??\n        DEFAULT_API_KEY;\n\n    const videoCount = playlist_details.totalVideos;\n    const channel = playlist_details.shortBylineText?.runs?.[0];\n    const badge = playlist_details.badges?.[0]?.metadataBadgeRenderer?.style.toLowerCase();\n\n    return new YouTubePlayList({\n        continuation: {\n            api: API_KEY,\n            token: getContinuationToken(playlist_details.contents),\n            clientVersion:\n                body.split('\"INNERTUBE_CONTEXT_CLIENT_VERSION\":\"')[1]?.split('\"')[0] ??\n                body.split('\"innertube_context_client_version\":\"')[1]?.split('\"')[0] ??\n                '<some version>'\n        },\n        id: playlist_details.playlistId || '',\n        title: playlist_details.title || '',\n        videoCount: parseInt(videoCount) || 0,\n        videos: videos,\n        url: url,\n        channel: {\n            id: channel?.navigationEndpoint?.browseEndpoint?.browseId || null,\n            name: channel?.text || null,\n            url: `https://www.youtube.com${\n                channel?.navigationEndpoint?.browseEndpoint?.canonicalBaseUrl ||\n                channel?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url\n            }`,\n            verified: Boolean(badge?.includes('verified')),\n            artist: Boolean(badge?.includes('artist'))\n        }\n    });\n}\n\nfunction getNormalPlaylist(response: any, body: any): YouTubePlayList {\n    const json_data =\n        response.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents[0]\n            .itemSectionRenderer.contents[0].playlistVideoListRenderer.contents;\n    const playlist_details = response.sidebar.playlistSidebarRenderer.items;\n\n    const API_KEY =\n        body.split('INNERTUBE_API_KEY\":\"')[1]?.split('\"')[0] ??\n        body.split('innertubeApiKey\":\"')[1]?.split('\"')[0] ??\n        DEFAULT_API_KEY;\n    const videos = getPlaylistVideos(json_data, 100);\n\n    const data = playlist_details[0].playlistSidebarPrimaryInfoRenderer;\n    if (!data.title.runs || !data.title.runs.length) throw new Error('Failed to Parse Playlist info.');\n\n    const author = playlist_details[1]?.playlistSidebarSecondaryInfoRenderer.videoOwner;\n    const views = data.stats.length === 3 ? data.stats[1].simpleText.replace(/\\D/g, '') : 0;\n    const lastUpdate =\n        data.stats\n            .find((x: any) => 'runs' in x && x['runs'].find((y: any) => y.text.toLowerCase().includes('last update')))\n            ?.runs.pop()?.text ?? null;\n    const videosCount = data.stats[0].runs[0].text.replace(/\\D/g, '') || 0;\n\n    const res = new YouTubePlayList({\n        continuation: {\n            api: API_KEY,\n            token: getContinuationToken(json_data),\n            clientVersion:\n                body.split('\"INNERTUBE_CONTEXT_CLIENT_VERSION\":\"')[1]?.split('\"')[0] ??\n                body.split('\"innertube_context_client_version\":\"')[1]?.split('\"')[0] ??\n                '<some version>'\n        },\n        id: data.title.runs[0].navigationEndpoint.watchEndpoint.playlistId,\n        title: data.title.runs[0].text,\n        videoCount: parseInt(videosCount) || 0,\n        lastUpdate: lastUpdate,\n        views: parseInt(views) || 0,\n        videos: videos,\n        url: `https://www.youtube.com/playlist?list=${data.title.runs[0].navigationEndpoint.watchEndpoint.playlistId}`,\n        link: `https://www.youtube.com${data.title.runs[0].navigationEndpoint.commandMetadata.webCommandMetadata.url}`,\n        channel: author\n            ? {\n                  name: author.videoOwnerRenderer.title.runs[0].text,\n                  id: author.videoOwnerRenderer.title.runs[0].navigationEndpoint.browseEndpoint.browseId,\n                  url: `https://www.youtube.com${\n                      author.videoOwnerRenderer.navigationEndpoint.commandMetadata.webCommandMetadata.url ||\n                      author.videoOwnerRenderer.navigationEndpoint.browseEndpoint.canonicalBaseUrl\n                  }`,\n                  icons: author.videoOwnerRenderer.thumbnail.thumbnails ?? []\n              }\n            : {},\n        thumbnail: data.thumbnailRenderer.playlistVideoThumbnailRenderer?.thumbnail.thumbnails.length\n            ? data.thumbnailRenderer.playlistVideoThumbnailRenderer.thumbnail.thumbnails[\n                  data.thumbnailRenderer.playlistVideoThumbnailRenderer.thumbnail.thumbnails.length - 1\n              ]\n            : null\n    });\n    return res;\n}\n\nfunction getWatchPlaylistVideos(data: any, limit = Infinity): YouTubeVideo[] {\n    const videos: YouTubeVideo[] = [];\n\n    for (let i = 0; i < data.length; i++) {\n        if (limit === videos.length) break;\n        const info = data[i].playlistPanelVideoRenderer;\n        if (!info || !info.shortBylineText) continue;\n        const channel_info = info.shortBylineText.runs[0];\n\n        videos.push(\n            new YouTubeVideo({\n                id: info.videoId,\n                duration: parseDuration(info.lengthText?.simpleText) || 0,\n                duration_raw: info.lengthText?.simpleText ?? '0:00',\n                thumbnails: info.thumbnail.thumbnails,\n                title: info.title.simpleText,\n                upcoming:\n                    info.thumbnailOverlays[0].thumbnailOverlayTimeStatusRenderer?.style === 'UPCOMING' || undefined,\n                channel: {\n                    id: channel_info.navigationEndpoint.browseEndpoint.browseId || undefined,\n                    name: channel_info.text || undefined,\n                    url: `https://www.youtube.com${\n                        channel_info.navigationEndpoint.browseEndpoint.canonicalBaseUrl ||\n                        channel_info.navigationEndpoint.commandMetadata.webCommandMetadata.url\n                    }`,\n                    icon: undefined\n                }\n            })\n        );\n    }\n\n    return videos;\n}\n\nfunction parseDuration(text: string): number {\n    if (!text) return 0;\n    const split = text.split(':');\n\n    switch (split.length) {\n        case 2:\n            return parseInt(split[0]) * 60 + parseInt(split[1]);\n\n        case 3:\n            return parseInt(split[0]) * 60 * 60 + parseInt(split[1]) * 60 + parseInt(split[2]);\n\n        default:\n            return 0;\n    }\n}","import { WebmElements, WebmHeader } from 'play-audio';\nimport { Duplex, DuplexOptions } from 'node:stream';\n\nenum DataType {\n    master,\n    string,\n    uint,\n    binary,\n    float\n}\n\nexport enum WebmSeekerState {\n    READING_HEAD = 'READING_HEAD',\n    READING_DATA = 'READING_DATA'\n}\n\ninterface WebmSeekerOptions extends DuplexOptions {\n    mode?: 'precise' | 'granular';\n}\n\nconst WEB_ELEMENT_KEYS = Object.keys(WebmElements);\n\nexport class WebmSeeker extends Duplex {\n    remaining?: Buffer;\n    state: WebmSeekerState;\n    chunk?: Buffer;\n    cursor: number;\n    header: WebmHeader;\n    headfound: boolean;\n    headerparsed: boolean;\n    seekfound: boolean;\n    private data_size: number;\n    private offset: number;\n    private data_length: number;\n    private sec: number;\n    private time: number;\n\n    constructor(sec: number, options: WebmSeekerOptions) {\n        super(options);\n        this.state = WebmSeekerState.READING_HEAD;\n        this.cursor = 0;\n        this.header = new WebmHeader();\n        this.headfound = false;\n        this.headerparsed = false;\n        this.seekfound = false;\n        this.data_length = 0;\n        this.data_size = 0;\n        this.offset = 0;\n        this.sec = sec;\n        this.time = Math.floor(sec / 10) * 10;\n    }\n\n    private get vint_length(): number {\n        let i = 0;\n        for (; i < 8; i++) {\n            if ((1 << (7 - i)) & this.chunk![this.cursor]) break;\n        }\n        return ++i;\n    }\n\n    private vint_value(): boolean {\n        if (!this.chunk) return false;\n        const length = this.vint_length;\n        if (this.chunk.length < this.cursor + length) return false;\n        let value = this.chunk[this.cursor] & ((1 << (8 - length)) - 1);\n        for (let i = this.cursor + 1; i < this.cursor + length; i++) value = (value << 8) + this.chunk[i];\n        this.data_size = length;\n        this.data_length = value;\n        return true;\n    }\n\n    cleanup() {\n        this.cursor = 0;\n        this.chunk = undefined;\n        this.remaining = undefined;\n    }\n\n    _read() {}\n\n    seek(content_length: number): Error | number {\n        let clusterlength = 0,\n            position = 0;\n        let time_left = (this.sec - this.time) * 1000 || 0;\n        time_left = Math.round(time_left / 20) * 20;\n        if (!this.header.segment.cues) return new Error('Failed to Parse Cues');\n\n        for (let i = 0; i < this.header.segment.cues.length; i++) {\n            const data = this.header.segment.cues[i];\n            if (Math.floor((data.time as number) / 1000) === this.time) {\n                position = data.position as number;\n                clusterlength = (this.header.segment.cues[i + 1]?.position || content_length) - position - 1;\n                break;\n            } else continue;\n        }\n        if (clusterlength === 0) return position;\n        return this.offset + Math.round(position + (time_left / 20) * (clusterlength / 500));\n    }\n\n    _write(chunk: Buffer, _: BufferEncoding, callback: (error?: Error | null) => void): void {\n        if (this.remaining) {\n            this.chunk = Buffer.concat([this.remaining, chunk]);\n            this.remaining = undefined;\n        } else this.chunk = chunk;\n\n        let err: Error | undefined;\n\n        if (this.state === WebmSeekerState.READING_HEAD) err = this.readHead();\n        else if (!this.seekfound) err = this.getClosestBlock();\n        else err = this.readTag();\n\n        if (err) callback(err);\n        else callback();\n    }\n\n    private readHead(): Error | undefined {\n        if (!this.chunk) return new Error('Chunk is missing');\n\n        while (this.chunk.length > this.cursor) {\n            const oldCursor = this.cursor;\n            const id = this.vint_length;\n            if (this.chunk.length < this.cursor + id) break;\n\n            const ebmlID = this.parseEbmlID(this.chunk.slice(this.cursor, this.cursor + id).toString('hex'));\n            this.cursor += id;\n\n            if (!this.vint_value()) {\n                this.cursor = oldCursor;\n                break;\n            }\n            if (!ebmlID) {\n                this.cursor += this.data_size + this.data_length;\n                continue;\n            }\n\n            if (!this.headfound) {\n                if (ebmlID.name === 'ebml') this.headfound = true;\n                else return new Error('Failed to find EBML ID at start of stream.');\n            }\n            const data = this.chunk.slice(\n                this.cursor + this.data_size,\n                this.cursor + this.data_size + this.data_length\n            );\n            const parse = this.header.parse(ebmlID, data);\n            if (parse instanceof Error) return parse;\n\n            // stop parsing the header once we have found the correct cue\n\n            if (ebmlID.name === 'seekHead') this.offset = oldCursor;\n\n            if (\n                ebmlID.name === 'cueClusterPosition' &&\n                this.header.segment.cues!.length > 2 &&\n                this.time === (this.header.segment.cues!.at(-2)!.time as number) / 1000\n            )\n                this.emit('headComplete');\n\n            if (ebmlID.type === DataType.master) {\n                this.cursor += this.data_size;\n                continue;\n            }\n\n            if (this.chunk.length < this.cursor + this.data_size + this.data_length) {\n                this.cursor = oldCursor;\n                break;\n            } else this.cursor += this.data_size + this.data_length;\n        }\n        this.remaining = this.chunk.slice(this.cursor);\n        this.cursor = 0;\n    }\n\n    private readTag(): Error | undefined {\n        if (!this.chunk) return new Error('Chunk is missing');\n\n        while (this.chunk.length > this.cursor) {\n            const oldCursor = this.cursor;\n            const id = this.vint_length;\n            if (this.chunk.length < this.cursor + id) break;\n\n            const ebmlID = this.parseEbmlID(this.chunk.slice(this.cursor, this.cursor + id).toString('hex'));\n            this.cursor += id;\n\n            if (!this.vint_value()) {\n                this.cursor = oldCursor;\n                break;\n            }\n            if (!ebmlID) {\n                this.cursor += this.data_size + this.data_length;\n                continue;\n            }\n\n            const data = this.chunk.slice(\n                this.cursor + this.data_size,\n                this.cursor + this.data_size + this.data_length\n            );\n            const parse = this.header.parse(ebmlID, data);\n            if (parse instanceof Error) return parse;\n\n            if (ebmlID.type === DataType.master) {\n                this.cursor += this.data_size;\n                continue;\n            }\n\n            if (this.chunk.length < this.cursor + this.data_size + this.data_length) {\n                this.cursor = oldCursor;\n                break;\n            } else this.cursor += this.data_size + this.data_length;\n\n            if (ebmlID.name === 'simpleBlock') {\n                const track = this.header.segment.tracks![this.header.audioTrack];\n                if (!track || track.trackType !== 2) return new Error('No audio Track in this webm file.');\n                if ((data[0] & 0xf) === track.trackNumber) this.push(data.slice(4));\n            }\n        }\n        this.remaining = this.chunk.slice(this.cursor);\n        this.cursor = 0;\n    }\n\n    private getClosestBlock(): Error | undefined {\n        if (this.sec === 0) {\n            this.seekfound = true;\n            return this.readTag();\n        }\n        if (!this.chunk) return new Error('Chunk is missing');\n        this.cursor = 0;\n        let positionFound = false;\n        while (!positionFound && this.cursor < this.chunk.length) {\n            this.cursor = this.chunk.indexOf('a3', this.cursor, 'hex');\n            if (this.cursor === -1) return new Error('Failed to find nearest Block.');\n            this.cursor++;\n            if (!this.vint_value()) return new Error('Failed to find correct simpleBlock in first chunk');\n            if (this.cursor + this.data_length + this.data_length > this.chunk.length) continue;\n            const data = this.chunk.slice(\n                this.cursor + this.data_size,\n                this.cursor + this.data_size + this.data_length\n            );\n            const track = this.header.segment.tracks![this.header.audioTrack];\n            if (!track || track.trackType !== 2) return new Error('No audio Track in this webm file.');\n            if ((data[0] & 0xf) === track.trackNumber) {\n                this.cursor += this.data_size + this.data_length;\n                this.push(data.slice(4));\n                positionFound = true;\n            } else continue;\n        }\n        if (!positionFound) return new Error('Failed to find nearest correct simple Block.');\n        this.seekfound = true;\n        return this.readTag();\n    }\n\n    private parseEbmlID(ebmlID: string) {\n        if (WEB_ELEMENT_KEYS.includes(ebmlID)) return WebmElements[ebmlID];\n        else return false;\n    }\n\n    _destroy(error: Error | null, callback: (error: Error | null) => void): void {\n        this.cleanup();\n        callback(error);\n    }\n\n    _final(callback: (error?: Error | null) => void): void {\n        this.cleanup();\n        callback();\n    }\n}\n","import { IncomingMessage } from 'node:http';\nimport { request_stream } from '../../Request';\nimport { parseAudioFormats, StreamOptions, StreamType } from '../stream';\nimport { video_stream_info } from '../utils/extractor';\nimport { Timer } from './LiveStream';\nimport { WebmSeeker, WebmSeekerState } from './WebmSeeker';\n\n/**\n * YouTube Stream Class for seeking audio to a timeStamp.\n */\nexport class SeekStream {\n    /**\n     * WebmSeeker Stream through which data passes\n     */\n    stream: WebmSeeker;\n    /**\n     * Type of audio data that we recieved from normal youtube url.\n     */\n    type: StreamType;\n    /**\n     * Audio Endpoint Format Url to get data from.\n     */\n    private url: string;\n    /**\n     * Used to calculate no of bytes data that we have recieved\n     */\n    private bytes_count: number;\n    /**\n     * Calculate per second bytes by using contentLength (Total bytes) / Duration (in seconds)\n     */\n    private per_sec_bytes: number;\n    /**\n     * Length of the header in bytes\n     */\n    private header_length: number;\n    /**\n     * Total length of audio file in bytes\n     */\n    private content_length: number;\n    /**\n     * YouTube video url. [ Used only for retrying purposes only. ]\n     */\n    private video_url: string;\n    /**\n     * Timer for looping data every 265 seconds.\n     */\n    private timer: Timer;\n    /**\n     * Quality given by user. [ Used only for retrying purposes only. ]\n     */\n    private quality: number;\n    /**\n     * Incoming message that we recieve.\n     *\n     * Storing this is essential.\n     * This helps to destroy the TCP connection completely if you stopped player in between the stream\n     */\n    private request: IncomingMessage | null;\n    /**\n     * YouTube Stream Class constructor\n     * @param url Audio Endpoint url.\n     * @param type Type of Stream\n     * @param duration Duration of audio playback [ in seconds ]\n     * @param headerLength Length of the header in bytes.\n     * @param contentLength Total length of Audio file in bytes.\n     * @param bitrate Bitrate provided by YouTube.\n     * @param video_url YouTube video url.\n     * @param options Options provided to stream function.\n     */\n    constructor(\n        url: string,\n        duration: number,\n        headerLength: number,\n        contentLength: number,\n        bitrate: number,\n        video_url: string,\n        options: StreamOptions\n    ) {\n        this.stream = new WebmSeeker(options.seek!, {\n            highWaterMark: 5 * 1000 * 1000,\n            readableObjectMode: true\n        });\n        this.url = url;\n        this.quality = options.quality as number;\n        this.type = StreamType.Opus;\n        this.bytes_count = 0;\n        this.video_url = video_url;\n        this.per_sec_bytes = bitrate ? Math.ceil(bitrate / 8) : Math.ceil(contentLength / duration);\n        this.header_length = headerLength;\n        this.content_length = contentLength;\n        this.request = null;\n        this.timer = new Timer(() => {\n            this.timer.reuse();\n            this.loop();\n        }, 265);\n        this.stream.on('close', () => {\n            this.timer.destroy();\n            this.cleanup();\n        });\n        this.seek();\n    }\n    /**\n     * **INTERNAL Function**\n     *\n     * Uses stream functions to parse Webm Head and gets Offset byte to seek to.\n     * @returns Nothing\n     */\n    private async seek(): Promise<void> {\n        const parse = await new Promise(async (res, rej) => {\n            if (!this.stream.headerparsed) {\n                const stream = await request_stream(this.url, {\n                    headers: {\n                        range: `bytes=0-${this.header_length}`\n                    }\n                }).catch((err: Error) => err);\n\n                if (stream instanceof Error) {\n                    rej(stream);\n                    return;\n                }\n                if (Number(stream.statusCode) >= 400) {\n                    rej(400);\n                    return;\n                }\n                this.request = stream;\n                stream.pipe(this.stream, { end: false });\n\n                // headComplete should always be called, leaving this here just in case\n                stream.once('end', () => {\n                    this.stream.state = WebmSeekerState.READING_DATA;\n                    res('');\n                });\n\n                this.stream.once('headComplete', () => {\n                    stream.unpipe(this.stream);\n                    stream.destroy();\n                    this.stream.state = WebmSeekerState.READING_DATA;\n                    res('');\n                });\n            } else res('');\n        }).catch((err) => err);\n        if (parse instanceof Error) {\n            this.stream.emit('error', parse);\n            this.bytes_count = 0;\n            this.per_sec_bytes = 0;\n            this.cleanup();\n            return;\n        } else if (parse === 400) {\n            await this.retry();\n            this.timer.reuse();\n            return this.seek();\n        }\n        const bytes = this.stream.seek(this.content_length);\n        if (bytes instanceof Error) {\n            this.stream.emit('error', bytes);\n            this.bytes_count = 0;\n            this.per_sec_bytes = 0;\n            this.cleanup();\n            return;\n        }\n\n        this.stream.seekfound = false;\n        this.bytes_count = bytes;\n        this.timer.reuse();\n        this.loop();\n    }\n    /**\n     * Retry if we get 404 or 403 Errors.\n     */\n    private async retry() {\n        const info = await video_stream_info(this.video_url);\n        const audioFormat = parseAudioFormats(info.format);\n        this.url = audioFormat[this.quality].url;\n    }\n    /**\n     * This cleans every used variable in class.\n     *\n     * This is used to prevent re-use of this class and helping garbage collector to collect it.\n     */\n    private cleanup() {\n        this.request?.destroy();\n        this.request = null;\n        this.url = '';\n    }\n    /**\n     * Getting data from audio endpoint url and passing it to stream.\n     *\n     * If 404 or 403 occurs, it will retry again.\n     */\n    private async loop() {\n        if (this.stream.destroyed) {\n            this.timer.destroy();\n            this.cleanup();\n            return;\n        }\n        const end: number = this.bytes_count + this.per_sec_bytes * 300;\n        const stream = await request_stream(this.url, {\n            headers: {\n                range: `bytes=${this.bytes_count}-${end >= this.content_length ? '' : end}`\n            }\n        }).catch((err: Error) => err);\n        if (stream instanceof Error) {\n            this.stream.emit('error', stream);\n            this.bytes_count = 0;\n            this.per_sec_bytes = 0;\n            this.cleanup();\n            return;\n        }\n        if (Number(stream.statusCode) >= 400) {\n            this.cleanup();\n            await this.retry();\n            this.timer.reuse();\n            this.loop();\n            return;\n        }\n        this.request = stream;\n        stream.pipe(this.stream, { end: false });\n\n        stream.once('error', async () => {\n            this.cleanup();\n            await this.retry();\n            this.timer.reuse();\n            this.loop();\n        });\n\n        stream.on('data', (chunk: any) => {\n            this.bytes_count += chunk.length;\n        });\n\n        stream.on('end', () => {\n            if (end >= this.content_length) {\n                this.timer.destroy();\n                this.stream.end();\n                this.cleanup();\n            }\n        });\n    }\n    /**\n     * Pauses timer.\n     * Stops running of loop.\n     *\n     * Useful if you don't want to get excess data to be stored in stream.\n     */\n    pause() {\n        this.timer.pause();\n    }\n    /**\n     * Resumes timer.\n     * Starts running of loop.\n     */\n    resume() {\n        this.timer.resume();\n    }\n}\n","import { request_content_length, request_stream } from '../Request';\nimport { LiveStream, Stream } from './classes/LiveStream';\nimport { SeekStream } from './classes/SeekStream';\nimport { InfoData, StreamInfoData } from './utils/constants';\nimport { video_stream_info } from './utils/extractor';\nimport { URL } from 'node:url';\n\nexport enum StreamType {\n    Arbitrary = 'arbitrary',\n    Raw = 'raw',\n    OggOpus = 'ogg/opus',\n    WebmOpus = 'webm/opus',\n    Opus = 'opus'\n}\n\nexport interface StreamOptions {\n    seek?: number;\n    quality?: number;\n    language?: string;\n    htmldata?: boolean;\n    precache?: number;\n    discordPlayerCompatibility?: boolean;\n}\n\n/**\n * Command to find audio formats from given format array\n * @param formats Formats to search from\n * @returns Audio Formats array\n */\nexport function parseAudioFormats(formats: any[]) {\n    const result: any[] = [];\n    formats.forEach((format) => {\n        const type = format.mimeType as string;\n        if (type.startsWith('audio')) {\n            format.codec = type.split('codecs=\"')[1].split('\"')[0];\n            format.container = type.split('audio/')[1].split(';')[0];\n            result.push(format);\n        }\n    });\n    return result;\n}\n/**\n * Type for YouTube Stream\n */\nexport type YouTubeStream = Stream | LiveStream | SeekStream;\n/**\n * Stream command for YouTube\n * @param url YouTube URL\n * @param options lets you add quality for stream\n * @returns Stream class with type and stream for playing.\n */\nexport async function stream(url: string, options: StreamOptions = {}): Promise<YouTubeStream> {\n    const info = await video_stream_info(url, { htmldata: options.htmldata, language: options.language });\n    return await stream_from_info(info, options);\n}\n/**\n * Stream command for YouTube using info from video_info or decipher_info function.\n * @param info video_info data\n * @param options lets you add quality for stream\n * @returns Stream class with type and stream for playing.\n */\nexport async function stream_from_info(\n    info: InfoData | StreamInfoData,\n    options: StreamOptions = {}\n): Promise<YouTubeStream> {\n    if (info.format.length === 0)\n        throw new Error('Upcoming and premiere videos that are not currently live cannot be streamed.');\n    if (options.quality && !Number.isInteger(options.quality))\n        throw new Error(\"Quality must be set to an integer.\")\n\n    const final: any[] = [];\n    if (\n        info.LiveStreamData.isLive === true &&\n        info.LiveStreamData.dashManifestUrl !== null &&\n        info.video_details.durationInSec === 0\n    ) {\n        return new LiveStream(\n            info.LiveStreamData.dashManifestUrl,\n            info.format[info.format.length - 1].targetDurationSec as number,\n            info.video_details.url,\n            options.precache\n        );\n    }\n\n    const audioFormat = parseAudioFormats(info.format);\n    if (typeof options.quality !== 'number') options.quality = audioFormat.length - 1;\n    else if (options.quality <= 0) options.quality = 0;\n    else if (options.quality >= audioFormat.length) options.quality = audioFormat.length - 1;\n    if (audioFormat.length !== 0) final.push(audioFormat[options.quality]);\n    else final.push(info.format[info.format.length - 1]);\n    let type: StreamType =\n        final[0].codec === 'opus' && final[0].container === 'webm' ? StreamType.WebmOpus : StreamType.Arbitrary;\n    await request_stream(`https://${new URL(final[0].url).host}/generate_204`);\n    if (type === StreamType.WebmOpus) {\n        if (!options.discordPlayerCompatibility) {\n            options.seek ??= 0;\n            if (options.seek >= info.video_details.durationInSec || options.seek < 0)\n                throw new Error(`Seeking beyond limit. [ 0 - ${info.video_details.durationInSec - 1}]`);\n            return new SeekStream(\n                final[0].url,\n                info.video_details.durationInSec,\n                final[0].indexRange.end,\n                Number(final[0].contentLength),\n                Number(final[0].bitrate),\n                info.video_details.url,\n                options\n            );\n        } else if (options.seek) throw new Error('Can not seek with discordPlayerCompatibility set to true.');\n    }\n\n    let contentLength;\n    if (final[0].contentLength) {\n        contentLength = Number(final[0].contentLength);\n    } else {\n        contentLength = await request_content_length(final[0].url);\n    }\n\n    return new Stream(\n        final[0].url,\n        type,\n        info.video_details.durationInSec,\n        contentLength,\n        info.video_details.url,\n        options\n    );\n}\n","import { YouTubeVideo } from '../classes/Video';\nimport { YouTubePlayList } from '../classes/Playlist';\nimport { YouTubeChannel } from '../classes/Channel';\nimport { YouTube } from '..';\nimport { YouTubeThumbnail } from '../classes/Thumbnail';\n\nconst BLURRED_THUMBNAILS = [\n    '-oaymwEpCOADEI4CSFryq4qpAxsIARUAAAAAGAElAADIQj0AgKJDeAHtAZmZGUI=',\n    '-oaymwEiCOADEI4CSFXyq4qpAxQIARUAAIhCGAFwAcABBu0BmZkZQg==',\n    '-oaymwEiCOgCEMoBSFXyq4qpAxQIARUAAIhCGAFwAcABBu0BZmbmQQ==',\n    '-oaymwEiCNAFEJQDSFXyq4qpAxQIARUAAIhCGAFwAcABBu0BZmZmQg==',\n    '-oaymwEdCNAFEJQDSFryq4qpAw8IARUAAIhCGAHtAWZmZkI=',\n    '-oaymwEdCNACELwBSFryq4qpAw8IARUAAIhCGAHtAT0K10E='\n];\n\nexport interface ParseSearchInterface {\n    type?: 'video' | 'playlist' | 'channel';\n    limit?: number;\n    language?: string;\n    unblurNSFWThumbnails?: boolean;\n}\n\nexport interface thumbnail {\n    width: string;\n    height: string;\n    url: string;\n}\n/**\n * Main command which converts html body data and returns the type of data requested.\n * @param html body of that request\n * @param options limit & type of YouTube search you want.\n * @returns Array of one of YouTube type.\n */\nexport function ParseSearchResult(html: string, options?: ParseSearchInterface): YouTube[] {\n    if (!html) throw new Error(\"Can't parse Search result without data\");\n    if (!options) options = { type: 'video', limit: 0 };\n    else if (!options.type) options.type = 'video';\n    const hasLimit = typeof options.limit === 'number' && options.limit > 0;\n    options.unblurNSFWThumbnails ??= false;\n\n    const data = html\n        .split('var ytInitialData = ')?.[1]\n        ?.split(';</script>')[0]\n        .split(/;\\s*(var|const|let)\\s/)[0];\n    const json_data = JSON.parse(data);\n    const results = [];\n    const details =\n        json_data.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents.flatMap(\n            (s: any) => s.itemSectionRenderer?.contents\n        );\n    for (const detail of details) {\n        if (hasLimit && results.length === options.limit) break;\n        if (!detail || (!detail.videoRenderer && !detail.channelRenderer && !detail.playlistRenderer)) continue;\n        switch (options.type) {\n            case 'video': {\n                const parsed = parseVideo(detail);\n                if (parsed) {\n                    if (options.unblurNSFWThumbnails) parsed.thumbnails.forEach(unblurThumbnail);\n                    results.push(parsed);\n                }\n                break;\n            }\n            case 'channel': {\n                const parsed = parseChannel(detail);\n                if (parsed) results.push(parsed);\n                break;\n            }\n            case 'playlist': {\n                const parsed = parsePlaylist(detail);\n                if (parsed) {\n                    if (options.unblurNSFWThumbnails && parsed.thumbnail) unblurThumbnail(parsed.thumbnail);\n                    results.push(parsed);\n                }\n                break;\n            }\n            default:\n                throw new Error(`Unknown search type: ${options.type}`);\n        }\n    }\n    return results;\n}\n/**\n * Function to convert [hour : minutes : seconds] format to seconds\n * @param duration hour : minutes : seconds format\n * @returns seconds\n */\nfunction parseDuration(duration: string): number {\n    if (!duration) return 0;\n    const args = duration.split(':');\n    let dur = 0;\n\n    switch (args.length) {\n        case 3:\n            dur = parseInt(args[0]) * 60 * 60 + parseInt(args[1]) * 60 + parseInt(args[2]);\n            break;\n        case 2:\n            dur = parseInt(args[0]) * 60 + parseInt(args[1]);\n            break;\n        default:\n            dur = parseInt(args[0]);\n    }\n\n    return dur;\n}\n/**\n * Function to parse Channel searches\n * @param data body of that channel request.\n * @returns YouTubeChannel class\n */\nexport function parseChannel(data?: any): YouTubeChannel {\n    if (!data || !data.channelRenderer) throw new Error('Failed to Parse YouTube Channel');\n    const badge = data.channelRenderer.ownerBadges?.[0]?.metadataBadgeRenderer?.style?.toLowerCase();\n    const url = `https://www.youtube.com${\n        data.channelRenderer.navigationEndpoint.browseEndpoint.canonicalBaseUrl ||\n        data.channelRenderer.navigationEndpoint.commandMetadata.webCommandMetadata.url\n    }`;\n    const thumbnail = data.channelRenderer.thumbnail.thumbnails[data.channelRenderer.thumbnail.thumbnails.length - 1];\n    const res = new YouTubeChannel({\n        id: data.channelRenderer.channelId,\n        name: data.channelRenderer.title.simpleText,\n        icon: {\n            url: thumbnail.url.replace('//', 'https://'),\n            width: thumbnail.width,\n            height: thumbnail.height\n        },\n        url: url,\n        verified: Boolean(badge?.includes('verified')),\n        artist: Boolean(badge?.includes('artist')),\n        subscribers: data.channelRenderer.subscriberCountText?.simpleText ?? '0 subscribers'\n    });\n\n    return res;\n}\n/**\n * Function to parse Video searches\n * @param data body of that video request.\n * @returns YouTubeVideo class\n */\nexport function parseVideo(data?: any): YouTubeVideo {\n    if (!data || !data.videoRenderer) throw new Error('Failed to Parse YouTube Video');\n\n    const channel = data.videoRenderer.ownerText.runs[0];\n    const badge = data.videoRenderer.ownerBadges?.[0]?.metadataBadgeRenderer?.style?.toLowerCase();\n    const durationText = data.videoRenderer.lengthText;\n    const res = new YouTubeVideo({\n        id: data.videoRenderer.videoId,\n        url: `https://www.youtube.com/watch?v=${data.videoRenderer.videoId}`,\n        title: data.videoRenderer.title.runs[0].text,\n        description: data.videoRenderer.detailedMetadataSnippets?.[0].snippetText.runs?.length\n            ? data.videoRenderer.detailedMetadataSnippets[0].snippetText.runs.map((run: any) => run.text).join('')\n            : '',\n        duration: durationText ? parseDuration(durationText.simpleText) : 0,\n        duration_raw: durationText ? durationText.simpleText : null,\n        thumbnails: data.videoRenderer.thumbnail.thumbnails,\n        channel: {\n            id: channel.navigationEndpoint.browseEndpoint.browseId || null,\n            name: channel.text || null,\n            url: `https://www.youtube.com${\n                channel.navigationEndpoint.browseEndpoint.canonicalBaseUrl ||\n                channel.navigationEndpoint.commandMetadata.webCommandMetadata.url\n            }`,\n            icons: data.videoRenderer.channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer.thumbnail\n                .thumbnails,\n            verified: Boolean(badge?.includes('verified')),\n            artist: Boolean(badge?.includes('artist'))\n        },\n        uploadedAt: data.videoRenderer.publishedTimeText?.simpleText ?? null,\n        upcoming: data.videoRenderer.upcomingEventData?.startTime\n            ? new Date(parseInt(data.videoRenderer.upcomingEventData.startTime) * 1000)\n            : undefined,\n        views: data.videoRenderer.viewCountText?.simpleText?.replace(/\\D/g, '') ?? 0,\n        live: durationText ? false : true\n    });\n\n    return res;\n}\n/**\n * Function to parse Playlist searches\n * @param data body of that playlist request.\n * @returns YouTubePlaylist class\n */\nexport function parsePlaylist(data?: any): YouTubePlayList {\n    if (!data || !data.playlistRenderer) throw new Error('Failed to Parse YouTube Playlist');\n\n    const thumbnail =\n        data.playlistRenderer.thumbnails[0].thumbnails[data.playlistRenderer.thumbnails[0].thumbnails.length - 1];\n    const channel = data.playlistRenderer.shortBylineText.runs?.[0];\n\n    const res = new YouTubePlayList(\n        {\n            id: data.playlistRenderer.playlistId,\n            title: data.playlistRenderer.title.simpleText,\n            thumbnail: {\n                id: data.playlistRenderer.playlistId,\n                url: thumbnail.url,\n                height: thumbnail.height,\n                width: thumbnail.width\n            },\n            channel: {\n                id: channel?.navigationEndpoint.browseEndpoint.browseId,\n                name: channel?.text,\n                url: `https://www.youtube.com${channel?.navigationEndpoint.commandMetadata.webCommandMetadata.url}`\n            },\n            videos: parseInt(data.playlistRenderer.videoCount.replace(/\\D/g, ''))\n        },\n        true\n    );\n\n    return res;\n}\n\nfunction unblurThumbnail(thumbnail: YouTubeThumbnail) {\n    if (BLURRED_THUMBNAILS.find((sqp) => thumbnail.url.includes(sqp))) {\n        thumbnail.url = thumbnail.url.split('?')[0];\n\n        // we need to update the size parameters as the sqp parameter also included a cropped size\n        switch (thumbnail.url.split('/').at(-1)!.split('.')[0]) {\n            case 'hq2':\n            case 'hqdefault':\n                thumbnail.width = 480;\n                thumbnail.height = 360;\n                break;\n            case 'hq720':\n                thumbnail.width = 1280;\n                thumbnail.height = 720;\n                break;\n            case 'sddefault':\n                thumbnail.width = 640;\n                thumbnail.height = 480;\n                break;\n            case 'mqdefault':\n                thumbnail.width = 320;\n                thumbnail.height = 180;\n                break;\n            case 'default':\n                thumbnail.width = 120;\n                thumbnail.height = 90;\n                break;\n            default:\n                thumbnail.width = thumbnail.height = NaN;\n        }\n    }\n}\n","import { request } from './../Request';\nimport { ParseSearchInterface, ParseSearchResult } from './utils/parser';\nimport { YouTubeVideo } from './classes/Video';\nimport { YouTubeChannel } from './classes/Channel';\nimport { YouTubePlayList } from './classes/Playlist';\n\nenum SearchType {\n    Video = 'EgIQAQ%253D%253D',\n    PlayList = 'EgIQAw%253D%253D',\n    Channel = 'EgIQAg%253D%253D'\n}\n\n/**\n * Type for YouTube returns\n */\nexport type YouTube = YouTubeVideo | YouTubeChannel | YouTubePlayList;\n/**\n * Command to search from YouTube\n * @param search The query to search\n * @param options limit & type of YouTube search you want.\n * @returns YouTube type.\n */\nexport async function yt_search(search: string, options: ParseSearchInterface = {}): Promise<YouTube[]> {\n    let url = 'https://www.youtube.com/results?search_query=' + search;\n    options.type ??= 'video';\n    if (url.indexOf('&sp=') === -1) {\n        url += '&sp=';\n        switch (options.type) {\n            case 'channel':\n                url += SearchType.Channel;\n                break;\n            case 'playlist':\n                url += SearchType.PlayList;\n                break;\n            case 'video':\n                url += SearchType.Video;\n                break;\n            default:\n                throw new Error(`Unknown search type: ${options.type}`);\n        }\n    }\n    const body = await request(url, {\n        headers: {\n            'accept-language': options.language || 'en-US;q=0.9'\n        }\n    });\n    if (body.indexOf('Our systems have detected unusual traffic from your computer network.') !== -1)\n        throw new Error('Captcha page: YouTube has detected that you are a bot!');\n    return ParseSearchResult(body, options);\n}\n","import { request } from '../Request';\nimport { SpotifyDataOptions } from '.';\nimport { AlbumJSON, PlaylistJSON, TrackJSON } from './constants';\n\nexport interface SpotifyTrackAlbum {\n    /**\n     * Spotify Track Album name\n     */\n    name: string;\n    /**\n     * Spotify Track Album url\n     */\n    url: string;\n    /**\n     * Spotify Track Album id\n     */\n    id: string;\n    /**\n     * Spotify Track Album release date\n     */\n    release_date: string;\n    /**\n     * Spotify Track Album release date **precise**\n     */\n    release_date_precision: string;\n    /**\n     * Spotify Track Album total tracks number\n     */\n    total_tracks: number;\n}\n\nexport interface SpotifyArtists {\n    /**\n     * Spotify Artist Name\n     */\n    name: string;\n    /**\n     * Spotify Artist Url\n     */\n    url: string;\n    /**\n     * Spotify Artist ID\n     */\n    id: string;\n}\n\nexport interface SpotifyThumbnail {\n    /**\n     * Spotify Thumbnail height\n     */\n    height: number;\n    /**\n     * Spotify Thumbnail width\n     */\n    width: number;\n    /**\n     * Spotify Thumbnail url\n     */\n    url: string;\n}\n\nexport interface SpotifyCopyright {\n    /**\n     * Spotify Copyright Text\n     */\n    text: string;\n    /**\n     * Spotify Copyright Type\n     */\n    type: string;\n}\n/**\n * Spotify Track Class\n */\nexport class SpotifyTrack {\n    /**\n     * Spotify Track Name\n     */\n    name: string;\n    /**\n     * Spotify Class type. == \"track\"\n     */\n    type: 'track' | 'playlist' | 'album';\n    /**\n     * Spotify Track ID\n     */\n    id: string;\n    /**\n     * Spotify Track ISRC\n     */\n    isrc: string;\n    /**\n     * Spotify Track url\n     */\n    url: string;\n    /**\n     * Spotify Track explicit info.\n     */\n    explicit: boolean;\n    /**\n     * Spotify Track playability info.\n     */\n    playable: boolean;\n    /**\n     * Spotify Track Duration in seconds\n     */\n    durationInSec: number;\n    /**\n     * Spotify Track Duration in milli seconds\n     */\n    durationInMs: number;\n    /**\n     * Spotify Track Artists data [ array ]\n     */\n    artists: SpotifyArtists[];\n    /**\n     * Spotify Track Album data\n     */\n    album: SpotifyTrackAlbum | undefined;\n    /**\n     * Spotify Track Thumbnail Data\n     */\n    thumbnail: SpotifyThumbnail | undefined;\n    /**\n     * Constructor for Spotify Track\n     * @param data\n     */\n    constructor(data: any) {\n        this.name = data.name;\n        this.id = data.id;\n        this.isrc = data.external_ids?.isrc || '';\n        this.type = 'track';\n        this.url = data.external_urls.spotify;\n        this.explicit = data.explicit;\n        this.playable = data.is_playable;\n        this.durationInMs = data.duration_ms;\n        this.durationInSec = Math.round(this.durationInMs / 1000);\n        const artists: SpotifyArtists[] = [];\n        data.artists.forEach((v: any) => {\n            artists.push({\n                name: v.name,\n                id: v.id,\n                url: v.external_urls.spotify\n            });\n        });\n        this.artists = artists;\n        if (!data.album?.name) this.album = undefined;\n        else {\n            this.album = {\n                name: data.album.name,\n                url: data.external_urls.spotify,\n                id: data.album.id,\n                release_date: data.album.release_date,\n                release_date_precision: data.album.release_date_precision,\n                total_tracks: data.album.total_tracks\n            };\n        }\n        if (!data.album?.images?.[0]) this.thumbnail = undefined;\n        else this.thumbnail = data.album.images[0];\n    }\n\n    toJSON(): TrackJSON {\n        return {\n            name: this.name,\n            id: this.id,\n            url: this.url,\n            explicit: this.explicit,\n            durationInMs: this.durationInMs,\n            durationInSec: this.durationInSec,\n            artists: this.artists,\n            album: this.album,\n            thumbnail: this.thumbnail\n        };\n    }\n}\n/**\n * Spotify Playlist Class\n */\nexport class SpotifyPlaylist {\n    /**\n     * Spotify Playlist Name\n     */\n    name: string;\n    /**\n     * Spotify Class type. == \"playlist\"\n     */\n    type: 'track' | 'playlist' | 'album';\n    /**\n     * Spotify Playlist collaborative boolean.\n     */\n    collaborative: boolean;\n    /**\n     * Spotify Playlist Description\n     */\n    description: string;\n    /**\n     * Spotify Playlist URL\n     */\n    url: string;\n    /**\n     * Spotify Playlist ID\n     */\n    id: string;\n    /**\n     * Spotify Playlist Thumbnail Data\n     */\n    thumbnail: SpotifyThumbnail;\n    /**\n     * Spotify Playlist Owner Artist data\n     */\n    owner: SpotifyArtists;\n    /**\n     * Spotify Playlist total tracks Count\n     */\n    tracksCount: number;\n    /**\n     * Spotify Playlist Spotify data\n     *\n     * @private\n     */\n    private spotifyData: SpotifyDataOptions;\n    /**\n     * Spotify Playlist fetched tracks Map\n     *\n     * @private\n     */\n    private fetched_tracks: Map<string, SpotifyTrack[]>;\n    /**\n     * Boolean to tell whether it is a searched result or not.\n     */\n    private readonly search: boolean;\n    /**\n     * Constructor for Spotify Playlist Class\n     * @param data JSON parsed data of playlist\n     * @param spotifyData Data about sporify token for furhter fetching.\n     */\n    constructor(data: any, spotifyData: SpotifyDataOptions, search: boolean) {\n        this.name = data.name;\n        this.type = 'playlist';\n        this.search = search;\n        this.collaborative = data.collaborative;\n        this.description = data.description;\n        this.url = data.external_urls.spotify;\n        this.id = data.id;\n        this.thumbnail = data.images[0];\n        this.owner = {\n            name: data.owner.display_name,\n            url: data.owner.external_urls.spotify,\n            id: data.owner.id\n        };\n        this.tracksCount = Number(data.tracks.total);\n        const videos: SpotifyTrack[] = [];\n        if (!this.search)\n            data.tracks.items.forEach((v: any) => {\n                if (v.track) videos.push(new SpotifyTrack(v.track));\n            });\n        this.fetched_tracks = new Map();\n        this.fetched_tracks.set('1', videos);\n        this.spotifyData = spotifyData;\n    }\n    /**\n     * Fetches Spotify Playlist tracks more than 100 tracks.\n     *\n     * For getting all tracks in playlist, see `total_pages` property.\n     * @returns Playlist Class.\n     */\n    async fetch() {\n        if (this.search) return this;\n        let fetching: number;\n        if (this.tracksCount > 1000) fetching = 1000;\n        else fetching = this.tracksCount;\n        if (fetching <= 100) return this;\n        const work = [];\n        for (let i = 2; i <= Math.ceil(fetching / 100); i++) {\n            work.push(\n                new Promise(async (resolve, reject) => {\n                    const response = await request(\n                        `https://api.spotify.com/v1/playlists/${this.id}/tracks?offset=${\n                            (i - 1) * 100\n                        }&limit=100&market=${this.spotifyData.market}`,\n                        {\n                            headers: {\n                                Authorization: `${this.spotifyData.token_type} ${this.spotifyData.access_token}`\n                            }\n                        }\n                    ).catch((err) => reject(`Response Error : \\n${err}`));\n                    const videos: SpotifyTrack[] = [];\n                    if (typeof response !== 'string') return;\n                    const json_data = JSON.parse(response);\n                    json_data.items.forEach((v: any) => {\n                        if (v.track) videos.push(new SpotifyTrack(v.track));\n                    });\n                    this.fetched_tracks.set(`${i}`, videos);\n                    resolve('Success');\n                })\n            );\n        }\n        await Promise.allSettled(work);\n        return this;\n    }\n    /**\n     * Spotify Playlist tracks are divided in pages.\n     *\n     * For example getting data of 101 - 200 videos in a playlist,\n     *\n     * ```ts\n     * const playlist = await play.spotify('playlist url')\n     *\n     * await playlist.fetch()\n     *\n     * const result = playlist.page(2)\n     * ```\n     * @param num Page Number\n     * @returns\n     */\n    page(num: number) {\n        if (!num) throw new Error('Page number is not provided');\n        if (!this.fetched_tracks.has(`${num}`)) throw new Error('Given Page number is invalid');\n        return this.fetched_tracks.get(`${num}`) as SpotifyTrack[];\n    }\n    /**\n     * Gets total number of pages in that playlist class.\n     * @see {@link SpotifyPlaylist.all_tracks}\n     */\n    get total_pages() {\n        return this.fetched_tracks.size;\n    }\n    /**\n     * Spotify Playlist total no of tracks that have been fetched so far.\n     */\n    get total_tracks() {\n        if (this.search) return this.tracksCount;\n        const page_number: number = this.total_pages;\n        return (page_number - 1) * 100 + (this.fetched_tracks.get(`${page_number}`) as SpotifyTrack[]).length;\n    }\n    /**\n     * Fetches all the tracks in the playlist and returns them\n     *\n     * ```ts\n     * const playlist = await play.spotify('playlist url')\n     *\n     * const tracks = await playlist.all_tracks()\n     * ```\n     * @returns An array of {@link SpotifyTrack}\n     */\n    async all_tracks(): Promise<SpotifyTrack[]> {\n        await this.fetch();\n\n        const tracks: SpotifyTrack[] = [];\n\n        for (const page of this.fetched_tracks.values()) tracks.push(...page);\n\n        return tracks;\n    }\n    /**\n     * Converts Class to JSON\n     * @returns JSON data\n     */\n    toJSON(): PlaylistJSON {\n        return {\n            name: this.name,\n            collaborative: this.collaborative,\n            description: this.description,\n            url: this.url,\n            id: this.id,\n            thumbnail: this.thumbnail,\n            owner: this.owner,\n            tracksCount: this.tracksCount\n        };\n    }\n}\n/**\n * Spotify Album Class\n */\nexport class SpotifyAlbum {\n    /**\n     * Spotify Album Name\n     */\n    name: string;\n    /**\n     * Spotify Class type. == \"album\"\n     */\n    type: 'track' | 'playlist' | 'album';\n    /**\n     * Spotify Album url\n     */\n    url: string;\n    /**\n     * Spotify Album id\n     */\n    id: string;\n    /**\n     * Spotify Album Thumbnail data\n     */\n    thumbnail: SpotifyThumbnail;\n    /**\n     * Spotify Album artists [ array ]\n     */\n    artists: SpotifyArtists[];\n    /**\n     * Spotify Album copyright data [ array ]\n     */\n    copyrights: SpotifyCopyright[];\n    /**\n     * Spotify Album Release date\n     */\n    release_date: string;\n    /**\n     * Spotify Album Release Date **precise**\n     */\n    release_date_precision: string;\n    /**\n     * Spotify Album total no of tracks\n     */\n    tracksCount: number;\n    /**\n     * Spotify Album Spotify data\n     *\n     * @private\n     */\n    private spotifyData: SpotifyDataOptions;\n    /**\n     * Spotify Album fetched tracks Map\n     *\n     * @private\n     */\n    private fetched_tracks: Map<string, SpotifyTrack[]>;\n    /**\n     * Boolean to tell whether it is a searched result or not.\n     */\n    private readonly search: boolean;\n    /**\n     * Constructor for Spotify Album Class\n     * @param data Json parsed album data\n     * @param spotifyData Spotify credentials\n     */\n    constructor(data: any, spotifyData: SpotifyDataOptions, search: boolean) {\n        this.name = data.name;\n        this.type = 'album';\n        this.id = data.id;\n        this.search = search;\n        this.url = data.external_urls.spotify;\n        this.thumbnail = data.images[0];\n        const artists: SpotifyArtists[] = [];\n        data.artists.forEach((v: any) => {\n            artists.push({\n                name: v.name,\n                id: v.id,\n                url: v.external_urls.spotify\n            });\n        });\n        this.artists = artists;\n        this.copyrights = data.copyrights;\n        this.release_date = data.release_date;\n        this.release_date_precision = data.release_date_precision;\n        this.tracksCount = data.total_tracks;\n        const videos: SpotifyTrack[] = [];\n        if (!this.search)\n            data.tracks.items.forEach((v: any) => {\n                videos.push(new SpotifyTrack(v));\n            });\n        this.fetched_tracks = new Map();\n        this.fetched_tracks.set('1', videos);\n        this.spotifyData = spotifyData;\n    }\n    /**\n     * Fetches Spotify Album tracks more than 50 tracks.\n     *\n     * For getting all tracks in album, see `total_pages` property.\n     * @returns Album Class.\n     */\n    async fetch() {\n        if (this.search) return this;\n        let fetching: number;\n        if (this.tracksCount > 500) fetching = 500;\n        else fetching = this.tracksCount;\n        if (fetching <= 50) return this;\n        const work = [];\n        for (let i = 2; i <= Math.ceil(fetching / 50); i++) {\n            work.push(\n                new Promise(async (resolve, reject) => {\n                    const response = await request(\n                        `https://api.spotify.com/v1/albums/${this.id}/tracks?offset=${(i - 1) * 50}&limit=50&market=${\n                            this.spotifyData.market\n                        }`,\n                        {\n                            headers: {\n                                Authorization: `${this.spotifyData.token_type} ${this.spotifyData.access_token}`\n                            }\n                        }\n                    ).catch((err) => reject(`Response Error : \\n${err}`));\n                    const videos: SpotifyTrack[] = [];\n                    if (typeof response !== 'string') return;\n                    const json_data = JSON.parse(response);\n                    json_data.items.forEach((v: any) => {\n                        if (v) videos.push(new SpotifyTrack(v));\n                    });\n                    this.fetched_tracks.set(`${i}`, videos);\n                    resolve('Success');\n                })\n            );\n        }\n        await Promise.allSettled(work);\n        return this;\n    }\n    /**\n     * Spotify Album tracks are divided in pages.\n     *\n     * For example getting data of 51 - 100 videos in a album,\n     *\n     * ```ts\n     * const album = await play.spotify('album url')\n     *\n     * await album.fetch()\n     *\n     * const result = album.page(2)\n     * ```\n     * @param num Page Number\n     * @returns\n     */\n    page(num: number) {\n        if (!num) throw new Error('Page number is not provided');\n        if (!this.fetched_tracks.has(`${num}`)) throw new Error('Given Page number is invalid');\n        return this.fetched_tracks.get(`${num}`);\n    }\n    /**\n     * Gets total number of pages in that album class.\n     * @see {@link SpotifyAlbum.all_tracks}\n     */\n    get total_pages() {\n        return this.fetched_tracks.size;\n    }\n    /**\n     * Spotify Album total no of tracks that have been fetched so far.\n     */\n    get total_tracks() {\n        if (this.search) return this.tracksCount;\n        const page_number: number = this.total_pages;\n        return (page_number - 1) * 100 + (this.fetched_tracks.get(`${page_number}`) as SpotifyTrack[]).length;\n    }\n    /**\n     * Fetches all the tracks in the album and returns them\n     *\n     * ```ts\n     * const album = await play.spotify('album url')\n     *\n     * const tracks = await album.all_tracks()\n     * ```\n     * @returns An array of {@link SpotifyTrack}\n     */\n    async all_tracks(): Promise<SpotifyTrack[]> {\n        await this.fetch();\n\n        const tracks: SpotifyTrack[] = [];\n\n        for (const page of this.fetched_tracks.values()) tracks.push(...page);\n\n        return tracks;\n    }\n    /**\n     * Converts Class to JSON\n     * @returns JSON data\n     */\n    toJSON(): AlbumJSON {\n        return {\n            name: this.name,\n            id: this.id,\n            type: this.type,\n            url: this.url,\n            thumbnail: this.thumbnail,\n            artists: this.artists,\n            copyrights: this.copyrights,\n            release_date: this.release_date,\n            release_date_precision: this.release_date_precision,\n            tracksCount: this.tracksCount\n        };\n    }\n}\n","import { request } from '../Request';\nimport { SpotifyAlbum, SpotifyPlaylist, SpotifyTrack } from './classes';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\n\nlet spotifyData: SpotifyDataOptions;\nif (existsSync('.data/spotify.data')) {\n    spotifyData = JSON.parse(readFileSync('.data/spotify.data', 'utf-8'));\n    spotifyData.file = true;\n}\n/**\n * Spotify Data options that are stored in spotify.data file.\n */\nexport interface SpotifyDataOptions {\n    client_id: string;\n    client_secret: string;\n    redirect_url?: string;\n    authorization_code?: string;\n    access_token?: string;\n    refresh_token?: string;\n    token_type?: string;\n    expires_in?: number;\n    expiry?: number;\n    market?: string;\n    file?: boolean;\n}\n\nconst pattern = /^((https:)?\\/\\/)?open\\.spotify\\.com\\/(?:intl\\-.{2}\\/)?(track|album|playlist)\\//;\n/**\n * Gets Spotify url details.\n *\n * ```ts\n * let spot = await play.spotify('spotify url')\n *\n * // spot.type === \"track\" | \"playlist\" | \"album\"\n *\n * if (spot.type === \"track\") {\n *      spot = spot as play.SpotifyTrack\n *      // Code with spotify track class.\n * }\n * ```\n * @param url Spotify Url\n * @returns A {@link SpotifyTrack} or {@link SpotifyPlaylist} or {@link SpotifyAlbum}\n */\nexport async function spotify(url: string): Promise<Spotify> {\n    if (!spotifyData) throw new Error('Spotify Data is missing\\nDid you forgot to do authorization ?');\n    const url_ = url.trim();\n    if (!url_.match(pattern)) throw new Error('This is not a Spotify URL');\n    if (url_.indexOf('track/') !== -1) {\n        const trackID = url_.split('track/')[1].split('&')[0].split('?')[0];\n        const response = await request(`https://api.spotify.com/v1/tracks/${trackID}?market=${spotifyData.market}`, {\n            headers: {\n                Authorization: `${spotifyData.token_type} ${spotifyData.access_token}`\n            }\n        }).catch((err: Error) => {\n            return err;\n        });\n        if (response instanceof Error) throw response;\n        const resObj = JSON.parse(response);\n        if (resObj.error) throw new Error(`Got ${resObj.error.status} from the spotify request: ${resObj.error.message}`);\n        return new SpotifyTrack(resObj);\n    } else if (url_.indexOf('album/') !== -1) {\n        const albumID = url.split('album/')[1].split('&')[0].split('?')[0];\n        const response = await request(`https://api.spotify.com/v1/albums/${albumID}?market=${spotifyData.market}`, {\n            headers: {\n                Authorization: `${spotifyData.token_type} ${spotifyData.access_token}`\n            }\n        }).catch((err: Error) => {\n            return err;\n        });\n        if (response instanceof Error) throw response;\n        const resObj = JSON.parse(response);\n        if (resObj.error) throw new Error(`Got ${resObj.error.status} from the spotify request: ${resObj.error.message}`);\n        return new SpotifyAlbum(resObj, spotifyData, false);\n    } else if (url_.indexOf('playlist/') !== -1) {\n        const playlistID = url.split('playlist/')[1].split('&')[0].split('?')[0];\n        const response = await request(\n            `https://api.spotify.com/v1/playlists/${playlistID}?market=${spotifyData.market}`,\n            {\n                headers: {\n                    Authorization: `${spotifyData.token_type} ${spotifyData.access_token}`\n                }\n            }\n        ).catch((err: Error) => {\n            return err;\n        });\n        if (response instanceof Error) throw response;\n        const resObj = JSON.parse(response);\n        if (resObj.error) throw new Error(`Got ${resObj.error.status} from the spotify request: ${resObj.error.message}`);\n        return new SpotifyPlaylist(resObj, spotifyData, false);\n    } else throw new Error('URL is out of scope for play-dl.');\n}\n/**\n * Validate Spotify url\n * @param url Spotify URL\n * @returns\n * ```ts\n * 'track' | 'playlist' | 'album' | 'search' | false\n * ```\n */\nexport function sp_validate(url: string): 'track' | 'playlist' | 'album' | 'search' | false {\n    const url_ = url.trim();\n    if (!url_.startsWith('https')) return 'search';\n    if (!url_.match(pattern)) return false;\n    if (url_.indexOf('track/') !== -1) {\n        return 'track';\n    } else if (url_.indexOf('album/') !== -1) {\n        return 'album';\n    } else if (url_.indexOf('playlist/') !== -1) {\n        return 'playlist';\n    } else return false;\n}\n/**\n * Fuction for authorizing for spotify data.\n * @param data Sportify Data options to validate\n * @returns boolean.\n */\nexport async function SpotifyAuthorize(data: SpotifyDataOptions, file: boolean): Promise<boolean> {\n    const response = await request(`https://accounts.spotify.com/api/token`, {\n        headers: {\n            'Authorization': `Basic ${Buffer.from(`${data.client_id}:${data.client_secret}`).toString('base64')}`,\n            'Content-Type': 'application/x-www-form-urlencoded'\n        },\n        body: `grant_type=authorization_code&code=${data.authorization_code}&redirect_uri=${encodeURI(\n            data.redirect_url as string\n        )}`,\n        method: 'POST'\n    }).catch((err: Error) => {\n        return err;\n    });\n    if (response instanceof Error) throw response;\n    const resp_json = JSON.parse(response);\n    spotifyData = {\n        client_id: data.client_id,\n        client_secret: data.client_secret,\n        redirect_url: data.redirect_url,\n        access_token: resp_json.access_token,\n        refresh_token: resp_json.refresh_token,\n        expires_in: Number(resp_json.expires_in),\n        expiry: Date.now() + (resp_json.expires_in - 1) * 1000,\n        token_type: resp_json.token_type,\n        market: data.market\n    };\n    if (file) writeFileSync('.data/spotify.data', JSON.stringify(spotifyData, undefined, 4));\n    else {\n        console.log(`Client ID : ${spotifyData.client_id}`);\n        console.log(`Client Secret : ${spotifyData.client_secret}`);\n        console.log(`Refresh Token : ${spotifyData.refresh_token}`);\n        console.log(`Market : ${spotifyData.market}`);\n        console.log(`\\nPaste above info in setToken function.`);\n    }\n    return true;\n}\n/**\n * Checks if spotify token is expired or not.\n *\n * Update token if returned false.\n * ```ts\n * if (play.is_expired()) {\n *      await play.refreshToken()\n * }\n * ```\n * @returns boolean\n */\nexport function is_expired(): boolean {\n    if (Date.now() >= (spotifyData.expiry as number)) return true;\n    else return false;\n}\n/**\n * type for Spotify Classes\n */\nexport type Spotify = SpotifyAlbum | SpotifyPlaylist | SpotifyTrack;\n/**\n * Function for searching songs on Spotify\n * @param query searching query\n * @param type \"album\" | \"playlist\" | \"track\"\n * @param limit max no of results\n * @returns Spotify type.\n */\nexport async function sp_search(\n    query: string,\n    type: 'album' | 'playlist' | 'track',\n    limit: number = 10\n): Promise<Spotify[]> {\n    const results: Spotify[] = [];\n    if (!spotifyData) throw new Error('Spotify Data is missing\\nDid you forget to do authorization ?');\n    if (query.length === 0) throw new Error('Pass some query to search.');\n    if (limit > 50 || limit < 0) throw new Error(`You crossed limit range of Spotify [ 0 - 50 ]`);\n    const response = await request(\n        `https://api.spotify.com/v1/search?type=${type}&q=${query}&limit=${limit}&market=${spotifyData.market}`,\n        {\n            headers: {\n                Authorization: `${spotifyData.token_type} ${spotifyData.access_token}`\n            }\n        }\n    ).catch((err: Error) => {\n        return err;\n    });\n    if (response instanceof Error) throw response;\n    const json_data = JSON.parse(response);\n    if (type === 'track') {\n        json_data.tracks.items.forEach((track: any) => {\n            results.push(new SpotifyTrack(track));\n        });\n    } else if (type === 'album') {\n        json_data.albums.items.forEach((album: any) => {\n            results.push(new SpotifyAlbum(album, spotifyData, true));\n        });\n    } else if (type === 'playlist') {\n        json_data.playlists.items.forEach((playlist: any) => {\n            results.push(new SpotifyPlaylist(playlist, spotifyData, true));\n        });\n    }\n    return results;\n}\n/**\n * Refreshes Token\n *\n * ```ts\n * if (play.is_expired()) {\n *      await play.refreshToken()\n * }\n * ```\n * @returns boolean\n */\nexport async function refreshToken(): Promise<boolean> {\n    const response = await request(`https://accounts.spotify.com/api/token`, {\n        headers: {\n            'Authorization': `Basic ${Buffer.from(`${spotifyData.client_id}:${spotifyData.client_secret}`).toString(\n                'base64'\n            )}`,\n            'Content-Type': 'application/x-www-form-urlencoded'\n        },\n        body: `grant_type=refresh_token&refresh_token=${spotifyData.refresh_token}`,\n        method: 'POST'\n    }).catch((err: Error) => {\n        return err;\n    });\n    if (response instanceof Error) return false;\n    const resp_json = JSON.parse(response);\n    spotifyData.access_token = resp_json.access_token;\n    spotifyData.expires_in = Number(resp_json.expires_in);\n    spotifyData.expiry = Date.now() + (resp_json.expires_in - 1) * 1000;\n    spotifyData.token_type = resp_json.token_type;\n    if (spotifyData.file) writeFileSync('.data/spotify.data', JSON.stringify(spotifyData, undefined, 4));\n    return true;\n}\n\nexport async function setSpotifyToken(options: SpotifyDataOptions) {\n    spotifyData = options;\n    spotifyData.file = false;\n    await refreshToken();\n}\n\nexport { SpotifyTrack, SpotifyAlbum, SpotifyPlaylist };\n","import { existsSync, readFileSync } from 'node:fs';\nimport { StreamType } from '../YouTube/stream';\nimport { request } from '../Request';\nimport { SoundCloudPlaylist, SoundCloudTrack, SoundCloudTrackFormat, SoundCloudStream } from './classes';\nlet soundData: SoundDataOptions;\nif (existsSync('.data/soundcloud.data')) {\n    soundData = JSON.parse(readFileSync('.data/soundcloud.data', 'utf-8'));\n}\n\ninterface SoundDataOptions {\n    client_id: string;\n}\n\nconst pattern = /^(?:(https?):\\/\\/)?(?:(?:www|m)\\.)?(api\\.soundcloud\\.com|soundcloud\\.com|snd\\.sc)\\/(.*)$/;\n/**\n * Gets info from a soundcloud url.\n *\n * ```ts\n * let sound = await play.soundcloud('soundcloud url')\n *\n * // sound.type === \"track\" | \"playlist\" | \"user\"\n *\n * if (sound.type === \"track\") {\n *      spot = spot as play.SoundCloudTrack\n *      // Code with SoundCloud track class.\n * }\n * ```\n * @param url soundcloud url\n * @returns A {@link SoundCloudTrack} or {@link SoundCloudPlaylist}\n */\nexport async function soundcloud(url: string): Promise<SoundCloud> {\n    if (!soundData) throw new Error('SoundCloud Data is missing\\nDid you forget to do authorization ?');\n    const url_ = url.trim();\n    if (!url_.match(pattern)) throw new Error('This is not a SoundCloud URL');\n\n    const data = await request(\n        `https://api-v2.soundcloud.com/resolve?url=${url_}&client_id=${soundData.client_id}`\n    ).catch((err: Error) => err);\n\n    if (data instanceof Error) throw data;\n\n    const json_data = JSON.parse(data);\n\n    if (json_data.kind !== 'track' && json_data.kind !== 'playlist')\n        throw new Error('This url is out of scope for play-dl.');\n\n    if (json_data.kind === 'track') return new SoundCloudTrack(json_data);\n    else return new SoundCloudPlaylist(json_data, soundData.client_id);\n}\n/**\n * Type of SoundCloud\n */\nexport type SoundCloud = SoundCloudTrack | SoundCloudPlaylist;\n/**\n * Function for searching in SoundCloud\n * @param query query to search\n * @param type 'tracks' | 'playlists' | 'albums'\n * @param limit max no. of results\n * @returns Array of SoundCloud type.\n */\nexport async function so_search(\n    query: string,\n    type: 'tracks' | 'playlists' | 'albums',\n    limit: number = 10\n): Promise<SoundCloud[]> {\n    const response = await request(\n        `https://api-v2.soundcloud.com/search/${type}?q=${query}&client_id=${soundData.client_id}&limit=${limit}`\n    );\n    const results: (SoundCloudPlaylist | SoundCloudTrack)[] = [];\n    const json_data = JSON.parse(response);\n    json_data.collection.forEach((x: any) => {\n        if (type === 'tracks') results.push(new SoundCloudTrack(x));\n        else results.push(new SoundCloudPlaylist(x, soundData.client_id));\n    });\n    return results;\n}\n/**\n * Main Function for creating a Stream of soundcloud\n * @param url soundcloud url\n * @param quality Quality to select from\n * @returns SoundCloud Stream\n */\nexport async function stream(url: string, quality?: number): Promise<SoundCloudStream> {\n    const data = await soundcloud(url);\n\n    if (data instanceof SoundCloudPlaylist) throw new Error(\"Streams can't be created from playlist urls\");\n\n    const HLSformats = parseHlsFormats(data.formats);\n    if (typeof quality !== 'number') quality = HLSformats.length - 1;\n    else if (quality <= 0) quality = 0;\n    else if (quality >= HLSformats.length) quality = HLSformats.length - 1;\n    const req_url = HLSformats[quality].url + '?client_id=' + soundData.client_id;\n    const s_data = JSON.parse(await request(req_url));\n    const type = HLSformats[quality].format.mime_type.startsWith('audio/ogg')\n        ? StreamType.OggOpus\n        : StreamType.Arbitrary;\n    return new SoundCloudStream(s_data.url, type);\n}\n/**\n * Gets Free SoundCloud Client ID.\n *\n * Use this in beginning of your code to add SoundCloud support.\n *\n * ```ts\n * play.getFreeClientID().then((clientID) => play.setToken({\n *      soundcloud : {\n *          client_id : clientID\n *      }\n * }))\n * ```\n * @returns client ID\n */\nexport async function getFreeClientID(): Promise<string> {\n    const data: any = await request('https://soundcloud.com/', {headers: {}}).catch(err => err);\n\n    if (data instanceof Error)\n        throw new Error(\"Failed to get response from soundcloud.com: \" + data.message);\n\n    const splitted = data.split('<script crossorigin src=\"');\n    const urls: string[] = [];\n    splitted.forEach((r: string) => {\n        if (r.startsWith('https')) {\n            urls.push(r.split('\"')[0]);\n        }\n    });\n    const data2 = await request(urls[urls.length - 1]);\n    return data2.split(',client_id:\"')[1].split('\"')[0];\n}\n/**\n * Function for creating a Stream of soundcloud using a SoundCloud Track Class\n * @param data SoundCloud Track Class\n * @param quality Quality to select from\n * @returns SoundCloud Stream\n */\nexport async function stream_from_info(data: SoundCloudTrack, quality?: number): Promise<SoundCloudStream> {\n    const HLSformats = parseHlsFormats(data.formats);\n    if (typeof quality !== 'number') quality = HLSformats.length - 1;\n    else if (quality <= 0) quality = 0;\n    else if (quality >= HLSformats.length) quality = HLSformats.length - 1;\n    const req_url = HLSformats[quality].url + '?client_id=' + soundData.client_id;\n    const s_data = JSON.parse(await request(req_url));\n    const type = HLSformats[quality].format.mime_type.startsWith('audio/ogg')\n        ? StreamType.OggOpus\n        : StreamType.Arbitrary;\n    return new SoundCloudStream(s_data.url, type);\n}\n/**\n * Function to check client ID\n * @param id Client ID\n * @returns boolean\n */\nexport async function check_id(id: string): Promise<boolean> {\n    const response = await request(`https://api-v2.soundcloud.com/search?client_id=${id}&q=Rick+Roll&limit=0`).catch(\n        (err: Error) => {\n            return err;\n        }\n    );\n    if (response instanceof Error) return false;\n    else return true;\n}\n/**\n * Validates a soundcloud url\n * @param url soundcloud url\n * @returns\n * ```ts\n * false | 'track' | 'playlist'\n * ```\n */\nexport async function so_validate(url: string): Promise<false | 'track' | 'playlist' | 'search'> {\n    const url_ = url.trim();\n    if (!url_.startsWith('https')) return 'search';\n    if (!url_.match(pattern)) return false;\n    const data = await request(\n        `https://api-v2.soundcloud.com/resolve?url=${url_}&client_id=${soundData.client_id}`\n    ).catch((err: Error) => err);\n\n    if (data instanceof Error) return false;\n\n    const json_data = JSON.parse(data);\n    if (json_data.kind === 'track') return 'track';\n    else if (json_data.kind === 'playlist') return 'playlist';\n    else return false;\n}\n/**\n * Function to select only hls streams from SoundCloud format array\n * @param data SoundCloud Track Format data\n * @returns HLS Formats Array\n */\nfunction parseHlsFormats(data: SoundCloudTrackFormat[]) {\n    const result: SoundCloudTrackFormat[] = [];\n    data.forEach((format) => {\n        if (format.format.protocol === 'hls') result.push(format);\n    });\n    return result;\n}\n\nexport function setSoundCloudToken(options: SoundDataOptions) {\n    soundData = options;\n}\n\nexport { SoundCloudTrack, SoundCloudPlaylist, SoundCloudStream };\n","import { request, request_stream } from '../Request';\nimport { Readable } from 'node:stream';\nimport { IncomingMessage } from 'node:http';\nimport { StreamType } from '../YouTube/stream';\nimport { Timer } from '../YouTube/classes/LiveStream';\nimport { PlaylistJSON, SoundTrackJSON } from './constants';\n\nexport interface SoundCloudUser {\n    /**\n     * SoundCloud User Name\n     */\n    name: string;\n    /**\n     * SoundCloud User ID\n     */\n    id: string;\n    /**\n     * SoundCloud User URL\n     */\n    url: string;\n    /**\n     * SoundCloud Class type. == \"user\"\n     */\n    type: 'track' | 'playlist' | 'user';\n    /**\n     * SoundCloud User Verified status\n     */\n    verified: boolean;\n    /**\n     * SoundCloud User Description\n     */\n    description: string;\n    /**\n     * SoundCloud User First Name\n     */\n    first_name: string;\n    /**\n     * SoundCloud User Full Name\n     */\n    full_name: string;\n    /**\n     * SoundCloud User Last Name\n     */\n    last_name: string;\n    /**\n     * SoundCloud User thumbnail URL\n     */\n    thumbnail: string;\n}\n\nexport interface SoundCloudTrackDeprecated {\n    /**\n     * SoundCloud Track fetched status\n     */\n    fetched: boolean;\n    /**\n     * SoundCloud Track ID\n     */\n    id: number;\n    /**\n     * SoundCloud Class type. == \"track\"\n     */\n    type: 'track';\n}\n\nexport interface SoundCloudTrackFormat {\n    /**\n     * SoundCloud Track Format Url\n     */\n    url: string;\n    /**\n     * SoundCloud Track Format preset\n     */\n    preset: string;\n    /**\n     * SoundCloud Track Format Duration\n     */\n    duration: number;\n    /**\n     * SoundCloud Track Format data containing protocol and mime_type\n     */\n    format: {\n        protocol: string;\n        mime_type: string;\n    };\n    /**\n     * SoundCloud Track Format quality\n     */\n    quality: string;\n}\n/**\n * SoundCloud Track Class\n */\nexport class SoundCloudTrack {\n    /**\n     * SoundCloud Track Name\n     */\n    name: string;\n    /**\n     * SoundCloud Track ID\n     */\n    id: number;\n    /**\n     * SoundCloud Track url\n     */\n    url: string;\n    /**\n     * User friendly SoundCloud track URL\n     */\n    permalink: string;\n    /**\n     * SoundCloud Track fetched status\n     */\n    fetched: boolean;\n    /**\n     * SoundCloud Class type. === \"track\"\n     */\n    type: 'track' | 'playlist' | 'user';\n    /**\n     * SoundCloud Track Duration in seconds\n     */\n    durationInSec: number;\n    /**\n     * SoundCloud Track Duration in miili seconds\n     */\n    durationInMs: number;\n    /**\n     * SoundCloud Track formats data\n     */\n    formats: SoundCloudTrackFormat[];\n    /**\n     * SoundCloud Track Publisher Data\n     */\n    publisher: {\n        name: string;\n        id: number;\n        artist: string;\n        contains_music: boolean;\n        writer_composer: string;\n    } | null;\n    /**\n     * SoundCloud Track thumbnail\n     */\n    thumbnail: string;\n    /**\n     * SoundCloud Track user data\n     */\n    user: SoundCloudUser;\n    /**\n     * Constructor for SoundCloud Track Class\n     * @param data JSON parsed track html data\n     */\n    constructor(data: any) {\n        this.name = data.title;\n        this.id = data.id;\n        this.url = data.uri;\n        this.permalink = data.permalink_url;\n        this.fetched = true;\n        this.type = 'track';\n        this.durationInSec = Math.round(Number(data.duration) / 1000);\n        this.durationInMs = Number(data.duration);\n        if (data.publisher_metadata)\n            this.publisher = {\n                name: data.publisher_metadata.publisher,\n                id: data.publisher_metadata.id,\n                artist: data.publisher_metadata.artist,\n                contains_music: Boolean(data.publisher_metadata.contains_music) || false,\n                writer_composer: data.publisher_metadata.writer_composer\n            };\n        else this.publisher = null;\n        this.formats = data.media.transcodings;\n        this.user = {\n            name: data.user.username,\n            id: data.user.id,\n            type: 'user',\n            url: data.user.permalink_url,\n            verified: Boolean(data.user.verified) || false,\n            description: data.user.description,\n            first_name: data.user.first_name,\n            full_name: data.user.full_name,\n            last_name: data.user.last_name,\n            thumbnail: data.user.avatar_url\n        };\n        this.thumbnail = data.artwork_url;\n    }\n    /**\n     * Converts class to JSON\n     * @returns JSON parsed Data\n     */\n    toJSON(): SoundTrackJSON {\n        return {\n            name: this.name,\n            id: this.id,\n            url: this.url,\n            permalink: this.permalink,\n            fetched: this.fetched,\n            durationInMs: this.durationInMs,\n            durationInSec: this.durationInSec,\n            publisher: this.publisher,\n            formats: this.formats,\n            thumbnail: this.thumbnail,\n            user: this.user\n        };\n    }\n}\n/**\n * SoundCloud Playlist Class\n */\nexport class SoundCloudPlaylist {\n    /**\n     * SoundCloud Playlist Name\n     */\n    name: string;\n    /**\n     * SoundCloud Playlist ID\n     */\n    id: number;\n    /**\n     * SoundCloud Playlist URL\n     */\n    url: string;\n    /**\n     * SoundCloud Class type. == \"playlist\"\n     */\n    type: 'track' | 'playlist' | 'user';\n    /**\n     * SoundCloud Playlist Sub type. == \"album\" for soundcloud albums\n     */\n    sub_type: string;\n    /**\n     * SoundCloud Playlist Total Duration in seconds\n     */\n    durationInSec: number;\n    /**\n     * SoundCloud Playlist Total Duration in milli seconds\n     */\n    durationInMs: number;\n    /**\n     * SoundCloud Playlist user data\n     */\n    user: SoundCloudUser;\n    /**\n     * SoundCloud Playlist tracks [ It can be fetched or not fetched ]\n     */\n    tracks: SoundCloudTrack[] | SoundCloudTrackDeprecated[];\n    /**\n     * SoundCloud Playlist tracks number\n     */\n    tracksCount: number;\n    /**\n     * SoundCloud Client ID provided by user\n     * @private\n     */\n    private client_id: string;\n    /**\n     * Constructor for SoundCloud Playlist\n     * @param data JSON parsed SoundCloud playlist data\n     * @param client_id Provided SoundCloud Client ID\n     */\n    constructor(data: any, client_id: string) {\n        this.name = data.title;\n        this.id = data.id;\n        this.url = data.uri;\n        this.client_id = client_id;\n        this.type = 'playlist';\n        this.sub_type = data.set_type;\n        this.durationInSec = Math.round(Number(data.duration) / 1000);\n        this.durationInMs = Number(data.duration);\n        this.user = {\n            name: data.user.username,\n            id: data.user.id,\n            type: 'user',\n            url: data.user.permalink_url,\n            verified: Boolean(data.user.verified) || false,\n            description: data.user.description,\n            first_name: data.user.first_name,\n            full_name: data.user.full_name,\n            last_name: data.user.last_name,\n            thumbnail: data.user.avatar_url\n        };\n        this.tracksCount = data.track_count;\n        const tracks: any[] = [];\n        data.tracks.forEach((track: any) => {\n            if (track.title) {\n                tracks.push(new SoundCloudTrack(track));\n            } else\n                tracks.push({\n                    id: track.id,\n                    fetched: false,\n                    type: 'track'\n                });\n        });\n        this.tracks = tracks;\n    }\n    /**\n     * Fetches all unfetched songs in a playlist.\n     *\n     * For fetching songs and getting all songs, see `fetched_tracks` property.\n     * @returns playlist class\n     */\n    async fetch(): Promise<SoundCloudPlaylist> {\n        const work: any[] = [];\n        for (let i = 0; i < this.tracks.length; i++) {\n            if (!this.tracks[i].fetched) {\n                work.push(\n                    new Promise(async (resolve) => {\n                        const num = i;\n                        const data = await request(\n                            `https://api-v2.soundcloud.com/tracks/${this.tracks[i].id}?client_id=${this.client_id}`\n                        );\n\n                        this.tracks[num] = new SoundCloudTrack(JSON.parse(data));\n                        resolve('');\n                    })\n                );\n            }\n        }\n        await Promise.allSettled(work);\n        return this;\n    }\n    /**\n     * Get total no. of fetched tracks\n     * @see {@link SoundCloudPlaylist.all_tracks}\n     */\n    get total_tracks(): number {\n        let count = 0;\n        this.tracks.forEach((track) => {\n            if (track instanceof SoundCloudTrack) count++;\n            else return;\n        });\n        return count;\n    }\n    /**\n     * Fetches all the tracks in the playlist and returns them\n     *\n     * ```ts\n     * const playlist = await play.soundcloud('playlist url')\n     *\n     * const tracks = await playlist.all_tracks()\n     * ```\n     * @returns An array of {@link SoundCloudTrack}\n     */\n    async all_tracks(): Promise<SoundCloudTrack[]> {\n        await this.fetch();\n\n        return this.tracks as SoundCloudTrack[];\n    }\n    /**\n     * Converts Class to JSON data\n     * @returns JSON parsed data\n     */\n    toJSON(): PlaylistJSON {\n        return {\n            name: this.name,\n            id: this.id,\n            sub_type: this.sub_type,\n            url: this.url,\n            durationInMs: this.durationInMs,\n            durationInSec: this.durationInSec,\n            tracksCount: this.tracksCount,\n            user: this.user,\n            tracks: this.tracks\n        };\n    }\n}\n/**\n * SoundCloud Stream class\n */\nexport class SoundCloudStream {\n    /**\n     * Readable Stream through which data passes\n     */\n    stream: Readable;\n    /**\n     * Type of audio data that we recieved from normal youtube url.\n     */\n    type: StreamType;\n    /**\n     * Dash Url containing segment urls.\n     * @private\n     */\n    private url: string;\n    /**\n     * Total time of downloaded segments data.\n     * @private\n     */\n    private downloaded_time: number;\n    /**\n     * Timer for looping code every 5 minutes\n     * @private\n     */\n    private timer: Timer;\n    /**\n     * Total segments Downloaded so far\n     * @private\n     */\n    private downloaded_segments: number;\n    /**\n     * Incoming message that we recieve.\n     *\n     * Storing this is essential.\n     * This helps to destroy the TCP connection completely if you stopped player in between the stream\n     * @private\n     */\n    private request: IncomingMessage | null;\n    /**\n     * Array of segment time. Useful for calculating downloaded_time.\n     */\n    private time: number[];\n    /**\n     * Array of segment_urls in dash file.\n     */\n    private segment_urls: string[];\n    /**\n     * Constructor for SoundCloud Stream\n     * @param url Dash url containing dash file.\n     * @param type Stream Type\n     */\n    constructor(url: string, type: StreamType = StreamType.Arbitrary) {\n        this.stream = new Readable({ highWaterMark: 5 * 1000 * 1000, read() {} });\n        this.type = type;\n        this.url = url;\n        this.downloaded_time = 0;\n        this.request = null;\n        this.downloaded_segments = 0;\n        this.time = [];\n        this.timer = new Timer(() => {\n            this.timer.reuse();\n            this.start();\n        }, 280);\n        this.segment_urls = [];\n        this.stream.on('close', () => {\n            this.cleanup();\n        });\n        this.start();\n    }\n    /**\n     * Parses SoundCloud dash file.\n     * @private\n     */\n    private async parser() {\n        const response = await request(this.url).catch((err: Error) => {\n            return err;\n        });\n        if (response instanceof Error) throw response;\n        const array = response.split('\\n');\n        array.forEach((val) => {\n            if (val.startsWith('#EXTINF:')) {\n                this.time.push(parseFloat(val.replace('#EXTINF:', '')));\n            } else if (val.startsWith('https')) {\n                this.segment_urls.push(val);\n            }\n        });\n        return;\n    }\n    /**\n     * Starts looping of code for getting all segments urls data\n     */\n    private async start() {\n        if (this.stream.destroyed) {\n            this.cleanup();\n            return;\n        }\n        this.time = [];\n        this.segment_urls = [];\n        this.downloaded_time = 0;\n        await this.parser();\n        this.segment_urls.splice(0, this.downloaded_segments);\n        this.loop();\n    }\n    /**\n     * Main Loop function for getting all segments urls data\n     */\n    private async loop() {\n        if (this.stream.destroyed) {\n            this.cleanup();\n            return;\n        }\n        if (this.time.length === 0 || this.segment_urls.length === 0) {\n            this.cleanup();\n            this.stream.push(null);\n            return;\n        }\n        this.downloaded_time += this.time.shift() as number;\n        this.downloaded_segments++;\n        const stream = await request_stream(this.segment_urls.shift() as string).catch((err: Error) => err);\n        if (stream instanceof Error) {\n            this.stream.emit('error', stream);\n            this.cleanup();\n            return;\n        }\n\n        this.request = stream;\n        stream.on('data', (c) => {\n            this.stream.push(c);\n        });\n        stream.on('end', () => {\n            if (this.downloaded_time >= 300) return;\n            else this.loop();\n        });\n        stream.once('error', (err) => {\n            this.stream.emit('error', err);\n        });\n    }\n    /**\n     * This cleans every used variable in class.\n     *\n     * This is used to prevent re-use of this class and helping garbage collector to collect it.\n     */\n    private cleanup() {\n        this.timer.destroy();\n        this.request?.destroy();\n        this.url = '';\n        this.downloaded_time = 0;\n        this.downloaded_segments = 0;\n        this.request = null;\n        this.time = [];\n        this.segment_urls = [];\n    }\n    /**\n     * Pauses timer.\n     * Stops running of loop.\n     *\n     * Useful if you don't want to get excess data to be stored in stream.\n     */\n    pause() {\n        this.timer.pause();\n    }\n    /**\n     * Resumes timer.\n     * Starts running of loop.\n     */\n    resume() {\n        this.timer.resume();\n    }\n}\n","import { URL } from 'node:url';\nimport { request, request_resolve_redirect } from '../Request';\nimport { DeezerAlbum, DeezerPlaylist, DeezerTrack } from './classes';\n\ninterface TypeData {\n    type: 'track' | 'playlist' | 'album' | 'search' | false;\n    id?: string;\n    error?: string;\n}\n\ninterface DeezerSearchOptions {\n    /**\n     * The type to search for `'track'`, `'playlist'` or `'album'`. Defaults to `'track'`.\n     */\n    type?: 'track' | 'playlist' | 'album';\n    /**\n     * The maximum number of results to return, maximum `100`, defaults to `10`.\n     */\n    limit?: number;\n    /**\n     * Whether the search should be fuzzy or only return exact matches. Defaults to `true`.\n     */\n    fuzzy?: boolean;\n}\n\ninterface DeezerAdvancedSearchOptions {\n    /**\n     * The maximum number of results to return, maximum `100`, defaults to `10`.\n     */\n    limit?: number;\n    /**\n     * The name of the artist.\n     */\n    artist?: string;\n    /**\n     * The title of the album.\n     */\n    album?: string;\n    /**\n     * The title of the track.\n     */\n    title?: string;\n    /**\n     * The label that released the track.\n     */\n    label?: string;\n    /**\n     * The minimum duration in seconds.\n     */\n    minDurationInSec?: number;\n    /**\n     * The maximum duration in seconds.\n     */\n    maxDurationInSec?: number;\n    /**\n     * The minimum BPM.\n     */\n    minBPM?: number;\n    /**\n     * The minimum BPM.\n     */\n    maxBPM?: number;\n}\n\nasync function internalValidate(url: string): Promise<TypeData> {\n    let urlObj;\n    try {\n        // will throw a TypeError if the input is not a valid URL so we need to catch it\n        urlObj = new URL(url);\n    } catch {\n        return { type: 'search' };\n    }\n\n    if (urlObj.protocol !== 'https:' && urlObj.protocol !== 'http:') {\n        return { type: 'search' };\n    }\n\n    let pathname = urlObj.pathname;\n    if (pathname.endsWith('/')) {\n        pathname = pathname.slice(0, -1);\n    }\n    const path = pathname.split('/');\n    switch (urlObj.hostname) {\n        case 'deezer.com':\n        case 'www.deezer.com': {\n            if (path.length === 4) {\n                const lang = path.splice(1, 1)[0];\n                if (!lang.match(/^[a-z]{2}$/)) {\n                    return { type: false };\n                }\n            } else if (path.length !== 3) {\n                return { type: false };\n            }\n\n            if ((path[1] === 'track' || path[1] === 'album' || path[1] === 'playlist') && path[2].match(/^\\d+$/)) {\n                return {\n                    type: path[1],\n                    id: path[2]\n                };\n            } else {\n                return { type: false };\n            }\n        }\n        case 'api.deezer.com': {\n            if (\n                path.length === 3 &&\n                (path[1] === 'track' || path[1] === 'album' || path[1] === 'playlist') &&\n                path[2].match(/^\\d+$/)\n            ) {\n                return {\n                    type: path[1],\n                    id: path[2]\n                };\n            } else {\n                return { type: false };\n            }\n        }\n        case 'deezer.page.link': {\n            if (path.length === 2 && path[1].match(/^[A-Za-z0-9]+$/)) {\n                const resolved = await request_resolve_redirect(url).catch((err) => err);\n\n                if (resolved instanceof Error) {\n                    return { type: false, error: resolved.message };\n                }\n\n                return await internalValidate(resolved);\n            } else {\n                return { type: false };\n            }\n        }\n        default:\n            return { type: 'search' };\n    }\n}\n\n/**\n * Shared type for Deezer tracks, playlists and albums\n */\nexport type Deezer = DeezerTrack | DeezerPlaylist | DeezerAlbum;\n\n/**\n * Fetches the information for a track, playlist or album on Deezer\n * @param url The track, playlist or album URL\n * @returns A {@link DeezerTrack}, {@link DeezerPlaylist} or {@link DeezerAlbum}\n * object depending on the provided URL.\n */\nexport async function deezer(url: string): Promise<Deezer> {\n    const typeData = await internalValidate(url.trim());\n\n    if (typeData.error) {\n        throw new Error(`This is not a Deezer track, playlist or album URL:\\n${typeData.error}`);\n    } else if (!typeData.type || typeData.type === 'search')\n        throw new Error('This is not a Deezer track, playlist or album URL');\n\n    const response = await request(`https://api.deezer.com/${typeData.type}/${typeData.id}`).catch((err: Error) => err);\n\n    if (response instanceof Error) throw response;\n\n    const jsonData = JSON.parse(response);\n\n    if (jsonData.error) {\n        throw new Error(`Deezer API Error: ${jsonData.error.type}: ${jsonData.error.message}`);\n    }\n\n    switch (typeData.type) {\n        case 'track':\n            return new DeezerTrack(jsonData, false);\n        case 'playlist':\n            return new DeezerPlaylist(jsonData, false);\n        case 'album':\n            return new DeezerAlbum(jsonData, false);\n    }\n}\n\n/**\n * Validates a Deezer URL\n * @param url The URL to validate\n * @returns The type of the URL either `'track'`, `'playlist'`, `'album'`, `'search'` or `false`.\n * `false` means that the provided URL was a wrongly formatted or an unsupported Deezer URL.\n */\nexport async function dz_validate(url: string): Promise<'track' | 'playlist' | 'album' | 'search' | false> {\n    const typeData = await internalValidate(url.trim());\n    return typeData.type;\n}\n\n/**\n * Searches Deezer for tracks, playlists or albums\n * @param query The search query\n * @param options Extra options to configure the search:\n *\n * * type?: The type to search for `'track'`, `'playlist'` or `'album'`. Defaults to `'track'`.\n * * limit?: The maximum number of results to return, maximum `100`, defaults to `10`.\n * * fuzzy?: Whether the search should be fuzzy or only return exact matches. Defaults to `true`.\n * @returns An array of tracks, playlists or albums\n */\nexport async function dz_search(query: string, options: DeezerSearchOptions): Promise<Deezer[]> {\n    let query_ = query.trim();\n\n    const type = options.type ?? 'track';\n    const limit = options.limit ?? 10;\n    const fuzzy = options.fuzzy ?? true;\n\n    if (query_.length === 0) throw new Error('A query is required to search.');\n    if (limit > 100) throw new Error('The maximum search limit for Deezer is 100');\n    if (limit < 1) throw new Error('The minimum search limit for Deezer is 1');\n    if (type !== 'track' && type !== 'album' && type != 'playlist')\n        throw new Error(`\"${type}\" is not a valid Deezer search type`);\n\n    query_ = encodeURIComponent(query_);\n    const response = await request(\n        `https://api.deezer.com/search/${type}/?q=${query_}&limit=${limit}${fuzzy ? '' : 'strict=on'}`\n    ).catch((err: Error) => err);\n\n    if (response instanceof Error) throw response;\n\n    const jsonData = JSON.parse(response);\n\n    if (jsonData.error) {\n        throw new Error(`Deezer API Error: ${jsonData.error.type}: ${jsonData.error.message}`);\n    }\n\n    let results: Deezer[] = [];\n    switch (type) {\n        case 'track':\n            results = jsonData.data.map((track: any) => new DeezerTrack(track, true));\n            break;\n        case 'playlist':\n            results = jsonData.data.map((playlist: any) => new DeezerPlaylist(playlist, true));\n            break;\n        case 'album':\n            results = jsonData.data.map((album: any) => new DeezerAlbum(album, true));\n            break;\n    }\n\n    return results;\n}\n\n/**\n * Searches Deezer for tracks using the specified metadata.\n * @param options The metadata and limit for the search\n *\n * * limit?: The maximum number of results to return, maximum `100`, defaults to `10`.\n * * artist?: The name of the artist\n * * album?: The title of the album\n * * title?: The title of the track\n * * label?: The label that released the track\n * * minDurationInSec?: The minimum duration in seconds\n * * maxDurationInSec?: The maximum duration in seconds\n * * minBpm?: The minimum BPM\n * * maxBpm?: The minimum BPM\n * @returns An array of tracks matching the metadata\n */\nexport async function dz_advanced_track_search(options: DeezerAdvancedSearchOptions): Promise<DeezerTrack[]> {\n    const limit = options.limit ?? 10;\n\n    if (limit > 100) throw new Error('The maximum search limit for Deezer is 100');\n    if (limit < 1) throw new Error('The minimum search limit for Deezer is 1');\n\n    const metadata: string[] = [];\n    if (options.artist) metadata.push(`artist:\"${encodeURIComponent(options.artist.trim())}\"`);\n\n    if (options.album) metadata.push(`album:\"${encodeURIComponent(options.album.trim())}\"`);\n\n    if (options.title) metadata.push(`track:\"${encodeURIComponent(options.title.trim())}\"`);\n\n    if (options.label) metadata.push(`label:\"${encodeURIComponent(options.label.trim())}\"`);\n\n    if (!isNaN(Number(options.minDurationInSec))) metadata.push(`dur_min:${options.minDurationInSec}`);\n\n    if (!isNaN(Number(options.maxDurationInSec))) metadata.push(`dur_max:${options.maxDurationInSec}`);\n\n    if (!isNaN(Number(options.minBPM))) metadata.push(`bpm_min:${options.minBPM}`);\n\n    if (!isNaN(Number(options.maxBPM))) metadata.push(`bpm_max:${options.maxBPM}`);\n\n    if (metadata.length === 0) throw new Error('At least one type of metadata is required.');\n\n    const response = await request(`https://api.deezer.com/search/track/?q=${metadata.join(' ')}&limit=${limit}`).catch(\n        (err: Error) => err\n    );\n\n    if (response instanceof Error) throw response;\n\n    const jsonData = JSON.parse(response);\n\n    if (jsonData.error) {\n        throw new Error(`Deezer API Error: ${jsonData.error.type}: ${jsonData.error.message}`);\n    }\n\n    const results = jsonData.data.map((track: any) => new DeezerTrack(track, true));\n\n    return results;\n}\n\nexport { DeezerTrack, DeezerAlbum, DeezerPlaylist };\n","import { request } from '../Request';\n\n/**\n * Interface representing an image on Deezer\n * available in four sizes\n */\ninterface DeezerImage {\n    /**\n     * The largest version of the image\n     */\n    xl: string;\n    /**\n     * The second largest version of the image\n     */\n    big: string;\n    /**\n     * The second smallest version of the image\n     */\n    medium: string;\n    /**\n     * The smallest version of the image\n     */\n    small: string;\n}\n\n/**\n * Interface representing a Deezer genre\n */\ninterface DeezerGenre {\n    /**\n     * The name of the genre\n     */\n    name: string;\n    /**\n     * The thumbnail of the genre available in four sizes\n     */\n    picture: DeezerImage;\n}\n\n/**\n * Interface representing a Deezer user account\n */\ninterface DeezerUser {\n    /**\n     * The id of the user\n     */\n    id: number;\n    /**\n     * The name of the user\n     */\n    name: string;\n}\n\n/**\n * Class representing a Deezer track\n */\nexport class DeezerTrack {\n    /**\n     * The id of the track\n     */\n    id: number;\n    /**\n     * The title of the track\n     */\n    title: string;\n    /**\n     * A shorter version of the title\n     */\n    shortTitle: string;\n    /**\n     * The URL of the track on Deezer\n     */\n    url: string;\n    /**\n     * The duration of the track in seconds\n     */\n    durationInSec: number;\n    /**\n     * The rank of the track\n     */\n    rank: number;\n    /**\n     * `true` if the track contains any explicit lyrics\n     */\n    explicit: boolean;\n    /**\n     * URL to a file containing the first 30 seconds of the track\n     */\n    previewURL: string;\n    /**\n     * The artist of the track\n     */\n    artist: DeezerArtist;\n    /**\n     * The album that this track is in\n     */\n    album: DeezerTrackAlbum;\n    /**\n     * The type, always `'track'`, useful to determine what the deezer function returned\n     */\n    type: 'track' | 'playlist' | 'album';\n\n    /**\n     * Signifies that some properties are not populated\n     *\n     * Partial tracks can be populated by calling {@link DeezerTrack.fetch}.\n     *\n     * `true` for tracks in search results and `false` if the track was fetched directly or expanded.\n     */\n    partial: boolean;\n\n    /**\n     * The position of the track in the album\n     *\n     * `undefined` for partial tracks\n     *\n     * @see {@link DeezerTrack.partial}\n     */\n    trackPosition?: number;\n    /**\n     * The number of the disk the track is on\n     *\n     * `undefined` for partial tracks\n     *\n     * @see {@link DeezerTrack.partial}\n     */\n    diskNumber?: number;\n    /**\n     * The release date\n     *\n     * `undefined` for partial tracks\n     *\n     * @see {@link DeezerTrack.partial}\n     */\n    releaseDate?: Date;\n    /**\n     * The number of beats per minute\n     *\n     * `undefined` for partial tracks\n     *\n     * @see {@link DeezerTrack.partial}\n     */\n    bpm?: number;\n    /**\n     * The gain of the track\n     *\n     * `undefined` for partial tracks\n     *\n     * @see {@link DeezerTrack.partial}\n     */\n    gain?: number;\n    /**\n     * The artists that have contributed to the track\n     *\n     * `undefined` for partial tracks\n     *\n     * @see {@link DeezerTrack.partial}\n     */\n    contributors?: DeezerArtist[];\n\n    /**\n     * Creates a Deezer track from the data in an API response\n     * @param data the data to use to create the track\n     * @param partial Whether the track should be partial\n     * @see {@link DeezerTrack.partial}\n     */\n    constructor(data: any, partial: boolean) {\n        this.id = data.id;\n        this.title = data.title;\n        this.shortTitle = data.title_short;\n        this.url = data.link;\n        this.durationInSec = data.duration;\n        this.rank = data.rank;\n        this.explicit = data.explicit_lyrics;\n        this.previewURL = data.preview;\n        this.artist = new DeezerArtist(data.artist);\n        this.album = new DeezerTrackAlbum(data.album);\n        this.type = 'track';\n\n        this.partial = partial;\n\n        if (!partial) {\n            this.trackPosition = data.track_position;\n            this.diskNumber = data.disk_number;\n            this.releaseDate = new Date(data.release_date);\n            this.bpm = data.bpm;\n            this.gain = data.gain;\n            this.contributors = [];\n\n            data.contributors.forEach((contributor: any) => {\n                this.contributors?.push(new DeezerArtist(contributor));\n            });\n        }\n    }\n\n    /**\n     * Fetches and populates the missing fields\n     *\n     * The property {@link partial} will be `false` if this method finishes successfully.\n     *\n     * @returns A promise with the same track this method was called on.\n     */\n    async fetch(): Promise<DeezerTrack> {\n        if (!this.partial) return this;\n\n        const response = await request(`https://api.deezer.com/track/${this.id}/`).catch((err: Error) => err);\n\n        if (response instanceof Error) throw response;\n        const jsonData = JSON.parse(response);\n\n        this.partial = false;\n\n        this.trackPosition = jsonData.track_position;\n        this.diskNumber = jsonData.disk_number;\n        this.releaseDate = new Date(jsonData.release_date);\n        this.bpm = jsonData.bpm;\n        this.gain = jsonData.gain;\n        this.contributors = [];\n\n        jsonData.contributors.forEach((contributor: any) => {\n            this.contributors?.push(new DeezerArtist(contributor));\n        });\n\n        return this;\n    }\n    /**\n     * Converts instances of this class to JSON data\n     * @returns JSON data.\n     */\n    toJSON() {\n        return {\n            id: this.id,\n            title: this.title,\n            shortTitle: this.shortTitle,\n            url: this.url,\n            durationInSec: this.durationInSec,\n            rank: this.rank,\n            explicit: this.explicit,\n            previewURL: this.previewURL,\n            artist: this.artist,\n            album: this.album,\n            type: this.type,\n            trackPosition: this.trackPosition,\n            diskNumber: this.diskNumber,\n            releaseDate: this.releaseDate,\n            bpm: this.bpm,\n            gain: this.gain,\n            contributors: this.contributors\n        };\n    }\n}\n/**\n * Class for Deezer Albums\n */\nexport class DeezerAlbum {\n    /**\n     * The id of the album\n     */\n    id: number;\n    /**\n     * The title of the album\n     */\n    title: string;\n    /**\n     * The URL to the album on Deezer\n     */\n    url: string;\n    /**\n     * The record type of the album (e.g. EP, ALBUM, etc ...)\n     */\n    recordType: string;\n    /**\n     * `true` if the album contains any explicit lyrics\n     */\n    explicit: boolean;\n    /**\n     * The artist of the album\n     */\n    artist: DeezerArtist;\n    /**\n     * The album cover available in four sizes\n     */\n    cover: DeezerImage;\n    /**\n     * The type, always `'album'`, useful to determine what the deezer function returned\n     */\n    type: 'track' | 'playlist' | 'album';\n    /**\n     * The number of tracks in the album\n     */\n    tracksCount: number;\n\n    /**\n     * Signifies that some properties are not populated\n     *\n     * Partial albums can be populated by calling {@link DeezerAlbum.fetch}.\n     *\n     * `true` for albums in search results and `false` if the album was fetched directly or expanded.\n     */\n    partial: boolean;\n\n    /**\n     * The **u**niversal **p**roduct **c**ode of the album\n     *\n     * `undefined` for partial albums\n     *\n     * @see {@link DeezerAlbum.partial}\n     */\n    upc?: string;\n    /**\n     * The duration of the album in seconds\n     *\n     * `undefined` for partial albums\n     *\n     * @see {@link DeezerAlbum.partial}\n     */\n    durationInSec?: number;\n    /**\n     * The number of fans the album has\n     *\n     * `undefined` for partial albums\n     *\n     * @see {@link DeezerAlbum.partial}\n     */\n    numberOfFans?: number;\n    /**\n     * The release date of the album\n     *\n     * `undefined` for partial albums\n     *\n     * @see {@link DeezerAlbum.partial}\n     */\n    releaseDate?: Date;\n    /**\n     * Whether the album is available\n     *\n     * `undefined` for partial albums\n     *\n     * @see {@link DeezerAlbum.partial}\n     */\n    available?: boolean;\n    /**\n     * The list of genres present in this album\n     *\n     * `undefined` for partial albums\n     *\n     * @see {@link DeezerAlbum.partial}\n     */\n    genres?: DeezerGenre[];\n    /**\n     * The contributors to the album\n     *\n     * `undefined` for partial albums\n     *\n     * @see {@link DeezerAlbum.partial}\n     */\n    contributors?: DeezerArtist[];\n\n    /**\n     * The list of tracks in the album\n     *\n     * empty (length === 0) for partial albums\n     *\n     * Use {@link DeezerAlbum.fetch} to populate the tracks and other properties\n     *\n     * @see {@link DeezerAlbum.partial}\n     */\n    tracks: DeezerTrack[];\n\n    /**\n     * Creates a Deezer album from the data in an API response\n     * @param data the data to use to create the album\n     * @param partial Whether the album should be partial\n     * @see {@link DeezerAlbum.partial}\n     */\n    constructor(data: any, partial: boolean) {\n        this.id = data.id;\n        this.title = data.title;\n        this.url = data.link;\n        this.recordType = data.record_type;\n        this.explicit = data.explicit_lyrics;\n        this.artist = new DeezerArtist(data.artist);\n        this.type = 'album';\n        this.tracksCount = data.nb_tracks;\n        this.contributors = [];\n        this.genres = [];\n        this.tracks = [];\n        this.cover = {\n            xl: data.cover_xl,\n            big: data.cover_big,\n            medium: data.cover_medium,\n            small: data.cover_small\n        };\n\n        this.partial = partial;\n\n        if (!partial) {\n            this.upc = data.upc;\n            this.durationInSec = data.duration;\n            this.numberOfFans = data.fans;\n            this.releaseDate = new Date(data.release_date);\n            this.available = data.available;\n\n            data.contributors.forEach((contributor: any) => {\n                this.contributors?.push(new DeezerArtist(contributor));\n            });\n\n            data.genres.data.forEach((genre: any) => {\n                this.genres?.push({\n                    name: genre.name,\n                    picture: {\n                        xl: `${genre.picture}?size=xl`,\n                        big: `${genre.picture}?size=big`,\n                        medium: `${genre.picture}?size=medium`,\n                        small: `${genre.picture}?size=small`\n                    }\n                });\n            });\n\n            const trackAlbum: any = {\n                id: this.id,\n                title: this.title,\n                cover_xl: this.cover.xl,\n                cover_big: this.cover.big,\n                cover_medium: this.cover.medium,\n                cover_small: this.cover.small,\n                release_date: data.release_date\n            };\n            data.tracks.data.forEach((track: any) => {\n                track.album = trackAlbum;\n                this.tracks.push(new DeezerTrack(track, true));\n            });\n        }\n    }\n\n    /**\n     * Fetches and populates the missing fields including all tracks.\n     *\n     * The property {@link DeezerAlbum.partial} will be `false` if this method finishes successfully.\n     *\n     * @returns A promise with the same album this method was called on.\n     */\n    async fetch(): Promise<DeezerAlbum> {\n        if (!this.partial) return this;\n\n        const response = await request(`https://api.deezer.com/album/${this.id}/`).catch((err: Error) => err);\n\n        if (response instanceof Error) throw response;\n        const jsonData = JSON.parse(response);\n\n        this.partial = false;\n\n        this.upc = jsonData.upc;\n        this.durationInSec = jsonData.duration;\n        this.numberOfFans = jsonData.fans;\n        this.releaseDate = new Date(jsonData.release_date);\n        this.available = jsonData.available;\n        this.contributors = [];\n        this.genres = [];\n        this.tracks = [];\n\n        jsonData.contributors.forEach((contributor: any) => {\n            this.contributors?.push(new DeezerArtist(contributor));\n        });\n\n        jsonData.genres.data.forEach((genre: any) => {\n            this.genres?.push({\n                name: genre.name,\n                picture: {\n                    xl: `${genre.picture}?size=xl`,\n                    big: `${genre.picture}?size=big`,\n                    medium: `${genre.picture}?size=medium`,\n                    small: `${genre.picture}?size=small`\n                }\n            });\n        });\n\n        const trackAlbum: any = {\n            id: this.id,\n            title: this.title,\n            cover_xl: this.cover.xl,\n            cover_big: this.cover.big,\n            cover_medium: this.cover.medium,\n            cover_small: this.cover.small,\n            release_date: jsonData.release_date\n        };\n        jsonData.tracks.data.forEach((track: any) => {\n            track.album = trackAlbum;\n            this.tracks.push(new DeezerTrack(track, true));\n        });\n\n        return this;\n    }\n    /**\n     * Fetches all the tracks in the album and returns them\n     *\n     * ```ts\n     * const album = await play.deezer('album url')\n     *\n     * const tracks = await album.all_tracks()\n     * ```\n     * @returns An array of {@link DeezerTrack}\n     */\n    async all_tracks(): Promise<DeezerTrack[]> {\n        await this.fetch();\n\n        return this.tracks as DeezerTrack[];\n    }\n    /**\n     * Converts instances of this class to JSON data\n     * @returns JSON data.\n     */\n    toJSON() {\n        return {\n            id: this.id,\n            title: this.title,\n            url: this.url,\n            recordType: this.recordType,\n            explicit: this.explicit,\n            artist: this.artist,\n            cover: this.cover,\n            type: this.type,\n            upc: this.upc,\n            tracksCount: this.tracksCount,\n            durationInSec: this.durationInSec,\n            numberOfFans: this.numberOfFans,\n            releaseDate: this.releaseDate,\n            available: this.available,\n            genres: this.genres,\n            contributors: this.contributors,\n            tracks: this.tracks.map((track) => track.toJSON())\n        };\n    }\n}\n/**\n * Class for Deezer Playlists\n */\nexport class DeezerPlaylist {\n    /**\n     * The id of the playlist\n     */\n    id: number;\n    /**\n     * The title of the playlist\n     */\n    title: string;\n    /**\n     * Whether the playlist is public or private\n     */\n    public: boolean;\n    /**\n     * The URL of the playlist on Deezer\n     */\n    url: string;\n    /**\n     * Cover picture of the playlist available in four sizes\n     */\n    picture: DeezerImage;\n    /**\n     * The date of the playlist's creation\n     */\n    creationDate: Date;\n    /**\n     * The type, always `'playlist'`, useful to determine what the deezer function returned\n     */\n    type: 'track' | 'playlist' | 'album';\n    /**\n     * The Deezer user that created the playlist\n     */\n    creator: DeezerUser;\n    /**\n     * The number of tracks in the playlist\n     */\n    tracksCount: number;\n\n    /**\n     * Signifies that some properties are not populated\n     *\n     * Partial playlists can be populated by calling {@link DeezerPlaylist.fetch}.\n     *\n     * `true` for playlists in search results and `false` if the album was fetched directly or expanded.\n     */\n    partial: boolean;\n\n    /**\n     * Description of the playlist\n     *\n     * `undefined` for partial playlists\n     *\n     * @see {@link DeezerPlaylist.partial}\n     */\n    description?: string;\n    /**\n     * Duration of the playlist in seconds\n     *\n     * `undefined` for partial playlists\n     *\n     * @see {@link DeezerPlaylist.partial}\n     */\n    durationInSec?: number;\n    /**\n     * `true` if the playlist is the loved tracks playlist\n     *\n     * `undefined` for partial playlists\n     *\n     * @see {@link DeezerPlaylist.partial}\n     */\n    isLoved?: boolean;\n    /**\n     * Whether multiple users have worked on the playlist\n     *\n     * `undefined` for partial playlists\n     *\n     * @see {@link DeezerPlaylist.partial}\n     */\n    collaborative?: boolean;\n    /**\n     * The number of fans the playlist has\n     *\n     * `undefined` for partial playlists\n     *\n     * @see {@link DeezerPlaylist.partial}\n     */\n    fans?: number;\n\n    /**\n     * The list of tracks in the playlist\n     *\n     * empty (length === 0) for partial and non public playlists\n     *\n     * Use {@link DeezerPlaylist.fetch} to populate the tracks and other properties\n     *\n     * @see {@link DeezerPlaylist.partial}\n     * @see {@link DeezerPlaylist.public}\n     */\n    tracks: DeezerTrack[];\n\n    /**\n     * Creates a Deezer playlist from the data in an API response\n     * @param data the data to use to create the playlist\n     * @param partial Whether the playlist should be partial\n     * @see {@link DeezerPlaylist.partial}\n     */\n    constructor(data: any, partial: boolean) {\n        this.id = data.id;\n        this.title = data.title;\n        this.public = data.public;\n        this.url = data.link;\n        this.creationDate = new Date(data.creation_date);\n        this.type = 'playlist';\n        this.tracksCount = data.nb_tracks;\n        this.tracks = [];\n\n        this.picture = {\n            xl: data.picture_xl,\n            big: data.picture_big,\n            medium: data.picture_medium,\n            small: data.picture_small\n        };\n\n        if (data.user) {\n            this.creator = {\n                id: data.user.id,\n                name: data.user.name\n            };\n        } else {\n            this.creator = {\n                id: data.creator.id,\n                name: data.creator.name\n            };\n        }\n\n        this.partial = partial;\n\n        if (!partial) {\n            this.description = data.description;\n            this.durationInSec = data.duration;\n            this.isLoved = data.is_loved_track;\n            this.collaborative = data.collaborative;\n            this.fans = data.fans;\n\n            if (this.public) {\n                this.tracks = data.tracks.data.map((track: any) => {\n                    return new DeezerTrack(track, true);\n                });\n            }\n        }\n    }\n\n    /**\n     * Fetches and populates the missing fields, including all tracks.\n     *\n     * The property {@link DeezerPlaylist.partial} will be `false` if this method finishes successfully.\n     *\n     * @returns A promise with the same playlist this method was called on.\n     */\n    async fetch(): Promise<DeezerPlaylist> {\n        if (!this.partial && (this.tracks.length === this.tracksCount || !this.public)) {\n            return this;\n        }\n\n        if (this.partial) {\n            const response = await request(`https://api.deezer.com/playlist/${this.id}/`).catch((err: Error) => err);\n\n            if (response instanceof Error) throw response;\n            const jsonData = JSON.parse(response);\n\n            this.partial = false;\n\n            this.description = jsonData.description;\n            this.durationInSec = jsonData.duration;\n            this.isLoved = jsonData.is_loved_track;\n            this.collaborative = jsonData.collaborative;\n            this.fans = jsonData.fans;\n\n            if (this.public) {\n                this.tracks = jsonData.tracks.data.map((track: any) => {\n                    return new DeezerTrack(track, true);\n                });\n            }\n        }\n\n        const currentTracksCount = this.tracks.length;\n        if (this.public && currentTracksCount !== this.tracksCount) {\n            let missing = this.tracksCount - currentTracksCount;\n\n            if (missing > 1000) missing = 1000;\n\n            const promises: Promise<DeezerTrack[]>[] = [];\n            for (let i = 1; i <= Math.ceil(missing / 100); i++) {\n                promises.push(\n                    new Promise(async (resolve, reject) => {\n                        const response = await request(\n                            `https://api.deezer.com/playlist/${this.id}/tracks?limit=100&index=${i * 100}`\n                        ).catch((err) => reject(err));\n\n                        if (typeof response !== 'string') return;\n                        const jsonData = JSON.parse(response);\n                        const tracks = jsonData.data.map((track: any) => {\n                            return new DeezerTrack(track, true);\n                        });\n\n                        resolve(tracks);\n                    })\n                );\n            }\n\n            const results = await Promise.allSettled(promises);\n            const newTracks: DeezerTrack[] = [];\n\n            for (const result of results) {\n                if (result.status === 'fulfilled') {\n                    newTracks.push(...result.value);\n                } else {\n                    throw result.reason;\n                }\n            }\n\n            this.tracks.push(...newTracks);\n        }\n\n        return this;\n    }\n    /**\n     * Fetches all the tracks in the playlist and returns them\n     *\n     * ```ts\n     * const playlist = await play.deezer('playlist url')\n     *\n     * const tracks = await playlist.all_tracks()\n     * ```\n     * @returns An array of {@link DeezerTrack}\n     */\n    async all_tracks(): Promise<DeezerTrack[]> {\n        await this.fetch();\n\n        return this.tracks as DeezerTrack[];\n    }\n    /**\n     * Converts instances of this class to JSON data\n     * @returns JSON data.\n     */\n    toJSON() {\n        return {\n            id: this.id,\n            title: this.title,\n            public: this.public,\n            url: this.url,\n            picture: this.picture,\n            creationDate: this.creationDate,\n            type: this.type,\n            creator: this.creator,\n            tracksCount: this.tracksCount,\n            description: this.description,\n            durationInSec: this.durationInSec,\n            isLoved: this.isLoved,\n            collaborative: this.collaborative,\n            fans: this.fans,\n            tracks: this.tracks.map((track) => track.toJSON())\n        };\n    }\n}\n\nclass DeezerTrackAlbum {\n    id: number;\n    title: string;\n    url: string;\n    cover: DeezerImage;\n    releaseDate?: Date;\n\n    constructor(data: any) {\n        this.id = data.id;\n        this.title = data.title;\n        this.url = `https://www.deezer.com/album/${data.id}/`;\n        this.cover = {\n            xl: data.cover_xl,\n            big: data.cover_big,\n            medium: data.cover_medium,\n            small: data.cover_small\n        };\n\n        if (data.release_date) this.releaseDate = new Date(data.release_date);\n    }\n}\n/**\n * Class representing a Deezer artist\n */\nclass DeezerArtist {\n    /**\n     * The id of the artist\n     */\n    id: number;\n    /**\n     * The name of the artist\n     */\n    name: string;\n    /**\n     * The URL of the artist on Deezer\n     */\n    url: string;\n\n    /**\n     * The picture of the artist available in four sizes\n     */\n    picture?: DeezerImage;\n    /**\n     * The of the artist on the track\n     */\n    role?: string;\n\n    constructor(data: any) {\n        this.id = data.id;\n        this.name = data.name;\n\n        this.url = data.link ? data.link : `https://www.deezer.com/artist/${data.id}/`;\n\n        if (data.picture_xl)\n            this.picture = {\n                xl: data.picture_xl,\n                big: data.picture_big,\n                medium: data.picture_medium,\n                small: data.picture_small\n            };\n\n        if (data.role) this.role = data.role;\n    }\n}\n","import { setUserAgent } from './Request/useragent';\nimport { setSoundCloudToken } from './SoundCloud';\nimport { setSpotifyToken } from './Spotify';\nimport { setCookieToken } from './YouTube/utils/cookie';\n\ninterface tokenOptions {\n    spotify?: {\n        client_id: string;\n        client_secret: string;\n        refresh_token: string;\n        market: string;\n    };\n    soundcloud?: {\n        client_id: string;\n    };\n    youtube?: {\n        cookie: string;\n    };\n    useragent?: string[];\n}\n/**\n * Sets\n *\n *  i> YouTube :- cookies.\n *\n *  ii> SoundCloud :- client ID.\n *\n *  iii> Spotify :- client ID, client secret, refresh token, market.\n *\n *  iv> Useragents :- array of string.\n *\n * locally in memory.\n *\n * Example :\n * ```ts\n * play.setToken({\n *      youtube : {\n *          cookie : \"Your Cookies\"\n *      }\n * }) // YouTube Cookies\n *\n * await play.setToken({\n *      spotify : {\n *          client_id: 'ID',\n            client_secret: 'secret',\n            refresh_token: 'token',\n            market: 'US'\n *      }\n * }) // Await this only when setting data for spotify\n * \n * play.setToken({\n *      useragent: ['Your User-agent']\n * }) // Use this to avoid 429 errors.\n * ```\n * @param options {@link tokenOptions}\n */\nexport async function setToken(options: tokenOptions) {\n    if (options.spotify) await setSpotifyToken(options.spotify);\n    if (options.soundcloud) setSoundCloudToken(options.soundcloud);\n    if (options.youtube) setCookieToken(options.youtube);\n    if (options.useragent) setUserAgent(options.useragent);\n}\n","import {\n    playlist_info,\n    video_basic_info,\n    video_info,\n    decipher_info,\n    yt_validate,\n    extractID,\n    YouTube,\n    YouTubeStream,\n    YouTubeChannel,\n    YouTubePlayList,\n    YouTubeVideo,\n    InfoData\n} from './YouTube';\nimport {\n    spotify,\n    sp_validate,\n    refreshToken,\n    is_expired,\n    SpotifyAlbum,\n    SpotifyPlaylist,\n    SpotifyTrack,\n    Spotify,\n    SpotifyAuthorize,\n    sp_search\n} from './Spotify';\nimport {\n    soundcloud,\n    so_validate,\n    SoundCloud,\n    SoundCloudStream,\n    getFreeClientID,\n    SoundCloudPlaylist,\n    SoundCloudTrack,\n    check_id,\n    so_search,\n    stream as so_stream,\n    stream_from_info as so_stream_info\n} from './SoundCloud';\nimport {\n    deezer,\n    dz_validate,\n    dz_advanced_track_search,\n    Deezer,\n    DeezerTrack,\n    DeezerPlaylist,\n    DeezerAlbum,\n    dz_search\n} from './Deezer';\nimport { setToken } from './token';\n\nenum AudioPlayerStatus {\n    Idle = 'idle',\n    Buffering = 'buffering',\n    Paused = 'paused',\n    Playing = 'playing',\n    AutoPaused = 'autopaused'\n}\n\ninterface SearchOptions {\n    limit?: number;\n    source?: {\n        youtube?: 'video' | 'playlist' | 'channel';\n        spotify?: 'album' | 'playlist' | 'track';\n        soundcloud?: 'tracks' | 'playlists' | 'albums';\n        deezer?: 'track' | 'playlist' | 'album';\n    };\n    fuzzy?: boolean;\n    language?: string;\n    /**\n     * !!! Before enabling this for public servers, please consider using Discord features like NSFW channels as not everyone in your server wants to see NSFW images. !!!\n     * Unblurred images will likely have different dimensions than specified in the {@link YouTubeThumbnail} objects.\n     */\n    unblurNSFWThumbnails?: boolean;\n}\n\nimport { createInterface } from 'node:readline';\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { stream as yt_stream, StreamOptions, stream_from_info as yt_stream_info } from './YouTube/stream';\nimport { yt_search } from './YouTube/search';\nimport { EventEmitter } from 'stream';\n\nasync function stream(url: string, options: { seek?: number } & StreamOptions): Promise<YouTubeStream>;\nasync function stream(url: string, options?: StreamOptions): Promise<YouTubeStream | SoundCloudStream>;\n/**\n * Creates a Stream [ YouTube or SoundCloud ] class from a url for playing.\n *\n * Example\n * ```ts\n * const source = await play.stream('youtube video URL') // YouTube Video Stream\n *\n * const source = await play.stream('soundcloud track URL') // SoundCloud Track Stream\n *\n * const source = await play.stream('youtube video URL', { seek : 45 }) // Seeks 45 seconds (approx.) in YouTube Video Stream\n *\n * const resource = createAudioResource(source.stream, {\n *      inputType : source.type\n * }) // Use discordjs voice createAudioResource function.\n * ```\n * @param url Video / Track URL\n * @param options\n *\n *  - `number` seek : No of seconds to seek in stream.\n *  - `string` language : Sets language of searched content [ YouTube search only. ], e.g. \"en-US\"\n *  - `number` quality : Quality number. [ 0 = Lowest, 1 = Medium, 2 = Highest ]\n *  - `boolean` htmldata : given data is html data or not\n *  - `number` precache : No of segments of data to store before looping [YouTube Live Stream only]. [ Defaults to 3 ]\n *  - `boolean` discordPlayerCompatibility : Conversion of Webm to Opus [ Defaults to false ]\n * @returns A {@link YouTubeStream} or {@link SoundCloudStream} Stream to play\n */\nasync function stream(url: string, options: StreamOptions = {}): Promise<YouTubeStream | SoundCloudStream> {\n    const url_ = url.trim();\n    if (url_.length === 0) throw new Error('Stream URL has a length of 0. Check your url again.');\n    if (options.htmldata) return await yt_stream(url_, options);\n    if (url_.indexOf('spotify') !== -1) {\n        throw new Error(\n            'Streaming from Spotify is not supported. Please use search() to find a similar track on YouTube or SoundCloud instead.'\n        );\n    }\n    if (url_.indexOf('deezer') !== -1) {\n        throw new Error(\n            'Streaming from Deezer is not supported. Please use search() to find a similar track on YouTube or SoundCloud instead.'\n        );\n    }\n    if (url_.indexOf('soundcloud') !== -1) return await so_stream(url_, options.quality);\n    else return await yt_stream(url_, options);\n}\n\nasync function search(query: string, options: { source: { deezer: 'album' } } & SearchOptions): Promise<DeezerAlbum[]>;\nasync function search(\n    query: string,\n    options: { source: { deezer: 'playlist' } } & SearchOptions\n): Promise<DeezerPlaylist[]>;\nasync function search(query: string, options: { source: { deezer: 'track' } } & SearchOptions): Promise<DeezerTrack[]>;\nasync function search(\n    query: string,\n    options: { source: { soundcloud: 'albums' } } & SearchOptions\n): Promise<SoundCloudPlaylist[]>;\nasync function search(\n    query: string,\n    options: { source: { soundcloud: 'playlists' } } & SearchOptions\n): Promise<SoundCloudPlaylist[]>;\nasync function search(\n    query: string,\n    options: { source: { soundcloud: 'tracks' } } & SearchOptions\n): Promise<SoundCloudTrack[]>;\nasync function search(\n    query: string,\n    options: { source: { spotify: 'album' } } & SearchOptions\n): Promise<SpotifyAlbum[]>;\nasync function search(\n    query: string,\n    options: { source: { spotify: 'playlist' } } & SearchOptions\n): Promise<SpotifyPlaylist[]>;\nasync function search(\n    query: string,\n    options: { source: { spotify: 'track' } } & SearchOptions\n): Promise<SpotifyTrack[]>;\nasync function search(\n    query: string,\n    options: { source: { youtube: 'channel' } } & SearchOptions\n): Promise<YouTubeChannel[]>;\nasync function search(\n    query: string,\n    options: { source: { youtube: 'playlist' } } & SearchOptions\n): Promise<YouTubePlayList[]>;\nasync function search(\n    query: string,\n    options: { source: { youtube: 'video' } } & SearchOptions\n): Promise<YouTubeVideo[]>;\nasync function search(query: string, options: { limit: number } & SearchOptions): Promise<YouTubeVideo[]>;\nasync function search(query: string, options?: SearchOptions): Promise<YouTubeVideo[]>;\n/**\n * Searches through a particular source and gives respective info.\n * \n * Example\n * ```ts\n * const searched = await play.search('Rick Roll', { source : { youtube : \"video\" } }) // YouTube Video Search\n * \n * const searched = await play.search('Rick Roll', { limit : 1 }) // YouTube Video Search but returns only 1 video.\n * \n * const searched = await play.search('Rick Roll', { source : { spotify : \"track\" } }) // Spotify Track Search\n * \n * const searched = await play.search('Rick Roll', { source : { soundcloud : \"tracks\" } }) // SoundCloud Track Search\n * \n * const searched = await play.search('Rick Roll', { source : { deezer : \"track\" } }) // Deezer Track Search\n * ```\n * @param query string to search.\n * @param options\n * \n *  - `number` limit : No of searches you want to have.\n *  - `string` language : Sets language of searched content [ YouTube search only. ], e.g. \"en-US\"\n *  - `boolean` unblurNSFWThumbnails : Unblurs NSFW thumbnails. Defaults to `false` [ YouTube search only. ]\n *              !!! Before enabling this for public servers, please consider using Discord features like NSFW channels as not everyone in your server wants to see NSFW images. !!!\n *              Unblurred images will likely have different dimensions than specified in the {@link YouTubeThumbnail} objects.\n *  - `boolean` fuzzy : Whether the search should be fuzzy or only return exact matches. Defaults to `true`. [ for `Deezer` Only ]\n *  - `Object` source : Contains type of source and type of result you want to have\n * ```ts\n *      - youtube : 'video' | 'playlist' | 'channel';\n        - spotify : 'album' | 'playlist' | 'track';\n        - soundcloud : 'tracks' | 'playlists' | 'albums';\n        - deezer : 'track' | 'playlist' | 'album';\n    ```\n * @returns Array of {@link YouTube} or {@link Spotify} or {@link SoundCloud} or {@link Deezer} type\n */\nasync function search(\n    query: string,\n    options: SearchOptions = {}\n): Promise<YouTube[] | Spotify[] | SoundCloud[] | Deezer[]> {\n    if (!options.source) options.source = { youtube: 'video' };\n    const query_ = encodeURIComponent(query.trim());\n    if (options.source.youtube)\n        return await yt_search(query_, {\n            limit: options.limit,\n            type: options.source.youtube,\n            language: options.language,\n            unblurNSFWThumbnails: options.unblurNSFWThumbnails\n        });\n    else if (options.source.spotify) return await sp_search(query_, options.source.spotify, options.limit);\n    else if (options.source.soundcloud) return await so_search(query_, options.source.soundcloud, options.limit);\n    else if (options.source.deezer)\n        return await dz_search(query_, { limit: options.limit, type: options.source.deezer, fuzzy: options.fuzzy });\n    else throw new Error('Not possible to reach Here LOL. Easter Egg of play-dl if someone get this.');\n}\n\nasync function stream_from_info(info: SoundCloudTrack, options?: StreamOptions): Promise<SoundCloudStream>;\nasync function stream_from_info(info: InfoData, options?: StreamOptions): Promise<YouTubeStream>;\n/**\n * Creates a Stream [ YouTube or SoundCloud ] class from video or track info for playing.\n *\n * Example\n * ```ts\n * const info = await video_info('youtube URL')\n * const source = await play.stream_from_info(info) // YouTube Video Stream\n *\n * const soundInfo = await play.soundcloud('SoundCloud URL')\n * const source = await play.stream_from_info(soundInfo) // SoundCloud Track Stream\n *\n * const source = await play.stream_from_info(info, { seek : 45 }) // Seeks 45 seconds (approx.) in YouTube Video Stream\n *\n * const resource = createAudioResource(source.stream, {\n *      inputType : source.type\n * }) // Use discordjs voice createAudioResource function.\n * ```\n * @param info YouTube video info OR SoundCloud track Class\n * @param options\n *\n *  - `number` seek : No of seconds to seek in stream.\n *  - `string` language : Sets language of searched content [ YouTube search only. ], e.g. \"en-US\"\n *  - `number` quality : Quality number. [ 0 = Lowest, 1 = Medium, 2 = Highest ]\n *  - `boolean` htmldata : given data is html data or not\n *  - `number` precache : No of segments of data to store before looping [YouTube Live Stream only]. [ Defaults to 3 ]\n *  - `boolean` discordPlayerCompatibility : Conversion of Webm to Opus[ Defaults to false ]\n * @returns A {@link YouTubeStream} or {@link SoundCloudStream} Stream to play\n */\nasync function stream_from_info(\n    info: InfoData | SoundCloudTrack,\n    options: StreamOptions = {}\n): Promise<YouTubeStream | SoundCloudStream> {\n    if (info instanceof SoundCloudTrack) return await so_stream_info(info, options.quality);\n    else return await yt_stream_info(info, options);\n}\n/**\n * Validates url that play-dl supports.\n *\n * - `so` - SoundCloud\n * - `sp` - Spotify\n * - `dz` - Deezer\n * - `yt` - YouTube\n * @param url URL\n * @returns\n * ```ts\n * 'so_playlist' / 'so_track' | 'sp_track' | 'sp_album' | 'sp_playlist' | 'dz_track' | 'dz_playlist' | 'dz_album' | 'yt_video' | 'yt_playlist' | 'search' | false\n * ```\n */\nasync function validate(\n    url: string\n): Promise<\n    | 'so_playlist'\n    | 'so_track'\n    | 'sp_track'\n    | 'sp_album'\n    | 'sp_playlist'\n    | 'dz_track'\n    | 'dz_playlist'\n    | 'dz_album'\n    | 'yt_video'\n    | 'yt_playlist'\n    | 'search'\n    | false\n> {\n    let check;\n    const url_ = url.trim();\n    if (!url_.startsWith('https')) return 'search';\n    if (url_.indexOf('spotify') !== -1) {\n        check = sp_validate(url_);\n        return check !== false ? (('sp_' + check) as 'sp_track' | 'sp_album' | 'sp_playlist') : false;\n    } else if (url_.indexOf('soundcloud') !== -1) {\n        check = await so_validate(url_);\n        return check !== false ? (('so_' + check) as 'so_playlist' | 'so_track') : false;\n    } else if (url_.indexOf('deezer') !== -1) {\n        check = await dz_validate(url_);\n        return check !== false ? (('dz_' + check) as 'dz_track' | 'dz_playlist' | 'dz_album') : false;\n    } else {\n        check = yt_validate(url_);\n        return check !== false ? (('yt_' + check) as 'yt_video' | 'yt_playlist') : false;\n    }\n}\n/**\n * Authorization interface for Spotify, SoundCloud and YouTube.\n *\n * Either stores info in `.data` folder or shows relevant data to be used in `setToken` function.\n *\n * ```ts\n * const play = require('play-dl')\n *\n * play.authorization()\n * ```\n *\n * Just run the above command and you will get a interface asking some questions.\n */\nfunction authorization(): void {\n    const ask = createInterface({\n        input: process.stdin,\n        output: process.stdout\n    });\n    ask.question('Do you want to save data in a file ? (Yes / No): ', (msg) => {\n        let file: boolean;\n        if (msg.toLowerCase() === 'yes') file = true;\n        else if (msg.toLowerCase() === 'no') file = false;\n        else {\n            console.log(\"That option doesn't exist. Try again...\");\n            ask.close();\n            return;\n        }\n        ask.question('Choose your service - sc (for SoundCloud) / sp (for Spotify)  / yo (for YouTube): ', (msg) => {\n            if (msg.toLowerCase().startsWith('sp')) {\n                let client_id: string, client_secret: string, redirect_url: string, market: string;\n                ask.question('Start by entering your Client ID : ', (id) => {\n                    client_id = id;\n                    ask.question('Now enter your Client Secret : ', (secret) => {\n                        client_secret = secret;\n                        ask.question('Enter your Redirect URL now : ', (url) => {\n                            redirect_url = url;\n                            console.log(\n                                '\\nIf you would like to know your region code visit : \\nhttps://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements \\n'\n                            );\n                            ask.question('Enter your region code (2-letter country code) : ', (mar) => {\n                                if (mar.length === 2) market = mar;\n                                else {\n                                    console.log(\n                                        \"That doesn't look like a valid region code, IN will be selected as default.\"\n                                    );\n                                    market = 'IN';\n                                }\n                                console.log(\n                                    '\\nNow open your browser and paste the below url, then authorize it and copy the redirected url. \\n'\n                                );\n                                console.log(\n                                    `https://accounts.spotify.com/authorize?client_id=${client_id}&response_type=code&redirect_uri=${encodeURI(\n                                        redirect_url\n                                    )} \\n`\n                                );\n                                ask.question('Paste the url which you just copied : ', async (url) => {\n                                    if (!existsSync('.data')) mkdirSync('.data');\n                                    const spotifyData = {\n                                        client_id,\n                                        client_secret,\n                                        redirect_url,\n                                        authorization_code: url.split('code=')[1],\n                                        market\n                                    };\n                                    const check = await SpotifyAuthorize(spotifyData, file);\n                                    if (check === false) throw new Error('Failed to get access token.');\n                                    ask.close();\n                                });\n                            });\n                        });\n                    });\n                });\n            } else if (msg.toLowerCase().startsWith('sc')) {\n                if (!file) {\n                    console.log('You already had a client ID, just paste that in setToken function.');\n                    ask.close();\n                    return;\n                }\n                ask.question('Client ID : ', async (id) => {\n                    let client_id = id;\n                    if (!client_id) {\n                        console.log(\"You didn't provide a client ID. Try again...\");\n                        ask.close();\n                        return;\n                    }\n                    if (!existsSync('.data')) mkdirSync('.data');\n                    console.log('Validating your client ID, hold on...');\n                    if (await check_id(client_id)) {\n                        console.log('Client ID has been validated successfully.');\n                        writeFileSync('.data/soundcloud.data', JSON.stringify({ client_id }, undefined, 4));\n                    } else console.log(\"That doesn't look like a valid client ID. Retry with a correct client ID.\");\n                    ask.close();\n                });\n            } else if (msg.toLowerCase().startsWith('yo')) {\n                if (!file) {\n                    console.log('You already had cookie, just paste that in setToken function.');\n                    ask.close();\n                    return;\n                }\n                ask.question('Cookies : ', (cook: string) => {\n                    if (!cook || cook.length === 0) {\n                        console.log(\"You didn't provide a cookie. Try again...\");\n                        ask.close();\n                        return;\n                    }\n                    if (!existsSync('.data')) mkdirSync('.data');\n                    console.log('Cookies has been added successfully.');\n                    let cookie: Object = {};\n                    cook.split(';').forEach((x) => {\n                        const arr = x.split('=');\n                        if (arr.length <= 1) return;\n                        const key = arr.shift()?.trim() as string;\n                        const value = arr.join('=').trim();\n                        Object.assign(cookie, { [key]: value });\n                    });\n                    writeFileSync('.data/youtube.data', JSON.stringify({ cookie }, undefined, 4));\n                    ask.close();\n                });\n            } else {\n                console.log(\"That option doesn't exist. Try again...\");\n                ask.close();\n            }\n        });\n    });\n}\n/**\n * Attaches paused, playing, autoPaused Listeners to discordjs voice AudioPlayer.\n *\n * Useful if you don't want extra data to be downloaded by play-dl.\n * @param player discordjs voice AudioPlayer\n * @param resource A {@link YouTubeStream} or {@link SoundCloudStream}\n */\nfunction attachListeners(player: EventEmitter, resource: YouTubeStream | SoundCloudStream) {\n    // cleanup existing listeners if they are still registered\n    type listenerType = (...args: any[]) => void;\n\n    const listeners = player.listeners(AudioPlayerStatus.Idle);\n    for (const cleanup of listeners) {\n        if ((cleanup as any).__playDlAttachedListener) {\n            cleanup();\n            player.removeListener(AudioPlayerStatus.Idle, cleanup as listenerType);\n        }\n    }\n\n    const pauseListener = () => resource.pause();\n    const resumeListener = () => resource.resume();\n    const idleListener = () => {\n        player.removeListener(AudioPlayerStatus.Paused, pauseListener);\n        player.removeListener(AudioPlayerStatus.AutoPaused, pauseListener);\n        player.removeListener(AudioPlayerStatus.Playing, resumeListener);\n    };\n    pauseListener.__playDlAttachedListener = true;\n    resumeListener.__playDlAttachedListener = true;\n    idleListener.__playDlAttachedListener = true;\n    player.on(AudioPlayerStatus.Paused, pauseListener);\n    player.on(AudioPlayerStatus.AutoPaused, pauseListener);\n    player.on(AudioPlayerStatus.Playing, resumeListener);\n    player.once(AudioPlayerStatus.Idle, idleListener);\n}\n\n// Export Main Commands\nexport {\n    DeezerAlbum,\n    DeezerPlaylist,\n    DeezerTrack,\n    SoundCloudPlaylist,\n    SoundCloudStream,\n    SoundCloudTrack,\n    SpotifyAlbum,\n    SpotifyPlaylist,\n    SpotifyTrack,\n    YouTubeChannel,\n    YouTubePlayList,\n    YouTubeVideo,\n    attachListeners,\n    authorization,\n    decipher_info,\n    deezer,\n    dz_advanced_track_search,\n    dz_validate,\n    extractID,\n    getFreeClientID,\n    is_expired,\n    playlist_info,\n    refreshToken,\n    search,\n    setToken,\n    so_validate,\n    soundcloud,\n    spotify,\n    sp_validate,\n    stream,\n    stream_from_info,\n    validate,\n    video_basic_info,\n    video_info,\n    yt_validate,\n    InfoData\n};\n\n// Export Types\nexport { Deezer, YouTube, SoundCloud, Spotify, YouTubeStream };\n\n// Export Default\nexport default {\n    DeezerAlbum,\n    DeezerPlaylist,\n    DeezerTrack,\n    SoundCloudPlaylist,\n    SoundCloudStream,\n    SoundCloudTrack,\n    SpotifyAlbum,\n    SpotifyPlaylist,\n    SpotifyTrack,\n    YouTubeChannel,\n    YouTubePlayList,\n    YouTubeVideo,\n    attachListeners,\n    authorization,\n    decipher_info,\n    deezer,\n    dz_advanced_track_search,\n    dz_validate,\n    extractID,\n    getFreeClientID,\n    is_expired,\n    playlist_info,\n    refreshToken,\n    search,\n    setToken,\n    so_validate,\n    soundcloud,\n    spotify,\n    sp_validate,\n    stream,\n    stream_from_info,\n    validate,\n    video_basic_info,\n    video_info,\n    yt_validate\n};\n"],"mappings":"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","names":[]}