import type { AudioQuality } from '@music-together/shared' import { normalizeKugouAudioUrl } from './kugouAudioUrl.js' export type KugouProviderQuality = | '128' | '320' | 'flac' | 'high' | 'super' | 'viper_clear' | 'viper_tape' | 'viper_atmos' | 'multitrack' export interface KugouV6Good { quality?: unknown hash?: unknown status?: unknown level?: unknown info?: { filesize?: unknown bitrate?: unknown extname?: unknown tracker_status?: unknown tracker_type?: unknown tracker_url?: unknown en_filesize?: unknown en_extname?: unknown en_tracker_url?: unknown en_ekey?: unknown } relate_goods?: unknown } export interface SelectedKugouV6Good { quality: KugouProviderQuality hash: string bitrate: number | null fileSize?: number plainUrl?: string encryptedUrl?: string encryptedFileSize?: number encryptedExtension?: string ekey?: string raw: KugouV6Good } const TECHNICAL_STEREO_QUALITIES = new Set([ '128', '320', 'flac', 'high', 'super', 'viper_clear', ]) function firstUrl(value: unknown): string | undefined { if (Array.isArray(value)) { const url = value.find((item) => typeof item === 'string' && item.length > 0) return typeof url === 'string' ? normalizeKugouAudioUrl(url) : undefined } return typeof value === 'string' && value ? normalizeKugouAudioUrl(value) : undefined } function positiveNumber(value: unknown): number | undefined { const number = Number(value) return Number.isFinite(number) && number > 0 ? number : undefined } function normalizeGood(raw: KugouV6Good): SelectedKugouV6Good | null { const quality = String(raw.quality ?? '').toLowerCase() as KugouProviderQuality if (!TECHNICAL_STEREO_QUALITIES.has(quality) && quality !== 'viper_tape') return null if (Number(raw.status) !== 1 || Number(raw.info?.tracker_status) !== 1 || raw.info?.tracker_type !== 'full') return null const plainUrl = firstUrl(raw.info?.tracker_url) const encryptedUrl = firstUrl(raw.info?.en_tracker_url) const ekey = typeof raw.info?.en_ekey === 'string' && raw.info.en_ekey ? raw.info.en_ekey : undefined if (!plainUrl && (!encryptedUrl || !ekey)) return null return { quality, hash: String(raw.hash ?? ''), bitrate: positiveNumber(raw.info?.bitrate) ?? null, fileSize: positiveNumber(raw.info?.filesize), plainUrl, encryptedUrl, encryptedFileSize: positiveNumber(raw.info?.en_filesize), encryptedExtension: typeof raw.info?.en_extname === 'string' ? raw.info.en_extname.toLowerCase().replace(/^\./, '') : undefined, ekey, raw, } } export function collectKugouV6Goods(response: Record): SelectedKugouV6Good[] { const data = Array.isArray(response.data) ? response.data : response.data ? [response.data] : [] const rawGoods: KugouV6Good[] = [] for (const item of data) { if (!item || typeof item !== 'object') continue const root = item as KugouV6Good rawGoods.push(root) if (Array.isArray(root.relate_goods)) rawGoods.push(...(root.relate_goods as KugouV6Good[])) } const seen = new Set() return rawGoods.flatMap((raw) => { const good = normalizeGood(raw) if (!good) return [] const key = `${good.quality}:${good.hash.toLowerCase()}` if (seen.has(key)) return [] seen.add(key) return [good] }) } function technicalScore(good: SelectedKugouV6Good): [number, number, number] { const defaultBitrate: Record = { '128': 128, '320': 320, flac: 900, high: 1800, super: 4000, viper_clear: 3500, viper_tape: 320, viper_atmos: 0, multitrack: 0, } return [good.bitrate ?? defaultBitrate[good.quality], good.fileSize ?? good.encryptedFileSize ?? 0, defaultBitrate[good.quality]] } function byTechnicalQuality(left: SelectedKugouV6Good, right: SelectedKugouV6Good): number { const a = technicalScore(left) const b = technicalScore(right) return b[0] - a[0] || b[1] - a[1] || b[2] - a[2] } export function selectKugouV6Good( goods: SelectedKugouV6Good[], requested: AudioQuality, ): SelectedKugouV6Good | null { const exact = (qualities: KugouProviderQuality[]) => qualities.flatMap((quality) => goods.filter((good) => good.quality === quality).sort(byTechnicalQuality))[0] if (requested === 'kugou_master') { return exact(['viper_tape']) ?? selectKugouV6Good(goods, 'kugou_hires') } if (requested === 'kugou_hires' || requested === 'highest') { return goods.filter((good) => TECHNICAL_STEREO_QUALITIES.has(good.quality)).sort(byTechnicalQuality)[0] ?? null } if (typeof requested === 'number' && requested >= 999) return exact(['flac', '320', '128']) ?? null if (typeof requested === 'number' && requested >= 320) return exact(['320', '128']) ?? null return exact(['128']) ?? null } export function kugouProviderQualityToAudioQuality(quality: KugouProviderQuality): AudioQuality { if (quality === 'viper_tape') return 'kugou_master' if (quality === 'super' || quality === 'viper_clear' || quality === 'high') return 'kugou_hires' if (quality === 'flac') return 999 if (quality === '320') return 320 return 128 }