{"version":3,"file":"index.mjs","names":["mkdir","fsMkdir","nodeError","writeFile","fsWriteFile","readFile","fsReadFile","fsRm","readFile","mkdir","writeFile"],"sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":["import {\n\tmkdir as fsMkdir,\n\treadFile as fsReadFile,\n\trm as fsRm,\n\twriteFile as fsWriteFile,\n} from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport fetchCookie from \"fetch-cookie\";\nimport ky from \"ky\";\nimport type { NodeError } from \"./types.js\";\n\nexport function isNode(): boolean {\n\treturn (\n\t\ttypeof process !== \"undefined\" &&\n\t\tprocess.versions !== undefined &&\n\t\ttypeof process.versions.node === \"string\"\n\t);\n}\n\nexport function isBun(): boolean {\n\treturn typeof globalThis.Bun !== \"undefined\";\n}\n\nexport async function mkdir(path: string): Promise<void> {\n\ttry {\n\t\tawait fsMkdir(path, { recursive: true });\n\t} catch (error: unknown) {\n\t\tconst nodeError = error as NodeError;\n\t\tif (nodeError.code !== \"EEXIST\") {\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\nexport async function writeFile(path: string, data: string): Promise<void> {\n\tawait fsWriteFile(path, data, \"utf-8\");\n}\n\nexport async function readFile(path: string): Promise<string> {\n\treturn await fsReadFile(path, \"utf-8\");\n}\n\nexport async function deleteFile(path: string): Promise<void> {\n\ttry {\n\t\tawait fsRm(path, { force: true });\n\t} catch {\n\t\t// ignore\n\t}\n}\n\nexport function getCachePath(appName: string): string {\n\treturn join(homedir(), \".cache\", appName);\n}\n\nconst cookieJar = fetchCookie(globalThis.fetch);\n\nexport const kyClient = ky.create({\n\tfetch: cookieJar,\n\theaders: {\n\t\t\"User-Agent\":\n\t\t\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/115.0.0.0 Safari/537.36\",\n\t\tAccept: \"application/json, text/javascript, */*; q=0.01\",\n\t\tReferer: \"https://www.musixmatch.com/\",\n\t\tOrigin: \"https://www.musixmatch.com\",\n\t},\n\tthrowHttpErrors: false,\n});\n","import { dirname, join } from \"node:path\";\nimport type {\n\tLyricsBody,\n\tLyricsResponse,\n\tMusixmatchResponse,\n\tRichSyncBody,\n\tRichSyncedLyricLine,\n\tSubtitleBody,\n\tSyncedLyric,\n\tTokenBody,\n\tTrack,\n\tTrackBody,\n\tTrackSearchBody,\n\tWordSyncedLyric,\n} from \"./types.js\";\nimport {\n\tdeleteFile,\n\tgetCachePath,\n\tkyClient,\n\tmkdir,\n\treadFile,\n\twriteFile,\n} from \"./utils.js\";\n\nexport class LyricsClient {\n\tprivate ROOT_URL = \"https://apic-desktop.musixmatch.com/ws/1.1/\";\n\tprivate token: string | null = null;\n\n\tprivate parseSubtitle(subtitleBody: string): SyncedLyric[] {\n\t\tconst lines = subtitleBody.split(\"\\n\");\n\t\tconst timestampMap = new Map<string, string[]>();\n\n\t\tfor (const line of lines) {\n\t\t\tconst trimmedLine = line.trim();\n\t\t\tif (!trimmedLine) continue;\n\n\t\t\tconst match = trimmedLine.match(/\\[(\\d{2}):(\\d{2})\\.(\\d{2})\\]\\s*(.+)/);\n\t\t\tif (match?.[1] && match[2] && match[3] && match[4]) {\n\t\t\t\tconst [, minutes, seconds, hundredths, text] = match;\n\t\t\t\tconst timestampKey = `${minutes}:${seconds}.${hundredths}`;\n\n\t\t\t\tif (!timestampMap.has(timestampKey)) {\n\t\t\t\t\ttimestampMap.set(timestampKey, []);\n\t\t\t\t}\n\t\t\t\tconst existingTexts = timestampMap.get(timestampKey);\n\t\t\t\tif (existingTexts) {\n\t\t\t\t\texistingTexts.push(text.trim());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst syncedLyrics: SyncedLyric[] = [];\n\n\t\tfor (const [timestampKey, textParts] of timestampMap) {\n\t\t\tconst match = timestampKey.match(/(\\d{2}):(\\d{2})\\.(\\d{2})/);\n\t\t\tif (!match?.[1] || !match[2] || !match[3]) continue;\n\n\t\t\tconst [, minutes, seconds, hundredths] = match;\n\t\t\tconst minutesNum = parseInt(minutes, 10);\n\t\t\tconst secondsNum = parseInt(seconds, 10);\n\t\t\tconst hundredthsNum = parseInt(hundredths, 10);\n\t\t\tconst msNum = hundredthsNum * 10;\n\t\t\tconst totalSeconds = minutesNum * 60 + secondsNum + hundredthsNum / 100;\n\n\t\t\tconst combinedText = textParts\n\t\t\t\t.filter((text) => text.length > 0)\n\t\t\t\t.join(\" \")\n\t\t\t\t.trim();\n\n\t\t\tif (combinedText) {\n\t\t\t\tsyncedLyrics.push({\n\t\t\t\t\ttext: combinedText,\n\t\t\t\t\ttime: {\n\t\t\t\t\t\ttotal: totalSeconds,\n\t\t\t\t\t\tminutes: minutesNum,\n\t\t\t\t\t\tseconds: secondsNum,\n\t\t\t\t\t\tms: msNum,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tsyncedLyrics.sort((a, b) => a.time.total - b.time.total);\n\n\t\treturn syncedLyrics;\n\t}\n\n\tprivate parseRichSync(richsyncBody: string): SyncedLyric[] {\n\t\ttry {\n\t\t\tconst richsyncData = JSON.parse(richsyncBody);\n\t\t\tconst timestampMap = new Map<number, string[]>();\n\n\t\t\tif (Array.isArray(richsyncData)) {\n\t\t\t\tfor (const item of richsyncData) {\n\t\t\t\t\tif (item.ts && item.l && Array.isArray(item.l)) {\n\t\t\t\t\t\tconst startTime = parseFloat(item.ts);\n\t\t\t\t\t\tfor (const lyricItem of item.l) {\n\t\t\t\t\t\t\tif (lyricItem.c) {\n\t\t\t\t\t\t\t\tif (!timestampMap.has(startTime)) {\n\t\t\t\t\t\t\t\t\ttimestampMap.set(startTime, []);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst existingTexts = timestampMap.get(startTime);\n\t\t\t\t\t\t\t\tif (existingTexts) {\n\t\t\t\t\t\t\t\t\texistingTexts.push(lyricItem.c);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst syncedLyrics: SyncedLyric[] = [];\n\n\t\t\tfor (const [startTime, textParts] of timestampMap) {\n\t\t\t\tconst minutes = Math.floor(startTime / 60);\n\t\t\t\tconst seconds = Math.floor(startTime % 60);\n\t\t\t\tconst ms = Math.floor((startTime % 1) * 1000);\n\n\t\t\t\tconst combinedText = textParts\n\t\t\t\t\t.filter((text) => text.trim().length > 0)\n\t\t\t\t\t.join(\" \")\n\t\t\t\t\t.trim();\n\n\t\t\t\tif (combinedText) {\n\t\t\t\t\tsyncedLyrics.push({\n\t\t\t\t\t\ttext: combinedText,\n\t\t\t\t\t\ttime: {\n\t\t\t\t\t\t\ttotal: startTime,\n\t\t\t\t\t\t\tminutes,\n\t\t\t\t\t\t\tseconds,\n\t\t\t\t\t\t\tms,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsyncedLyrics.sort((a, b) => a.time.total - b.time.total);\n\n\t\t\treturn syncedLyrics;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tprivate parseRichSyncToWords(richsyncBody: string): RichSyncedLyricLine[] {\n\t\ttry {\n\t\t\tconst richsyncData = JSON.parse(richsyncBody);\n\t\t\tconst lines: RichSyncedLyricLine[] = [];\n\n\t\t\tif (Array.isArray(richsyncData)) {\n\t\t\t\tfor (const item of richsyncData) {\n\t\t\t\t\tif (\n\t\t\t\t\t\titem.ts !== undefined &&\n\t\t\t\t\t\titem.te !== undefined &&\n\t\t\t\t\t\titem.l &&\n\t\t\t\t\t\tArray.isArray(item.l)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst startTimeTotal = parseFloat(item.ts);\n\t\t\t\t\t\tconst endTimeTotal = parseFloat(item.te);\n\n\t\t\t\t\t\tconst startTime = {\n\t\t\t\t\t\t\ttotal: startTimeTotal,\n\t\t\t\t\t\t\tminutes: Math.floor(startTimeTotal / 60),\n\t\t\t\t\t\t\tseconds: Math.floor(startTimeTotal % 60),\n\t\t\t\t\t\t\tms: Math.round(startTimeTotal * 1000) % 1000,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst endTime = {\n\t\t\t\t\t\t\ttotal: endTimeTotal,\n\t\t\t\t\t\t\tminutes: Math.floor(endTimeTotal / 60),\n\t\t\t\t\t\t\tseconds: Math.floor(endTimeTotal % 60),\n\t\t\t\t\t\t\tms: Math.round(endTimeTotal * 1000) % 1000,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst words: WordSyncedLyric[] = [];\n\t\t\t\t\t\tfor (const wordItem of item.l) {\n\t\t\t\t\t\t\tif (wordItem.c !== undefined && wordItem.o !== undefined) {\n\t\t\t\t\t\t\t\tconst offset = parseFloat(wordItem.o);\n\t\t\t\t\t\t\t\tconst wordTimeTotal = startTimeTotal + offset;\n\t\t\t\t\t\t\t\twords.push({\n\t\t\t\t\t\t\t\t\ttext: wordItem.c,\n\t\t\t\t\t\t\t\t\ttime: {\n\t\t\t\t\t\t\t\t\t\ttotal: wordTimeTotal,\n\t\t\t\t\t\t\t\t\t\tminutes: Math.floor(wordTimeTotal / 60),\n\t\t\t\t\t\t\t\t\t\tseconds: Math.floor(wordTimeTotal % 60),\n\t\t\t\t\t\t\t\t\t\tms: Math.round(wordTimeTotal * 1000) % 1000,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlines.push({\n\t\t\t\t\t\t\tstartTime,\n\t\t\t\t\t\t\tendTime,\n\t\t\t\t\t\t\twords,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn lines;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tprivate formatToLrc(syncedLyrics: SyncedLyric[]): string {\n\t\treturn syncedLyrics\n\t\t\t.map((line) => {\n\t\t\t\tconst min = line.time.minutes.toString().padStart(2, \"0\");\n\t\t\t\tconst sec = line.time.seconds.toString().padStart(2, \"0\");\n\t\t\t\tconst ms = Math.floor(line.time.ms / 10)\n\t\t\t\t\t.toString()\n\t\t\t\t\t.padStart(2, \"0\");\n\t\t\t\treturn `[${min}:${sec}.${ms}] ${line.text}`;\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\t}\n\n\tprivate async rawGet(\n\t\taction: string,\n\t\tquery: Array<[string, string]> = [],\n\t): Promise<Response> {\n\t\tif (action !== \"token.get\" && this.token === null) {\n\t\t\tawait this.getToken();\n\t\t}\n\n\t\tquery.push([\"app_id\", \"web-desktop-app-v1.0\"]);\n\t\tif (this.token !== null) {\n\t\t\tquery.push([\"usertoken\", this.token]);\n\t\t}\n\t\tconst timestamp = Date.now().toString();\n\t\tquery.push([\"t\", timestamp]);\n\t\tconst url = this.ROOT_URL + action;\n\t\tconst params = new URLSearchParams(query);\n\n\t\ttry {\n\t\t\tconst response = await kyClient.get(`${url}?${params.toString()}`);\n\t\t\treturn response;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error making Musixmatch request:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate async apiGet(\n\t\taction: string,\n\t\tquery: Array<[string, string]> = [],\n\t): Promise<Response> {\n\t\tconst queryClone = query.map(([k, v]) => [k, v] as [string, string]);\n\t\tconst response = await this.rawGet(action, queryClone);\n\n\t\tif (action !== \"token.get\") {\n\t\t\tconst clone = response.clone();\n\t\t\ttry {\n\t\t\t\tconst data = (await clone.json()) as MusixmatchResponse<unknown>;\n\t\t\t\tif (data?.message?.header?.status_code === 401) {\n\t\t\t\t\tthis.token = null;\n\t\t\t\t\tconst tokenPath = join(\n\t\t\t\t\t\tgetCachePath(\"syncedlyrics\"),\n\t\t\t\t\t\t\"musixmatch_token.json\",\n\t\t\t\t\t);\n\t\t\t\t\tawait deleteFile(tokenPath);\n\t\t\t\t\tconst retryQueryClone = query.map(\n\t\t\t\t\t\t([k, v]) => [k, v] as [string, string],\n\t\t\t\t\t);\n\t\t\t\t\treturn await this.rawGet(action, retryQueryClone);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// ignore parse errors\n\t\t\t}\n\t\t}\n\n\t\treturn response;\n\t}\n\n\tprivate async getToken(): Promise<void> {\n\t\tconst tokenPath = join(\n\t\t\tgetCachePath(\"syncedlyrics\"),\n\t\t\t\"musixmatch_token.json\",\n\t\t);\n\t\tconst currentTime = Math.floor(Date.now() / 1000);\n\n\t\ttry {\n\t\t\tconst tokenData = await readFile(tokenPath);\n\t\t\tconst cachedTokenData = JSON.parse(tokenData);\n\t\t\tconst cachedToken = cachedTokenData.token;\n\t\t\tconst expirationTime = cachedTokenData.expiration_time;\n\n\t\t\tif (cachedToken && expirationTime && currentTime < expirationTime) {\n\t\t\t\tthis.token = cachedToken;\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch {\n\t\t\tconsole.warn(\"No valid cached token found, fetching a new one.\");\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await this.apiGet(\"token.get\", [\n\t\t\t\t[\"user_language\", \"en\"],\n\t\t\t]);\n\t\t\tconst data = (await response.json()) as MusixmatchResponse<TokenBody>;\n\n\t\t\tif (data.message.header.status_code === 401) {\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10000));\n\t\t\t\treturn this.getToken();\n\t\t\t}\n\n\t\t\tconst newToken = data.message.body.user_token;\n\t\t\tconst expirationTime = currentTime + 600;\n\n\t\t\tthis.token = newToken;\n\n\t\t\tconst tokenData = {\n\t\t\t\ttoken: newToken,\n\t\t\t\texpiration_time: expirationTime,\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tconst tokenDir = dirname(tokenPath);\n\t\t\t\tawait mkdir(tokenDir);\n\t\t\t\tawait writeFile(tokenPath, JSON.stringify(tokenData));\n\t\t\t} catch (writeError) {\n\t\t\t\tconsole.warn(\"Could not cache token:\", writeError);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error getting Musixmatch token:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Get synced lyrics with timestamps by ISRC code\n\t * @param isrc - International Standard Recording Code\n\t * @returns Promise<LyricsResponse>\n\t */\n\tasync getSynced(isrc: string): Promise<LyricsResponse> {\n\t\ttry {\n\t\t\tconst trackResponse = await this.apiGet(\"track.get\", [\n\t\t\t\t[\"track_isrc\", isrc],\n\t\t\t]);\n\t\t\tconst trackData =\n\t\t\t\t(await trackResponse.json()) as MusixmatchResponse<TrackBody>;\n\n\t\t\tif (trackData.message.header.status_code !== 200) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Track not found for ISRC: ${isrc}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst track = trackData.message.body.track;\n\t\t\tconst trackId = track.track_id;\n\n\t\t\t// try get richsync first (more detailed timing)\n\t\t\ttry {\n\t\t\t\tconst richsyncResponse = await this.apiGet(\"track.richsync.get\", [\n\t\t\t\t\t[\"track_id\", trackId.toString()],\n\t\t\t\t]);\n\t\t\t\tconst richsyncData =\n\t\t\t\t\t(await richsyncResponse.json()) as MusixmatchResponse<RichSyncBody>;\n\n\t\t\t\tif (richsyncData.message.header.status_code === 200) {\n\t\t\t\t\tconst richsync = richsyncData.message.body.richsync;\n\t\t\t\t\tconst syncedLyrics = this.parseRichSync(richsync.richsync_body);\n\n\t\t\t\t\tif (syncedLyrics.length > 0) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tsyncedLyrics,\n\t\t\t\t\t\t\trichSyncedLyrics: this.parseRichSyncToWords(\n\t\t\t\t\t\t\t\trichsync.richsync_body,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tlyrics: this.formatToLrc(syncedLyrics),\n\t\t\t\t\t\t\thasTimestamps: true,\n\t\t\t\t\t\t\tsongInfo: {\n\t\t\t\t\t\t\t\ttitle: track.track_name,\n\t\t\t\t\t\t\t\tartist: track.artist_name,\n\t\t\t\t\t\t\t\talbum: track.album_name,\n\t\t\t\t\t\t\t\tduration: track.track_length,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// fallback to subtitle if richsync fails\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst subtitleResponse = await this.apiGet(\"track.subtitle.get\", [\n\t\t\t\t\t[\"track_id\", trackId.toString()],\n\t\t\t\t]);\n\t\t\t\tconst subtitleData =\n\t\t\t\t\t(await subtitleResponse.json()) as MusixmatchResponse<SubtitleBody>;\n\n\t\t\t\tif (subtitleData.message.header.status_code === 200) {\n\t\t\t\t\tconst subtitle = subtitleData.message.body.subtitle;\n\t\t\t\t\tconst syncedLyrics = this.parseSubtitle(subtitle.subtitle_body);\n\n\t\t\t\t\tif (syncedLyrics.length > 0) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tsyncedLyrics,\n\t\t\t\t\t\t\tlyrics: this.formatToLrc(syncedLyrics),\n\t\t\t\t\t\t\thasTimestamps: true,\n\t\t\t\t\t\t\tsongInfo: {\n\t\t\t\t\t\t\t\ttitle: track.track_name,\n\t\t\t\t\t\t\t\tartist: track.artist_name,\n\t\t\t\t\t\t\t\talbum: track.album_name,\n\t\t\t\t\t\t\t\tduration: track.track_length,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// fallback to regular lyrics\n\t\t\t}\n\n\t\t\tconst lyricsResponse = await this.apiGet(\"track.lyrics.get\", [\n\t\t\t\t[\"track_id\", trackId.toString()],\n\t\t\t]);\n\t\t\tconst lyricsData =\n\t\t\t\t(await lyricsResponse.json()) as MusixmatchResponse<LyricsBody>;\n\n\t\t\tif (lyricsData.message.header.status_code !== 200) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"No lyrics found for this track\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst lyrics = lyricsData.message.body.lyrics;\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tlyrics: lyrics.lyrics_body,\n\t\t\t\thasTimestamps: false,\n\t\t\t\tsongInfo: {\n\t\t\t\t\ttitle: track.track_name,\n\t\t\t\t\tartist: track.artist_name,\n\t\t\t\t\talbum: track.album_name,\n\t\t\t\t\tduration: track.track_length,\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error fetching synced lyrics by ISRC:\", error);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror:\n\t\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\",\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Search for track and get synced lyrics with timestamps\n\t * @param query - Search query (artist and track name)\n\t * @returns Promise<LyricsResponse>\n\t */\n\tasync searchSynced(query: string): Promise<LyricsResponse> {\n\t\tif (/^[A-Z]{2}-?[A-Z0-9]{3}-?\\d{2}-?\\d{5}$/i.test(query.trim())) {\n\t\t\treturn this.getSynced(query.trim().replace(/-/g, \"\"));\n\t\t}\n\n\t\ttry {\n\t\t\t// Search for track\n\t\t\tconst searchResponse = await this.apiGet(\"track.search\", [\n\t\t\t\t[\"q\", query],\n\t\t\t\t[\"page_size\", \"1\"],\n\t\t\t]);\n\n\t\t\tconst searchData =\n\t\t\t\t(await searchResponse.json()) as MusixmatchResponse<TrackSearchBody>;\n\n\t\t\tif (\n\t\t\t\tsearchData.message.header.status_code !== 200 ||\n\t\t\t\t!searchData.message.body.track_list ||\n\t\t\t\tsearchData.message.body.track_list.length === 0\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `No tracks found for query: ${query}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst trackList = searchData.message.body.track_list;\n\t\t\tconst firstTrack = trackList[0];\n\t\t\tif (!firstTrack) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"No tracks found in search results\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst track = firstTrack.track;\n\t\t\tconst trackId = track.track_id;\n\n\t\t\ttry {\n\t\t\t\tconst richsyncResponse = await this.apiGet(\"track.richsync.get\", [\n\t\t\t\t\t[\"track_id\", trackId.toString()],\n\t\t\t\t]);\n\t\t\t\tconst richsyncData =\n\t\t\t\t\t(await richsyncResponse.json()) as MusixmatchResponse<RichSyncBody>;\n\n\t\t\t\tif (richsyncData.message.header.status_code === 200) {\n\t\t\t\t\tconst richsync = richsyncData.message.body.richsync;\n\t\t\t\t\tconst syncedLyrics = this.parseRichSync(richsync.richsync_body);\n\n\t\t\t\t\tif (syncedLyrics.length > 0) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tsyncedLyrics,\n\t\t\t\t\t\t\trichSyncedLyrics: this.parseRichSyncToWords(\n\t\t\t\t\t\t\t\trichsync.richsync_body,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tlyrics: this.formatToLrc(syncedLyrics),\n\t\t\t\t\t\t\thasTimestamps: true,\n\t\t\t\t\t\t\tsongInfo: {\n\t\t\t\t\t\t\t\ttitle: track.track_name,\n\t\t\t\t\t\t\t\tartist: track.artist_name,\n\t\t\t\t\t\t\t\talbum: track.album_name,\n\t\t\t\t\t\t\t\tduration: track.track_length,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fallback to subtitle if richsync fails\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst subtitleResponse = await this.apiGet(\"track.subtitle.get\", [\n\t\t\t\t\t[\"track_id\", trackId.toString()],\n\t\t\t\t]);\n\t\t\t\tconst subtitleData =\n\t\t\t\t\t(await subtitleResponse.json()) as MusixmatchResponse<SubtitleBody>;\n\n\t\t\t\tif (subtitleData.message.header.status_code === 200) {\n\t\t\t\t\tconst subtitle = subtitleData.message.body.subtitle;\n\t\t\t\t\tconst syncedLyrics = this.parseSubtitle(subtitle.subtitle_body);\n\n\t\t\t\t\tif (syncedLyrics.length > 0) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tsyncedLyrics,\n\t\t\t\t\t\t\tlyrics: this.formatToLrc(syncedLyrics),\n\t\t\t\t\t\t\thasTimestamps: true,\n\t\t\t\t\t\t\tsongInfo: {\n\t\t\t\t\t\t\t\ttitle: track.track_name,\n\t\t\t\t\t\t\t\tartist: track.artist_name,\n\t\t\t\t\t\t\t\talbum: track.album_name,\n\t\t\t\t\t\t\t\tduration: track.track_length,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// fallback to regular lyrics\n\t\t\t}\n\n\t\t\tconst lyricsResponse = await this.apiGet(\"track.lyrics.get\", [\n\t\t\t\t[\"track_id\", trackId.toString()],\n\t\t\t]);\n\t\t\tconst lyricsData =\n\t\t\t\t(await lyricsResponse.json()) as MusixmatchResponse<LyricsBody>;\n\n\t\t\tif (lyricsData.message.header.status_code !== 200) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"No lyrics found for this track\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst lyrics = lyricsData.message.body.lyrics;\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tlyrics: lyrics.lyrics_body,\n\t\t\t\thasTimestamps: false,\n\t\t\t\tsongInfo: {\n\t\t\t\t\ttitle: track.track_name,\n\t\t\t\t\tartist: track.artist_name,\n\t\t\t\t\talbum: track.album_name,\n\t\t\t\t\tduration: track.track_length,\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error searching and fetching synced lyrics:\", error);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror:\n\t\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\",\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Get lyrics by ISRC code\n\t * @param isrc - International Standard Recording Code\n\t * @returns Promise<LyricsResponse>\n\t */\n\tasync get(isrc: string): Promise<LyricsResponse> {\n\t\ttry {\n\t\t\tconst trackResponse = await this.apiGet(\"track.get\", [\n\t\t\t\t[\"track_isrc\", isrc],\n\t\t\t]);\n\n\t\t\tconst trackData =\n\t\t\t\t(await trackResponse.json()) as MusixmatchResponse<TrackBody>;\n\n\t\t\tif (trackData.message.header.status_code !== 200) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Track not found for ISRC: ${isrc}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst track = trackData.message.body.track;\n\t\t\tconst trackId = track.track_id;\n\n\t\t\tconst lyricsResponse = await this.apiGet(\"track.lyrics.get\", [\n\t\t\t\t[\"track_id\", trackId.toString()],\n\t\t\t]);\n\n\t\t\tconst lyricsData =\n\t\t\t\t(await lyricsResponse.json()) as MusixmatchResponse<LyricsBody>;\n\n\t\t\tif (lyricsData.message.header.status_code !== 200) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"Lyrics not found for this track\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst lyrics = lyricsData.message.body.lyrics;\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tlyrics: lyrics.lyrics_body,\n\t\t\t\tsongInfo: {\n\t\t\t\t\ttitle: track.track_name,\n\t\t\t\t\tartist: track.artist_name,\n\t\t\t\t\talbum: track.album_name,\n\t\t\t\t\tduration: track.track_length,\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error fetching lyrics by ISRC:\", error);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror:\n\t\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\",\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Search for track and get lyrics\n\t * @param query - Search query (artist and track name)\n\t * @returns Promise<LyricsResponse>\n\t */\n\tasync search(query: string): Promise<LyricsResponse> {\n\t\tif (/^[A-Z]{2}-?[A-Z0-9]{3}-?\\d{2}-?\\d{5}$/i.test(query.trim())) {\n\t\t\treturn this.get(query.trim().replace(/-/g, \"\"));\n\t\t}\n\n\t\ttry {\n\t\t\tconst searchResponse = await this.apiGet(\"track.search\", [\n\t\t\t\t[\"q\", query],\n\t\t\t\t[\"page_size\", \"1\"],\n\t\t\t]);\n\n\t\t\tconst searchData =\n\t\t\t\t(await searchResponse.json()) as MusixmatchResponse<TrackSearchBody>;\n\n\t\t\tif (\n\t\t\t\tsearchData.message.header.status_code !== 200 ||\n\t\t\t\t!searchData.message.body.track_list ||\n\t\t\t\tsearchData.message.body.track_list.length === 0\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `No tracks found for query: ${query}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst trackList = searchData.message.body.track_list;\n\t\t\tconst firstTrack = trackList[0];\n\t\t\tif (!firstTrack) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"No tracks found in search results\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst track = firstTrack.track;\n\t\t\tconst trackId = track.track_id;\n\n\t\t\tconst lyricsResponse = await this.apiGet(\"track.lyrics.get\", [\n\t\t\t\t[\"track_id\", trackId.toString()],\n\t\t\t]);\n\n\t\t\tconst lyricsData =\n\t\t\t\t(await lyricsResponse.json()) as MusixmatchResponse<LyricsBody>;\n\n\t\t\tif (lyricsData.message.header.status_code !== 200) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"Lyrics not found for this track\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst lyrics = lyricsData.message.body.lyrics;\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tlyrics: lyrics.lyrics_body,\n\t\t\t\tsongInfo: {\n\t\t\t\t\ttitle: track.track_name,\n\t\t\t\t\tartist: track.artist_name,\n\t\t\t\t\talbum: track.album_name,\n\t\t\t\t\tduration: track.track_length,\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error searching and fetching lyrics:\", error);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror:\n\t\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\",\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Get track information by ISRC without lyrics\n\t * @param isrc - International Standard Recording Code\n\t * @returns Promise<Track>\n\t */\n\tasync getTrack(isrc: string): Promise<Track> {\n\t\ttry {\n\t\t\tconst response = await this.apiGet(\"track.get\", [[\"track_isrc\", isrc]]);\n\n\t\t\tconst data = (await response.json()) as MusixmatchResponse<TrackBody>;\n\n\t\t\tif (data.message.header.status_code !== 200) {\n\t\t\t\tthrow new Error(`Track not found for ISRC: ${isrc}`);\n\t\t\t}\n\n\t\t\treturn data.message.body.track;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error fetching track by ISRC:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\nexport const lyricsClient = new LyricsClient();\nexport type { LyricsResponse, SongInfo, SyncedLyric, Track } from \"./types.js\";\n"],"mappings":";;;;;;;AAwBA,eAAsBA,QAAM,MAA6B;CACxD,IAAI;EACH,MAAMC,MAAQ,MAAM,EAAE,WAAW,KAAK,CAAC;CACxC,SAAS,OAAgB;EAExB,IAAIC,MAAU,SAAS,UACtB,MAAM;CAER;AACD;AAEA,eAAsBC,YAAU,MAAc,MAA6B;CAC1E,MAAMC,UAAY,MAAM,MAAM,OAAO;AACtC;AAEA,eAAsBC,WAAS,MAA+B;CAC7D,OAAO,MAAMC,SAAW,MAAM,OAAO;AACtC;AAEA,eAAsB,WAAW,MAA6B;CAC7D,IAAI;EACH,MAAMC,GAAK,MAAM,EAAE,OAAO,KAAK,CAAC;CACjC,QAAQ,CAER;AACD;AAEA,SAAgB,aAAa,SAAyB;CACrD,OAAO,KAAK,QAAQ,GAAG,UAAU,OAAO;AACzC;AAEA,MAAM,YAAY,YAAY,WAAW,KAAK;AAE9C,MAAa,WAAW,GAAG,OAAO;CACjC,OAAO;CACP,SAAS;EACR,cACC;EACD,QAAQ;EACR,SAAS;EACT,QAAQ;CACT;CACA,iBAAiB;AAClB,CAAC;;;;AC3CD,IAAa,eAAb,MAA0B;;kBACN;eACY;;CAE/B,AAAQ,cAAc,cAAqC;EAC1D,MAAM,QAAQ,aAAa,MAAM,IAAI;EACrC,MAAM,+BAAe,IAAI,IAAsB;EAE/C,KAAK,MAAM,QAAQ,OAAO;GACzB,MAAM,cAAc,KAAK,KAAK;GAC9B,IAAI,CAAC,aAAa;GAElB,MAAM,QAAQ,YAAY,MAAM,qCAAqC;GACrE,mDAAI,MAAQ,OAAM,MAAM,MAAM,MAAM,MAAM,MAAM,IAAI;IACnD,MAAM,GAAG,SAAS,SAAS,YAAY,QAAQ;IAC/C,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG;IAE9C,IAAI,CAAC,aAAa,IAAI,YAAY,GACjC,aAAa,IAAI,cAAc,CAAC,CAAC;IAElC,MAAM,gBAAgB,aAAa,IAAI,YAAY;IACnD,IAAI,eACH,cAAc,KAAK,KAAK,KAAK,CAAC;GAEhC;EACD;EAEA,MAAM,eAA8B,CAAC;EAErC,KAAK,MAAM,CAAC,cAAc,cAAc,cAAc;GACrD,MAAM,QAAQ,aAAa,MAAM,0BAA0B;GAC3D,IAAI,gDAAC,MAAQ,OAAM,CAAC,MAAM,MAAM,CAAC,MAAM,IAAI;GAE3C,MAAM,GAAG,SAAS,SAAS,cAAc;GACzC,MAAM,aAAa,SAAS,SAAS,EAAE;GACvC,MAAM,aAAa,SAAS,SAAS,EAAE;GACvC,MAAM,gBAAgB,SAAS,YAAY,EAAE;GAC7C,MAAM,QAAQ,gBAAgB;GAC9B,MAAM,eAAe,aAAa,KAAK,aAAa,gBAAgB;GAEpE,MAAM,eAAe,UACnB,QAAQ,SAAS,KAAK,SAAS,CAAC,CAAC,CACjC,KAAK,GAAG,CAAC,CACT,KAAK;GAEP,IAAI,cACH,aAAa,KAAK;IACjB,MAAM;IACN,MAAM;KACL,OAAO;KACP,SAAS;KACT,SAAS;KACT,IAAI;IACL;GACD,CAAC;EAEH;EAEA,aAAa,MAAM,GAAG,MAAM,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK;EAEvD,OAAO;CACR;CAEA,AAAQ,cAAc,cAAqC;EAC1D,IAAI;GACH,MAAM,eAAe,KAAK,MAAM,YAAY;GAC5C,MAAM,+BAAe,IAAI,IAAsB;GAE/C,IAAI,MAAM,QAAQ,YAAY,GAC7B;SAAK,MAAM,QAAQ,cAClB,IAAI,KAAK,MAAM,KAAK,KAAK,MAAM,QAAQ,KAAK,CAAC,GAAG;KAC/C,MAAM,YAAY,WAAW,KAAK,EAAE;KACpC,KAAK,MAAM,aAAa,KAAK,GAC5B,IAAI,UAAU,GAAG;MAChB,IAAI,CAAC,aAAa,IAAI,SAAS,GAC9B,aAAa,IAAI,WAAW,CAAC,CAAC;MAE/B,MAAM,gBAAgB,aAAa,IAAI,SAAS;MAChD,IAAI,eACH,cAAc,KAAK,UAAU,CAAC;KAEhC;IAEF;GACD;GAGD,MAAM,eAA8B,CAAC;GAErC,KAAK,MAAM,CAAC,WAAW,cAAc,cAAc;IAClD,MAAM,UAAU,KAAK,MAAM,YAAY,EAAE;IACzC,MAAM,UAAU,KAAK,MAAM,YAAY,EAAE;IACzC,MAAM,KAAK,KAAK,MAAO,YAAY,IAAK,GAAI;IAE5C,MAAM,eAAe,UACnB,QAAQ,SAAS,KAAK,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,CACxC,KAAK,GAAG,CAAC,CACT,KAAK;IAEP,IAAI,cACH,aAAa,KAAK;KACjB,MAAM;KACN,MAAM;MACL,OAAO;MACP;MACA;MACA;KACD;IACD,CAAC;GAEH;GAEA,aAAa,MAAM,GAAG,MAAM,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK;GAEvD,OAAO;EACR,QAAQ;GACP,OAAO,CAAC;EACT;CACD;CAEA,AAAQ,qBAAqB,cAA6C;EACzE,IAAI;GACH,MAAM,eAAe,KAAK,MAAM,YAAY;GAC5C,MAAM,QAA+B,CAAC;GAEtC,IAAI,MAAM,QAAQ,YAAY,GAC7B;SAAK,MAAM,QAAQ,cAClB,IACC,KAAK,OAAO,UACZ,KAAK,OAAO,UACZ,KAAK,KACL,MAAM,QAAQ,KAAK,CAAC,GACnB;KACD,MAAM,iBAAiB,WAAW,KAAK,EAAE;KACzC,MAAM,eAAe,WAAW,KAAK,EAAE;KAEvC,MAAM,YAAY;MACjB,OAAO;MACP,SAAS,KAAK,MAAM,iBAAiB,EAAE;MACvC,SAAS,KAAK,MAAM,iBAAiB,EAAE;MACvC,IAAI,KAAK,MAAM,iBAAiB,GAAI,IAAI;KACzC;KAEA,MAAM,UAAU;MACf,OAAO;MACP,SAAS,KAAK,MAAM,eAAe,EAAE;MACrC,SAAS,KAAK,MAAM,eAAe,EAAE;MACrC,IAAI,KAAK,MAAM,eAAe,GAAI,IAAI;KACvC;KAEA,MAAM,QAA2B,CAAC;KAClC,KAAK,MAAM,YAAY,KAAK,GAC3B,IAAI,SAAS,MAAM,UAAa,SAAS,MAAM,QAAW;MAEzD,MAAM,gBAAgB,iBADP,WAAW,SAAS,CACS;MAC5C,MAAM,KAAK;OACV,MAAM,SAAS;OACf,MAAM;QACL,OAAO;QACP,SAAS,KAAK,MAAM,gBAAgB,EAAE;QACtC,SAAS,KAAK,MAAM,gBAAgB,EAAE;QACtC,IAAI,KAAK,MAAM,gBAAgB,GAAI,IAAI;OACxC;MACD,CAAC;KACF;KAGD,MAAM,KAAK;MACV;MACA;MACA;KACD,CAAC;IACF;GACD;GAGD,OAAO;EACR,QAAQ;GACP,OAAO,CAAC;EACT;CACD;CAEA,AAAQ,YAAY,cAAqC;EACxD,OAAO,aACL,KAAK,SAAS;GAMd,OAAO,IALK,KAAK,KAAK,QAAQ,SAAS,CAAC,CAAC,SAAS,GAAG,GAKxC,EAAE,GAJH,KAAK,KAAK,QAAQ,SAAS,CAAC,CAAC,SAAS,GAAG,GAIjC,EAAE,GAHX,KAAK,MAAM,KAAK,KAAK,KAAK,EAAE,CAAC,CACtC,SAAS,CAAC,CACV,SAAS,GAAG,GACY,EAAE,IAAI,KAAK;EACtC,CAAC,CAAC,CACD,KAAK,IAAI;CACZ;CAEA,MAAc,OACb,QACA,QAAiC,CAAC,GACd;EACpB,IAAI,WAAW,eAAe,KAAK,UAAU,MAC5C,MAAM,KAAK,SAAS;EAGrB,MAAM,KAAK,CAAC,UAAU,sBAAsB,CAAC;EAC7C,IAAI,KAAK,UAAU,MAClB,MAAM,KAAK,CAAC,aAAa,KAAK,KAAK,CAAC;EAErC,MAAM,YAAY,KAAK,IAAI,CAAC,CAAC,SAAS;EACtC,MAAM,KAAK,CAAC,KAAK,SAAS,CAAC;EAC3B,MAAM,MAAM,KAAK,WAAW;EAC5B,MAAM,SAAS,IAAI,gBAAgB,KAAK;EAExC,IAAI;GAEH,OAAO,MADgB,SAAS,IAAI,GAAG,IAAI,GAAG,OAAO,SAAS,GAAG;EAElE,SAAS,OAAO;GACf,QAAQ,MAAM,oCAAoC,KAAK;GACvD,MAAM;EACP;CACD;CAEA,MAAc,OACb,QACA,QAAiC,CAAC,GACd;EACpB,MAAM,aAAa,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAqB;EACnE,MAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,UAAU;EAErD,IAAI,WAAW,aAAa;GAC3B,MAAM,QAAQ,SAAS,MAAM;GAC7B,IAAI;;IACH,MAAM,OAAQ,MAAM,MAAM,KAAK;IAC/B,0DAAI,KAAM,gFAAS,sEAAQ,iBAAgB,KAAK;KAC/C,KAAK,QAAQ;KAKb,MAAM,WAJY,KACjB,aAAa,cAAc,GAC3B,uBAEwB,CAAC;KAC1B,MAAM,kBAAkB,MAAM,KAC5B,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAClB;KACA,OAAO,MAAM,KAAK,OAAO,QAAQ,eAAe;IACjD;GACD,QAAQ,CAER;EACD;EAEA,OAAO;CACR;CAEA,MAAc,WAA0B;EACvC,MAAM,YAAY,KACjB,aAAa,cAAc,GAC3B,uBACD;EACA,MAAM,cAAc,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EAEhD,IAAI;GACH,MAAM,YAAY,MAAMC,WAAS,SAAS;GAC1C,MAAM,kBAAkB,KAAK,MAAM,SAAS;GAC5C,MAAM,cAAc,gBAAgB;GACpC,MAAM,iBAAiB,gBAAgB;GAEvC,IAAI,eAAe,kBAAkB,cAAc,gBAAgB;IAClE,KAAK,QAAQ;IACb;GACD;EACD,QAAQ;GACP,QAAQ,KAAK,kDAAkD;EAChE;EAEA,IAAI;GAIH,MAAM,OAAQ,OAAM,MAHG,KAAK,OAAO,aAAa,CAC/C,CAAC,iBAAiB,IAAI,CACvB,CAAC,EAC2B,CAAC,KAAK;GAElC,IAAI,KAAK,QAAQ,OAAO,gBAAgB,KAAK;IAC5C,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,GAAK,CAAC;IACzD,OAAO,KAAK,SAAS;GACtB;GAEA,MAAM,WAAW,KAAK,QAAQ,KAAK;GACnC,MAAM,iBAAiB,cAAc;GAErC,KAAK,QAAQ;GAEb,MAAM,YAAY;IACjB,OAAO;IACP,iBAAiB;GAClB;GAEA,IAAI;IAEH,MAAMC,QADW,QAAQ,SACN,CAAC;IACpB,MAAMC,YAAU,WAAW,KAAK,UAAU,SAAS,CAAC;GACrD,SAAS,YAAY;IACpB,QAAQ,KAAK,0BAA0B,UAAU;GAClD;EACD,SAAS,OAAO;GACf,QAAQ,MAAM,mCAAmC,KAAK;GACtD,MAAM;EACP;CACD;;;;;;CAOA,MAAM,UAAU,MAAuC;EACtD,IAAI;GAIH,MAAM,YACJ,OAAM,MAJoB,KAAK,OAAO,aAAa,CACpD,CAAC,cAAc,IAAI,CACpB,CAAC,EAEoB,CAAC,KAAK;GAE3B,IAAI,UAAU,QAAQ,OAAO,gBAAgB,KAC5C,OAAO;IACN,SAAS;IACT,OAAO,6BAA6B;GACrC;GAGD,MAAM,QAAQ,UAAU,QAAQ,KAAK;GACrC,MAAM,UAAU,MAAM;GAGtB,IAAI;IAIH,MAAM,eACJ,OAAM,MAJuB,KAAK,OAAO,sBAAsB,CAChE,CAAC,YAAY,QAAQ,SAAS,CAAC,CAChC,CAAC,EAEuB,CAAC,KAAK;IAE9B,IAAI,aAAa,QAAQ,OAAO,gBAAgB,KAAK;KACpD,MAAM,WAAW,aAAa,QAAQ,KAAK;KAC3C,MAAM,eAAe,KAAK,cAAc,SAAS,aAAa;KAE9D,IAAI,aAAa,SAAS,GACzB,OAAO;MACN,SAAS;MACT;MACA,kBAAkB,KAAK,qBACtB,SAAS,aACV;MACA,QAAQ,KAAK,YAAY,YAAY;MACrC,eAAe;MACf,UAAU;OACT,OAAO,MAAM;OACb,QAAQ,MAAM;OACd,OAAO,MAAM;OACb,UAAU,MAAM;MACjB;KACD;IAEF;GACD,QAAQ,CAER;GAEA,IAAI;IAIH,MAAM,eACJ,OAAM,MAJuB,KAAK,OAAO,sBAAsB,CAChE,CAAC,YAAY,QAAQ,SAAS,CAAC,CAChC,CAAC,EAEuB,CAAC,KAAK;IAE9B,IAAI,aAAa,QAAQ,OAAO,gBAAgB,KAAK;KACpD,MAAM,WAAW,aAAa,QAAQ,KAAK;KAC3C,MAAM,eAAe,KAAK,cAAc,SAAS,aAAa;KAE9D,IAAI,aAAa,SAAS,GACzB,OAAO;MACN,SAAS;MACT;MACA,QAAQ,KAAK,YAAY,YAAY;MACrC,eAAe;MACf,UAAU;OACT,OAAO,MAAM;OACb,QAAQ,MAAM;OACd,OAAO,MAAM;OACb,UAAU,MAAM;MACjB;KACD;IAEF;GACD,QAAQ,CAER;GAKA,MAAM,aACJ,OAAM,MAJqB,KAAK,OAAO,oBAAoB,CAC5D,CAAC,YAAY,QAAQ,SAAS,CAAC,CAChC,CAAC,EAEqB,CAAC,KAAK;GAE5B,IAAI,WAAW,QAAQ,OAAO,gBAAgB,KAC7C,OAAO;IACN,SAAS;IACT,OAAO;GACR;GAID,OAAO;IACN,SAAS;IACT,QAHc,WAAW,QAAQ,KAAK,OAGvB;IACf,eAAe;IACf,UAAU;KACT,OAAO,MAAM;KACb,QAAQ,MAAM;KACd,OAAO,MAAM;KACb,UAAU,MAAM;IACjB;GACD;EACD,SAAS,OAAO;GACf,QAAQ,MAAM,yCAAyC,KAAK;GAC5D,OAAO;IACN,SAAS;IACT,OACC,iBAAiB,QAAQ,MAAM,UAAU;GAC3C;EACD;CACD;;;;;;CAOA,MAAM,aAAa,OAAwC;EAC1D,IAAI,yCAAyC,KAAK,MAAM,KAAK,CAAC,GAC7D,OAAO,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC;EAGrD,IAAI;GAOH,MAAM,aACJ,OAAM,MANqB,KAAK,OAAO,gBAAgB,CACxD,CAAC,KAAK,KAAK,GACX,CAAC,aAAa,GAAG,CAClB,CAAC,EAGqB,CAAC,KAAK;GAE5B,IACC,WAAW,QAAQ,OAAO,gBAAgB,OAC1C,CAAC,WAAW,QAAQ,KAAK,cACzB,WAAW,QAAQ,KAAK,WAAW,WAAW,GAE9C,OAAO;IACN,SAAS;IACT,OAAO,8BAA8B;GACtC;GAID,MAAM,aADY,WAAW,QAAQ,KAAK,WACb;GAC7B,IAAI,CAAC,YACJ,OAAO;IACN,SAAS;IACT,OAAO;GACR;GAGD,MAAM,QAAQ,WAAW;GACzB,MAAM,UAAU,MAAM;GAEtB,IAAI;IAIH,MAAM,eACJ,OAAM,MAJuB,KAAK,OAAO,sBAAsB,CAChE,CAAC,YAAY,QAAQ,SAAS,CAAC,CAChC,CAAC,EAEuB,CAAC,KAAK;IAE9B,IAAI,aAAa,QAAQ,OAAO,gBAAgB,KAAK;KACpD,MAAM,WAAW,aAAa,QAAQ,KAAK;KAC3C,MAAM,eAAe,KAAK,cAAc,SAAS,aAAa;KAE9D,IAAI,aAAa,SAAS,GACzB,OAAO;MACN,SAAS;MACT;MACA,kBAAkB,KAAK,qBACtB,SAAS,aACV;MACA,QAAQ,KAAK,YAAY,YAAY;MACrC,eAAe;MACf,UAAU;OACT,OAAO,MAAM;OACb,QAAQ,MAAM;OACd,OAAO,MAAM;OACb,UAAU,MAAM;MACjB;KACD;IAEF;GACD,QAAQ,CAER;GAEA,IAAI;IAIH,MAAM,eACJ,OAAM,MAJuB,KAAK,OAAO,sBAAsB,CAChE,CAAC,YAAY,QAAQ,SAAS,CAAC,CAChC,CAAC,EAEuB,CAAC,KAAK;IAE9B,IAAI,aAAa,QAAQ,OAAO,gBAAgB,KAAK;KACpD,MAAM,WAAW,aAAa,QAAQ,KAAK;KAC3C,MAAM,eAAe,KAAK,cAAc,SAAS,aAAa;KAE9D,IAAI,aAAa,SAAS,GACzB,OAAO;MACN,SAAS;MACT;MACA,QAAQ,KAAK,YAAY,YAAY;MACrC,eAAe;MACf,UAAU;OACT,OAAO,MAAM;OACb,QAAQ,MAAM;OACd,OAAO,MAAM;OACb,UAAU,MAAM;MACjB;KACD;IAEF;GACD,QAAQ,CAER;GAKA,MAAM,aACJ,OAAM,MAJqB,KAAK,OAAO,oBAAoB,CAC5D,CAAC,YAAY,QAAQ,SAAS,CAAC,CAChC,CAAC,EAEqB,CAAC,KAAK;GAE5B,IAAI,WAAW,QAAQ,OAAO,gBAAgB,KAC7C,OAAO;IACN,SAAS;IACT,OAAO;GACR;GAID,OAAO;IACN,SAAS;IACT,QAHc,WAAW,QAAQ,KAAK,OAGvB;IACf,eAAe;IACf,UAAU;KACT,OAAO,MAAM;KACb,QAAQ,MAAM;KACd,OAAO,MAAM;KACb,UAAU,MAAM;IACjB;GACD;EACD,SAAS,OAAO;GACf,QAAQ,MAAM,+CAA+C,KAAK;GAClE,OAAO;IACN,SAAS;IACT,OACC,iBAAiB,QAAQ,MAAM,UAAU;GAC3C;EACD;CACD;;;;;;CAOA,MAAM,IAAI,MAAuC;EAChD,IAAI;GAKH,MAAM,YACJ,OAAM,MALoB,KAAK,OAAO,aAAa,CACpD,CAAC,cAAc,IAAI,CACpB,CAAC,EAGoB,CAAC,KAAK;GAE3B,IAAI,UAAU,QAAQ,OAAO,gBAAgB,KAC5C,OAAO;IACN,SAAS;IACT,OAAO,6BAA6B;GACrC;GAGD,MAAM,QAAQ,UAAU,QAAQ,KAAK;GACrC,MAAM,UAAU,MAAM;GAMtB,MAAM,aACJ,OAAM,MALqB,KAAK,OAAO,oBAAoB,CAC5D,CAAC,YAAY,QAAQ,SAAS,CAAC,CAChC,CAAC,EAGqB,CAAC,KAAK;GAE5B,IAAI,WAAW,QAAQ,OAAO,gBAAgB,KAC7C,OAAO;IACN,SAAS;IACT,OAAO;GACR;GAKD,OAAO;IACN,SAAS;IACT,QAJc,WAAW,QAAQ,KAAK,OAIvB;IACf,UAAU;KACT,OAAO,MAAM;KACb,QAAQ,MAAM;KACd,OAAO,MAAM;KACb,UAAU,MAAM;IACjB;GACD;EACD,SAAS,OAAO;GACf,QAAQ,MAAM,kCAAkC,KAAK;GACrD,OAAO;IACN,SAAS;IACT,OACC,iBAAiB,QAAQ,MAAM,UAAU;GAC3C;EACD;CACD;;;;;;CAOA,MAAM,OAAO,OAAwC;EACpD,IAAI,yCAAyC,KAAK,MAAM,KAAK,CAAC,GAC7D,OAAO,KAAK,IAAI,MAAM,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC;EAG/C,IAAI;GAMH,MAAM,aACJ,OAAM,MANqB,KAAK,OAAO,gBAAgB,CACxD,CAAC,KAAK,KAAK,GACX,CAAC,aAAa,GAAG,CAClB,CAAC,EAGqB,CAAC,KAAK;GAE5B,IACC,WAAW,QAAQ,OAAO,gBAAgB,OAC1C,CAAC,WAAW,QAAQ,KAAK,cACzB,WAAW,QAAQ,KAAK,WAAW,WAAW,GAE9C,OAAO;IACN,SAAS;IACT,OAAO,8BAA8B;GACtC;GAID,MAAM,aADY,WAAW,QAAQ,KAAK,WACb;GAC7B,IAAI,CAAC,YACJ,OAAO;IACN,SAAS;IACT,OAAO;GACR;GAGD,MAAM,QAAQ,WAAW;GACzB,MAAM,UAAU,MAAM;GAMtB,MAAM,aACJ,OAAM,MALqB,KAAK,OAAO,oBAAoB,CAC5D,CAAC,YAAY,QAAQ,SAAS,CAAC,CAChC,CAAC,EAGqB,CAAC,KAAK;GAE5B,IAAI,WAAW,QAAQ,OAAO,gBAAgB,KAC7C,OAAO;IACN,SAAS;IACT,OAAO;GACR;GAKD,OAAO;IACN,SAAS;IACT,QAJc,WAAW,QAAQ,KAAK,OAIvB;IACf,UAAU;KACT,OAAO,MAAM;KACb,QAAQ,MAAM;KACd,OAAO,MAAM;KACb,UAAU,MAAM;IACjB;GACD;EACD,SAAS,OAAO;GACf,QAAQ,MAAM,wCAAwC,KAAK;GAC3D,OAAO;IACN,SAAS;IACT,OACC,iBAAiB,QAAQ,MAAM,UAAU;GAC3C;EACD;CACD;;;;;;CAOA,MAAM,SAAS,MAA8B;EAC5C,IAAI;GAGH,MAAM,OAAQ,OAAM,MAFG,KAAK,OAAO,aAAa,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC,EAE1C,CAAC,KAAK;GAElC,IAAI,KAAK,QAAQ,OAAO,gBAAgB,KACvC,MAAM,IAAI,MAAM,6BAA6B,MAAM;GAGpD,OAAO,KAAK,QAAQ,KAAK;EAC1B,SAAS,OAAO;GACf,QAAQ,MAAM,iCAAiC,KAAK;GACpD,MAAM;EACP;CACD;AACD;AAEA,MAAa,eAAe,IAAI,aAAa"}