import type { QualityKey, Source } from './api.ts'; export const SOURCE_LABEL: Record = { netease: '网易云', qq: 'QQ 音乐' }; export const QUALITY_BADGE: Record = { standard: '128K', high: '320K', lossless: 'FLAC', hires: 'HI-RES', master: 'MASTER', spatial: 'SPATIAL', effect: 'EFFECT', }; export const QUALITY_LABEL: Record = { standard: '标准', high: '极高', lossless: '无损', hires: 'Hi-Res', master: '超清母带', spatial: '空间音频', effect: '高清臻品', }; const QUALITY_RANK: QualityKey[] = ['effect', 'spatial', 'master', 'hires', 'lossless', 'high', 'standard']; export function isGold(q: string | null | undefined): boolean { return q === 'hires' || q === 'master' || q === 'spatial' || q === 'effect'; } /** 该曲目声明的最佳音质(用于徽章) */ export function bestQuality(qs: QualityKey[] | undefined): QualityKey | undefined { if (!qs?.length) return undefined; return QUALITY_RANK.find((q) => qs.includes(q)); } export function formatDuration(ms?: number | null): string { if (!ms) return '--:--'; const s = Math.round(ms / 1000); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`; } /** 总时长:1 小时 12 分 / 38 分钟 */ export function formatTotal(ms?: number | null): string { if (!ms) return ''; const min = Math.round(ms / 60000); return min >= 60 ? `${Math.floor(min / 60)} 小时 ${min % 60} 分` : `${Math.max(1, min)} 分钟`; } export function formatSeconds(sec: number): string { if (!Number.isFinite(sec)) return '0:00'; const s = Math.floor(sec); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`; } export function formatSize(bytes?: number | null): string { if (!bytes) return '—'; if (bytes >= 1073741824) return `${(bytes / 1073741824).toFixed(2)} GB`; if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(1)} MB`; return `${Math.max(1, Math.round(bytes / 1024))} KB`; } export function formatSpeed(bps: number): string { return bps >= 1048576 ? `${(bps / 1048576).toFixed(1)} MB/s` : `${Math.round(bps / 1024)} KB/s`; } export function formatDate(ts?: number | string | null): string { if (!ts) return ''; const d = new Date(ts); return Number.isNaN(d.getTime()) ? '' : `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; } const dtf = new Intl.DateTimeFormat('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); export function formatDateTime(ts: string): string { return dtf.format(new Date(ts)); } export function coverThumb(url: string | undefined | null, size = 120): string | undefined { if (!url) return undefined; const https = url.replace(/^http:/, 'https:'); if (/music\.126\.net/.test(https)) return `${https.split('?')[0]}?param=${size}y${size}`; if (/y\.gtimg\.cn\/music\/photo_new\/T00\dR\d+x\d+/.test(https)) { const s = size <= 150 ? 150 : size <= 300 ? 300 : 800; return https.replace(/R\d+x\d+/, `R${s}x${s}`); } return https; } export const STAGE_LABEL: Record = { resolve: '解析音源', metadata: '获取信息', download: '下载中', lyric: '保存歌词', cover: '获取封面', tag: '写入标签', };