音乐模块调整

This commit is contained in:
2026-09-12 15:47:16 +08:00
parent d702ed0d31
commit ffa410b399
33 changed files with 5452 additions and 2237 deletions
+33 -13
View File
@@ -224,10 +224,27 @@ export const useMusicStore = defineStore('music', () => {
// 任务历史持久化(localStorage,应用重启后保留记录;恢复时不尝试续传)
const TASKS_KEY = 'thing.music.tasks'
const MAX_TASKS = 30
/** 单次持久化的体积上限:超过则剥离 rawSearch(懒解析字段)后重试,避免撑爆 localStorage 配额 */
const MAX_PERSIST_CHARS = 1_500_000
let persistTimer: ReturnType<typeof setTimeout> | null = null
/** 剥离仅解析期使用的重字段(只影响「重新下载」对懒解析歌曲的能力) */
function stripHeavy(s: MusicSong): MusicSong {
const { rawSearch, defaultDownloadHeaders, defaultDownloadCookies, ...rest } = s
void rawSearch
void defaultDownloadHeaders
void defaultDownloadCookies
return rest
}
function persistTasks() {
const base = tasks.value.slice(0, MAX_TASKS)
try {
localStorage.setItem(TASKS_KEY, JSON.stringify(tasks.value.slice(0, MAX_TASKS)))
let payload = JSON.stringify(base)
if (payload.length > MAX_PERSIST_CHARS) {
payload = JSON.stringify(base.map((t) => ({ ...t, songsData: t.songsData.map(stripHeavy) })))
}
localStorage.setItem(TASKS_KEY, payload)
} catch {
// localStorage 不可用/超限时静默降级(仅影响历史记录)
}
@@ -314,10 +331,11 @@ export const useMusicStore = defineStore('music', () => {
} else if (type === 'done') {
const st = task.songs.find((s) => s.key === ev.key)
if (st) {
// 幂等:重复的 done 事件(断点续传/重发)不再累加 doneCount
if (st.status !== 'done') task.doneCount++
st.status = 'done'
st.downloaded = st.total || st.downloaded
}
task.doneCount++
} else if (type === 'error') {
const st = task.songs.find((s) => s.key === ev.key)
if (st) {
@@ -350,14 +368,15 @@ export const useMusicStore = defineStore('music', () => {
)
}
/** 懒解析:解析单首歌曲的真实下载链接(试听前调用),成功后原地更新
* 搜索结果并返回带链接的最新歌曲对象;失败返回 null */
async function resolveSong(source: string, index: number): Promise<MusicSong | null> {
/** 懒解析:解析单首歌曲的真实下载链接(试听/上传前调用),成功后原地更新
* 搜索结果并返回带链接的最新歌曲对象;失败返回 null
* `quality` 为音质档位 label"" = 最高),解析时按「≤所选最优档」封顶。 */
async function resolveSong(source: string, index: number, quality = ''): Promise<MusicSong | null> {
const song = results.value[source]?.[index]
if (!song) return null
if (song.downloadUrl) return song
if (!song.rawSearch) return null
const v = (await invoke('music_resolve', { song })) as {
const v = (await invoke('music_resolve', { song, quality })) as {
songs: (MusicSong | null)[]
}
const resolved = v.songs?.[0]
@@ -369,8 +388,8 @@ export const useMusicStore = defineStore('music', () => {
return resolved
}
/** 启动下载。engine='rust' 时交给下载器模块(任务在其列表中管理),返回跳过的无链接歌曲数
* 否则走 musicdl 桥接返回 0 */
/** 启动下载。engine='rust' 时交给下载器模块(任务在其列表中管理);
* 否则走 musicdl 桥接返回实际使用的引擎与跳过的无链接歌曲数 */
async function startDownload(
songs: MusicSong[],
opts: {
@@ -383,8 +402,8 @@ export const useMusicStore = defineStore('music', () => {
/** 目标音质 label"" 表示最高);解析下载时按「≤所选最优档」封顶 */
quality?: string
}
): Promise<number> {
if (songs.length === 0) return 0
): Promise<{ engine: 'musicdl' | 'rust'; skipped: number; taskId?: string }> {
if (songs.length === 0) return { engine: 'musicdl', skipped: 0 }
await ensureDownloadListener()
const quality = opts.quality ?? ''
@@ -426,7 +445,7 @@ export const useMusicStore = defineStore('music', () => {
throw e
}
}
return skipped
return { engine: 'rust', skipped }
}
const taskId = crypto.randomUUID()
@@ -468,7 +487,7 @@ export const useMusicStore = defineStore('music', () => {
task.errorMessage = String(e)
throw e
}
return 0
return { engine: 'musicdl', skipped: 0, taskId }
}
async function cancelDownload(taskId: string) {
@@ -520,7 +539,8 @@ export const useMusicStore = defineStore('music', () => {
proxyUrl: proxy,
engine: s.downloadEngine,
maxConcurrent: s.maxConcurrent,
quality: s.defaultDownloadQuality ?? ''
// 沿用原任务的目标音质("最高" 存为空串),而不是当前默认音质
quality: task.quality ?? s.defaultDownloadQuality ?? ''
})
}