1 line
300 KiB
Plaintext
1 line
300 KiB
Plaintext
{"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":"iFACA,sCACA,gCACA,2FCHA,6EAEA,GAAI,GACJ,AAAI,GAAW,oBAAoB,GAC/B,GAAc,KAAK,MAAM,GAAa,qBAAsB,OAAO,CAAC,EACpE,EAAY,KAAO,IAQhB,aAA0C,CAC7C,GAAI,GAAS,GACb,GAAI,EAAC,GAAa,OAClB,QAAW,CAAC,EAAK,IAAU,QAAO,QAAQ,EAAY,MAAM,EACxD,GAAU,GAAG,KAAO,KAExB,MAAO,GACX,CAPgB,mBAST,YAAmB,EAAa,EAAwB,CAC3D,MAAK,IAAa,OAClB,GAAM,EAAI,KAAK,EACf,EAAQ,EAAM,KAAK,EACnB,OAAO,OAAO,EAAY,OAAQ,EAAG,GAAM,CAAM,CAAC,EAC3C,IAJ0B,EAKrC,CANgB,kBAQT,aAAwB,CAC3B,AAAI,EAAY,QAAU,EAAY,MAClC,GAAc,qBAAsB,KAAK,UAAU,EAAa,OAAW,CAAC,CAAC,CACrF,CAHgB,qBAKT,YAAwB,EAA6B,CACxD,GAAI,GAAO,EAAQ,OACf,EAAiB,CAAC,EACtB,EAAK,MAAM,GAAG,EAAE,QAAQ,AAAC,GAAM,CAC3B,GAAM,GAAM,EAAE,MAAM,GAAG,EACvB,GAAI,EAAI,QAAU,EAAG,OACrB,GAAM,GAAM,EAAI,MAAM,GAAG,KAAK,EACxB,EAAQ,EAAI,KAAK,GAAG,EAAE,KAAK,EACjC,OAAO,OAAO,EAAQ,EAAG,GAAM,CAAM,CAAC,CAC1C,CAAC,EACD,EAAc,CAAE,QAAO,EACvB,EAAY,KAAO,EACvB,CAZgB,uBA0BT,YAAuB,EAA4B,CACtD,AAAI,CAAC,GAAa,QAClB,GAAW,QAAQ,AAAC,GAAc,CAC9B,EAAE,MAAM,GAAG,EAAE,QAAQ,AAAC,GAAM,CACxB,GAAM,GAAM,EAAE,MAAM,GAAG,EACvB,GAAI,EAAI,QAAU,EAAG,OACrB,GAAM,GAAM,EAAI,MAAM,GAAG,KAAK,EACxB,EAAQ,EAAI,KAAK,GAAG,EAAE,KAAK,EACjC,GAAU,EAAK,CAAK,CACxB,CAAC,CACL,CAAC,EACD,GAAa,EACjB,CAZgB,2tCC3DT,YAAsB,EAAuB,CAChD,EAAW,KAAK,GAAG,CAAK,CAC5B,CAFgB,qBAIhB,YAAsB,EAAa,EAAqB,CACpD,SAAM,KAAK,KAAK,CAAG,EACnB,EAAM,KAAK,MAAM,CAAG,EACb,KAAK,MAAM,KAAK,OAAO,EAAK,GAAM,EAAM,EAAE,EAAI,CACzD,CAJS,qBAMF,aAA8B,CACjC,GAAM,GAAS,GAAa,EAAG,EAAW,OAAS,CAAC,EACpD,MAAO,GAAW,EACtB,CAHgB,2BFQT,WAAwB,EAAiB,EAAuB,CAAE,OAAQ,KAAM,EAA6B,CAChH,MAAO,IAAI,SAAQ,MAAO,EAAS,IAAW,CAC1C,GAAI,GAAM,KAAM,GAAa,EAAS,CAAO,EAAE,MAAM,AAAC,GAAe,CAAG,EACxE,GAAI,YAAe,OAAO,CACtB,EAAO,CAAG,EACV,MACJ,CACA,AAAI,OAAO,EAAI,UAAU,GAAK,KAAO,OAAO,EAAI,UAAU,EAAI,KAC1D,GAAM,KAAM,GAAe,EAAI,QAAQ,SAAoB,CAAO,GAEtE,EAAQ,CAAG,CACf,CAAC,CACL,CAZgB,sBAmBhB,YAAyB,EAAiB,EAAuB,CAAE,OAAQ,KAAM,EAA6B,CAC1G,MAAO,IAAI,SAAQ,MAAO,EAAS,IAAW,CAC1C,GAAI,GAAM,KAAM,GAAa,EAAS,CAAO,EAAE,MAAM,AAAC,GAAe,CAAG,EACxE,GAAI,YAAe,OAAO,CACtB,EAAO,CAAG,EACV,MACJ,CACA,GAAI,OAAO,EAAI,UAAU,GAAK,KAAO,OAAO,EAAI,UAAU,EAAI,IAC1D,EAAM,KAAM,IAAgB,EAAI,QAAQ,SAAoB,CAAO,UAC5D,OAAO,EAAI,UAAU,EAAI,IAAK,CACrC,EAAO,GAAI,OAAM,OAAO,EAAI,6BAA6B,CAAC,EAC1D,MACJ,CACA,EAAQ,CAAG,CACf,CAAC,CACL,CAfS,wBAsBF,WAAiB,EAAiB,EAAuB,CAAE,OAAQ,KAAM,EAAoB,CAChG,MAAO,IAAI,SAAQ,MAAO,EAAS,IAAW,CAC1C,GAAI,GAAgB,GACpB,GAAI,EAAQ,QAAS,CACjB,GAAI,GAAO,GAAW,EACtB,AAAI,MAAO,IAAS,UAAY,EAAQ,SACpC,QAAO,OAAO,EAAQ,QAAS,CAAE,OAAQ,CAAK,CAAC,EAC/C,EAAgB,GAExB,CACA,GAAI,EAAQ,UAAW,CACnB,GAAM,GAAU,CAAC,EACjB,OAAW,KAAU,QAAO,QAAQ,EAAQ,SAAS,EACjD,EAAQ,KAAK,EAAO,KAAK,GAAG,CAAC,EAGjC,GAAI,EAAQ,SAAW,EAAG,CACtB,AAAK,EAAQ,SAAS,GAAQ,QAAU,CAAC,GACzC,GAAM,GAAkB,EAAgB,KAAK,EAAQ,QAAQ,SAAW,GACxE,OAAO,OAAO,EAAQ,QAAS,CAAE,OAAQ,GAAG,EAAQ,KAAK,IAAI,IAAI,GAAkB,CAAC,CACxF,CACJ,CACA,AAAI,EAAQ,SACR,GAAQ,QAAU,IACX,EAAQ,QACX,kBAAmB,oBACnB,aAAc,GAAmB,CACrC,GAEJ,GAAM,GAAM,KAAM,IAAgB,EAAS,CAAO,EAAE,MAAM,AAAC,GAAe,CAAG,EAC7E,GAAI,YAAe,OAAO,CACtB,EAAO,CAAG,EACV,MACJ,CACA,GAAI,EAAI,SAAW,EAAI,QAAQ,cAAe,CAC1C,GAAI,EAAQ,UACR,OAAW,KAAU,GAAI,QAAQ,cAAe,CAC5C,GAAM,GAAQ,EAAO,MAAM,GAAG,EAAE,GAAG,KAAK,EAAE,MAAM,GAAG,EACnD,EAAQ,UAAU,EAAM,MAAM,GAAe,EAAM,KAAK,GAAG,CAC/D,CAEJ,AAAI,GACA,GAAc,EAAI,QAAQ,aAAa,CAE/C,CACA,GAAM,GAAiB,CAAC,EACpB,EACE,EAAW,EAAI,QAAQ,oBAC7B,AAAI,IAAa,OAAQ,EAAU,GAAa,EAC3C,AAAI,IAAa,KAAM,EAAU,GAAuB,EACpD,IAAa,WAAW,GAAU,GAAc,GAEzD,AAAI,EACA,GAAI,KAAK,CAAO,EAChB,EAAQ,YAAY,OAAO,EAC3B,EAAQ,GAAG,OAAQ,AAAC,GAAM,EAAK,KAAK,CAAC,CAAC,EACtC,EAAQ,GAAG,MAAO,IAAM,EAAQ,EAAK,KAAK,EAAE,CAAC,CAAC,GAE9C,GAAI,YAAY,OAAO,EACvB,EAAI,GAAG,OAAQ,AAAC,GAAM,EAAK,KAAK,CAAC,CAAC,EAClC,EAAI,GAAG,MAAO,IAAM,EAAQ,EAAK,KAAK,EAAE,CAAC,CAAC,EAElD,CAAC,CACL,CA/DgB,eAiET,WAAkC,EAA8B,CACnE,MAAO,IAAI,SAAQ,MAAO,EAAS,IAAW,CAC1C,GAAI,GAAM,KAAM,GAAa,EAAK,CAAE,OAAQ,MAAO,CAAC,EAAE,MAAM,AAAC,GAAe,CAAG,EAC/E,GAAI,YAAe,OAAO,CACtB,EAAO,CAAG,EACV,MACJ,CACA,GAAM,GAAa,OAAO,EAAI,UAAU,EACxC,GAAI,EAAa,IACb,EAAQ,CAAG,UACJ,EAAa,IAAK,CACzB,GAAM,GAAW,KAAM,GAAyB,EAAI,QAAQ,QAAkB,EAAE,MAAM,AAAC,GAAQ,CAAG,EAClG,GAAI,YAAoB,OAAO,CAC3B,EAAO,CAAQ,EACf,MACJ,CAEA,EAAQ,CAAQ,CACpB,KACI,GAAO,GAAI,OAAM,GAAG,EAAI,eAAe,EAAI,kBAAkB,GAAK,CAAC,CAE3E,CAAC,CACL,CAtBgB,gCAwBT,YAAgC,EAA8B,CACjE,MAAO,IAAI,SAAQ,MAAO,EAAS,IAAW,CAC1C,GAAI,GAAM,KAAM,GAAa,EAAK,CAAE,OAAQ,MAAO,CAAC,EAAE,MAAM,AAAC,GAAe,CAAG,EAC/E,GAAI,YAAe,OAAO,CACtB,EAAO,CAAG,EACV,MACJ,CACA,GAAM,GAAa,OAAO,EAAI,UAAU,EACxC,GAAI,EAAa,IACb,EAAQ,OAAO,EAAI,QAAQ,iBAAiB,CAAC,UACtC,EAAa,IAAK,CACzB,GAAM,GAAS,KAAM,GAAyB,EAAI,QAAQ,QAAkB,EAAE,MAAM,AAAC,GAAQ,CAAG,EAChG,GAAI,YAAkB,OAAO,CACzB,EAAO,CAAM,EACb,MACJ,CAEA,GAAM,GAAO,KAAM,IAAuB,CAAM,EAAE,MAAM,AAAC,GAAQ,CAAG,EACpE,GAAI,YAAgB,OAAO,CACvB,EAAO,CAAI,EACX,MACJ,CAEA,EAAQ,CAAI,CAChB,KACI,GACI,GAAI,OAAM,4CAA4C,EAAI,eAAe,EAAI,kBAAkB,GAAK,CACxG,CAER,CAAC,CACL,CA9BgB,+BAsChB,WAAsB,EAAiB,EAAuB,CAAC,EAA6B,CACxF,MAAO,IAAI,SAAQ,CAAC,EAAS,IAAW,CACpC,GAAM,GAAI,GAAI,IAAI,CAAO,EACzB,EAAQ,SAAW,MACnB,GAAM,GAA8B,CAChC,KAAM,EAAE,SACR,KAAM,EAAE,SAAW,EAAE,OACrB,QAAS,EAAQ,SAAW,CAAC,EAC7B,OAAQ,EAAQ,MACpB,EAEM,EAAM,GAAa,EAAa,CAAO,EAC7C,EAAI,GAAG,QAAS,AAAC,GAAQ,CACrB,EAAO,CAAG,CACd,CAAC,EACG,EAAQ,SAAW,QAAQ,EAAI,MAAM,EAAQ,IAAI,EACrD,EAAI,IAAI,CACZ,CAAC,CACL,CAlBS,oBG5LT,wCCAA,sDAWA,GAAM,GAAS,mBACT,GAAiB,wCACjB,GAAiB,wCACjB,GAAW,MAAM,MAAkB,MACnC,EAAS,MAAM,KAAU,MACzB,GAAU,SAAS,QAAa,SAChC,GAAW,YACX,GAAmB,sDACnB,GAAiB,kDACjB,GAAkB,8CAClB,GACF,oHAGE,GAAa,GAAI,QACnB,QAAQ,gBAAqB,IAAS,MAAoB,IAAS,MAAkB,IAAS,MAAmB,IAAS,sBAC9H,EACM,GAAkB,GAAI,QACxB,GACI,eAAe,8BAAwC,wBAAmC,MAC3F,uCACsB,UAE7B,EACM,GAAiB,GAAI,QAAO,WAAW,KAAU,KAAoB,GAAG,EACxE,GAAe,GAAI,QAAO,WAAW,KAAU,KAAkB,GAAG,EACpE,GAAgB,GAAI,QAAO,WAAW,KAAU,KAAmB,GAAG,EACtE,GAAc,GAAI,QAAO,WAAW,KAAU,KAAiB,GAAG,EAMxE,YAAmB,EAAc,CAC7B,GAAM,GAAkB,GAAgB,KAAK,CAAI,EAC3C,EAAgB,GAAW,KAAK,CAAI,EAC1C,GAAI,CAAC,GAAmB,CAAC,EAAe,MAAO,MAE/C,GAAM,GAAS,EAAc,GAAG,QAAQ,MAAO,KAAK,EAC9C,EAAc,EAAc,GAAG,QAAQ,MAAO,KAAK,EACnD,EAAgB,EAAgB,GAAG,QAAQ,MAAO,KAAK,EAEzD,EAAS,GAAe,KAAK,CAAW,EACtC,EAAa,GAAU,EAAO,GAAG,QAAQ,MAAO,KAAK,EAAE,QAAQ,kBAAmB,EAAE,EAE1F,EAAS,GAAa,KAAK,CAAW,EACtC,GAAM,GAAW,GAAU,EAAO,GAAG,QAAQ,MAAO,KAAK,EAAE,QAAQ,kBAAmB,EAAE,EAExF,EAAS,GAAc,KAAK,CAAW,EACvC,GAAM,GAAY,GAAU,EAAO,GAAG,QAAQ,MAAO,KAAK,EAAE,QAAQ,kBAAmB,EAAE,EAEzF,EAAS,GAAY,KAAK,CAAW,EACrC,GAAM,GAAU,GAAU,EAAO,GAAG,QAAQ,MAAO,KAAK,EAAE,QAAQ,kBAAmB,EAAE,EAEjF,EAAO,IAAI,CAAC,EAAY,EAAU,EAAW,CAAO,EAAE,KAAK,GAAG,KAC9D,EAAQ,UAAU,UAAe,SAAY,aAAgB,uBAC7D,EAAiB,GAAI,QAAO,EAAO,GAAG,EACtC,EAAS,CAAC,EAChB,KAAQ,GAAS,EAAe,KAAK,CAAa,KAAO,MAErD,OADY,EAAO,IAAM,EAAO,IAAM,EAAO,QAEpC,GACD,EAAO,KAAK,KAAK,EAAO,IAAI,EAC5B,UACC,GACD,EAAO,KAAK,IAAI,EAChB,UACC,GACD,EAAO,KAAK,KAAK,EAAO,IAAI,EAC5B,UACC,GACD,EAAO,KAAK,KAAK,EAAO,IAAI,EAC5B,MAGZ,MAAO,EACX,CA3CS,kBAkDT,YAA2B,EAAkB,EAAmB,CAC5D,GAAI,GAAM,EAAU,MAAM,EAAE,EACtB,EAAM,EAAO,OACnB,OAAS,GAAI,EAAG,EAAI,EAAK,IAAK,CAC1B,GAAI,GAAQ,EAAO,GACf,EACJ,OAAQ,EAAM,MAAM,EAAG,CAAC,OACf,KACD,EAAM,SAAS,EAAM,MAAM,CAAC,CAAC,EAC7B,GAAc,EAAK,CAAG,EACtB,UACC,KACD,EAAI,QAAQ,EACZ,UACC,KACD,EAAM,SAAS,EAAM,MAAM,CAAC,CAAC,EAC7B,EAAM,EAAI,MAAM,CAAG,EACnB,UACC,KACD,EAAM,SAAS,EAAM,MAAM,CAAC,CAAC,EAC7B,EAAI,OAAO,EAAG,CAAG,EACjB,MAEZ,CACA,MAAO,GAAI,KAAK,EAAE,CACtB,CAzBS,0BA+BT,YAAuB,EAAiB,EAAkB,CACtD,GAAM,GAAQ,EAAM,GACpB,EAAM,GAAK,EAAM,GACjB,EAAM,GAAY,CACtB,CAJS,sBAWT,YAAsB,EAAuB,EAAa,CACtD,GAAI,CAAC,EAAO,IAAK,OAEjB,GAAM,GAAc,mBAAmB,EAAO,GAAG,EAE3C,EAAa,GAAI,IAAI,CAAW,EACtC,EAAW,aAAa,IAAI,aAAc,KAAK,EAE3C,GACA,EAAW,aAAa,IAAI,EAAO,IAAM,YAAa,CAAG,EAE7D,EAAO,IAAM,EAAW,SAAS,CACrC,CAZS,qBAmBT,kBAAsC,EAA0B,EAA+C,CAC3G,GAAM,GAAO,KAAM,GAAQ,CAAW,EAChC,EAAS,GAAU,CAAI,EAC7B,SAAQ,QAAQ,AAAC,GAAW,CACxB,GAAM,GAAS,EAAO,iBAAmB,EAAO,OAChD,GAAI,EAAQ,CACR,GAAM,GAAS,OAAO,YAAY,GAAI,IAAgB,CAAM,CAAC,EAC7D,OAAO,OAAO,EAAQ,CAAM,EAC5B,MAAO,GAAO,gBACd,MAAO,GAAO,MAClB,CACA,GAAI,GAAU,EAAO,EAAG,CACpB,GAAM,GAAM,GAAkB,EAAQ,EAAO,CAAC,EAC9C,GAAa,EAAQ,CAAG,EACxB,MAAO,GAAO,EACd,MAAO,GAAO,EAClB,CACJ,CAAC,EACM,CACX,CAnBsB,wBC1If,WAAqB,CAqCxB,YAAY,EAAY,CAAC,EAAG,CACxB,GAAI,CAAC,EAAM,KAAM,IAAI,OAAM,0BAA0B,KAAK,YAAY,0BAA0B,EAChG,KAAK,KAAO,UACZ,KAAK,KAAO,EAAK,MAAQ,KACzB,KAAK,SAAW,CAAC,CAAC,EAAK,UAAY,GACnC,KAAK,OAAS,CAAC,CAAC,EAAK,QAAU,GAC/B,KAAK,GAAK,EAAK,IAAM,KACrB,KAAK,IAAM,EAAK,KAAO,KACvB,KAAK,MAAQ,EAAK,OAAS,CAAC,CAAE,IAAK,KAAM,MAAO,EAAG,OAAQ,CAAE,CAAC,EAC9D,KAAK,YAAc,EAAK,aAAe,IAC3C,CAOA,QAAQ,EAAU,CAAE,KAAM,CAAE,EAAuB,CAC/C,GAAI,MAAO,GAAQ,MAAS,UAAY,EAAQ,KAAO,EAAG,KAAM,IAAI,OAAM,mBAAmB,EAC7F,GAAI,CAAC,KAAK,QAAQ,IAAI,IAAK,OAC3B,GAAM,GAAM,KAAK,QAAQ,IAAI,IAAI,MAAM,IAAI,EAAE,GAAG,MAAM,IAAI,EAAE,GAC5D,MAAO,MAAK,QAAQ,IAAI,IAAI,QAAQ,KAAK,MAAS,KAAK,EAAQ,QAAQ,CAC3E,CAKA,UAAmB,CACf,MAAO,MAAK,MAAQ,EACxB,CAKA,QAAsB,CAClB,MAAO,CACH,KAAM,KAAK,KACX,SAAU,KAAK,SACf,OAAQ,KAAK,OACb,GAAI,KAAK,GACT,IAAK,KAAK,IACV,MAAO,KAAK,MACZ,KAAM,KAAK,KACX,YAAa,KAAK,WACtB,CACJ,CACJ,EAnFO,sBCjBA,WAAuB,CAK1B,YAAY,EAAW,CACnB,KAAK,IAAM,EAAK,IAChB,KAAK,MAAQ,EAAK,MAClB,KAAK,OAAS,EAAK,MACvB,CAEA,QAAS,CACL,MAAO,CACH,IAAK,KAAK,IACV,MAAO,KAAK,MACZ,OAAQ,KAAK,MACjB,CACJ,CACJ,EAlBO,wBC0HA,WAAmB,CAuFtB,YAAY,EAAW,CACnB,GAAI,CAAC,EAAM,KAAM,IAAI,OAAM,oBAAoB,KAAK,YAAY,mBAAmB,EAEnF,KAAK,GAAK,EAAK,IAAM,OACrB,KAAK,IAAM,mCAAmC,KAAK,KACnD,KAAK,KAAO,QACZ,KAAK,MAAQ,EAAK,OAAS,OAC3B,KAAK,YAAc,EAAK,aAAe,OACvC,KAAK,YAAc,EAAK,cAAgB,OACxC,KAAK,cAAiB,GAAK,SAAW,EAAI,EAAI,EAAK,WAAa,EAChE,KAAK,WAAa,EAAK,YAAc,OACrC,KAAK,OAAS,EAAK,QAAU,OAC7B,KAAK,SAAW,EAAK,SACrB,KAAK,MAAQ,SAAS,EAAK,KAAK,GAAK,EACrC,GAAM,GAAa,CAAC,EACpB,OAAW,KAAS,GAAK,WACrB,EAAW,KAAK,GAAI,GAAiB,CAAK,CAAC,EAE/C,KAAK,WAAa,GAAc,CAAC,EACjC,KAAK,QAAU,GAAI,GAAe,EAAK,OAAO,GAAK,CAAC,EACpD,KAAK,MAAQ,EAAK,OAAS,EAC3B,KAAK,KAAO,CAAC,CAAC,EAAK,KACnB,KAAK,QAAU,CAAC,CAAC,EAAK,QACtB,KAAK,KAAO,EAAK,MAAQ,CAAC,EAC1B,KAAK,kBAAoB,EAAK,mBAAqB,OACnD,KAAK,MAAQ,EAAK,OAAS,CAAC,EAC5B,KAAK,SAAW,EAAK,UAAY,CAAC,CACtC,CAKA,UAAmB,CACf,MAAO,MAAK,KAAO,EACvB,CAKA,QAAuB,CACnB,MAAO,CACH,GAAI,KAAK,GACT,IAAK,KAAK,IACV,MAAO,KAAK,MACZ,YAAa,KAAK,YAClB,cAAe,KAAK,cACpB,YAAa,KAAK,YAClB,WAAY,KAAK,WACjB,UAAW,KAAK,WAAW,KAAK,WAAW,OAAS,GAAG,OAAO,GAAK,KAAK,WACxE,QAAS,KAAK,QACd,MAAO,KAAK,MACZ,KAAM,KAAK,KACX,MAAO,KAAK,MACZ,KAAM,KAAK,KACX,QAAS,KAAK,QACd,kBAAmB,KAAK,kBACxB,MAAO,KAAK,MACZ,SAAU,KAAK,QACnB,CACJ,CACJ,EAnJO,oBCrHP,GAAM,IAAW,kDAIV,OAAsB,CAkEzB,YAAY,EAAW,EAAe,GAAO,CAdrC,mBAIJ,CAAC,EAWD,GAAI,CAAC,EAAM,KAAM,IAAI,OAAM,0BAA0B,KAAK,YAAY,0BAA0B,EAChG,KAAK,QAAU,EACf,KAAK,eAAiB,GAAI,KAC1B,KAAK,KAAO,WACZ,AAAI,EAAc,KAAK,cAAc,CAAI,EACpC,KAAK,QAAQ,CAAI,CAC1B,CAKQ,QAAQ,EAAW,CACvB,KAAK,GAAK,EAAK,IAAM,OACrB,KAAK,IAAM,EAAK,KAAO,OACvB,KAAK,MAAQ,EAAK,OAAS,OAC3B,KAAK,WAAa,EAAK,YAAc,EACrC,KAAK,WAAa,EAAK,YAAc,OACrC,KAAK,MAAQ,EAAK,OAAS,EAC3B,KAAK,KAAO,EAAK,MAAQ,OACzB,KAAK,QAAU,GAAI,GAAe,EAAK,OAAO,GAAK,OACnD,KAAK,UAAY,EAAK,UAAY,GAAI,GAAiB,EAAK,SAAS,EAAI,OACzE,KAAK,OAAS,EAAK,QAAU,CAAC,EAC9B,KAAK,UACL,KAAK,eAAe,IAAI,GAAG,KAAK,UAAW,KAAK,MAAwB,EACxE,KAAK,cAAc,IAAM,EAAK,cAAc,KAAO,OACnD,KAAK,cAAc,MAAQ,EAAK,cAAc,OAAS,OACvD,KAAK,cAAc,cAAgB,EAAK,cAAc,eAAiB,kBAC3E,CAKQ,cAAc,EAAW,CAC7B,KAAK,GAAK,EAAK,IAAM,OACrB,KAAK,IAAM,KAAK,GAAK,yCAAyC,KAAK,KAAO,OAC1E,KAAK,MAAQ,EAAK,OAAS,OAC3B,KAAK,UAAY,GAAI,GAAiB,EAAK,SAAS,GAAK,OACzD,KAAK,QAAU,EAAK,SAAW,OAC/B,KAAK,OAAS,CAAC,EACf,KAAK,WAAa,EAAK,QAAU,EACjC,KAAK,KAAO,OACZ,KAAK,WAAa,OAClB,KAAK,MAAQ,CACjB,MAQM,MAAK,EAAQ,IAAmC,CAClD,GAAI,CAAC,KAAK,eAAiB,CAAC,KAAK,cAAc,MAAO,MAAO,CAAC,EAE9D,GAAM,GAAW,KAAM,GAAQ,GAAG,KAAW,KAAK,cAAc,wBAAyB,CACrF,OAAQ,OACR,KAAM,KAAK,UAAU,CACjB,aAAc,KAAK,cAAc,MACjC,QAAS,CACL,OAAQ,CACJ,iBAAkB,EAClB,GAAI,KACJ,GAAI,KACJ,WAAY,MACZ,cAAe,KAAK,cAAc,aACtC,EACA,KAAM,CAAC,EACP,QAAS,CAAC,CACd,CACJ,CAAC,CACL,CAAC,EAEK,EACF,KAAK,MAAM,CAAQ,GAAG,0BAA0B,IAAI,+BAA+B,kBACvF,GAAI,CAAC,EAAU,MAAO,CAAC,EAEvB,GAAM,GAAkB,GAAkB,EAAU,CAAK,EACzD,YAAK,eAAe,IAAI,GAAG,KAAK,UAAW,CAAe,EAC1D,KAAK,cAAc,MAAQ,EAAqB,CAAQ,EACjD,CACX,MAUM,OAAM,EAAM,IAAoC,CAElD,GAAI,CADiB,KAAK,cAAc,MACrB,MAAO,MAG1B,IAFI,EAAM,GAAG,GAAM,KAEZ,MAAO,MAAK,cAAc,OAAU,UAAY,KAAK,cAAc,MAAM,QAAQ,CACpF,KAAK,UACL,GAAM,GAAM,KAAM,MAAK,KAAK,EAG5B,GAFA,GAAO,EAAI,OACP,GAAO,GACP,CAAC,EAAI,OAAQ,KACrB,CAEA,MAAO,KACX,CAiBA,KAAK,EAAgC,CACjC,GAAI,CAAC,EAAQ,KAAM,IAAI,OAAM,6BAA6B,EAC1D,GAAI,CAAC,KAAK,eAAe,IAAI,GAAG,GAAQ,EAAG,KAAM,IAAI,OAAM,8BAA8B,EACzF,MAAO,MAAK,eAAe,IAAI,GAAG,GAAQ,CAC9C,IAKI,cAAc,CACd,MAAO,MAAK,eAAe,IAC/B,IAMI,eAAe,CACf,GAAM,GAAsB,KAAK,YACjC,MAAQ,GAAc,GAAK,IAAO,KAAK,eAAe,IAAI,GAAG,GAAa,EAAqB,MACnG,MAYM,aAAsC,CACxC,KAAM,MAAK,MAAM,EAEjB,GAAM,GAAS,CAAC,EAEhB,OAAW,KAAQ,MAAK,eAAe,OAAO,EAAG,EAAO,KAAK,GAAG,CAAI,EAEpE,MAAO,EACX,CAKA,QAAuB,CACnB,MAAO,CACH,GAAI,KAAK,GACT,MAAO,KAAK,MACZ,UAAW,KAAK,WAAW,OAAO,GAAK,KAAK,UAC5C,QAAS,KAAK,QACd,IAAK,KAAK,IACV,OAAQ,KAAK,MACjB,CACJ,CACJ,EAnPO,uBCJP,sDAaA,GAAM,GAAmB,wBACnB,GAAsB,sCACtB,EAAkB,0CAClB,GACF,wIACE,GACF,0JAkBG,WAAqB,EAAsD,CAC9E,GAAM,GAAO,EAAI,KAAK,EACtB,GAAI,EAAK,QAAQ,OAAO,IAAM,GAC1B,GAAI,EAAK,WAAW,OAAO,EACvB,GAAI,EAAK,MAAM,EAAa,EAAG,CAC3B,GAAI,GAOJ,MANA,AAAI,GAAK,SAAS,WAAW,EAAG,EAAK,EAAK,MAAM,WAAW,EAAE,GAAG,MAAM,WAAW,EAAE,GAC9E,AAAI,EAAK,SAAS,oBAAoB,EACvC,EAAK,EAAK,MAAM,oBAAoB,EAAE,GAAG,MAAM,WAAW,EAAE,GAC3D,AAAI,EAAK,SAAS,qBAAqB,EACxC,EAAK,EAAK,MAAM,qBAAqB,EAAE,GAAG,MAAM,WAAW,EAAE,GAC5D,EAAK,EAAK,MAAM,UAAU,EAAE,IAAI,MAAM,WAAW,EAAE,GACpD,GAAI,MAAM,CAAgB,EAAU,QAC5B,EAChB,KAAO,OAAO,OAEd,OAAI,GAAK,MAAM,CAAgB,EAAU,QAChC,EAAK,MAAM,EAAmB,EAAU,WACrC,aAGhB,OAAK,GAAK,MAAM,EAAgB,EACpB,WAD8B,EAAY,EAAK,QAAQ,oBAAqB,EAAE,CAAC,CAGnG,CAxBgB,mBAgChB,YAAwB,EAAiC,CACrD,GAAI,EAAQ,WAAW,UAAU,GAAK,EAAQ,MAAM,EAAa,EAAG,CAChE,GAAI,GAaJ,GAZA,AAAI,EAAQ,SAAS,WAAW,EAC5B,EAAK,EAAQ,MAAM,WAAW,EAAE,GAAG,MAAM,WAAW,EAAE,GACnD,AAAI,EAAQ,SAAS,oBAAoB,EAC5C,EAAK,EAAQ,MAAM,oBAAoB,EAAE,GAAG,MAAM,WAAW,EAAE,GAC5D,AAAI,EAAQ,SAAS,qBAAqB,EAC7C,EAAK,EAAQ,MAAM,qBAAqB,EAAE,GAAG,MAAM,WAAW,EAAE,GAC7D,AAAI,EAAQ,SAAS,mBAAmB,EAC3C,EAAK,EAAQ,MAAM,mBAAmB,EAAE,GAAG,MAAM,WAAW,EAAE,GAE9D,EAAM,GAAQ,MAAM,UAAU,EAAE,IAAM,EAAQ,MAAM,KAAK,EAAE,IAAI,MAAM,WAAW,EAAE,GAGlF,EAAG,MAAM,CAAgB,EAAG,MAAO,EAC3C,SAAW,EAAQ,MAAM,CAAgB,EACrC,MAAO,GAGX,MAAO,EACX,CArBS,uBA2BF,YAAmB,EAAqB,CAC3C,GAAM,GAAQ,EAAY,CAAG,EAC7B,GAAI,CAAC,GAAS,IAAU,SAAU,KAAM,IAAI,OAAM,oDAAoD,EACtG,GAAM,GAAO,EAAI,KAAK,EACtB,GAAI,EAAK,WAAW,OAAO,EACvB,GAAI,EAAK,QAAQ,OAAO,IAAM,GAAI,CAC9B,GAAM,GAAW,GAAe,CAAI,EACpC,GAAI,CAAC,EAAU,KAAM,IAAI,OAAM,oDAAoD,EACnF,MAAO,EACX,KACI,OAAO,GAAK,MAAM,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,OAE1C,OAAO,EAClB,CAbgB,kBA8BhB,kBAAuC,EAAa,EAAuB,CAAC,EAAsB,CAC9F,GAAI,MAAO,IAAQ,SAAU,KAAM,IAAI,OAAM,uDAAuD,EACpG,GAAM,GAAO,EAAI,KAAK,EAClB,EACE,EAAY,CAAC,EACnB,GAAI,EAAQ,SACR,EAAO,MACJ,CACH,GAAM,GAAW,GAAe,CAAI,EACpC,GAAI,CAAC,EAAU,KAAM,IAAI,OAAM,iCAAiC,EAChE,GAAM,GAAU,mCAAmC,mBACnD,EAAO,KAAM,GAAQ,EAAS,CAC1B,QAAS,CACL,kBAAmB,EAAQ,UAAY,aAC3C,EACA,QAAS,GACT,WACJ,CAAC,CACL,CACA,GAAI,EAAK,QAAQ,uEAAuE,IAAM,GAC1F,KAAM,IAAI,OAAM,wDAAwD,EAC5E,GAAM,GAAc,EACf,MAAM,gCAAgC,IAAI,IACzC,MAAM,aAAY,EAAE,GACrB,MAAM,+BAA+B,EAAE,GAC5C,GAAI,CAAC,EAAa,KAAM,IAAI,OAAM,4CAA4C,EAC9E,GAAM,GAAe,EAChB,MAAM,sBAAsB,IAAI,IAC/B,MAAM,aAAY,EAAE,GACrB,MAAM,uBAAuB,EAAE,GACpC,GAAI,CAAC,EAAc,KAAM,IAAI,OAAM,qCAAqC,EACxE,GAAM,GAAkB,KAAK,MAAM,CAAW,EACxC,EAAmB,KAAK,MAAM,CAAY,EAC1C,EAAM,EAAgB,aAExB,EAAoB,GACpB,EAAW,GACf,GAAI,EAAgB,kBAAkB,SAAW,KAC7C,GAAI,EAAgB,kBAAkB,SAAW,yBAA0B,CACvE,GAAI,EAAQ,SACR,KAAM,IAAI,OACN,gFAAgF,EAAI,SACxF,EACJ,EAAoB,GACpB,GAAM,GACF,EAAiB,OAAO,sBAAsB,cAAc,sBAAsB,YAC7E,eAAe,QAAQ,kBAChC,AAAI,GACA,OAAO,OAAO,EAAW,CACrB,mBAAoB,EAAQ,cAC5B,QAAS,EAAQ,aACrB,CAAC,EAGL,GAAM,GAAgB,KAAM,IAAuB,EAAI,QAAS,EAAW,EAAM,EAAI,EACrF,EAAgB,cAAgB,EAAc,cAC9C,EAAiB,SAAS,0BAA0B,iBAAmB,EAAc,aACzF,SAAW,EAAgB,kBAAkB,SAAW,sBAAuB,EAAW,OAEtF,MAAM,IAAI,OACN;AAAA,EACI,EAAgB,kBAAkB,YAAY,4BAA4B,OAAO,YACjF,EAAgB,kBAAkB,YAAY,mBAAmB,OAAO,YACxE,EAAgB,kBAAkB,QAE1C,EAER,GAAM,GACF,EAAiB,SAAS,0BAA0B,SAAS,SAAS,SAAS,IAAI,4BAC7E,OAAO,mBACX,EAAQ,GAAW,SAAS,IAAI,uBAAuB,OAAO,YAAY,EAC1E,EAAc,0BAA0B,EAAK,MAAM,WAAW,EAAE,GAAG,MAAM,GAAG,EAAE,KAC9E,EAAoB,CAAC,EAC3B,EAAiB,SAAS,0BAA0B,iBAAiB,iBAAiB,QAAQ,QAC1F,AAAC,GAAa,CACV,AAAI,EAAI,sBACJ,EAAQ,KAAK,mCAAmC,EAAI,qBAAqB,SAAS,EAClF,EAAI,qBAAqB,UACzB,EAAI,oBAAoB,SAAS,QAAQ,AAAC,GAAW,CACjD,AAAI,EAAE,sBACF,EAAQ,KAAK,mCAAmC,EAAE,qBAAqB,SAAS,CACxF,CAAC,CACT,CACJ,EACA,GAAM,GAAc,EAAgB,YAAY,0BAC1C,GAAY,EAAiB,iBAAiB,KAAK,AAAC,GAAc,GAAM,oCAAoC,iBAAmB,yCAAyC,GAAG,mCAAmC,QAAQ,qCAAqC,MAC5P,KAAK,AAAC,GAAY,EAAG,oCAAoC,GAAG,qCAAqC,gBAEhG,GAAe,CAAC,EACtB,AAAI,IACA,GAAU,QAAQ,AAAC,GAAW,CAC1B,GAAI,CAAC,EAAE,uBAAwB,OAC/B,GAAM,GAAM,EAAE,uBAER,GAAO,EAAI,aAAa,qBAAqB,MAAM,YAAc,EAAI,aAAa,qBAAqB,MAAM,MAAM,KAAK,AAAC,GAAU,EAAE,IAAI,GAAG,KAC5I,GAAW,EAAI,UAAU,IAAI,AAAC,GAAc,CAAC,EAAK,gBAAgB,MAAM,WAAW,YAAY,EAAK,GAAK,gBAAgB,kBAAoB,EAAK,gBAAgB,kBAAkB,MAAM,IAAI,AAAC,IAAU,GAAE,IAAI,EAAE,KAAK,EAAE,GAAM,EAAK,gBAAgB,iBAAiB,YAAc,EAAK,gBAAgB,kBAAkB,YAAc,EAAE,CAAC,EAC1U,GAAW,OAAO,YAAY,IAAY,CAAC,CAAC,EAC5C,GAAK,EAAI,aAAa,qBAAqB,oBAAoB,cAAc,SAC5E,EAAI,UAAU,KAAK,AAAC,GAAW,EAAE,gBAAgB,MAAM,WAAW,YAAY,GAAK,MAAM,GAAG,gBAAgB,gBAAgB,MAAM,KAAK,AAAC,GAAW,EAAE,kBAAkB,GAAG,mBAAmB,eAAe,QAEnN,GAAM,KAAK,CAAC,QAAM,IAAK,GAAK,mCAAmC,KAAO,QAAS,EAAQ,CAAC,CAC5F,CAAC,EAEL,GAAM,IACF,EAAiB,eAAe,sBAAsB,4BAA4B,2BAA2B,WAAW,8BAA8B,YAAY,KAC9J,AAAC,GAAW,EAAE,MAAQ,sBAC1B,GAAG,OAAO,SACR,GAA2B,CAAC,EAClC,GAAI,GACA,OAAW,CAAE,oBAAqB,IAC9B,GAAS,KAAK,CACV,MAAO,EAAgB,MAAM,WAC7B,UAAW,GAAa,EAAgB,qBAAuB,GAAI,EACnE,QAAS,EAAgB,qBAAuB,IAChD,WAAY,EAAgB,UAAU,UAC1C,CAAC,EAGT,GAAI,IACJ,GAAI,EACA,GAAI,EAAY,qBAAqB,eACjC,GAAe,GAAI,MAAK,EAAY,qBAAqB,cAAc,MACtE,CACD,GAAM,GACF,EAAgB,kBAAkB,kBAAkB,0BAA0B,aACzE,+BAA+B,mBACxC,GAAe,GAAI,MAAK,SAAS,CAAS,EAAI,GAAI,CACtD,CAGJ,GAAM,IAAe,EAAiB,SAAS,0BAA0B,QAAQ,QAAQ,SACpF,KAAK,AAAC,GAAiB,EAAQ,wBAAwB,GACtD,yBAAyB,aAAa,aAAa,iBAAiB,KAClE,AAAC,GAAgB,EAAO,sBAAsB,YAAY,WAAa,QAAU,EAAO,oCAAoC,WAAW,sBAAsB,YAAY,WAAa,MAC1L,EAEE,GAAgB,GAAI,GAAa,CACnC,GAAI,EAAI,QACR,MAAO,EAAI,MACX,YAAa,EAAI,iBACjB,SAAU,OAAO,EAAI,aAAa,EAClC,aAAc,GAAa,EAAI,aAAa,EAC5C,WAAY,EAAY,YACxB,OAAQ,EAAY,sBAAsB,eAC1C,SAAU,GACV,WAAY,EAAI,UAAU,WAC1B,QAAS,CACL,KAAM,EAAI,OACV,GAAI,EAAI,UACR,IAAK,mCAAmC,EAAI,YAC5C,SAAU,QAAQ,GAAO,SAAS,UAAU,CAAC,EAC7C,OAAQ,QAAQ,GAAO,SAAS,QAAQ,CAAC,EACzC,MAAO,GAAW,WAAW,YAAc,MAC/C,EACA,MAAO,EAAI,UACX,KAAM,EAAI,SACV,MAAO,SACH,IAAc,sBAAsB,YAAY,eAAe,kBAAkB,MAAM,QAAQ,OAAQ,EAAE,GACzG,IAAc,oCAAoC,WAAW,sBAAsB,YAAY,eAAe,kBAAkB,MAAM,QAAQ,OAAQ,EAAE,GAAK,CACjK,EACA,KAAM,EAAI,cACV,QAAS,EAAI,UACb,oBACA,SACA,WACJ,CAAC,EACG,EAAS,CAAC,EACd,MAAK,IACD,GAAO,KAAK,GAAI,EAAgB,cAAc,SAAW,CAAC,CAAE,EAC5D,EAAO,KAAK,GAAI,EAAgB,cAAc,iBAAmB,CAAC,CAAE,EAKhE,EAAkB,CAAM,EAAE,SAAW,GAAK,CAAC,EAAQ,UACnD,GAAS,KAAM,IAAkB,EAAI,QAAS,EAAW,CAAI,IAQ9D,CACH,eANmB,CACnB,OAAQ,GAAc,KACtB,gBAAiB,EAAgB,eAAe,iBAAmB,KACnE,eAAgB,EAAgB,eAAe,gBAAkB,IACrE,EAGI,cACA,SACA,iBACA,eAAgB,CACpB,CACJ,CA9LsB,yBA0MtB,iBAAwC,EAAa,EAAuB,CAAC,EAA4B,CACrG,GAAI,MAAO,IAAQ,SAAU,KAAM,IAAI,OAAM,uDAAuD,EACpG,GAAI,GACE,EAAY,CAAC,EACnB,GAAI,EAAQ,SACR,EAAO,MACJ,CACH,GAAM,GAAW,GAAe,CAAG,EACnC,GAAI,CAAC,EAAU,KAAM,IAAI,OAAM,iCAAiC,EAChE,GAAM,GAAU,mCAAmC,mBACnD,EAAO,KAAM,GAAQ,EAAS,CAC1B,QAAS,CAAE,kBAAmB,gBAAiB,EAC/C,QAAS,GACT,WACJ,CAAC,CACL,CACA,GAAI,EAAK,QAAQ,uEAAuE,IAAM,GAC1F,KAAM,IAAI,OAAM,wDAAwD,EAC5E,GAAM,GAAc,EACf,MAAM,gCAAgC,IAAI,IACzC,MAAM,aAAY,EAAE,GACrB,MAAM,+BAA+B,EAAE,GAC5C,GAAI,CAAC,EAAa,KAAM,IAAI,OAAM,4CAA4C,EAC9E,GAAM,GAAkB,KAAK,MAAM,CAAW,EAC1C,EAAW,GACf,GAAI,EAAgB,kBAAkB,SAAW,KAC7C,GAAI,EAAgB,kBAAkB,SAAW,yBAA0B,CACvE,GAAI,EAAQ,SACR,KAAM,IAAI,OACN,gFAAgF,EAAgB,aAAa,SACjH,EAEJ,GAAM,GAAe,EAChB,MAAM,sBAAsB,IAAI,IAC/B,MAAM,aAAY,EAAE,GACrB,MAAM,uBAAuB,EAAE,GACpC,GAAI,CAAC,EAAc,KAAM,IAAI,OAAM,qCAAqC,EAExE,GAAM,GACF,KAAK,MAAM,CAAY,EAAE,OAAO,sBAAsB,cAAc,sBAAsB,YACrF,eAAe,QAAQ,kBAChC,AAAI,GACA,OAAO,OAAO,EAAW,CACrB,mBAAoB,EAAQ,cAC5B,QAAS,EAAQ,aACrB,CAAC,EAGL,GAAM,GAAgB,KAAM,IACxB,EAAgB,aAAa,QAC7B,EACA,EACA,EACJ,EACA,EAAgB,cAAgB,EAAc,aAClD,SAAW,EAAgB,kBAAkB,SAAW,sBAAuB,EAAW,OAEtF,MAAM,IAAI,OACN;AAAA,EACI,EAAgB,kBAAkB,YAAY,4BAA4B,OAAO,YACjF,EAAgB,kBAAkB,YAAY,mBAAmB,OAAO,YACxE,EAAgB,kBAAkB,QAE1C,EAER,GAAM,GAAc,0BAA0B,EAAK,MAAM,WAAW,EAAE,GAAG,MAAM,GAAG,EAAE,KAC9E,EAAW,OAAO,EAAgB,aAAa,aAAa,EAC5D,EAAgB,CAClB,IAAK,mCAAmC,EAAgB,aAAa,UACrE,cAAgB,GAAW,EAAI,EAAI,IAAa,CACpD,EACI,EAAS,CAAC,EACd,AAAK,GACD,GAAO,KAAK,GAAI,EAAgB,cAAc,SAAW,CAAC,CAAE,EAC5D,EAAO,KAAK,GAAI,EAAgB,cAAc,iBAAmB,CAAC,CAAE,EAKhE,EAAkB,CAAM,EAAE,SAAW,GAAK,CAAC,EAAQ,UACnD,GAAS,KAAM,IAAkB,EAAgB,aAAa,QAAS,EAAW,CAAI,IAI9F,GAAM,GAAiB,CACnB,OAAQ,EAAgB,aAAa,cACrC,gBAAiB,EAAgB,eAAe,iBAAmB,KACnE,eAAgB,EAAgB,eAAe,gBAAkB,IACrE,EACA,MAAO,MAAM,GACT,CACI,iBACA,cACA,SACA,eACJ,EACA,EACJ,CACJ,CAlGsB,yBAwGtB,YAAsB,EAAyB,CAC3C,GAAM,GAAI,OAAO,CAAO,EAClB,EAAI,KAAK,MAAM,EAAI,IAAI,EACvB,EAAI,KAAK,MAAO,EAAI,KAAQ,EAAE,EAC9B,EAAI,KAAK,MAAO,EAAI,KAAQ,EAAE,EAE9B,EAAW,EAAI,EAAK,GAAI,GAAK,IAAI,IAAM,GAAK,IAAM,GAClD,EAAW,EAAI,EAAK,GAAI,GAAK,IAAI,IAAM,GAAK,IAAM,MAClD,EAAW,EAAI,EAAK,EAAI,GAAK,IAAI,IAAM,EAAK,KAClD,MAAO,GAAW,EAAW,CACjC,CAVS,qBA8BT,kBAAiC,EAAa,EAAuB,CAAC,EAAsB,CACxF,GAAM,GAAO,KAAM,IAAiB,EAAI,KAAK,EAAG,CAAO,EACvD,MAAO,MAAM,GAAc,CAAI,CACnC,CAHsB,mBAUtB,iBACI,EACA,EAAsB,GACZ,CACV,MACI,GAAK,eAAe,SAAW,IAC/B,EAAK,eAAe,kBAAoB,MACxC,EAAK,cAAc,gBAAkB,GAG9B,EAAK,OAAO,OAAS,GAAM,GAAK,OAAO,GAAG,iBAAmB,EAAK,OAAO,GAAG,SAC/E,IAAY,GAAK,OAAS,EAAkB,EAAK,MAAM,GAC3D,EAAK,OAAS,KAAM,IAAgB,EAAK,OAAQ,EAAK,WAAW,GAC1D,CAEf,CAfsB,qBAkCtB,kBAAoC,EAAa,EAA2B,CAAC,EAA6B,CACtG,GAAI,CAAC,GAAO,MAAO,IAAQ,SAAU,KAAM,IAAI,OAAM,mCAAmC,MAAO,KAAM,EACrG,GAAI,GAAO,EAAI,KAAK,EAEpB,GADK,EAAK,WAAW,OAAO,GAAG,GAAO,yCAAyC,KAC3E,EAAK,QAAQ,OAAO,IAAM,GAAI,KAAM,IAAI,OAAM,4BAA4B,EAE9E,GAAI,EAAK,SAAS,mBAAmB,EAAG,CACpC,GAAM,GAAS,GAAI,IAAI,CAAI,EAC3B,EAAO,SAAW,kBAClB,EAAO,EAAO,SAAS,CAC3B,CAEA,GAAM,GAAO,KAAM,GAAQ,EAAM,CAC7B,QAAS,CACL,kBAAmB,EAAQ,UAAY,aAC3C,CACJ,CAAC,EACD,GAAI,EAAK,QAAQ,uEAAuE,IAAM,GAC1F,KAAM,IAAI,OAAM,wDAAwD,EAC5E,GAAM,GAAW,KAAK,MAClB,EACK,MAAM,sBAAsB,EAAE,GAC9B,MAAM,aAAY,EAAE,GACpB,MAAM,uBAAuB,EAAE,EACxC,EACA,GAAI,EAAS,OACT,GAAI,EAAS,OAAO,GAAG,yBAAyB,OAAS,QACrD,GAAI,CAAC,EAAQ,WACT,KAAM,IAAI,OACN;AAAA,EAA+B,EAAS,OAAO,GAAG,wBAAwB,KAAK,YACnF,MACD,MAAI,GAAS,OAAO,GAAG,eAAe,OAAS,QAC5C,GAAI,OAAM;AAAA,EAA+B,EAAS,OAAO,GAAG,cAAc,KAAK,KAAK,GAAG,MAAM,EAC5F,GAAI,OAAM;AAAA,uBAAoD,EAE7E,MAAI,GAAS,qBACF,GAAiB,EAAU,EAAM,CAAI,EAClC,GAAkB,EAAU,CAAI,CAClD,CAtCsB,sBA6Cf,YAA2B,EAAW,EAAQ,IAA0B,CAC3E,GAAM,GAAS,CAAC,EAEhB,OAAS,GAAI,EAAG,EAAI,EAAK,QACjB,IAAU,EAAO,OADQ,IAAK,CAElC,GAAM,GAAO,EAAK,GAAG,sBACrB,AAAI,CAAC,GAAQ,CAAC,EAAK,iBAEnB,EAAO,KACH,GAAI,GAAa,CACb,GAAI,EAAK,QACT,SAAU,SAAS,EAAK,aAAa,GAAK,EAC1C,aAAc,EAAK,YAAY,YAAc,OAC7C,WAAY,EAAK,UAAU,WAC3B,MAAO,EAAK,MAAM,KAAK,GAAG,KAC1B,SAAU,EAAK,mBAAmB,UAC5B,GAAI,MAAK,SAAS,EAAK,kBAAkB,SAAS,EAAI,GAAI,EAC1D,OACN,QAAS,CACL,GAAI,EAAK,gBAAgB,KAAK,GAAG,mBAAmB,eAAe,UAAY,OAC/E,KAAM,EAAK,gBAAgB,KAAK,GAAG,MAAQ,OAC3C,IAAK,0BACD,EAAK,gBAAgB,KAAK,GAAG,mBAAmB,eAAe,kBAC/D,EAAK,gBAAgB,KAAK,GAAG,mBAAmB,gBAAgB,mBAAmB,MAEvF,KAAM,MACV,CACJ,CAAC,CACL,CACJ,CACA,MAAO,EACX,CA/BgB,0BAqCT,WAA8B,EAAmB,CACpD,MAAO,GAAK,KAAK,AAAC,GAAW,OAAO,KAAK,CAAC,EAAE,KAAO,0BAA0B,GAAG,yBAC3E,sBAAsB,qBAAqB,KACpD,CAHgB,4BAKhB,kBACI,EACA,EACA,EACA,EACoD,CACpD,GAAM,GACF,EAAK,MAAM,sBAAsB,EAAE,IAAI,MAAM,GAAG,EAAE,IAClD,EAAK,MAAM,oBAAoB,EAAE,IAAI,MAAM,GAAG,EAAE,IAChD,EACE,EACF,EAAK,MAAM,gBAAgB,EAAE,IAAI,MAAM,GAAG,EAAE,GAAG,WAAW,UAAW,GAAG,GACxE,EAAK,MAAM,gBAAgB,EAAE,IAAI,MAAM,GAAG,EAAE,GAAG,WAAW,UAAW,GAAG,EAC5E,GAAI,CAAC,EACD,KAAM,IAAI,OAAM,iFAAiF,IAAU,EAE/G,GAAM,GAAuB,KAAM,GAAQ,sDAAsD,sBAA4B,CACzH,OAAQ,OACR,KAAM,KAAK,UAAU,CACjB,QAAS,CACL,OAAQ,CACJ,iBAAkB,EAClB,GAAI,KACJ,GAAI,KACJ,WAAY,MACZ,cACI,EAAK,MAAM,sCAAsC,EAAE,IAAI,MAAM,GAAG,EAAE,IAClE,EAAK,MAAM,sCAAsC,EAAE,IAAI,MAAM,GAAG,EAAE,IAClE,gBACR,EACA,KAAM,CAAC,EACP,QAAS,CAAC,CACd,EACA,aAAc,CACV,YAAa,CACT,IAAK,YAAY,kBACrB,CACJ,EACA,eAAgB,EACpB,CAAC,EACD,QAAS,GACT,WACJ,CAAC,EAEK,EAAW,KAAK,MAAM,CAAoB,EAAE,QAAQ,GAAG,eAAe,SAEtE,EAAY,KAAM,GAAQ,2BAA2B,EAAS,YAAY,YAAa,CACzF,OAAQ,OACR,QAAS,CACL,eAAgB,mCACpB,EACA,KAAM,GAAI,IAAgB,CACtB,CAAC,UAAW,KAAK,UAAU,CAAQ,CAAC,EACpC,CAAC,gBAAiB,CAAY,CAClC,CAAC,EAAE,SAAS,EACZ,QAAS,GACT,WACJ,CAAC,EAED,GAAI,EAAU,SAAS,+BAA+B,EAClD,KAAM,IAAI,OAAM,2DAA2D,GAAS,EAExF,GAAM,GAAgB,KAAK,MAAM,CAAS,EAE1C,GAAI,EAAc,GAAG,eAAe,kBAAkB,SAAW,KAC7D,KAAM,IAAI,OACN,qFAAqF;AAAA,EACjF,EAAc,GAAG,eAAe,kBAAkB,YAAY,4BAA4B,OACrF,YACL,EAAc,GAAG,eAAe,kBAAkB,YAAY,mBAAmB,OAAO,YAEhG,EAEJ,GAAM,GAAgB,EAAc,GAAG,eAAe,cAEtD,MAAI,GACO,CACH,gBACA,cAAe,EAAc,GAAG,SAAS,SAAS,0BAA0B,gBAChF,EAEG,CAAE,eAAc,CAC3B,CAlFe,+BAoFf,kBAAiC,EAAiB,EAAsC,EAA8B,CAClH,GAAM,GACF,EAAK,MAAM,sBAAsB,EAAE,IAAI,MAAM,GAAG,EAAE,IAClD,EAAK,MAAM,oBAAoB,EAAE,IAAI,MAAM,GAAG,EAAE,IAChD,EAEE,EAAW,KAAM,GAAQ,kDAAkD,sBAA4B,CACzG,OAAQ,OACR,KAAM,KAAK,UAAU,CACjB,QAAS,CACL,OAAQ,CACJ,WAAY,UACZ,cAAe,QACf,GAAI,KACJ,SAAU,MACV,iBAAkB,CACtB,CACJ,EACA,QAAS,EACT,gBAAiB,CAAE,uBAAwB,CAAE,gBAAiB,kBAAmB,CAAE,EACnF,eAAgB,GAChB,YAAa,EACjB,CAAC,EACD,QAAS,GACT,WACJ,CAAC,EAED,MAAO,MAAK,MAAM,CAAQ,EAAE,cAAc,OAC9C,CA5Be,0BA8Bf,YAA0B,EAAe,EAAW,EAA8B,CAC9E,GAAM,GAAmB,EAAS,SAAS,0BAA0B,UAAU,SAC/E,GAAI,CAAC,EACD,KAAM,IAAI,OAAM,2DAA2D,EAE/E,GAAM,GAAS,GAAuB,EAAiB,QAAQ,EACzD,EACF,EAAK,MAAM,sBAAsB,EAAE,IAAI,MAAM,GAAG,EAAE,IAClD,EAAK,MAAM,oBAAoB,EAAE,IAAI,MAAM,GAAG,EAAE,IAChD,EAEE,EAAa,EAAiB,YAC9B,EAAU,EAAiB,iBAAiB,OAAO,GACnD,EAAQ,EAAiB,SAAS,IAAI,uBAAuB,MAAM,YAAY,EAErF,MAAO,IAAI,GAAgB,CACvB,aAAc,CACV,IAAK,EACL,MAAO,EAAqB,EAAiB,QAAQ,EACrD,cACI,EAAK,MAAM,sCAAsC,EAAE,IAAI,MAAM,GAAG,EAAE,IAClE,EAAK,MAAM,sCAAsC,EAAE,IAAI,MAAM,GAAG,EAAE,IAClE,gBACR,EACA,GAAI,EAAiB,YAAc,GACnC,MAAO,EAAiB,OAAS,GACjC,WAAY,SAAS,CAAU,GAAK,EACpC,OAAQ,EACR,IAAK,EACL,QAAS,CACL,GAAI,GAAS,oBAAoB,gBAAgB,UAAY,KAC7D,KAAM,GAAS,MAAQ,KACvB,IAAK,0BACD,GAAS,oBAAoB,gBAAgB,kBAC7C,GAAS,oBAAoB,iBAAiB,oBAAoB,MAEtE,SAAU,QAAQ,GAAO,SAAS,UAAU,CAAC,EAC7C,OAAQ,QAAQ,GAAO,SAAS,QAAQ,CAAC,CAC7C,CACJ,CAAC,CACL,CAxCS,yBA0CT,YAA2B,EAAe,EAA4B,CAClE,GAAM,GACF,EAAS,SAAS,+BAA+B,KAAK,GAAG,YAAY,QAAQ,oBAAoB,SAAS,GACrG,oBAAoB,SAAS,GAAG,0BAA0B,SAC7D,EAAmB,EAAS,QAAQ,wBAAwB,MAE5D,EACF,EAAK,MAAM,sBAAsB,EAAE,IAAI,MAAM,GAAG,EAAE,IAClD,EAAK,MAAM,oBAAoB,EAAE,IAAI,MAAM,GAAG,EAAE,IAChD,EACE,EAAS,GAAkB,EAAW,GAAG,EAEzC,EAAO,EAAiB,GAAG,mCACjC,GAAI,CAAC,EAAK,MAAM,MAAQ,CAAC,EAAK,MAAM,KAAK,OAAQ,KAAM,IAAI,OAAM,gCAAgC,EAEjG,GAAM,GAAS,EAAiB,IAAI,qCAAqC,WACnE,EAAQ,EAAK,MAAM,SAAW,EAAI,EAAK,MAAM,GAAG,WAAW,QAAQ,MAAO,EAAE,EAAI,EAChF,EACF,EAAK,MACA,KAAK,AAAC,GAAW,QAAU,IAAK,EAAE,KAAQ,KAAK,AAAC,GAAW,EAAE,KAAK,YAAY,EAAE,SAAS,aAAa,CAAC,CAAC,GACvG,KAAK,IAAI,GAAG,MAAQ,KACxB,EAAc,EAAK,MAAM,GAAG,KAAK,GAAG,KAAK,QAAQ,MAAO,EAAE,GAAK,EAoCrE,MAlCY,IAAI,GAAgB,CAC5B,aAAc,CACV,IAAK,EACL,MAAO,EAAqB,CAAS,EACrC,cACI,EAAK,MAAM,sCAAsC,EAAE,IAAI,MAAM,GAAG,EAAE,IAClE,EAAK,MAAM,sCAAsC,EAAE,IAAI,MAAM,GAAG,EAAE,IAClE,gBACR,EACA,GAAI,EAAK,MAAM,KAAK,GAAG,mBAAmB,cAAc,WACxD,MAAO,EAAK,MAAM,KAAK,GAAG,KAC1B,WAAY,SAAS,CAAW,GAAK,EACrC,WAAY,EACZ,MAAO,SAAS,CAAK,GAAK,EAC1B,OAAQ,EACR,IAAK,yCAAyC,EAAK,MAAM,KAAK,GAAG,mBAAmB,cAAc,aAClG,KAAM,0BAA0B,EAAK,MAAM,KAAK,GAAG,mBAAmB,gBAAgB,mBAAmB,MACzG,QAAS,EACH,CACI,KAAM,EAAO,mBAAmB,MAAM,KAAK,GAAG,KAC9C,GAAI,EAAO,mBAAmB,MAAM,KAAK,GAAG,mBAAmB,eAAe,SAC9E,IAAK,0BACD,EAAO,mBAAmB,mBAAmB,gBAAgB,mBAAmB,KAChF,EAAO,mBAAmB,mBAAmB,eAAe,mBAEhE,MAAO,EAAO,mBAAmB,UAAU,YAAc,CAAC,CAC9D,EACA,CAAC,EACP,UAAW,EAAK,kBAAkB,gCAAgC,UAAU,WAAW,OACjF,EAAK,kBAAkB,+BAA+B,UAAU,WAC5D,EAAK,kBAAkB,+BAA+B,UAAU,WAAW,OAAS,GAExF,IACV,CAAC,CAEL,CA1DS,0BA4DT,YAAgC,EAAW,EAAQ,IAA0B,CACzE,GAAM,GAAyB,CAAC,EAEhC,OAAS,GAAI,EAAG,EAAI,EAAK,QACjB,IAAU,EAAO,OADQ,IAAK,CAElC,GAAM,GAAO,EAAK,GAAG,2BACrB,GAAI,CAAC,GAAQ,CAAC,EAAK,gBAAiB,SACpC,GAAM,GAAe,EAAK,gBAAgB,KAAK,GAE/C,EAAO,KACH,GAAI,GAAa,CACb,GAAI,EAAK,QACT,SAAU,GAAc,EAAK,YAAY,UAAU,GAAK,EACxD,aAAc,EAAK,YAAY,YAAc,OAC7C,WAAY,EAAK,UAAU,WAC3B,MAAO,EAAK,MAAM,WAClB,SACI,EAAK,kBAAkB,GAAG,oCAAoC,QAAU,YAAc,OAC1F,QAAS,CACL,GAAI,EAAa,mBAAmB,eAAe,UAAY,OAC/D,KAAM,EAAa,MAAQ,OAC3B,IAAK,0BACD,EAAa,mBAAmB,eAAe,kBAC/C,EAAa,mBAAmB,gBAAgB,mBAAmB,MAEvE,KAAM,MACV,CACJ,CAAC,CACL,CACJ,CAEA,MAAO,EACX,CAhCS,+BAkCT,YAAuB,EAAsB,CACzC,GAAI,CAAC,EAAM,MAAO,GAClB,GAAM,GAAQ,EAAK,MAAM,GAAG,EAE5B,OAAQ,EAAM,YACL,GACD,MAAO,UAAS,EAAM,EAAE,EAAI,GAAK,SAAS,EAAM,EAAE,MAEjD,GACD,MAAO,UAAS,EAAM,EAAE,EAAI,GAAK,GAAK,SAAS,EAAM,EAAE,EAAI,GAAK,SAAS,EAAM,EAAE,UAGjF,MAAO,GAEnB,CAdS,sBN30BT,gCAKO,YAAiB,CA0DpB,YAAY,EAAkB,EAAkB,EAAmB,EAAmB,CAClF,KAAK,OAAS,GAAI,IAAS,CAAE,cAAe,EAAI,IAAO,IAAM,MAAO,CAAC,CAAE,CAAC,EACxE,KAAK,KAAO,YACZ,KAAK,SAAW,EAChB,KAAK,SAAW,EAChB,KAAK,SAAW,GAChB,KAAK,SAAW,EAChB,KAAK,UAAY,EACjB,KAAK,SAAW,GAAY,EAC5B,KAAK,WAAa,GAAI,GAAM,IAAM,CAC9B,KAAK,aAAa,EAClB,KAAK,WAAW,MAAM,CAC1B,EAAG,IAAI,EACP,KAAK,OAAO,GAAG,QAAS,IAAM,CAC1B,KAAK,QAAQ,CACjB,CAAC,EACD,KAAK,gBAAgB,CACzB,CAMQ,SAAU,CACd,KAAK,cAAc,QAAQ,EAC3B,KAAK,WAAW,QAAQ,EACxB,KAAK,SAAS,QAAQ,EACtB,KAAK,UAAY,GACjB,KAAK,QAAU,OACf,KAAK,SAAW,GAChB,KAAK,SAAW,GAChB,KAAK,SAAW,CACpB,MAMc,eAAe,CACzB,GAAM,GAAO,KAAM,GAAkB,KAAK,SAAS,EACnD,MAAI,GAAK,eAAe,iBAAiB,MAAK,SAAW,EAAK,eAAe,iBACtE,KAAK,gBAAgB,CAChC,MAMc,kBAAkB,CAE5B,GAAM,GAAc,AADH,MAAM,GAAQ,KAAK,QAAQ,GAEvC,MAAM,uBAAuB,EAAE,GAC/B,MAAM,kBAAkB,EAAE,GAC1B,MAAM,mBAAmB,EAI9B,GAHI,EAAY,EAAY,OAAS,KAAO,IAAI,EAAY,IAAI,EAChE,KAAK,SAAW,EAAY,EAAY,OAAS,GAAG,MAAM,WAAW,EAAE,GAAG,MAAM,YAAY,EAAE,GAC9F,KAAM,GAAe,WAAW,GAAI,IAAI,KAAK,QAAQ,EAAE,mBAAmB,EACtE,KAAK,WAAa,EAAG,CACrB,GAAM,GAAO,EAAY,EAAY,OAAS,GACzC,MAAM,eAAe,EAAE,GACvB,MAAM,gBAAgB,EAAE,GACxB,WAAW,sBAAuB,EAAE,EACpC,MAAM,KAAK,EAChB,AAAI,EAAK,EAAK,OAAS,KAAO,IAAI,EAAK,IAAI,EACvC,EAAK,OAAS,KAAK,UAAU,EAAK,OAAO,EAAG,EAAK,OAAS,KAAK,QAAQ,EAC3E,KAAK,SAAW,OAAO,EAAK,GAAG,MAAM,KAAK,EAAE,GAAG,MAAM,GAAG,EAAE,EAAE,EAC5D,KAAK,WAAW,EAAK,MAAM,CAC/B,CACJ,MAKc,YAAW,EAAa,CAClC,OAAS,GAAI,EAAG,GAAK,EAAK,IACtB,KAAM,IAAI,SAAQ,KAAO,IAAY,CACjC,GAAM,GAAS,KAAM,GAAe,KAAK,SAAW,MAAQ,KAAK,QAAQ,EAAE,MAAM,AAAC,GAAe,CAAG,EACpG,GAAI,YAAkB,OAAO,CACzB,KAAK,OAAO,KAAK,QAAS,CAAM,EAChC,MACJ,CACA,KAAK,QAAU,EACf,EAAO,GAAG,OAAQ,AAAC,GAAM,CACrB,KAAK,OAAO,KAAK,CAAC,CACtB,CAAC,EACD,EAAO,GAAG,MAAO,IAAM,CACnB,KAAK,WACL,EAAQ,EAAE,CACd,CAAC,EACD,EAAO,KAAK,QAAS,AAAC,GAAQ,CAC1B,KAAK,OAAO,KAAK,QAAS,CAAG,CACjC,CAAC,CACL,CAAC,EAEL,KAAK,aAAe,GAAI,GAAM,IAAM,CAChC,KAAK,KAAK,EACV,KAAK,cAAc,MAAM,CAC7B,EAAG,KAAK,QAAQ,CACpB,CAMQ,MAAO,CACX,MAAO,IAAI,SAAQ,KAAO,IAAY,CAClC,GAAM,GAAS,KAAM,GAAe,KAAK,SAAW,MAAQ,KAAK,QAAQ,EAAE,MAAM,AAAC,GAAe,CAAG,EACpG,GAAI,YAAkB,OAAO,CACzB,KAAK,OAAO,KAAK,QAAS,CAAM,EAChC,MACJ,CACA,KAAK,QAAU,EACf,EAAO,GAAG,OAAQ,AAAC,GAAM,CACrB,KAAK,OAAO,KAAK,CAAC,CACtB,CAAC,EACD,EAAO,GAAG,MAAO,IAAM,CACnB,KAAK,WACL,EAAQ,EAAE,CACd,CAAC,EACD,EAAO,KAAK,QAAS,AAAC,GAAQ,CAC1B,KAAK,OAAO,KAAK,QAAS,CAAG,CACjC,CAAC,CACL,CAAC,CACL,CAIA,OAAQ,CAAC,CAIT,QAAS,CAAC,CACd,EA9LO,mBAkMA,YAAa,CAqDhB,YACI,EACA,EACA,EACA,EACA,EACA,EACF,CACE,KAAK,OAAS,GAAI,IAAS,CAAE,cAAe,EAAI,IAAO,IAAM,MAAO,CAAC,CAAE,CAAC,EACxE,KAAK,IAAM,EACX,KAAK,QAAU,EAAQ,QACvB,KAAK,KAAO,EACZ,KAAK,YAAc,EACnB,KAAK,UAAY,EACjB,KAAK,cAAgB,KAAK,KAAK,EAAgB,CAAQ,EACvD,KAAK,eAAiB,EACtB,KAAK,QAAU,KACf,KAAK,MAAQ,GAAI,GAAM,IAAM,CACzB,KAAK,MAAM,MAAM,EACjB,KAAK,KAAK,CACd,EAAG,GAAG,EACN,KAAK,OAAO,GAAG,QAAS,IAAM,CAC1B,KAAK,MAAM,QAAQ,EACnB,KAAK,QAAQ,CACjB,CAAC,EACD,KAAK,KAAK,CACd,MAIc,QAAQ,CAClB,GAAM,GAAO,KAAM,GAAkB,KAAK,SAAS,EAC7C,EAAc,EAAkB,EAAK,MAAM,EACjD,KAAK,IAAM,EAAY,KAAK,SAAS,GACzC,CAMQ,SAAU,CACd,KAAK,SAAS,QAAQ,EACtB,KAAK,QAAU,KACf,KAAK,IAAM,EACf,MAMc,OAAO,CACjB,GAAI,KAAK,OAAO,UAAW,CACvB,KAAK,MAAM,QAAQ,EACnB,KAAK,QAAQ,EACb,MACJ,CACA,GAAM,GAAc,KAAK,YAAc,KAAK,cAAgB,IACtD,EAAS,KAAM,GAAe,KAAK,IAAK,CAC1C,QAAS,CACL,MAAO,SAAS,KAAK,eAAe,GAAO,KAAK,eAAiB,GAAK,GAC1E,CACJ,CAAC,EAAE,MAAM,AAAC,GAAe,CAAG,EAC5B,GAAI,YAAkB,OAAO,CACzB,KAAK,OAAO,KAAK,QAAS,CAAM,EAChC,KAAK,YAAc,EACnB,KAAK,cAAgB,EACrB,KAAK,QAAQ,EACb,MACJ,CACA,GAAI,OAAO,EAAO,UAAU,GAAK,IAAK,CAClC,KAAK,QAAQ,EACb,KAAM,MAAK,MAAM,EACjB,KAAK,MAAM,MAAM,EACjB,KAAK,KAAK,EACV,MACJ,CACA,KAAK,QAAU,EACf,EAAO,GAAG,OAAQ,AAAC,GAAM,CACrB,KAAK,OAAO,KAAK,CAAC,CACtB,CAAC,EAED,EAAO,KAAK,QAAS,SAAY,CAC7B,KAAK,QAAQ,EACb,KAAM,MAAK,MAAM,EACjB,KAAK,MAAM,MAAM,EACjB,KAAK,KAAK,CACd,CAAC,EAED,EAAO,GAAG,OAAQ,AAAC,GAAe,CAC9B,KAAK,aAAe,EAAM,MAC9B,CAAC,EAED,EAAO,GAAG,MAAO,IAAM,CACnB,AAAI,GAAO,KAAK,gBACZ,MAAK,MAAM,QAAQ,EACnB,KAAK,OAAO,KAAK,IAAI,EACrB,KAAK,QAAQ,EAErB,CAAC,CACL,CAOA,OAAQ,CACJ,KAAK,MAAM,MAAM,CACrB,CAKA,QAAS,CACL,KAAK,MAAM,OAAO,CACtB,CACJ,EAzKO,eA+KA,WAAY,CAkCf,YAAY,EAAsB,EAAc,CAC5C,KAAK,SAAW,EAChB,KAAK,WAAa,EAClB,KAAK,UAAY,EACjB,KAAK,OAAS,GACd,KAAK,UAAY,GACjB,KAAK,WAAa,QAAQ,OAAO,EAAE,GACnC,KAAK,MAAQ,WAAW,KAAK,SAAU,KAAK,WAAa,GAAI,CACjE,CAKA,OAAQ,CACJ,MAAI,CAAC,KAAK,QAAU,CAAC,KAAK,UACtB,MAAK,OAAS,GACd,aAAa,KAAK,KAAK,EACvB,KAAK,UAAY,KAAK,UAAa,SAAQ,OAAO,EAAE,GAAK,KAAK,YACvD,IACG,EAClB,CAKA,QAAS,CACL,MAAI,MAAK,QAAU,CAAC,KAAK,UACrB,MAAK,OAAS,GACd,KAAK,WAAa,QAAQ,OAAO,EAAE,GACnC,KAAK,MAAQ,WAAW,KAAK,SAAU,KAAK,UAAY,GAAI,EACrD,IACG,EAClB,CAKA,OAAQ,CACJ,MAAK,MAAK,UAOI,GANV,cAAa,KAAK,KAAK,EACvB,KAAK,UAAY,KAAK,WACtB,KAAK,OAAS,GACd,KAAK,WAAa,QAAQ,OAAO,EAAE,GACnC,KAAK,MAAQ,WAAW,KAAK,SAAU,KAAK,WAAa,GAAI,EACtD,GAEf,CAMA,SAAU,CACN,aAAa,KAAK,KAAK,EACvB,KAAK,UAAY,GACjB,KAAK,SAAW,IAAM,CAAC,EACvB,KAAK,WAAa,EAClB,KAAK,UAAY,EACjB,KAAK,OAAS,GACd,KAAK,WAAa,CACtB,CACJ,EA/FO,aO3XP,4DACA,sCAmBA,GAAM,IAAmB,OAAO,KAAK,EAAY,EAE1C,gBAAyB,GAAO,CAenC,YAAY,EAAa,EAA4B,CACjD,MAAM,CAAO,EACb,KAAK,MAAQ,eACb,KAAK,OAAS,EACd,KAAK,OAAS,GAAI,IAClB,KAAK,UAAY,GACjB,KAAK,aAAe,GACpB,KAAK,UAAY,GACjB,KAAK,YAAc,EACnB,KAAK,UAAY,EACjB,KAAK,OAAS,EACd,KAAK,IAAM,EACX,KAAK,KAAO,KAAK,MAAM,EAAM,EAAE,EAAI,EACvC,IAEY,cAAsB,CAC9B,GAAI,GAAI,EACR,KAAO,EAAI,GACF,KAAM,EAAI,EAAM,KAAK,MAAO,KAAK,SAD5B,IACV,CAEJ,MAAO,EAAE,CACb,CAEQ,YAAsB,CAC1B,GAAI,CAAC,KAAK,MAAO,MAAO,GACxB,GAAM,GAAS,KAAK,YACpB,GAAI,KAAK,MAAM,OAAS,KAAK,OAAS,EAAQ,MAAO,GACrD,GAAI,GAAQ,KAAK,MAAM,KAAK,QAAY,IAAM,EAAI,GAAW,EAC7D,OAAS,GAAI,KAAK,OAAS,EAAG,EAAI,KAAK,OAAS,EAAQ,IAAK,EAAS,IAAS,GAAK,KAAK,MAAM,GAC/F,YAAK,UAAY,EACjB,KAAK,YAAc,EACZ,EACX,CAEA,SAAU,CACN,KAAK,OAAS,EACd,KAAK,MAAQ,OACb,KAAK,UAAY,MACrB,CAEA,OAAQ,CAAC,CAET,KAAK,EAAwC,CACzC,GAAI,GAAgB,EAChB,EAAW,EACX,EAAa,MAAK,IAAM,KAAK,MAAQ,KAAQ,EAEjD,GADA,EAAY,KAAK,MAAM,EAAY,EAAE,EAAI,GACrC,CAAC,KAAK,OAAO,QAAQ,KAAM,MAAO,IAAI,OAAM,sBAAsB,EAEtE,OAAS,GAAI,EAAG,EAAI,KAAK,OAAO,QAAQ,KAAK,OAAQ,IAAK,CACtD,GAAM,GAAO,KAAK,OAAO,QAAQ,KAAK,GACtC,GAAI,KAAK,MAAO,EAAK,KAAkB,GAAI,IAAM,KAAK,KAAM,CACxD,EAAW,EAAK,SAChB,EAAiB,MAAK,OAAO,QAAQ,KAAK,EAAI,IAAI,UAAY,GAAkB,EAAW,EAC3F,KACJ,KAAO,SACX,CACA,MAAI,KAAkB,EAAU,EACzB,KAAK,OAAS,KAAK,MAAM,EAAY,EAAY,GAAO,GAAgB,IAAI,CACvF,CAEA,OAAO,EAAe,EAAmB,EAAgD,CACrF,AAAI,KAAK,UACL,MAAK,MAAQ,OAAO,OAAO,CAAC,KAAK,UAAW,CAAK,CAAC,EAClD,KAAK,UAAY,QACd,KAAK,MAAQ,EAEpB,GAAI,GAEJ,AAAI,KAAK,QAAU,eAA8B,EAAM,KAAK,SAAS,EAChE,AAAK,KAAK,UACV,EAAM,KAAK,QAAQ,EADE,EAAM,KAAK,gBAAgB,EAGrD,AAAI,EAAK,EAAS,CAAG,EAChB,EAAS,CAClB,CAEQ,UAA8B,CAClC,GAAI,CAAC,KAAK,MAAO,MAAO,IAAI,OAAM,kBAAkB,EAEpD,KAAO,KAAK,MAAM,OAAS,KAAK,QAAQ,CACpC,GAAM,GAAY,KAAK,OACjB,EAAK,KAAK,YAChB,GAAI,KAAK,MAAM,OAAS,KAAK,OAAS,EAAI,MAE1C,GAAM,GAAS,KAAK,YAAY,KAAK,MAAM,MAAM,KAAK,OAAQ,KAAK,OAAS,CAAE,EAAE,SAAS,KAAK,CAAC,EAG/F,GAFA,KAAK,QAAU,EAEX,CAAC,KAAK,WAAW,EAAG,CACpB,KAAK,OAAS,EACd,KACJ,CACA,GAAI,CAAC,EAAQ,CACT,KAAK,QAAU,KAAK,UAAY,KAAK,YACrC,QACJ,CAEA,GAAI,CAAC,KAAK,UACN,GAAI,EAAO,OAAS,OAAQ,KAAK,UAAY,OACxC,OAAO,IAAI,OAAM,4CAA4C,EAEtE,GAAM,GAAO,KAAK,MAAM,MACpB,KAAK,OAAS,KAAK,UACnB,KAAK,OAAS,KAAK,UAAY,KAAK,WACxC,EACM,EAAQ,KAAK,OAAO,MAAM,EAAQ,CAAI,EAC5C,GAAI,YAAiB,OAAO,MAAO,GAanC,GATI,EAAO,OAAS,YAAY,MAAK,OAAS,GAG1C,EAAO,OAAS,sBAChB,KAAK,OAAO,QAAQ,KAAM,OAAS,GACnC,KAAK,OAAU,KAAK,OAAO,QAAQ,KAAM,GAAG,EAAE,EAAG,KAAkB,KAEnE,KAAK,KAAK,cAAc,EAExB,EAAO,OAAS,EAAiB,CACjC,KAAK,QAAU,KAAK,UACpB,QACJ,CAEA,GAAI,KAAK,MAAM,OAAS,KAAK,OAAS,KAAK,UAAY,KAAK,YAAa,CACrE,KAAK,OAAS,EACd,KACJ,KAAO,MAAK,QAAU,KAAK,UAAY,KAAK,WAChD,CACA,KAAK,UAAY,KAAK,MAAM,MAAM,KAAK,MAAM,EAC7C,KAAK,OAAS,CAClB,CAEQ,SAA6B,CACjC,GAAI,CAAC,KAAK,MAAO,MAAO,IAAI,OAAM,kBAAkB,EAEpD,KAAO,KAAK,MAAM,OAAS,KAAK,QAAQ,CACpC,GAAM,GAAY,KAAK,OACjB,EAAK,KAAK,YAChB,GAAI,KAAK,MAAM,OAAS,KAAK,OAAS,EAAI,MAE1C,GAAM,GAAS,KAAK,YAAY,KAAK,MAAM,MAAM,KAAK,OAAQ,KAAK,OAAS,CAAE,EAAE,SAAS,KAAK,CAAC,EAG/F,GAFA,KAAK,QAAU,EAEX,CAAC,KAAK,WAAW,EAAG,CACpB,KAAK,OAAS,EACd,KACJ,CACA,GAAI,CAAC,EAAQ,CACT,KAAK,QAAU,KAAK,UAAY,KAAK,YACrC,QACJ,CAEA,GAAM,GAAO,KAAK,MAAM,MACpB,KAAK,OAAS,KAAK,UACnB,KAAK,OAAS,KAAK,UAAY,KAAK,WACxC,EACM,EAAQ,KAAK,OAAO,MAAM,EAAQ,CAAI,EAC5C,GAAI,YAAiB,OAAO,MAAO,GAEnC,GAAI,EAAO,OAAS,EAAiB,CACjC,KAAK,QAAU,KAAK,UACpB,QACJ,CAEA,GAAI,KAAK,MAAM,OAAS,KAAK,OAAS,KAAK,UAAY,KAAK,YAAa,CACrE,KAAK,OAAS,EACd,KACJ,KAAO,MAAK,QAAU,KAAK,UAAY,KAAK,YAE5C,GAAI,EAAO,OAAS,cAAe,CAC/B,GAAM,GAAQ,KAAK,OAAO,QAAQ,OAAQ,KAAK,OAAO,YACtD,GAAI,CAAC,GAAS,EAAM,YAAc,EAAG,MAAO,IAAI,OAAM,mCAAmC,EACzF,AAAK,GAAK,GAAK,MAAS,EAAM,aAAa,KAAK,KAAK,EAAK,MAAM,CAAC,CAAC,CACtE,CACJ,CACA,KAAK,UAAY,KAAK,MAAM,MAAM,KAAK,MAAM,EAC7C,KAAK,OAAS,CAClB,CAEQ,iBAAqC,CACzC,GAAI,KAAK,MAAQ,EACb,YAAK,UAAY,GACV,KAAK,QAAQ,EAExB,GAAI,CAAC,KAAK,MAAO,MAAO,IAAI,OAAM,kBAAkB,EACpD,KAAK,OAAS,EACd,GAAI,GAAgB,GACpB,KAAO,CAAC,GAAiB,KAAK,OAAS,KAAK,MAAM,QAAQ,CAEtD,GADA,KAAK,OAAS,KAAK,MAAM,QAAQ,KAAM,KAAK,OAAQ,KAAK,EACrD,KAAK,SAAW,GAAI,MAAO,IAAI,OAAM,+BAA+B,EAExE,GADA,KAAK,SACD,CAAC,KAAK,WAAW,EAAG,MAAO,IAAI,OAAM,mDAAmD,EAC5F,GAAI,KAAK,OAAS,KAAK,YAAc,KAAK,YAAc,KAAK,MAAM,OAAQ,SAC3E,GAAM,GAAO,KAAK,MAAM,MACpB,KAAK,OAAS,KAAK,UACnB,KAAK,OAAS,KAAK,UAAY,KAAK,WACxC,EACM,EAAQ,KAAK,OAAO,QAAQ,OAAQ,KAAK,OAAO,YACtD,GAAI,CAAC,GAAS,EAAM,YAAc,EAAG,MAAO,IAAI,OAAM,mCAAmC,EACzF,GAAK,GAAK,GAAK,MAAS,EAAM,YAC1B,KAAK,QAAU,KAAK,UAAY,KAAK,YACrC,KAAK,KAAK,EAAK,MAAM,CAAC,CAAC,EACvB,EAAgB,OACb,SACX,CACA,MAAK,GACL,MAAK,UAAY,GACV,KAAK,QAAQ,GAFO,GAAI,OAAM,8CAA8C,CAGvF,CAEQ,YAAY,EAAgB,CAChC,MAAI,IAAiB,SAAS,CAAM,EAAU,GAAa,GAC/C,EAChB,CAEA,SAAS,EAAqB,EAA+C,CACzE,KAAK,QAAQ,EACb,EAAS,CAAK,CAClB,CAEA,OAAO,EAAgD,CACnD,KAAK,QAAQ,EACb,EAAS,CACb,CACJ,EAhPO,mBCZA,YAAiB,CA2DpB,YACI,EACA,EACA,EACA,EACA,EACA,EACA,EACF,CACE,KAAK,OAAS,GAAI,IAAW,EAAQ,KAAO,CACxC,cAAe,EAAI,IAAO,IAC1B,mBAAoB,EACxB,CAAC,EACD,KAAK,IAAM,EACX,KAAK,QAAU,EAAQ,QACvB,KAAK,KAAO,OACZ,KAAK,YAAc,EACnB,KAAK,UAAY,EACjB,KAAK,cAAgB,AAAU,KAAK,KAAf,EAAoB,EAAU,EAAe,EAAgB,CAA9B,EACpD,KAAK,cAAgB,EACrB,KAAK,eAAiB,EACtB,KAAK,QAAU,KACf,KAAK,MAAQ,GAAI,GAAM,IAAM,CACzB,KAAK,MAAM,MAAM,EACjB,KAAK,KAAK,CACd,EAAG,GAAG,EACN,KAAK,OAAO,GAAG,QAAS,IAAM,CAC1B,KAAK,MAAM,QAAQ,EACnB,KAAK,QAAQ,CACjB,CAAC,EACD,KAAK,KAAK,CACd,MAOc,OAAsB,CAChC,GAAM,GAAQ,KAAM,IAAI,SAAQ,MAAO,EAAK,IAAQ,CAChD,GAAK,KAAK,OAAO,aA8BV,EAAI,EAAE,MA9BkB,CAC3B,GAAM,GAAS,KAAM,GAAe,KAAK,IAAK,CAC1C,QAAS,CACL,MAAO,WAAW,KAAK,eAC3B,CACJ,CAAC,EAAE,MAAM,AAAC,GAAe,CAAG,EAE5B,GAAI,YAAkB,OAAO,CACzB,EAAI,CAAM,EACV,MACJ,CACA,GAAI,OAAO,EAAO,UAAU,GAAK,IAAK,CAClC,EAAI,GAAG,EACP,MACJ,CACA,KAAK,QAAU,EACf,EAAO,KAAK,KAAK,OAAQ,CAAE,IAAK,EAAM,CAAC,EAGvC,EAAO,KAAK,MAAO,IAAM,CACrB,KAAK,OAAO,MAAQ,eACpB,EAAI,EAAE,CACV,CAAC,EAED,KAAK,OAAO,KAAK,eAAgB,IAAM,CACnC,EAAO,OAAO,KAAK,MAAM,EACzB,EAAO,QAAQ,EACf,KAAK,OAAO,MAAQ,eACpB,EAAI,EAAE,CACV,CAAC,CACL,CACJ,CAAC,EAAE,MAAM,AAAC,GAAQ,CAAG,EACrB,GAAI,YAAiB,OAAO,CACxB,KAAK,OAAO,KAAK,QAAS,CAAK,EAC/B,KAAK,YAAc,EACnB,KAAK,cAAgB,EACrB,KAAK,QAAQ,EACb,MACJ,SAAW,IAAU,IACjB,YAAM,MAAK,MAAM,EACjB,KAAK,MAAM,MAAM,EACV,KAAK,KAAK,EAErB,GAAM,GAAQ,KAAK,OAAO,KAAK,KAAK,cAAc,EAClD,GAAI,YAAiB,OAAO,CACxB,KAAK,OAAO,KAAK,QAAS,CAAK,EAC/B,KAAK,YAAc,EACnB,KAAK,cAAgB,EACrB,KAAK,QAAQ,EACb,MACJ,CAEA,KAAK,OAAO,UAAY,GACxB,KAAK,YAAc,EACnB,KAAK,MAAM,MAAM,EACjB,KAAK,KAAK,CACd,MAIc,QAAQ,CAClB,GAAM,GAAO,KAAM,GAAkB,KAAK,SAAS,EAC7C,EAAc,EAAkB,EAAK,MAAM,EACjD,KAAK,IAAM,EAAY,KAAK,SAAS,GACzC,CAMQ,SAAU,CACd,KAAK,SAAS,QAAQ,EACtB,KAAK,QAAU,KACf,KAAK,IAAM,EACf,MAMc,OAAO,CACjB,GAAI,KAAK,OAAO,UAAW,CACvB,KAAK,MAAM,QAAQ,EACnB,KAAK,QAAQ,EACb,MACJ,CACA,GAAM,GAAc,KAAK,YAAc,KAAK,cAAgB,IACtD,EAAS,KAAM,GAAe,KAAK,IAAK,CAC1C,QAAS,CACL,MAAO,SAAS,KAAK,eAAe,GAAO,KAAK,eAAiB,GAAK,GAC1E,CACJ,CAAC,EAAE,MAAM,AAAC,GAAe,CAAG,EAC5B,GAAI,YAAkB,OAAO,CACzB,KAAK,OAAO,KAAK,QAAS,CAAM,EAChC,KAAK,YAAc,EACnB,KAAK,cAAgB,EACrB,KAAK,QAAQ,EACb,MACJ,CACA,GAAI,OAAO,EAAO,UAAU,GAAK,IAAK,CAClC,KAAK,QAAQ,EACb,KAAM,MAAK,MAAM,EACjB,KAAK,MAAM,MAAM,EACjB,KAAK,KAAK,EACV,MACJ,CACA,KAAK,QAAU,EACf,EAAO,KAAK,KAAK,OAAQ,CAAE,IAAK,EAAM,CAAC,EAEvC,EAAO,KAAK,QAAS,SAAY,CAC7B,KAAK,QAAQ,EACb,KAAM,MAAK,MAAM,EACjB,KAAK,MAAM,MAAM,EACjB,KAAK,KAAK,CACd,CAAC,EAED,EAAO,GAAG,OAAQ,AAAC,GAAe,CAC9B,KAAK,aAAe,EAAM,MAC9B,CAAC,EAED,EAAO,GAAG,MAAO,IAAM,CACnB,AAAI,GAAO,KAAK,gBACZ,MAAK,MAAM,QAAQ,EACnB,KAAK,OAAO,IAAI,EAChB,KAAK,QAAQ,EAErB,CAAC,CACL,CAOA,OAAQ,CACJ,KAAK,MAAM,MAAM,CACrB,CAKA,QAAS,CACL,KAAK,MAAM,OAAO,CACtB,CACJ,EAnPO,mBCLP,gCAwBO,WAA2B,EAAgB,CAC9C,GAAM,GAAgB,CAAC,EACvB,SAAQ,QAAQ,AAAC,GAAW,CACxB,GAAM,GAAO,EAAO,SACpB,AAAI,EAAK,WAAW,OAAO,GACvB,GAAO,MAAQ,EAAK,MAAM,UAAU,EAAE,GAAG,MAAM,GAAG,EAAE,GACpD,EAAO,UAAY,EAAK,MAAM,QAAQ,EAAE,GAAG,MAAM,GAAG,EAAE,GACtD,EAAO,KAAK,CAAM,EAE1B,CAAC,EACM,CACX,CAXgB,yBAsBhB,kBAA6B,EAAa,EAAyB,CAAC,EAA2B,CAC3F,GAAM,GAAO,KAAM,GAAkB,EAAK,CAAE,SAAU,EAAQ,SAAU,SAAU,EAAQ,QAAS,CAAC,EACpG,MAAO,MAAM,IAAiB,EAAM,CAAO,CAC/C,CAHsB,eAUtB,kBACI,EACA,EAAyB,CAAC,EACJ,CACtB,GAAI,EAAK,OAAO,SAAW,EACvB,KAAM,IAAI,OAAM,8EAA8E,EAClG,GAAI,EAAQ,SAAW,CAAC,OAAO,UAAU,EAAQ,OAAO,EACpD,KAAM,IAAI,OAAM,oCAAoC,EAExD,GAAM,GAAe,CAAC,EACtB,GACI,EAAK,eAAe,SAAW,IAC/B,EAAK,eAAe,kBAAoB,MACxC,EAAK,cAAc,gBAAkB,EAErC,MAAO,IAAI,IACP,EAAK,eAAe,gBACpB,EAAK,OAAO,EAAK,OAAO,OAAS,GAAG,kBACpC,EAAK,cAAc,IACnB,EAAQ,QACZ,EAGJ,GAAM,GAAc,EAAkB,EAAK,MAAM,EACjD,AAAI,MAAO,GAAQ,SAAY,SAAU,EAAQ,QAAU,EAAY,OAAS,EAC3E,AAAI,EAAQ,SAAW,EAAG,EAAQ,QAAU,EACxC,EAAQ,SAAW,EAAY,QAAQ,GAAQ,QAAU,EAAY,OAAS,GACvF,AAAI,EAAY,SAAW,EAAG,EAAM,KAAK,EAAY,EAAQ,QAAQ,EAChE,EAAM,KAAK,EAAK,OAAO,EAAK,OAAO,OAAS,EAAE,EACnD,GAAI,GACA,EAAM,GAAG,QAAU,QAAU,EAAM,GAAG,YAAc,OAAS,YAAsB,YAEvF,GADA,KAAM,GAAe,WAAW,GAAI,IAAI,EAAM,GAAG,GAAG,EAAE,mBAAmB,EACrE,IAAS,YACT,GAAK,EAAQ,4BAaN,GAAI,EAAQ,KAAM,KAAM,IAAI,OAAM,2DAA2D,MAb3D,CAErC,GADA,EAAQ,OAAS,EACb,EAAQ,MAAQ,EAAK,cAAc,eAAiB,EAAQ,KAAO,EACnE,KAAM,IAAI,OAAM,+BAA+B,EAAK,cAAc,cAAgB,IAAI,EAC1F,MAAO,IAAI,IACP,EAAM,GAAG,IACT,EAAK,cAAc,cACnB,EAAM,GAAG,WAAW,IACpB,OAAO,EAAM,GAAG,aAAa,EAC7B,OAAO,EAAM,GAAG,OAAO,EACvB,EAAK,cAAc,IACnB,CACJ,CACJ,CAGJ,GAAI,GACJ,MAAI,GAAM,GAAG,cACT,EAAgB,OAAO,EAAM,GAAG,aAAa,EAE7C,EAAgB,KAAM,IAAuB,EAAM,GAAG,GAAG,EAGtD,GAAI,IACP,EAAM,GAAG,IACT,EACA,EAAK,cAAc,cACnB,EACA,EAAK,cAAc,IACnB,CACJ,CACJ,CAhEsB,yBCvDtB,GAAM,IAAqB,CACvB,mEACA,2DACA,2DACA,2DACA,mDACA,kDACJ,EAoBO,YAA2B,EAAc,EAA2C,CACvF,GAAI,CAAC,EAAM,KAAM,IAAI,OAAM,wCAAwC,EACnE,AAAK,EACK,EAAQ,MAAM,GAAQ,KAAO,SADzB,EAAU,CAAE,KAAM,QAAS,MAAO,CAAE,EAElD,GAAM,GAAW,MAAO,GAAQ,OAAU,UAAY,EAAQ,MAAQ,EACtE,EAAQ,uBAAyB,GAEjC,GAAM,GAAO,EACR,MAAM,sBAAsB,IAAI,IAC/B,MAAM,aAAY,EAAE,GACrB,MAAM,uBAAuB,EAAE,GAC9B,EAAY,KAAK,MAAM,CAAI,EAC3B,EAAU,CAAC,EACX,EACF,EAAU,SAAS,+BAA+B,gBAAgB,oBAAoB,SAAS,QAC3F,AAAC,GAAW,EAAE,qBAAqB,QACvC,EACJ,OAAW,KAAU,GAAS,CAC1B,GAAI,GAAY,EAAQ,SAAW,EAAQ,MAAO,MAClD,GAAI,GAAC,GAAW,CAAC,EAAO,eAAiB,CAAC,EAAO,iBAAmB,CAAC,EAAO,kBAC5E,OAAQ,EAAQ,UACP,QAAS,CACV,GAAM,GAAS,GAAW,CAAM,EAChC,AAAI,GACI,GAAQ,sBAAsB,EAAO,WAAW,QAAQ,EAAe,EAC3E,EAAQ,KAAK,CAAM,GAEvB,KACJ,KACK,UAAW,CACZ,GAAM,GAAS,GAAa,CAAM,EAClC,AAAI,GAAQ,EAAQ,KAAK,CAAM,EAC/B,KACJ,KACK,WAAY,CACb,GAAM,GAAS,GAAc,CAAM,EACnC,AAAI,GACI,GAAQ,sBAAwB,EAAO,WAAW,GAAgB,EAAO,SAAS,EACtF,EAAQ,KAAK,CAAM,GAEvB,KACJ,SAEI,KAAM,IAAI,OAAM,wBAAwB,EAAQ,MAAM,EAElE,CACA,MAAO,EACX,CA/CgB,0BAqDhB,YAAuB,EAA0B,CAC7C,GAAI,CAAC,EAAU,MAAO,GACtB,GAAM,GAAO,EAAS,MAAM,GAAG,EAC3B,EAAM,EAEV,OAAQ,EAAK,YACJ,GACD,EAAM,SAAS,EAAK,EAAE,EAAI,GAAK,GAAK,SAAS,EAAK,EAAE,EAAI,GAAK,SAAS,EAAK,EAAE,EAC7E,UACC,GACD,EAAM,SAAS,EAAK,EAAE,EAAI,GAAK,SAAS,EAAK,EAAE,EAC/C,cAEA,EAAM,SAAS,EAAK,EAAE,EAG9B,MAAO,EACX,CAjBS,sBAuBF,YAAsB,EAA4B,CACrD,GAAI,CAAC,GAAQ,CAAC,EAAK,gBAAiB,KAAM,IAAI,OAAM,iCAAiC,EACrF,GAAM,GAAQ,EAAK,gBAAgB,cAAc,IAAI,uBAAuB,OAAO,YAAY,EACzF,EAAM,0BACR,EAAK,gBAAgB,mBAAmB,eAAe,kBACvD,EAAK,gBAAgB,mBAAmB,gBAAgB,mBAAmB,MAEzE,EAAY,EAAK,gBAAgB,UAAU,WAAW,EAAK,gBAAgB,UAAU,WAAW,OAAS,GAe/G,MAdY,IAAI,GAAe,CAC3B,GAAI,EAAK,gBAAgB,UACzB,KAAM,EAAK,gBAAgB,MAAM,WACjC,KAAM,CACF,IAAK,EAAU,IAAI,QAAQ,KAAM,UAAU,EAC3C,MAAO,EAAU,MACjB,OAAQ,EAAU,MACtB,EACA,IAAK,EACL,SAAU,QAAQ,GAAO,SAAS,UAAU,CAAC,EAC7C,OAAQ,QAAQ,GAAO,SAAS,QAAQ,CAAC,EACzC,YAAa,EAAK,gBAAgB,qBAAqB,YAAc,eACzE,CAAC,CAGL,CAvBgB,qBA6BT,YAAoB,EAA0B,CACjD,GAAI,CAAC,GAAQ,CAAC,EAAK,cAAe,KAAM,IAAI,OAAM,+BAA+B,EAEjF,GAAM,GAAU,EAAK,cAAc,UAAU,KAAK,GAC5C,EAAQ,EAAK,cAAc,cAAc,IAAI,uBAAuB,OAAO,YAAY,EACvF,EAAe,EAAK,cAAc,WA+BxC,MA9BY,IAAI,GAAa,CACzB,GAAI,EAAK,cAAc,QACvB,IAAK,mCAAmC,EAAK,cAAc,UAC3D,MAAO,EAAK,cAAc,MAAM,KAAK,GAAG,KACxC,YAAa,EAAK,cAAc,2BAA2B,GAAG,YAAY,MAAM,OAC1E,EAAK,cAAc,yBAAyB,GAAG,YAAY,KAAK,IAAI,AAAC,GAAa,EAAI,IAAI,EAAE,KAAK,EAAE,EACnG,GACN,SAAU,EAAe,GAAc,EAAa,UAAU,EAAI,EAClE,aAAc,EAAe,EAAa,WAAa,KACvD,WAAY,EAAK,cAAc,UAAU,WACzC,QAAS,CACL,GAAI,EAAQ,mBAAmB,eAAe,UAAY,KAC1D,KAAM,EAAQ,MAAQ,KACtB,IAAK,0BACD,EAAQ,mBAAmB,eAAe,kBAC1C,EAAQ,mBAAmB,gBAAgB,mBAAmB,MAElE,MAAO,EAAK,cAAc,mCAAmC,iCAAiC,UACzF,WACL,SAAU,QAAQ,GAAO,SAAS,UAAU,CAAC,EAC7C,OAAQ,QAAQ,GAAO,SAAS,QAAQ,CAAC,CAC7C,EACA,WAAY,EAAK,cAAc,mBAAmB,YAAc,KAChE,SAAU,EAAK,cAAc,mBAAmB,UAC1C,GAAI,MAAK,SAAS,EAAK,cAAc,kBAAkB,SAAS,EAAI,GAAI,EACxE,OACN,MAAO,EAAK,cAAc,eAAe,YAAY,QAAQ,MAAO,EAAE,GAAK,EAC3E,KAAM,EACV,CAAC,CAGL,CArCgB,mBA2CT,YAAuB,EAA6B,CACvD,GAAI,CAAC,GAAQ,CAAC,EAAK,iBAAkB,KAAM,IAAI,OAAM,kCAAkC,EAEvF,GAAM,GACF,EAAK,iBAAiB,WAAW,GAAG,WAAW,EAAK,iBAAiB,WAAW,GAAG,WAAW,OAAS,GACrG,EAAU,EAAK,iBAAiB,gBAAgB,OAAO,GAsB7D,MApBY,IAAI,GACZ,CACI,GAAI,EAAK,iBAAiB,WAC1B,MAAO,EAAK,iBAAiB,MAAM,WACnC,UAAW,CACP,GAAI,EAAK,iBAAiB,WAC1B,IAAK,EAAU,IACf,OAAQ,EAAU,OAClB,MAAO,EAAU,KACrB,EACA,QAAS,CACL,GAAI,GAAS,mBAAmB,eAAe,SAC/C,KAAM,GAAS,KACf,IAAK,0BAA0B,GAAS,mBAAmB,gBAAgB,mBAAmB,KAClG,EACA,OAAQ,SAAS,EAAK,iBAAiB,WAAW,QAAQ,MAAO,EAAE,CAAC,CACxE,EACA,EACJ,CAGJ,CA5BgB,sBA8BhB,YAAyB,EAA6B,CAClD,GAAI,GAAmB,KAAK,AAAC,GAAQ,EAAU,IAAI,SAAS,CAAG,CAAC,EAI5D,OAHA,EAAU,IAAM,EAAU,IAAI,MAAM,GAAG,EAAE,GAGjC,EAAU,IAAI,MAAM,GAAG,EAAE,GAAG,EAAE,EAAG,MAAM,GAAG,EAAE,QAC3C,UACA,YACD,EAAU,MAAQ,IAClB,EAAU,OAAS,IACnB,UACC,QACD,EAAU,MAAQ,KAClB,EAAU,OAAS,IACnB,UACC,YACD,EAAU,MAAQ,IAClB,EAAU,OAAS,IACnB,UACC,YACD,EAAU,MAAQ,IAClB,EAAU,OAAS,IACnB,UACC,UACD,EAAU,MAAQ,IAClB,EAAU,OAAS,GACnB,cAEA,EAAU,MAAQ,EAAU,OAAS,IAGrD,CA/BS,wBC7LT,kBAAgC,EAAgB,EAAgC,CAAC,EAAuB,CACpG,GAAI,GAAM,gDAAkD,EAE5D,GADA,EAAQ,OAAS,QACb,EAAI,QAAQ,MAAM,IAAM,GAExB,OADA,GAAO,OACC,EAAQ,UACP,UACD,GAAO,mBACP,UACC,WACD,GAAO,mBACP,UACC,QACD,GAAO,mBACP,cAEA,KAAM,IAAI,OAAM,wBAAwB,EAAQ,MAAM,EAGlE,GAAM,GAAO,KAAM,GAAQ,EAAK,CAC5B,QAAS,CACL,kBAAmB,EAAQ,UAAY,aAC3C,CACJ,CAAC,EACD,GAAI,EAAK,QAAQ,uEAAuE,IAAM,GAC1F,KAAM,IAAI,OAAM,wDAAwD,EAC5E,MAAO,IAAkB,EAAM,CAAO,CAC1C,CA3BsB,kBCoDf,WAAmB,CAqDtB,YAAY,EAAW,CACnB,KAAK,KAAO,EAAK,KACjB,KAAK,GAAK,EAAK,GACf,KAAK,KAAO,EAAK,cAAc,MAAQ,GACvC,KAAK,KAAO,QACZ,KAAK,IAAM,EAAK,cAAc,QAC9B,KAAK,SAAW,EAAK,SACrB,KAAK,SAAW,EAAK,YACrB,KAAK,aAAe,EAAK,YACzB,KAAK,cAAgB,KAAK,MAAM,KAAK,aAAe,GAAI,EACxD,GAAM,GAA4B,CAAC,EACnC,EAAK,QAAQ,QAAQ,AAAC,GAAW,CAC7B,EAAQ,KAAK,CACT,KAAM,EAAE,KACR,GAAI,EAAE,GACN,IAAK,EAAE,cAAc,OACzB,CAAC,CACL,CAAC,EACD,KAAK,QAAU,EACf,AAAK,EAAK,OAAO,KAEb,KAAK,MAAQ,CACT,KAAM,EAAK,MAAM,KACjB,IAAK,EAAK,cAAc,QACxB,GAAI,EAAK,MAAM,GACf,aAAc,EAAK,MAAM,aACzB,uBAAwB,EAAK,MAAM,uBACnC,aAAc,EAAK,MAAM,YAC7B,EATmB,KAAK,MAAQ,OAWpC,AAAK,EAAK,OAAO,SAAS,GACrB,KAAK,UAAY,EAAK,MAAM,OAAO,GADV,KAAK,UAAY,MAEnD,CAEA,QAAoB,CAChB,MAAO,CACH,KAAM,KAAK,KACX,GAAI,KAAK,GACT,IAAK,KAAK,IACV,SAAU,KAAK,SACf,aAAc,KAAK,aACnB,cAAe,KAAK,cACpB,QAAS,KAAK,QACd,MAAO,KAAK,MACZ,UAAW,KAAK,SACpB,CACJ,CACJ,EApGO,oBAwGA,WAAsB,CA0DzB,YAAY,EAAW,EAAiC,EAAiB,CACrE,KAAK,KAAO,EAAK,KACjB,KAAK,KAAO,WACZ,KAAK,OAAS,EACd,KAAK,cAAgB,EAAK,cAC1B,KAAK,YAAc,EAAK,YACxB,KAAK,IAAM,EAAK,cAAc,QAC9B,KAAK,GAAK,EAAK,GACf,KAAK,UAAY,EAAK,OAAO,GAC7B,KAAK,MAAQ,CACT,KAAM,EAAK,MAAM,aACjB,IAAK,EAAK,MAAM,cAAc,QAC9B,GAAI,EAAK,MAAM,EACnB,EACA,KAAK,YAAc,OAAO,EAAK,OAAO,KAAK,EAC3C,GAAM,GAAyB,CAAC,EAChC,AAAK,KAAK,QACN,EAAK,OAAO,MAAM,QAAQ,AAAC,GAAW,CAClC,AAAI,EAAE,OAAO,EAAO,KAAK,GAAI,GAAa,EAAE,KAAK,CAAC,CACtD,CAAC,EACL,KAAK,eAAiB,GAAI,KAC1B,KAAK,eAAe,IAAI,IAAK,CAAM,EACnC,KAAK,YAAc,CACvB,MAOM,QAAQ,CACV,GAAI,KAAK,OAAQ,MAAO,MACxB,GAAI,GAGJ,GAFA,AAAI,KAAK,YAAc,IAAM,EAAW,IACnC,EAAW,KAAK,YACjB,GAAY,IAAK,MAAO,MAC5B,GAAM,GAAO,CAAC,EACd,OAAS,GAAI,EAAG,GAAK,KAAK,KAAK,EAAW,GAAG,EAAG,IAC5C,EAAK,KACD,GAAI,SAAQ,MAAO,EAAS,IAAW,CACnC,GAAM,GAAW,KAAM,GACnB,wCAAwC,KAAK,oBACxC,GAAI,GAAK,wBACO,KAAK,YAAY,SACtC,CACI,QAAS,CACL,cAAe,GAAG,KAAK,YAAY,cAAc,KAAK,YAAY,cACtE,CACJ,CACJ,EAAE,MAAM,AAAC,GAAQ,EAAO;AAAA,EAAsB,GAAK,CAAC,EAC9C,EAAyB,CAAC,EAChC,GAAI,MAAO,IAAa,SAAU,OAElC,AADkB,KAAK,MAAM,CAAQ,EAC3B,MAAM,QAAQ,AAAC,GAAW,CAChC,AAAI,EAAE,OAAO,EAAO,KAAK,GAAI,GAAa,EAAE,KAAK,CAAC,CACtD,CAAC,EACD,KAAK,eAAe,IAAI,GAAG,IAAK,CAAM,EACtC,EAAQ,SAAS,CACrB,CAAC,CACL,EAEJ,YAAM,SAAQ,WAAW,CAAI,EACtB,IACX,CAgBA,KAAK,EAAa,CACd,GAAI,CAAC,EAAK,KAAM,IAAI,OAAM,6BAA6B,EACvD,GAAI,CAAC,KAAK,eAAe,IAAI,GAAG,GAAK,EAAG,KAAM,IAAI,OAAM,8BAA8B,EACtF,MAAO,MAAK,eAAe,IAAI,GAAG,GAAK,CAC3C,IAKI,cAAc,CACd,MAAO,MAAK,eAAe,IAC/B,IAII,eAAe,CACf,GAAI,KAAK,OAAQ,MAAO,MAAK,YAC7B,GAAM,GAAsB,KAAK,YACjC,MAAQ,GAAc,GAAK,IAAO,KAAK,eAAe,IAAI,GAAG,GAAa,EAAqB,MACnG,MAWM,aAAsC,CACxC,KAAM,MAAK,MAAM,EAEjB,GAAM,GAAyB,CAAC,EAEhC,OAAW,KAAQ,MAAK,eAAe,OAAO,EAAG,EAAO,KAAK,GAAG,CAAI,EAEpE,MAAO,EACX,CAKA,QAAuB,CACnB,MAAO,CACH,KAAM,KAAK,KACX,cAAe,KAAK,cACpB,YAAa,KAAK,YAClB,IAAK,KAAK,IACV,GAAI,KAAK,GACT,UAAW,KAAK,UAChB,MAAO,KAAK,MACZ,YAAa,KAAK,WACtB,CACJ,CACJ,EAhMO,uBAoMA,WAAmB,CA8DtB,YAAY,EAAW,EAAiC,EAAiB,CACrE,KAAK,KAAO,EAAK,KACjB,KAAK,KAAO,QACZ,KAAK,GAAK,EAAK,GACf,KAAK,OAAS,EACd,KAAK,IAAM,EAAK,cAAc,QAC9B,KAAK,UAAY,EAAK,OAAO,GAC7B,GAAM,GAA4B,CAAC,EACnC,EAAK,QAAQ,QAAQ,AAAC,GAAW,CAC7B,EAAQ,KAAK,CACT,KAAM,EAAE,KACR,GAAI,EAAE,GACN,IAAK,EAAE,cAAc,OACzB,CAAC,CACL,CAAC,EACD,KAAK,QAAU,EACf,KAAK,WAAa,EAAK,WACvB,KAAK,aAAe,EAAK,aACzB,KAAK,uBAAyB,EAAK,uBACnC,KAAK,YAAc,EAAK,aACxB,GAAM,GAAyB,CAAC,EAChC,AAAK,KAAK,QACN,EAAK,OAAO,MAAM,QAAQ,AAAC,GAAW,CAClC,EAAO,KAAK,GAAI,GAAa,CAAC,CAAC,CACnC,CAAC,EACL,KAAK,eAAiB,GAAI,KAC1B,KAAK,eAAe,IAAI,IAAK,CAAM,EACnC,KAAK,YAAc,CACvB,MAOM,QAAQ,CACV,GAAI,KAAK,OAAQ,MAAO,MACxB,GAAI,GAGJ,GAFA,AAAI,KAAK,YAAc,IAAK,EAAW,IAClC,EAAW,KAAK,YACjB,GAAY,GAAI,MAAO,MAC3B,GAAM,GAAO,CAAC,EACd,OAAS,GAAI,EAAG,GAAK,KAAK,KAAK,EAAW,EAAE,EAAG,IAC3C,EAAK,KACD,GAAI,SAAQ,MAAO,EAAS,IAAW,CACnC,GAAM,GAAW,KAAM,GACnB,qCAAqC,KAAK,oBAAqB,GAAI,GAAK,sBACpE,KAAK,YAAY,SAErB,CACI,QAAS,CACL,cAAe,GAAG,KAAK,YAAY,cAAc,KAAK,YAAY,cACtE,CACJ,CACJ,EAAE,MAAM,AAAC,GAAQ,EAAO;AAAA,EAAsB,GAAK,CAAC,EAC9C,EAAyB,CAAC,EAChC,GAAI,MAAO,IAAa,SAAU,OAElC,AADkB,KAAK,MAAM,CAAQ,EAC3B,MAAM,QAAQ,AAAC,GAAW,CAChC,AAAI,GAAG,EAAO,KAAK,GAAI,GAAa,CAAC,CAAC,CAC1C,CAAC,EACD,KAAK,eAAe,IAAI,GAAG,IAAK,CAAM,EACtC,EAAQ,SAAS,CACrB,CAAC,CACL,EAEJ,YAAM,SAAQ,WAAW,CAAI,EACtB,IACX,CAgBA,KAAK,EAAa,CACd,GAAI,CAAC,EAAK,KAAM,IAAI,OAAM,6BAA6B,EACvD,GAAI,CAAC,KAAK,eAAe,IAAI,GAAG,GAAK,EAAG,KAAM,IAAI,OAAM,8BAA8B,EACtF,MAAO,MAAK,eAAe,IAAI,GAAG,GAAK,CAC3C,IAKI,cAAc,CACd,MAAO,MAAK,eAAe,IAC/B,IAII,eAAe,CACf,GAAI,KAAK,OAAQ,MAAO,MAAK,YAC7B,GAAM,GAAsB,KAAK,YACjC,MAAQ,GAAc,GAAK,IAAO,KAAK,eAAe,IAAI,GAAG,GAAa,EAAqB,MACnG,MAWM,aAAsC,CACxC,KAAM,MAAK,MAAM,EAEjB,GAAM,GAAyB,CAAC,EAEhC,OAAW,KAAQ,MAAK,eAAe,OAAO,EAAG,EAAO,KAAK,GAAG,CAAI,EAEpE,MAAO,EACX,CAKA,QAAoB,CAChB,MAAO,CACH,KAAM,KAAK,KACX,GAAI,KAAK,GACT,KAAM,KAAK,KACX,IAAK,KAAK,IACV,UAAW,KAAK,UAChB,QAAS,KAAK,QACd,WAAY,KAAK,WACjB,aAAc,KAAK,aACnB,uBAAwB,KAAK,uBAC7B,YAAa,KAAK,WACtB,CACJ,CACJ,EA3MO,oBCpXP,6EAEA,GAAI,GACJ,AAAI,GAAW,oBAAoB,GAC/B,GAAc,KAAK,MAAM,GAAa,qBAAsB,OAAO,CAAC,EACpE,EAAY,KAAO,IAmBvB,GAAM,IAAU,iFAiBhB,kBAA8B,EAA+B,CACzD,GAAI,CAAC,EAAa,KAAM,IAAI,OAAM;AAAA,qCAA+D,EACjG,GAAM,GAAO,EAAI,KAAK,EACtB,GAAI,CAAC,EAAK,MAAM,EAAO,EAAG,KAAM,IAAI,OAAM,2BAA2B,EACrE,GAAI,EAAK,QAAQ,QAAQ,IAAM,GAAI,CAC/B,GAAM,GAAU,EAAK,MAAM,QAAQ,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE,GAC3D,EAAW,KAAM,GAAQ,qCAAqC,YAAkB,EAAY,SAAU,CACxG,QAAS,CACL,cAAe,GAAG,EAAY,cAAc,EAAY,cAC5D,CACJ,CAAC,EAAE,MAAM,AAAC,GACC,CACV,EACD,GAAI,YAAoB,OAAO,KAAM,GACrC,GAAM,GAAS,KAAK,MAAM,CAAQ,EAClC,GAAI,EAAO,MAAO,KAAM,IAAI,OAAM,OAAO,EAAO,MAAM,oCAAoC,EAAO,MAAM,SAAS,EAChH,MAAO,IAAI,GAAa,CAAM,CAClC,SAAW,EAAK,QAAQ,QAAQ,IAAM,GAAI,CACtC,GAAM,GAAU,EAAI,MAAM,QAAQ,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE,GAC1D,EAAW,KAAM,GAAQ,qCAAqC,YAAkB,EAAY,SAAU,CACxG,QAAS,CACL,cAAe,GAAG,EAAY,cAAc,EAAY,cAC5D,CACJ,CAAC,EAAE,MAAM,AAAC,GACC,CACV,EACD,GAAI,YAAoB,OAAO,KAAM,GACrC,GAAM,GAAS,KAAK,MAAM,CAAQ,EAClC,GAAI,EAAO,MAAO,KAAM,IAAI,OAAM,OAAO,EAAO,MAAM,oCAAoC,EAAO,MAAM,SAAS,EAChH,MAAO,IAAI,GAAa,EAAQ,EAAa,EAAK,CACtD,SAAW,EAAK,QAAQ,WAAW,IAAM,GAAI,CACzC,GAAM,GAAa,EAAI,MAAM,WAAW,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE,GAChE,EAAW,KAAM,GACnB,wCAAwC,YAAqB,EAAY,SACzE,CACI,QAAS,CACL,cAAe,GAAG,EAAY,cAAc,EAAY,cAC5D,CACJ,CACJ,EAAE,MAAM,AAAC,GACE,CACV,EACD,GAAI,YAAoB,OAAO,KAAM,GACrC,GAAM,GAAS,KAAK,MAAM,CAAQ,EAClC,GAAI,EAAO,MAAO,KAAM,IAAI,OAAM,OAAO,EAAO,MAAM,oCAAoC,EAAO,MAAM,SAAS,EAChH,MAAO,IAAI,GAAgB,EAAQ,EAAa,EAAK,CACzD,KAAO,MAAM,IAAI,OAAM,kCAAkC,CAC7D,CA/CsB,gBAwDf,YAAqB,EAAgE,CACxF,GAAM,GAAO,EAAI,KAAK,EACtB,MAAK,GAAK,WAAW,OAAO,EACvB,EAAK,MAAM,EAAO,EACnB,EAAK,QAAQ,QAAQ,IAAM,GACpB,QACA,EAAK,QAAQ,QAAQ,IAAM,GAC3B,QACA,EAAK,QAAQ,WAAW,IAAM,GAC9B,WACG,GAPmB,GADK,QAS1C,CAXgB,oBAiBhB,kBAAuC,EAA0B,EAAiC,CAC9F,GAAM,GAAW,KAAM,GAAQ,yCAA0C,CACrE,QAAS,CACL,cAAiB,SAAS,OAAO,KAAK,GAAG,EAAK,aAAa,EAAK,eAAe,EAAE,SAAS,QAAQ,IAClG,eAAgB,mCACpB,EACA,KAAM,sCAAsC,EAAK,mCAAmC,UAChF,EAAK,YACT,IACA,OAAQ,MACZ,CAAC,EAAE,MAAM,AAAC,GACC,CACV,EACD,GAAI,YAAoB,OAAO,KAAM,GACrC,GAAM,GAAY,KAAK,MAAM,CAAQ,EACrC,SAAc,CACV,UAAW,EAAK,UAChB,cAAe,EAAK,cACpB,aAAc,EAAK,aACnB,aAAc,EAAU,aACxB,cAAe,EAAU,cACzB,WAAY,OAAO,EAAU,UAAU,EACvC,OAAQ,KAAK,IAAI,EAAK,GAAU,WAAa,GAAK,IAClD,WAAY,EAAU,WACtB,OAAQ,EAAK,MACjB,EACA,AAAI,EAAM,GAAc,qBAAsB,KAAK,UAAU,EAAa,OAAW,CAAC,CAAC,EAEnF,SAAQ,IAAI,eAAe,EAAY,WAAW,EAClD,QAAQ,IAAI,mBAAmB,EAAY,eAAe,EAC1D,QAAQ,IAAI,mBAAmB,EAAY,eAAe,EAC1D,QAAQ,IAAI,YAAY,EAAY,QAAQ,EAC5C,QAAQ,IAAI;AAAA,uCAA0C,GAEnD,EACX,CAnCsB,yBA+Cf,aAA+B,CAClC,MAAI,MAAK,IAAI,GAAM,EAAY,MAEnC,CAHgB,mBAehB,kBACI,EACA,EACA,EAAgB,GACE,CAClB,GAAM,GAAqB,CAAC,EAC5B,GAAI,CAAC,EAAa,KAAM,IAAI,OAAM;AAAA,qCAA+D,EACjG,GAAI,EAAM,SAAW,EAAG,KAAM,IAAI,OAAM,4BAA4B,EACpE,GAAI,EAAQ,IAAM,EAAQ,EAAG,KAAM,IAAI,OAAM,+CAA+C,EAC5F,GAAM,GAAW,KAAM,GACnB,0CAA0C,OAAU,WAAe,YAAgB,EAAY,SAC/F,CACI,QAAS,CACL,cAAe,GAAG,EAAY,cAAc,EAAY,cAC5D,CACJ,CACJ,EAAE,MAAM,AAAC,GACE,CACV,EACD,GAAI,YAAoB,OAAO,KAAM,GACrC,GAAM,GAAY,KAAK,MAAM,CAAQ,EACrC,MAAI,KAAS,QACT,EAAU,OAAO,MAAM,QAAQ,AAAC,GAAe,CAC3C,EAAQ,KAAK,GAAI,GAAa,CAAK,CAAC,CACxC,CAAC,EACE,AAAI,IAAS,QAChB,EAAU,OAAO,MAAM,QAAQ,AAAC,GAAe,CAC3C,EAAQ,KAAK,GAAI,GAAa,EAAO,EAAa,EAAI,CAAC,CAC3D,CAAC,EACM,IAAS,YAChB,EAAU,UAAU,MAAM,QAAQ,AAAC,GAAkB,CACjD,EAAQ,KAAK,GAAI,GAAgB,EAAU,EAAa,EAAI,CAAC,CACjE,CAAC,EAEE,CACX,CAnCsB,kBA8CtB,mBAAuD,CACnD,GAAM,GAAW,KAAM,GAAQ,yCAA0C,CACrE,QAAS,CACL,cAAiB,SAAS,OAAO,KAAK,GAAG,EAAY,aAAa,EAAY,eAAe,EAAE,SAC3F,QACJ,IACA,eAAgB,mCACpB,EACA,KAAM,0CAA0C,EAAY,gBAC5D,OAAQ,MACZ,CAAC,EAAE,MAAM,AAAC,GACC,CACV,EACD,GAAI,YAAoB,OAAO,MAAO,GACtC,GAAM,GAAY,KAAK,MAAM,CAAQ,EACrC,SAAY,aAAe,EAAU,aACrC,EAAY,WAAa,OAAO,EAAU,UAAU,EACpD,EAAY,OAAS,KAAK,IAAI,EAAK,GAAU,WAAa,GAAK,IAC/D,EAAY,WAAa,EAAU,WAC/B,EAAY,MAAM,GAAc,qBAAsB,KAAK,UAAU,EAAa,OAAW,CAAC,CAAC,EAC5F,EACX,CArBsB,qBAuBtB,kBAAsC,EAA6B,CAC/D,EAAc,EACd,EAAY,KAAO,GACnB,KAAM,IAAa,CACvB,CAJsB,wBCvPtB,yDCCA,wCA4FO,WAAsB,CA2DzB,YAAY,EAAW,CACnB,KAAK,KAAO,EAAK,MACjB,KAAK,GAAK,EAAK,GACf,KAAK,IAAM,EAAK,IAChB,KAAK,UAAY,EAAK,cACtB,KAAK,QAAU,GACf,KAAK,KAAO,QACZ,KAAK,cAAgB,KAAK,MAAM,OAAO,EAAK,QAAQ,EAAI,GAAI,EAC5D,KAAK,aAAe,OAAO,EAAK,QAAQ,EACxC,AAAI,EAAK,mBACL,KAAK,UAAY,CACb,KAAM,EAAK,mBAAmB,UAC9B,GAAI,EAAK,mBAAmB,GAC5B,OAAQ,EAAK,mBAAmB,OAChC,eAAgB,QAAQ,EAAK,mBAAmB,cAAc,GAAK,GACnE,gBAAiB,EAAK,mBAAmB,eAC7C,EACC,KAAK,UAAY,KACtB,KAAK,QAAU,EAAK,MAAM,aAC1B,KAAK,KAAO,CACR,KAAM,EAAK,KAAK,SAChB,GAAI,EAAK,KAAK,GACd,KAAM,OACN,IAAK,EAAK,KAAK,cACf,SAAU,QAAQ,EAAK,KAAK,QAAQ,GAAK,GACzC,YAAa,EAAK,KAAK,YACvB,WAAY,EAAK,KAAK,WACtB,UAAW,EAAK,KAAK,UACrB,UAAW,EAAK,KAAK,UACrB,UAAW,EAAK,KAAK,UACzB,EACA,KAAK,UAAY,EAAK,WAC1B,CAKA,QAAyB,CACrB,MAAO,CACH,KAAM,KAAK,KACX,GAAI,KAAK,GACT,IAAK,KAAK,IACV,UAAW,KAAK,UAChB,QAAS,KAAK,QACd,aAAc,KAAK,aACnB,cAAe,KAAK,cACpB,UAAW,KAAK,UAChB,QAAS,KAAK,QACd,UAAW,KAAK,UAChB,KAAM,KAAK,IACf,CACJ,CACJ,EA/GO,uBAmHA,WAAyB,CAmD5B,YAAY,EAAW,EAAmB,CACtC,KAAK,KAAO,EAAK,MACjB,KAAK,GAAK,EAAK,GACf,KAAK,IAAM,EAAK,IAChB,KAAK,UAAY,EACjB,KAAK,KAAO,WACZ,KAAK,SAAW,EAAK,SACrB,KAAK,cAAgB,KAAK,MAAM,OAAO,EAAK,QAAQ,EAAI,GAAI,EAC5D,KAAK,aAAe,OAAO,EAAK,QAAQ,EACxC,KAAK,KAAO,CACR,KAAM,EAAK,KAAK,SAChB,GAAI,EAAK,KAAK,GACd,KAAM,OACN,IAAK,EAAK,KAAK,cACf,SAAU,QAAQ,EAAK,KAAK,QAAQ,GAAK,GACzC,YAAa,EAAK,KAAK,YACvB,WAAY,EAAK,KAAK,WACtB,UAAW,EAAK,KAAK,UACrB,UAAW,EAAK,KAAK,UACrB,UAAW,EAAK,KAAK,UACzB,EACA,KAAK,YAAc,EAAK,YACxB,GAAM,GAAgB,CAAC,EACvB,EAAK,OAAO,QAAQ,AAAC,GAAe,CAChC,AAAI,EAAM,MACN,EAAO,KAAK,GAAI,GAAgB,CAAK,CAAC,EAEtC,EAAO,KAAK,CACR,GAAI,EAAM,GACV,QAAS,GACT,KAAM,OACV,CAAC,CACT,CAAC,EACD,KAAK,OAAS,CAClB,MAOM,QAAqC,CACvC,GAAM,GAAc,CAAC,EACrB,OAAS,GAAI,EAAG,EAAI,KAAK,OAAO,OAAQ,IACpC,AAAK,KAAK,OAAO,GAAG,SAChB,EAAK,KACD,GAAI,SAAQ,KAAO,IAAY,CAC3B,GAAM,GAAM,EACN,EAAO,KAAM,GACf,wCAAwC,KAAK,OAAO,GAAG,gBAAgB,KAAK,WAChF,EAEA,KAAK,OAAO,GAAO,GAAI,GAAgB,KAAK,MAAM,CAAI,CAAC,EACvD,EAAQ,EAAE,CACd,CAAC,CACL,EAGR,YAAM,SAAQ,WAAW,CAAI,EACtB,IACX,IAKI,eAAuB,CACvB,GAAI,GAAQ,EACZ,YAAK,OAAO,QAAQ,AAAC,GAAU,CAC3B,GAAI,YAAiB,GAAiB,QACjC,OACT,CAAC,EACM,CACX,MAWM,aAAyC,CAC3C,YAAM,MAAK,MAAM,EAEV,KAAK,MAChB,CAKA,QAAuB,CACnB,MAAO,CACH,KAAM,KAAK,KACX,GAAI,KAAK,GACT,SAAU,KAAK,SACf,IAAK,KAAK,IACV,aAAc,KAAK,aACnB,cAAe,KAAK,cACpB,YAAa,KAAK,YAClB,KAAM,KAAK,KACX,OAAQ,KAAK,MACjB,CACJ,CACJ,EA5JO,0BAgKA,WAAuB,CAkD1B,YAAY,EAAa,EAAmB,YAAsB,CAC9D,KAAK,OAAS,GAAI,IAAS,CAAE,cAAe,EAAI,IAAO,IAAM,MAAO,CAAC,CAAE,CAAC,EACxE,KAAK,KAAO,EACZ,KAAK,IAAM,EACX,KAAK,gBAAkB,EACvB,KAAK,QAAU,KACf,KAAK,oBAAsB,EAC3B,KAAK,KAAO,CAAC,EACb,KAAK,MAAQ,GAAI,GAAM,IAAM,CACzB,KAAK,MAAM,MAAM,EACjB,KAAK,MAAM,CACf,EAAG,GAAG,EACN,KAAK,aAAe,CAAC,EACrB,KAAK,OAAO,GAAG,QAAS,IAAM,CAC1B,KAAK,QAAQ,CACjB,CAAC,EACD,KAAK,MAAM,CACf,MAKc,SAAS,CACnB,GAAM,GAAW,KAAM,GAAQ,KAAK,GAAG,EAAE,MAAM,AAAC,GACrC,CACV,EACD,GAAI,YAAoB,OAAO,KAAM,GAErC,AADc,EAAS,MAAM;AAAA,CAAI,EAC3B,QAAQ,AAAC,GAAQ,CACnB,AAAI,EAAI,WAAW,UAAU,EACzB,KAAK,KAAK,KAAK,WAAW,EAAI,QAAQ,WAAY,EAAE,CAAC,CAAC,EAC/C,EAAI,WAAW,OAAO,GAC7B,KAAK,aAAa,KAAK,CAAG,CAElC,CAAC,CAEL,MAIc,QAAQ,CAClB,GAAI,KAAK,OAAO,UAAW,CACvB,KAAK,QAAQ,EACb,MACJ,CACA,KAAK,KAAO,CAAC,EACb,KAAK,aAAe,CAAC,EACrB,KAAK,gBAAkB,EACvB,KAAM,MAAK,OAAO,EAClB,KAAK,aAAa,OAAO,EAAG,KAAK,mBAAmB,EACpD,KAAK,KAAK,CACd,MAIc,OAAO,CACjB,GAAI,KAAK,OAAO,UAAW,CACvB,KAAK,QAAQ,EACb,MACJ,CACA,GAAI,KAAK,KAAK,SAAW,GAAK,KAAK,aAAa,SAAW,EAAG,CAC1D,KAAK,QAAQ,EACb,KAAK,OAAO,KAAK,IAAI,EACrB,MACJ,CACA,KAAK,iBAAmB,KAAK,KAAK,MAAM,EACxC,KAAK,sBACL,GAAM,GAAS,KAAM,GAAe,KAAK,aAAa,MAAM,CAAW,EAAE,MAAM,AAAC,GAAe,CAAG,EAClG,GAAI,YAAkB,OAAO,CACzB,KAAK,OAAO,KAAK,QAAS,CAAM,EAChC,KAAK,QAAQ,EACb,MACJ,CAEA,KAAK,QAAU,EACf,EAAO,GAAG,OAAQ,AAAC,GAAM,CACrB,KAAK,OAAO,KAAK,CAAC,CACtB,CAAC,EACD,EAAO,GAAG,MAAO,IAAM,CACnB,AAAI,KAAK,iBAAmB,KACvB,KAAK,KAAK,CACnB,CAAC,EACD,EAAO,KAAK,QAAS,AAAC,GAAQ,CAC1B,KAAK,OAAO,KAAK,QAAS,CAAG,CACjC,CAAC,CACL,CAMQ,SAAU,CACd,KAAK,MAAM,QAAQ,EACnB,KAAK,SAAS,QAAQ,EACtB,KAAK,IAAM,GACX,KAAK,gBAAkB,EACvB,KAAK,oBAAsB,EAC3B,KAAK,QAAU,KACf,KAAK,KAAO,CAAC,EACb,KAAK,aAAe,CAAC,CACzB,CAOA,OAAQ,CACJ,KAAK,MAAM,MAAM,CACrB,CAKA,QAAS,CACL,KAAK,MAAM,OAAO,CACtB,CACJ,EAvKO,wBD5WP,GAAI,GACJ,AAAI,GAAW,uBAAuB,GAClC,GAAY,KAAK,MAAM,GAAa,wBAAyB,OAAO,CAAC,GAOzE,GAAM,IAAU,2FAiBhB,kBAAiC,EAAkC,CAC/D,GAAI,CAAC,EAAW,KAAM,IAAI,OAAM;AAAA,qCAAkE,EAClG,GAAM,GAAO,EAAI,KAAK,EACtB,GAAI,CAAC,EAAK,MAAM,EAAO,EAAG,KAAM,IAAI,OAAM,8BAA8B,EAExE,GAAM,GAAO,KAAM,GACf,6CAA6C,eAAkB,EAAU,WAC7E,EAAE,MAAM,AAAC,GAAe,CAAG,EAE3B,GAAI,YAAgB,OAAO,KAAM,GAEjC,GAAM,GAAY,KAAK,MAAM,CAAI,EAEjC,GAAI,EAAU,OAAS,SAAW,EAAU,OAAS,WACjD,KAAM,IAAI,OAAM,uCAAuC,EAE3D,MAAI,GAAU,OAAS,QAAgB,GAAI,GAAgB,CAAS,EACxD,GAAI,GAAmB,EAAW,EAAU,SAAS,CACrE,CAlBsB,mBA8BtB,kBACI,EACA,EACA,EAAgB,GACK,CACrB,GAAM,GAAW,KAAM,GACnB,wCAAwC,OAAU,eAAmB,EAAU,mBAAmB,GACtG,EACM,EAAoD,CAAC,EAE3D,MADkB,MAAK,MAAM,CAAQ,EAC3B,WAAW,QAAQ,AAAC,GAAW,CACrC,AAAI,IAAS,SAAU,EAAQ,KAAK,GAAI,GAAgB,CAAC,CAAC,EACrD,EAAQ,KAAK,GAAI,GAAmB,EAAG,EAAU,SAAS,CAAC,CACpE,CAAC,EACM,CACX,CAfsB,kBAsBtB,kBAA6B,EAAa,EAA6C,CACnF,GAAM,GAAO,KAAM,IAAW,CAAG,EAEjC,GAAI,YAAgB,GAAoB,KAAM,IAAI,OAAM,6CAA6C,EAErG,GAAM,GAAa,GAAgB,EAAK,OAAO,EAC/C,AAAI,MAAO,IAAY,SAAU,EAAU,EAAW,OAAS,EAC1D,AAAI,GAAW,EAAG,EAAU,EACxB,GAAW,EAAW,QAAQ,GAAU,EAAW,OAAS,GACrE,GAAM,GAAU,EAAW,GAAS,IAAM,cAAgB,EAAU,UAC9D,EAAS,KAAK,MAAM,KAAM,GAAQ,CAAO,CAAC,EAC1C,EAAO,EAAW,GAAS,OAAO,UAAU,WAAW,WAAW,EAClE,WACA,YACN,MAAO,IAAI,GAAiB,EAAO,IAAK,CAAI,CAChD,CAfsB,eA8BtB,mBAAyD,CACrD,GAAM,GAAY,KAAM,GAAQ,0BAA2B,CAAC,QAAS,CAAC,CAAC,CAAC,EAAE,MAAM,GAAO,CAAG,EAE1F,GAAI,YAAgB,OAChB,KAAM,IAAI,OAAM,+CAAiD,EAAK,OAAO,EAEjF,GAAM,GAAW,EAAK,MAAM,2BAA2B,EACjD,EAAiB,CAAC,EACxB,SAAS,QAAQ,AAAC,GAAc,CAC5B,AAAI,EAAE,WAAW,OAAO,GACpB,EAAK,KAAK,EAAE,MAAM,GAAG,EAAE,EAAE,CAEjC,CAAC,EAEM,AADO,MAAM,GAAQ,EAAK,EAAK,OAAS,EAAE,GACpC,MAAM,cAAc,EAAE,GAAG,MAAM,GAAG,EAAE,EACrD,CAfsB,wBAsBtB,kBAAuC,EAAuB,EAA6C,CACvG,GAAM,GAAa,GAAgB,EAAK,OAAO,EAC/C,AAAI,MAAO,IAAY,SAAU,EAAU,EAAW,OAAS,EAC1D,AAAI,GAAW,EAAG,EAAU,EACxB,GAAW,EAAW,QAAQ,GAAU,EAAW,OAAS,GACrE,GAAM,GAAU,EAAW,GAAS,IAAM,cAAgB,EAAU,UAC9D,EAAS,KAAK,MAAM,KAAM,GAAQ,CAAO,CAAC,EAC1C,EAAO,EAAW,GAAS,OAAO,UAAU,WAAW,WAAW,EAClE,WACA,YACN,MAAO,IAAI,GAAiB,EAAO,IAAK,CAAI,CAChD,CAXsB,yBAiBtB,kBAA+B,EAA8B,CAMzD,MAAI,EALa,KAAM,GAAQ,kDAAkD,uBAAwB,EAAE,MACvG,AAAC,GACU,CAEf,WACwB,OAE5B,CARsB,iBAiBtB,kBAAkC,EAA+D,CAC7F,GAAM,GAAO,EAAI,KAAK,EACtB,GAAI,CAAC,EAAK,WAAW,OAAO,EAAG,MAAO,SACtC,GAAI,CAAC,EAAK,MAAM,EAAO,EAAG,MAAO,GACjC,GAAM,GAAO,KAAM,GACf,6CAA6C,eAAkB,EAAU,WAC7E,EAAE,MAAM,AAAC,GAAe,CAAG,EAE3B,GAAI,YAAgB,OAAO,MAAO,GAElC,GAAM,GAAY,KAAK,MAAM,CAAI,EACjC,MAAI,GAAU,OAAS,QAAgB,QAC9B,EAAU,OAAS,WAAmB,WACnC,EAChB,CAdsB,oBAoBtB,YAAyB,EAA+B,CACpD,GAAM,GAAkC,CAAC,EACzC,SAAK,QAAQ,AAAC,GAAW,CACrB,AAAI,EAAO,OAAO,WAAa,OAAO,EAAO,KAAK,CAAM,CAC5D,CAAC,EACM,CACX,CANS,wBAQF,YAA4B,EAA2B,CAC1D,EAAY,CAChB,CAFgB,2BEpMhB,gCCwDO,WAAkB,CA8GrB,YAAY,EAAW,EAAkB,CACrC,KAAK,GAAK,EAAK,GACf,KAAK,MAAQ,EAAK,MAClB,KAAK,WAAa,EAAK,YACvB,KAAK,IAAM,EAAK,KAChB,KAAK,cAAgB,EAAK,SAC1B,KAAK,KAAO,EAAK,KACjB,KAAK,SAAW,EAAK,gBACrB,KAAK,WAAa,EAAK,QACvB,KAAK,OAAS,GAAI,GAAa,EAAK,MAAM,EAC1C,KAAK,MAAQ,GAAI,IAAiB,EAAK,KAAK,EAC5C,KAAK,KAAO,QAEZ,KAAK,QAAU,EAEV,GACD,MAAK,cAAgB,EAAK,eAC1B,KAAK,WAAa,EAAK,YACvB,KAAK,YAAc,GAAI,MAAK,EAAK,YAAY,EAC7C,KAAK,IAAM,EAAK,IAChB,KAAK,KAAO,EAAK,KACjB,KAAK,aAAe,CAAC,EAErB,EAAK,aAAa,QAAQ,AAAC,GAAqB,CAC5C,KAAK,cAAc,KAAK,GAAI,GAAa,CAAW,CAAC,CACzD,CAAC,EAET,MASM,QAA8B,CAChC,GAAI,CAAC,KAAK,QAAS,MAAO,MAE1B,GAAM,GAAW,KAAM,GAAQ,gCAAgC,KAAK,KAAK,EAAE,MAAM,AAAC,GAAe,CAAG,EAEpG,GAAI,YAAoB,OAAO,KAAM,GACrC,GAAM,GAAW,KAAK,MAAM,CAAQ,EAEpC,YAAK,QAAU,GAEf,KAAK,cAAgB,EAAS,eAC9B,KAAK,WAAa,EAAS,YAC3B,KAAK,YAAc,GAAI,MAAK,EAAS,YAAY,EACjD,KAAK,IAAM,EAAS,IACpB,KAAK,KAAO,EAAS,KACrB,KAAK,aAAe,CAAC,EAErB,EAAS,aAAa,QAAQ,AAAC,GAAqB,CAChD,KAAK,cAAc,KAAK,GAAI,GAAa,CAAW,CAAC,CACzD,CAAC,EAEM,IACX,CAKA,QAAS,CACL,MAAO,CACH,GAAI,KAAK,GACT,MAAO,KAAK,MACZ,WAAY,KAAK,WACjB,IAAK,KAAK,IACV,cAAe,KAAK,cACpB,KAAM,KAAK,KACX,SAAU,KAAK,SACf,WAAY,KAAK,WACjB,OAAQ,KAAK,OACb,MAAO,KAAK,MACZ,KAAM,KAAK,KACX,cAAe,KAAK,cACpB,WAAY,KAAK,WACjB,YAAa,KAAK,YAClB,IAAK,KAAK,IACV,KAAM,KAAK,KACX,aAAc,KAAK,YACvB,CACJ,CACJ,EAlMO,mBAsMA,WAAkB,CAyHrB,YAAY,EAAW,EAAkB,CAqBrC,GApBA,KAAK,GAAK,EAAK,GACf,KAAK,MAAQ,EAAK,MAClB,KAAK,IAAM,EAAK,KAChB,KAAK,WAAa,EAAK,YACvB,KAAK,SAAW,EAAK,gBACrB,KAAK,OAAS,GAAI,GAAa,EAAK,MAAM,EAC1C,KAAK,KAAO,QACZ,KAAK,YAAc,EAAK,UACxB,KAAK,aAAe,CAAC,EACrB,KAAK,OAAS,CAAC,EACf,KAAK,OAAS,CAAC,EACf,KAAK,MAAQ,CACT,GAAI,EAAK,SACT,IAAK,EAAK,UACV,OAAQ,EAAK,aACb,MAAO,EAAK,WAChB,EAEA,KAAK,QAAU,EAEX,CAAC,EAAS,CACV,KAAK,IAAM,EAAK,IAChB,KAAK,cAAgB,EAAK,SAC1B,KAAK,aAAe,EAAK,KACzB,KAAK,YAAc,GAAI,MAAK,EAAK,YAAY,EAC7C,KAAK,UAAY,EAAK,UAEtB,EAAK,aAAa,QAAQ,AAAC,GAAqB,CAC5C,KAAK,cAAc,KAAK,GAAI,GAAa,CAAW,CAAC,CACzD,CAAC,EAED,EAAK,OAAO,KAAK,QAAQ,AAAC,GAAe,CACrC,KAAK,QAAQ,KAAK,CACd,KAAM,EAAM,KACZ,QAAS,CACL,GAAI,GAAG,EAAM,kBACb,IAAK,GAAG,EAAM,mBACd,OAAQ,GAAG,EAAM,sBACjB,MAAO,GAAG,EAAM,oBACpB,CACJ,CAAC,CACL,CAAC,EAED,GAAM,GAAkB,CACpB,GAAI,KAAK,GACT,MAAO,KAAK,MACZ,SAAU,KAAK,MAAM,GACrB,UAAW,KAAK,MAAM,IACtB,aAAc,KAAK,MAAM,OACzB,YAAa,KAAK,MAAM,MACxB,aAAc,EAAK,YACvB,EACA,EAAK,OAAO,KAAK,QAAQ,AAAC,GAAe,CACrC,EAAM,MAAQ,EACd,KAAK,OAAO,KAAK,GAAI,GAAY,EAAO,EAAI,CAAC,CACjD,CAAC,CACL,CACJ,MASM,QAA8B,CAChC,GAAI,CAAC,KAAK,QAAS,MAAO,MAE1B,GAAM,GAAW,KAAM,GAAQ,gCAAgC,KAAK,KAAK,EAAE,MAAM,AAAC,GAAe,CAAG,EAEpG,GAAI,YAAoB,OAAO,KAAM,GACrC,GAAM,GAAW,KAAK,MAAM,CAAQ,EAEpC,KAAK,QAAU,GAEf,KAAK,IAAM,EAAS,IACpB,KAAK,cAAgB,EAAS,SAC9B,KAAK,aAAe,EAAS,KAC7B,KAAK,YAAc,GAAI,MAAK,EAAS,YAAY,EACjD,KAAK,UAAY,EAAS,UAC1B,KAAK,aAAe,CAAC,EACrB,KAAK,OAAS,CAAC,EACf,KAAK,OAAS,CAAC,EAEf,EAAS,aAAa,QAAQ,AAAC,GAAqB,CAChD,KAAK,cAAc,KAAK,GAAI,GAAa,CAAW,CAAC,CACzD,CAAC,EAED,EAAS,OAAO,KAAK,QAAQ,AAAC,GAAe,CACzC,KAAK,QAAQ,KAAK,CACd,KAAM,EAAM,KACZ,QAAS,CACL,GAAI,GAAG,EAAM,kBACb,IAAK,GAAG,EAAM,mBACd,OAAQ,GAAG,EAAM,sBACjB,MAAO,GAAG,EAAM,oBACpB,CACJ,CAAC,CACL,CAAC,EAED,GAAM,GAAkB,CACpB,GAAI,KAAK,GACT,MAAO,KAAK,MACZ,SAAU,KAAK,MAAM,GACrB,UAAW,KAAK,MAAM,IACtB,aAAc,KAAK,MAAM,OACzB,YAAa,KAAK,MAAM,MACxB,aAAc,EAAS,YAC3B,EACA,SAAS,OAAO,KAAK,QAAQ,AAAC,GAAe,CACzC,EAAM,MAAQ,EACd,KAAK,OAAO,KAAK,GAAI,GAAY,EAAO,EAAI,CAAC,CACjD,CAAC,EAEM,IACX,MAWM,aAAqC,CACvC,YAAM,MAAK,MAAM,EAEV,KAAK,MAChB,CAKA,QAAS,CACL,MAAO,CACH,GAAI,KAAK,GACT,MAAO,KAAK,MACZ,IAAK,KAAK,IACV,WAAY,KAAK,WACjB,SAAU,KAAK,SACf,OAAQ,KAAK,OACb,MAAO,KAAK,MACZ,KAAM,KAAK,KACX,IAAK,KAAK,IACV,YAAa,KAAK,YAClB,cAAe,KAAK,cACpB,aAAc,KAAK,aACnB,YAAa,KAAK,YAClB,UAAW,KAAK,UAChB,OAAQ,KAAK,OACb,aAAc,KAAK,aACnB,OAAQ,KAAK,OAAO,IAAI,AAAC,GAAU,EAAM,OAAO,CAAC,CACrD,CACJ,CACJ,EAvRO,mBA2RA,WAAqB,CA0GxB,YAAY,EAAW,EAAkB,CACrC,KAAK,GAAK,EAAK,GACf,KAAK,MAAQ,EAAK,MAClB,KAAK,OAAS,EAAK,OACnB,KAAK,IAAM,EAAK,KAChB,KAAK,aAAe,GAAI,MAAK,EAAK,aAAa,EAC/C,KAAK,KAAO,WACZ,KAAK,YAAc,EAAK,UACxB,KAAK,OAAS,CAAC,EAEf,KAAK,QAAU,CACX,GAAI,EAAK,WACT,IAAK,EAAK,YACV,OAAQ,EAAK,eACb,MAAO,EAAK,aAChB,EAEA,AAAI,EAAK,KACL,KAAK,QAAU,CACX,GAAI,EAAK,KAAK,GACd,KAAM,EAAK,KAAK,IACpB,EAEA,KAAK,QAAU,CACX,GAAI,EAAK,QAAQ,GACjB,KAAM,EAAK,QAAQ,IACvB,EAGJ,KAAK,QAAU,EAEV,GACD,MAAK,YAAc,EAAK,YACxB,KAAK,cAAgB,EAAK,SAC1B,KAAK,QAAU,EAAK,eACpB,KAAK,cAAgB,EAAK,cAC1B,KAAK,KAAO,EAAK,KAEb,KAAK,QACL,MAAK,OAAS,EAAK,OAAO,KAAK,IAAI,AAAC,GACzB,GAAI,GAAY,EAAO,EAAI,CACrC,GAGb,MASM,QAAiC,CACnC,GAAI,CAAC,KAAK,SAAY,MAAK,OAAO,SAAW,KAAK,aAAe,CAAC,KAAK,QACnE,MAAO,MAGX,GAAI,KAAK,QAAS,CACd,GAAM,GAAW,KAAM,GAAQ,mCAAmC,KAAK,KAAK,EAAE,MAAM,AAAC,GAAe,CAAG,EAEvG,GAAI,YAAoB,OAAO,KAAM,GACrC,GAAM,GAAW,KAAK,MAAM,CAAQ,EAEpC,KAAK,QAAU,GAEf,KAAK,YAAc,EAAS,YAC5B,KAAK,cAAgB,EAAS,SAC9B,KAAK,QAAU,EAAS,eACxB,KAAK,cAAgB,EAAS,cAC9B,KAAK,KAAO,EAAS,KAEjB,KAAK,QACL,MAAK,OAAS,EAAS,OAAO,KAAK,IAAI,AAAC,GAC7B,GAAI,GAAY,EAAO,EAAI,CACrC,EAET,CAEA,GAAM,GAAqB,KAAK,OAAO,OACvC,GAAI,KAAK,QAAU,IAAuB,KAAK,YAAa,CACxD,GAAI,GAAU,KAAK,YAAc,EAEjC,AAAI,EAAU,KAAM,GAAU,KAE9B,GAAM,GAAqC,CAAC,EAC5C,OAAS,GAAI,EAAG,GAAK,KAAK,KAAK,EAAU,GAAG,EAAG,IAC3C,EAAS,KACL,GAAI,SAAQ,MAAO,EAAS,IAAW,CACnC,GAAM,GAAW,KAAM,GACnB,mCAAmC,KAAK,6BAA6B,EAAI,KAC7E,EAAE,MAAM,AAAC,GAAQ,EAAO,CAAG,CAAC,EAE5B,GAAI,MAAO,IAAa,SAAU,OAElC,GAAM,GAAS,AADE,KAAK,MAAM,CAAQ,EACZ,KAAK,IAAI,AAAC,GACvB,GAAI,GAAY,EAAO,EAAI,CACrC,EAED,EAAQ,CAAM,CAClB,CAAC,CACL,EAGJ,GAAM,GAAU,KAAM,SAAQ,WAAW,CAAQ,EAC3C,EAA2B,CAAC,EAElC,OAAW,KAAU,GACjB,GAAI,EAAO,SAAW,YAClB,EAAU,KAAK,GAAG,EAAO,KAAK,MAE9B,MAAM,GAAO,OAIrB,KAAK,OAAO,KAAK,GAAG,CAAS,CACjC,CAEA,MAAO,KACX,MAWM,aAAqC,CACvC,YAAM,MAAK,MAAM,EAEV,KAAK,MAChB,CAKA,QAAS,CACL,MAAO,CACH,GAAI,KAAK,GACT,MAAO,KAAK,MACZ,OAAQ,KAAK,OACb,IAAK,KAAK,IACV,QAAS,KAAK,QACd,aAAc,KAAK,aACnB,KAAM,KAAK,KACX,QAAS,KAAK,QACd,YAAa,KAAK,YAClB,YAAa,KAAK,YAClB,cAAe,KAAK,cACpB,QAAS,KAAK,QACd,cAAe,KAAK,cACpB,KAAM,KAAK,KACX,OAAQ,KAAK,OAAO,IAAI,AAAC,GAAU,EAAM,OAAO,CAAC,CACrD,CACJ,CACJ,EAxQO,sBA0QP,YAAuB,CAOnB,YAAY,EAAW,CACnB,KAAK,GAAK,EAAK,GACf,KAAK,MAAQ,EAAK,MAClB,KAAK,IAAM,gCAAgC,EAAK,MAChD,KAAK,MAAQ,CACT,GAAI,EAAK,SACT,IAAK,EAAK,UACV,OAAQ,EAAK,aACb,MAAO,EAAK,WAChB,EAEI,EAAK,cAAc,MAAK,YAAc,GAAI,MAAK,EAAK,YAAY,EACxE,CACJ,EApBA,yBAwBA,WAAmB,CAuBf,YAAY,EAAW,CACnB,KAAK,GAAK,EAAK,GACf,KAAK,KAAO,EAAK,KAEjB,KAAK,IAAM,EAAK,KAAO,EAAK,KAAO,iCAAiC,EAAK,MAErE,EAAK,YACL,MAAK,QAAU,CACX,GAAI,EAAK,WACT,IAAK,EAAK,YACV,OAAQ,EAAK,eACb,MAAO,EAAK,aAChB,GAEA,EAAK,MAAM,MAAK,KAAO,EAAK,KACpC,CACJ,EAvCA,oBD3vBA,kBAAgC,EAAgC,CAC5D,GAAI,GACJ,GAAI,CAEA,EAAS,GAAI,IAAI,CAAG,CACxB,MAAE,CACE,MAAO,CAAE,KAAM,QAAS,CAC5B,CAEA,GAAI,EAAO,WAAa,UAAY,EAAO,WAAa,QACpD,MAAO,CAAE,KAAM,QAAS,EAG5B,GAAI,GAAW,EAAO,SACtB,AAAI,EAAS,SAAS,GAAG,GACrB,GAAW,EAAS,MAAM,EAAG,EAAE,GAEnC,GAAM,GAAO,EAAS,MAAM,GAAG,EAC/B,OAAQ,EAAO,cACN,iBACA,iBAAkB,CACnB,GAAI,EAAK,SAAW,GAEhB,GAAI,CAAC,AADQ,EAAK,OAAO,EAAG,CAAC,EAAE,GACrB,MAAM,YAAY,EACxB,MAAO,CAAE,KAAM,EAAM,UAElB,EAAK,SAAW,EACvB,MAAO,CAAE,KAAM,EAAM,EAGzB,MAAK,GAAK,KAAO,SAAW,EAAK,KAAO,SAAW,EAAK,KAAO,aAAe,EAAK,GAAG,MAAM,OAAO,EACxF,CACH,KAAM,EAAK,GACX,GAAI,EAAK,EACb,EAEO,CAAE,KAAM,EAAM,CAE7B,KACK,iBACD,MACI,GAAK,SAAW,GACf,GAAK,KAAO,SAAW,EAAK,KAAO,SAAW,EAAK,KAAO,aAC3D,EAAK,GAAG,MAAM,OAAO,EAEd,CACH,KAAM,EAAK,GACX,GAAI,EAAK,EACb,EAEO,CAAE,KAAM,EAAM,MAGxB,mBACD,GAAI,EAAK,SAAW,GAAK,EAAK,GAAG,MAAM,gBAAgB,EAAG,CACtD,GAAM,GAAW,KAAM,GAAyB,CAAG,EAAE,MAAM,AAAC,GAAQ,CAAG,EAEvE,MAAI,aAAoB,OACb,CAAE,KAAM,GAAO,MAAO,EAAS,OAAQ,EAG3C,KAAM,IAAiB,CAAQ,CAC1C,KACI,OAAO,CAAE,KAAM,EAAM,UAIzB,MAAO,CAAE,KAAM,QAAS,EAEpC,CArEe,yBAkFf,kBAA6B,EAA8B,CACvD,GAAM,GAAW,KAAM,IAAiB,EAAI,KAAK,CAAC,EAElD,GAAI,EAAS,MACT,KAAM,IAAI,OAAM;AAAA,EAAuD,EAAS,OAAO,EACpF,GAAI,CAAC,EAAS,MAAQ,EAAS,OAAS,SAC3C,KAAM,IAAI,OAAM,mDAAmD,EAEvE,GAAM,GAAW,KAAM,GAAQ,0BAA0B,EAAS,QAAQ,EAAS,IAAI,EAAE,MAAM,AAAC,GAAe,CAAG,EAElH,GAAI,YAAoB,OAAO,KAAM,GAErC,GAAM,GAAW,KAAK,MAAM,CAAQ,EAEpC,GAAI,EAAS,MACT,KAAM,IAAI,OAAM,qBAAqB,EAAS,MAAM,SAAS,EAAS,MAAM,SAAS,EAGzF,OAAQ,EAAS,UACR,QACD,MAAO,IAAI,GAAY,EAAU,EAAK,MACrC,WACD,MAAO,IAAI,GAAe,EAAU,EAAK,MACxC,QACD,MAAO,IAAI,GAAY,EAAU,EAAK,EAElD,CA1BsB,eAkCtB,kBAAkC,EAAyE,CAEvG,MAAO,AADU,MAAM,IAAiB,EAAI,KAAK,CAAC,GAClC,IACpB,CAHsB,oBAetB,kBAAgC,EAAe,EAAiD,CAC5F,GAAI,GAAS,EAAM,KAAK,EAElB,EAAO,EAAQ,MAAQ,QACvB,EAAQ,EAAQ,OAAS,GACzB,EAAQ,EAAQ,OAAS,GAE/B,GAAI,EAAO,SAAW,EAAG,KAAM,IAAI,OAAM,gCAAgC,EACzE,GAAI,EAAQ,IAAK,KAAM,IAAI,OAAM,4CAA4C,EAC7E,GAAI,EAAQ,EAAG,KAAM,IAAI,OAAM,0CAA0C,EACzE,GAAI,IAAS,SAAW,IAAS,SAAW,GAAQ,WAChD,KAAM,IAAI,OAAM,IAAI,sCAAyC,EAEjE,EAAS,mBAAmB,CAAM,EAClC,GAAM,GAAW,KAAM,GACnB,iCAAiC,QAAW,WAAgB,IAAQ,EAAQ,GAAK,aACrF,EAAE,MAAM,AAAC,GAAe,CAAG,EAE3B,GAAI,YAAoB,OAAO,KAAM,GAErC,GAAM,GAAW,KAAK,MAAM,CAAQ,EAEpC,GAAI,EAAS,MACT,KAAM,IAAI,OAAM,qBAAqB,EAAS,MAAM,SAAS,EAAS,MAAM,SAAS,EAGzF,GAAI,GAAoB,CAAC,EACzB,OAAQ,OACC,QACD,EAAU,EAAS,KAAK,IAAI,AAAC,GAAe,GAAI,GAAY,EAAO,EAAI,CAAC,EACxE,UACC,WACD,EAAU,EAAS,KAAK,IAAI,AAAC,GAAkB,GAAI,GAAe,EAAU,EAAI,CAAC,EACjF,UACC,QACD,EAAU,EAAS,KAAK,IAAI,AAAC,GAAe,GAAI,GAAY,EAAO,EAAI,CAAC,EACxE,MAGR,MAAO,EACX,CAxCsB,kBAyDtB,kBAA+C,EAA8D,CACzG,GAAM,GAAQ,EAAQ,OAAS,GAE/B,GAAI,EAAQ,IAAK,KAAM,IAAI,OAAM,4CAA4C,EAC7E,GAAI,EAAQ,EAAG,KAAM,IAAI,OAAM,0CAA0C,EAEzE,GAAM,GAAqB,CAAC,EAiB5B,GAhBI,EAAQ,QAAQ,EAAS,KAAK,WAAW,mBAAmB,EAAQ,OAAO,KAAK,CAAC,IAAI,EAErF,EAAQ,OAAO,EAAS,KAAK,UAAU,mBAAmB,EAAQ,MAAM,KAAK,CAAC,IAAI,EAElF,EAAQ,OAAO,EAAS,KAAK,UAAU,mBAAmB,EAAQ,MAAM,KAAK,CAAC,IAAI,EAElF,EAAQ,OAAO,EAAS,KAAK,UAAU,mBAAmB,EAAQ,MAAM,KAAK,CAAC,IAAI,EAEjF,MAAM,OAAO,EAAQ,gBAAgB,CAAC,GAAG,EAAS,KAAK,WAAW,EAAQ,kBAAkB,EAE5F,MAAM,OAAO,EAAQ,gBAAgB,CAAC,GAAG,EAAS,KAAK,WAAW,EAAQ,kBAAkB,EAE5F,MAAM,OAAO,EAAQ,MAAM,CAAC,GAAG,EAAS,KAAK,WAAW,EAAQ,QAAQ,EAExE,MAAM,OAAO,EAAQ,MAAM,CAAC,GAAG,EAAS,KAAK,WAAW,EAAQ,QAAQ,EAEzE,EAAS,SAAW,EAAG,KAAM,IAAI,OAAM,4CAA4C,EAEvF,GAAM,GAAW,KAAM,GAAQ,0CAA0C,EAAS,KAAK,GAAG,WAAW,GAAO,EAAE,MAC1G,AAAC,GAAe,CACpB,EAEA,GAAI,YAAoB,OAAO,KAAM,GAErC,GAAM,GAAW,KAAK,MAAM,CAAQ,EAEpC,GAAI,EAAS,MACT,KAAM,IAAI,OAAM,qBAAqB,EAAS,MAAM,SAAS,EAAS,MAAM,SAAS,EAKzF,MAFgB,GAAS,KAAK,IAAI,AAAC,GAAe,GAAI,GAAY,EAAO,EAAI,CAAC,CAGlF,CAxCsB,iCEpMtB,kBAA+B,EAAuB,CAClD,AAAI,EAAQ,SAAS,KAAM,IAAgB,EAAQ,OAAO,EACtD,EAAQ,YAAY,GAAmB,EAAQ,UAAU,EACzD,EAAQ,SAAS,GAAe,EAAQ,OAAO,EAC/C,EAAQ,WAAW,GAAa,EAAQ,SAAS,CACzD,CALsB,iBCoBtB,iDACA,0EAiCA,kBAAsB,EAAa,EAAyB,CAAC,EAA8C,CACvG,GAAM,GAAO,EAAI,KAAK,EACtB,GAAI,EAAK,SAAW,EAAG,KAAM,IAAI,OAAM,qDAAqD,EAC5F,GAAI,EAAQ,SAAU,MAAO,MAAM,IAAU,EAAM,CAAO,EAC1D,GAAI,EAAK,QAAQ,SAAS,IAAM,GAC5B,KAAM,IAAI,OACN,wHACJ,EAEJ,GAAI,EAAK,QAAQ,QAAQ,IAAM,GAC3B,KAAM,IAAI,OACN,uHACJ,EAEJ,MAAI,GAAK,QAAQ,YAAY,IAAM,GAAW,KAAM,IAAU,EAAM,EAAQ,OAAO,EACvE,KAAM,IAAU,EAAM,CAAO,CAC7C,CAhBe,eA+Ff,kBACI,EACA,EAAyB,CAAC,EAC8B,CACxD,AAAK,EAAQ,QAAQ,GAAQ,OAAS,CAAE,QAAS,OAAQ,GACzD,GAAM,GAAS,mBAAmB,EAAM,KAAK,CAAC,EAC9C,GAAI,EAAQ,OAAO,QACf,MAAO,MAAM,IAAU,EAAQ,CAC3B,MAAO,EAAQ,MACf,KAAM,EAAQ,OAAO,QACrB,SAAU,EAAQ,SAClB,qBAAsB,EAAQ,oBAClC,CAAC,EACA,GAAI,EAAQ,OAAO,QAAS,MAAO,MAAM,IAAU,EAAQ,EAAQ,OAAO,QAAS,EAAQ,KAAK,EAChG,GAAI,EAAQ,OAAO,WAAY,MAAO,MAAM,IAAU,EAAQ,EAAQ,OAAO,WAAY,EAAQ,KAAK,EACtG,GAAI,EAAQ,OAAO,OACpB,MAAO,MAAM,IAAU,EAAQ,CAAE,MAAO,EAAQ,MAAO,KAAM,EAAQ,OAAO,OAAQ,MAAO,EAAQ,KAAM,CAAC,EACzG,KAAM,IAAI,OAAM,4EAA4E,CACrG,CAlBe,eAkDf,kBACI,EACA,EAAyB,CAAC,EACe,CACzC,MAAI,aAAgB,GAAwB,KAAM,IAAe,EAAM,EAAQ,OAAO,EAC1E,KAAM,IAAe,EAAM,CAAO,CAClD,CANe,yBAoBf,kBACI,EAcF,CACE,GAAI,GACE,EAAO,EAAI,KAAK,EACtB,MAAK,GAAK,WAAW,OAAO,EACxB,EAAK,QAAQ,SAAS,IAAM,GAC5B,GAAQ,GAAY,CAAI,EACjB,IAAU,GAAU,MAAQ,EAAqD,IACjF,EAAK,QAAQ,YAAY,IAAM,GACtC,GAAQ,KAAM,IAAY,CAAI,EACvB,IAAU,GAAU,MAAQ,EAAwC,IACpE,EAAK,QAAQ,QAAQ,IAAM,GAClC,GAAQ,KAAM,IAAY,CAAI,EACvB,IAAU,GAAU,MAAQ,EAAqD,IAExF,GAAQ,EAAY,CAAI,EACjB,IAAU,GAAU,MAAQ,EAAwC,IAZzC,QAc1C,CAhCe,iBA8Cf,aAA+B,CAC3B,GAAM,GAAM,GAAgB,CACxB,MAAO,QAAQ,MACf,OAAQ,QAAQ,MACpB,CAAC,EACD,EAAI,SAAS,oDAAqD,AAAC,GAAQ,CACvE,GAAI,GACJ,GAAI,EAAI,YAAY,IAAM,MAAO,EAAO,WAC/B,EAAI,YAAY,IAAM,KAAM,EAAO,OACvC,CACD,QAAQ,IAAI,yCAAyC,EACrD,EAAI,MAAM,EACV,MACJ,CACA,EAAI,SAAS,qFAAsF,AAAC,GAAQ,CACxG,GAAI,EAAI,YAAY,EAAE,WAAW,IAAI,EAAG,CACpC,GAAI,GAAmB,EAAuB,EAAsB,EACpE,EAAI,SAAS,sCAAuC,AAAC,GAAO,CACxD,EAAY,EACZ,EAAI,SAAS,kCAAmC,AAAC,GAAW,CACxD,EAAgB,EAChB,EAAI,SAAS,iCAAkC,AAAC,GAAQ,CACpD,EAAe,EACf,QAAQ,IACJ;AAAA;AAAA;AAAA,CACJ,EACA,EAAI,SAAS,oDAAqD,AAAC,GAAQ,CACvE,AAAI,EAAI,SAAW,EAAG,EAAS,EAE3B,SAAQ,IACJ,6EACJ,EACA,EAAS,MAEb,QAAQ,IACJ;AAAA;AAAA,CACJ,EACA,QAAQ,IACJ,oDAAoD,qCAA6C,UAC7F,CACJ;AAAA,CACJ,EACA,EAAI,SAAS,yCAA0C,KAAO,IAAQ,CAClE,AAAK,GAAW,OAAO,GAAG,GAAU,OAAO,EAC3C,GAAM,GAAc,CAChB,YACA,gBACA,eACA,mBAAoB,EAAI,MAAM,OAAO,EAAE,GACvC,QACJ,EAEA,GAAI,AADU,KAAM,IAAiB,EAAa,CAAI,IACxC,GAAO,KAAM,IAAI,OAAM,6BAA6B,EAClE,EAAI,MAAM,CACd,CAAC,CACL,CAAC,CACL,CAAC,CACL,CAAC,CACL,CAAC,CACL,SAAW,EAAI,YAAY,EAAE,WAAW,IAAI,EAAG,CAC3C,GAAI,CAAC,EAAM,CACP,QAAQ,IAAI,oEAAoE,EAChF,EAAI,MAAM,EACV,MACJ,CACA,EAAI,SAAS,eAAgB,KAAO,IAAO,CACvC,GAAI,GAAY,EAChB,GAAI,CAAC,EAAW,CACZ,QAAQ,IAAI,8CAA8C,EAC1D,EAAI,MAAM,EACV,MACJ,CACA,AAAK,GAAW,OAAO,GAAG,GAAU,OAAO,EAC3C,QAAQ,IAAI,uCAAuC,EACnD,AAAI,KAAM,IAAS,CAAS,EACxB,SAAQ,IAAI,4CAA4C,EACxD,GAAc,wBAAyB,KAAK,UAAU,CAAE,WAAU,EAAG,OAAW,CAAC,CAAC,GAC/E,QAAQ,IAAI,2EAA2E,EAC9F,EAAI,MAAM,CACd,CAAC,CACL,SAAW,EAAI,YAAY,EAAE,WAAW,IAAI,EAAG,CAC3C,GAAI,CAAC,EAAM,CACP,QAAQ,IAAI,+DAA+D,EAC3E,EAAI,MAAM,EACV,MACJ,CACA,EAAI,SAAS,aAAc,AAAC,GAAiB,CACzC,GAAI,CAAC,GAAQ,EAAK,SAAW,EAAG,CAC5B,QAAQ,IAAI,2CAA2C,EACvD,EAAI,MAAM,EACV,MACJ,CACA,AAAK,GAAW,OAAO,GAAG,GAAU,OAAO,EAC3C,QAAQ,IAAI,sCAAsC,EAClD,GAAI,GAAiB,CAAC,EACtB,EAAK,MAAM,GAAG,EAAE,QAAQ,AAAC,GAAM,CAC3B,GAAM,GAAM,EAAE,MAAM,GAAG,EACvB,GAAI,EAAI,QAAU,EAAG,OACrB,GAAM,GAAM,EAAI,MAAM,GAAG,KAAK,EACxB,EAAQ,EAAI,KAAK,GAAG,EAAE,KAAK,EACjC,OAAO,OAAO,EAAQ,EAAG,GAAM,CAAM,CAAC,CAC1C,CAAC,EACD,GAAc,qBAAsB,KAAK,UAAU,CAAE,QAAO,EAAG,OAAW,CAAC,CAAC,EAC5E,EAAI,MAAM,CACd,CAAC,CACL,KACI,SAAQ,IAAI,yCAAyC,EACrD,EAAI,MAAM,CAElB,CAAC,CACL,CAAC,CACL,CA/GS,sBAuHT,YAAyB,EAAsB,EAA4C,CAIvF,GAAM,GAAY,EAAO,UAAU,MAAsB,EACzD,OAAW,KAAW,GAClB,AAAK,EAAgB,0BACjB,GAAQ,EACR,EAAO,eAAe,OAAwB,CAAuB,GAI7E,GAAM,GAAgB,MAAM,EAAS,MAAM,EAArB,iBAChB,EAAiB,MAAM,EAAS,OAAO,EAAtB,kBACjB,EAAe,MAAM,CACvB,EAAO,eAAe,SAA0B,CAAa,EAC7D,EAAO,eAAe,aAA8B,CAAa,EACjE,EAAO,eAAe,UAA2B,CAAc,CACnE,EAJqB,gBAKrB,EAAc,yBAA2B,GACzC,EAAe,yBAA2B,GAC1C,EAAa,yBAA2B,GACxC,EAAO,GAAG,SAA0B,CAAa,EACjD,EAAO,GAAG,aAA8B,CAAa,EACrD,EAAO,GAAG,UAA2B,CAAc,EACnD,EAAO,KAAK,OAAwB,CAAY,CACpD,CA1BS,wBAwET,GAAO,IAAQ,CACX,cACA,iBACA,cACA,qBACA,mBACA,kBACA,eACA,kBACA,eACA,iBACA,kBACA,eACA,mBACA,iBACA,gBACA,UACA,4BACA,eACA,aACA,mBACA,cACA,iBACA,gBACA,UACA,YACA,eACA,cACA,WACA,eACA,UACA,oBACA,YACA,oBACA,cACA,aACJ","names":[]} |