fix(api): 修复失败缓存清不掉、自定义音源解析及请求逻辑问题

- musicParser: clearMusicCache 改清内存失败缓存(覆盖含 lxMusic 全部策略);unblock 解析加 15s 整体超时兜底,超时降级不触发重复重试
- parseFromCustomApi: GET 请求按 apiUrl 是否已带查询串选择 ?/& 分隔符;br 由字符串码率映射,修复恒为 NaN
- request: 301 登录态失效直接拒绝(原 retryCount=3 与日志矛盾),并对 config.params 加可选链防护
This commit is contained in:
alger
2026-07-05 19:53:29 +08:00
parent 58d4ffee2a
commit 4734c4d566
3 changed files with 33 additions and 20 deletions
+16 -14
View File
@@ -160,21 +160,11 @@ export class CacheManager {
// 清除URL缓存
await deleteData('music_url_cache', id);
console.log(`清除歌曲 ${id} 的URL缓存`);
// 清除失败缓存 - 需要遍历所有策略
const strategies = ['custom', 'gdmusic', 'unblockMusic'];
for (const strategy of strategies) {
const cacheKey = `${id}_${strategy}`;
try {
await deleteData('music_failed_cache', cacheKey);
} catch {
// 忽略删除不存在缓存的错误
}
}
console.log(`清除歌曲 ${id} 的失败缓存`);
} catch (error) {
console.error('清除缓存失败:', error);
console.error('清除URL缓存失败:', error);
}
// 清除内存失败缓存(覆盖所有策略,含 lxMusic)
CacheManager.clearFailedCache(id);
}
}
@@ -237,7 +227,19 @@ const getGDMusicAudio = async (id: number, data: SongResult): Promise<ParsedMusi
const getUnblockMusicAudio = (id: number, data: SongResult, sources: any[]) => {
const filteredSources = sources.filter((source) => source !== 'gdmusic');
console.log(`使用unblockMusic解析,音源:`, filteredSources);
return window.api.unblockMusic(id, cloneDeep(data), cloneDeep(filteredSources));
// 整体超时兜底:unblock 全链路(IPC → match())本身无超时,底层请求挂起时会
// 导致渲染进程无限等待、起播/切歌长时间无响应。超时解析为 null 以走降级,
// 且不抛异常(避免触发 RetryHelper 的多次重试放大等待时间)。
const UNBLOCK_TIMEOUT = 15000;
return Promise.race([
window.api.unblockMusic(id, cloneDeep(data), cloneDeep(filteredSources)),
new Promise((resolve) => {
setTimeout(() => {
console.warn(`unblockMusic 解析超时(${UNBLOCK_TIMEOUT}ms),放弃等待`);
resolve(null);
}, UNBLOCK_TIMEOUT);
})
]);
};
/**
+13 -2
View File
@@ -72,7 +72,9 @@ export const parseFromCustomApi = async (
response = await axios.post(plugin.apiUrl, finalParams, { timeout });
} else {
// 默认为 GET
const finalUrl = `${plugin.apiUrl}?${new URLSearchParams(finalParams).toString()}`;
// apiUrl 本身可能已带查询串(如 xxx/api.php?type=url),需按情况选择 ? 或 &
const separator = plugin.apiUrl.includes('?') ? '&' : '?';
const finalUrl = `${plugin.apiUrl}${separator}${new URLSearchParams(finalParams).toString()}`;
console.log('自定义API:发送 GET 请求到:', finalUrl);
response = await axios.get(finalUrl, { timeout });
}
@@ -83,11 +85,20 @@ export const parseFromCustomApi = async (
if (musicUrl && typeof musicUrl === 'string') {
console.log('自定义API:成功获取URL');
// 5. 组装成应用所需的标准格式并返回
// quality 是 'standard'/'higher'/'exhigh'/'lossless'/'hires' 等字符串,
// 直接 parseInt 会得到 NaN,这里映射为对应码率(bps)
const QUALITY_BITRATE: Record<string, number> = {
standard: 128000,
higher: 192000,
exhigh: 320000,
lossless: 999000,
hires: 1900000
};
return {
data: {
data: {
url: musicUrl,
br: parseInt(quality) * 1000,
br: QUALITY_BITRATE[quality] ?? 320000,
size: 0,
md5: '',
platform: plugin.name.toLowerCase().replace(/\s/g, ''),
+4 -4
View File
@@ -89,13 +89,13 @@ request.interceptors.response.use(
return Promise.reject(error);
}
// 处理 301 状态码
if (error.response?.status === 301 && config.params.noLogin !== true) {
// 处理 301 状态码:登录态失效,重试同一请求无意义,清除登录信息后直接拒绝
if (error.response?.status === 301 && config.params?.noLogin !== true) {
// 使用 store mutation 清除用户信息
const userStore = useUserStore();
userStore.handleLogout();
console.log(`301 状态码,清除登录信息后重试第 ${config.retryCount}`);
config.retryCount = 3;
console.log('301 状态码:登录态已失效,已清除登录信息,不再重试');
return Promise.reject(error);
}
// 检查是否还可以重试