From 659c9f9a4cc1a2b026f47b5696c4c32e696dfacb Mon Sep 17 00:00:00 2001 From: alger Date: Sun, 14 Sep 2025 01:03:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/api/music.ts | 190 +-------- src/renderer/api/musicParser.ts | 632 ++++++++++++++++++++++++++++ src/renderer/hooks/MusicHook.ts | 16 +- src/renderer/hooks/MusicListHook.ts | 8 +- 4 files changed, 654 insertions(+), 192 deletions(-) create mode 100644 src/renderer/api/musicParser.ts diff --git a/src/renderer/api/music.ts b/src/renderer/api/music.ts index e92029e..edabfa8 100644 --- a/src/renderer/api/music.ts +++ b/src/renderer/api/music.ts @@ -1,17 +1,10 @@ -import { cloneDeep } from 'lodash'; - import { musicDB } from '@/hooks/MusicHook'; import { useSettingsStore, useUserStore } from '@/store'; import type { ILyric } from '@/types/lyric'; import type { SongResult } from '@/types/music'; -import { isElectron } from '@/utils'; import request from '@/utils/request'; -import requestMusic from '@/utils/request_music'; -import { searchAndGetBilibiliAudioUrl } from './bilibili'; -import type { ParsedMusicResult } from './gdmusic'; -import { parseFromGDMusic } from './gdmusic'; -import { parseFromCustomApi } from './parseFromCustomApi'; +import { MusicParser, type MusicParseResult } from './musicParser'; const { addData, getData, deleteData } = musicDB; @@ -89,188 +82,17 @@ export const getMusicLrc = async (id: number) => { } }; -/** - * 从Bilibili获取音频URL - * @param data 歌曲数据 - * @returns 解析结果 - */ -const getBilibiliAudio = async (data: SongResult) => { - const songName = data?.name || ''; - const artistName = - Array.isArray(data?.ar) && data.ar.length > 0 && data.ar[0]?.name ? data.ar[0].name : ''; - const albumName = data?.al && typeof data.al === 'object' && data.al?.name ? data.al.name : ''; - - const searchQuery = [songName, artistName, albumName].filter(Boolean).join(' ').trim(); - console.log('开始搜索bilibili音频:', searchQuery); - - const url = await searchAndGetBilibiliAudioUrl(searchQuery); - return { - data: { - code: 200, - message: 'success', - data: { url } - } - }; -}; - -/** - * 从GD音乐台获取音频URL - * @param id 歌曲ID - * @param data 歌曲数据 - * @returns 解析结果,失败时返回null - */ -const getGDMusicAudio = async (id: number, data: SongResult): Promise => { - // <-- 在这里明确声明返回类型 - try { - const gdResult = await parseFromGDMusic(id, data, '999'); - if (gdResult) { - return gdResult; - } - } catch (error) { - console.error('GD音乐台解析失败:', error); - } - return null; -}; - -/** - * 使用unblockMusic解析音频URL - * @param id 歌曲ID - * @param data 歌曲数据 - * @param sources 音源列表 - * @returns 解析结果 - */ -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)); -}; - /** * 获取解析后的音乐URL * @param id 歌曲ID * @param data 歌曲数据 * @returns 解析结果 */ -export const getParsingMusicUrl = async (id: number, data: SongResult) => { - try { - if (isElectron) { - let musicSources: any[] = []; - let quality: string = 'higher'; - try { - const settingStore = useSettingsStore(); - const enableMusicUnblock = settingStore?.setData?.enableMusicUnblock; - - // 如果禁用了音乐解析功能,则直接返回空结果 - if (!enableMusicUnblock) { - return Promise.resolve({ data: { code: 404, message: '音乐解析功能已禁用' } }); - } - - // 1. 确定使用的音源列表(自定义或全局) - const songId = String(id); - const savedSourceStr = (() => { - try { - return localStorage.getItem(`song_source_${songId}`); - } catch (e) { - console.warn('读取本地存储失败,忽略自定义音源', e); - return null; - } - })(); - - if (savedSourceStr) { - try { - musicSources = JSON.parse(savedSourceStr); - console.log(`使用歌曲 ${id} 自定义音源:`, musicSources); - } catch (e) { - console.error('解析音源设置失败,回退到默认全局设置', e); - musicSources = settingStore?.setData?.enabledMusicSources || []; - } - } else { - // 使用全局音源设置 - musicSources = settingStore?.setData?.enabledMusicSources || []; - console.log(`使用全局音源设置:`, musicSources); - } - - quality = settingStore?.setData?.musicQuality || 'higher'; - } catch (e) { - console.error('读取设置失败,使用默认配置', e); - musicSources = []; - quality = 'higher'; - } - - // 优先级 1: 自定义 API - try { - const hasCustom = Array.isArray(musicSources) && musicSources.includes('custom'); - const customEnabled = (() => { - try { - const st = useSettingsStore(); - return Boolean(st?.setData?.customApiPlugin); - } catch { - return false; - } - })(); - if (hasCustom && customEnabled) { - console.log('尝试使用 自定义API 解析...'); - const customResult = await parseFromCustomApi(id, data, quality); - if (customResult) { - return customResult; // 成功则直接返回 - } - console.log('自定义API解析失败,继续尝试其他音源...'); - } - } catch (e) { - console.error('自定义API解析发生异常,继续尝试其他音源', e); - } - - // 优先级 2: Bilibili - try { - if (Array.isArray(musicSources) && musicSources.includes('bilibili')) { - console.log('尝试使用 Bilibili 解析...'); - const bilibiliResult = await getBilibiliAudio(data); - if (bilibiliResult?.data?.data?.url) { - return bilibiliResult; - } - console.log('Bilibili解析失败,继续尝试其他音源...'); - } - } catch (e) { - console.error('Bilibili解析发生异常,继续尝试其他音源', e); - } - - // 优先级 3: GD 音乐台 - try { - if (Array.isArray(musicSources) && musicSources.includes('gdmusic')) { - console.log('尝试使用 GD音乐台 解析...'); - const gdResult = await getGDMusicAudio(id, data); - if (gdResult) { - return gdResult; - } - console.log('GD音乐台解析失败,继续尝试其他音源...'); - } - } catch (e) { - console.error('GD音乐台解析发生异常,继续尝试其他音源', e); - } - - // 优先级 4: UnblockMusic (migu, kugou, pyncmd) - try { - const unblockSources = (Array.isArray(musicSources) ? musicSources : []).filter( - (source) => !['custom', 'bilibili', 'gdmusic'].includes(source) - ); - if (unblockSources.length > 0) { - console.log('尝试使用 UnblockMusic 解析:', unblockSources); - // 捕获内部可能的异常 - return await getUnblockMusicAudio(id, data, unblockSources); - } else { - console.warn('UnblockMusic API 不可用,跳过此解析方式'); - } - } catch (e) { - console.error('UnblockMusic 解析发生异常,继续后备方案', e); - } - } - } catch (e) { - console.error('getParsingMusicUrl 执行异常,将使用后备方案:', e); - } - - // 后备方案:使用API请求 - console.log('无可用音源或不在Electron环境中,使用API请求'); - return requestMusic.get('/music', { params: { id } }); +export const getParsingMusicUrl = async ( + id: number, + data: SongResult +): Promise => { + return await MusicParser.parseMusic(id, data); }; // 收藏歌曲 diff --git a/src/renderer/api/musicParser.ts b/src/renderer/api/musicParser.ts new file mode 100644 index 0000000..d78f9f1 --- /dev/null +++ b/src/renderer/api/musicParser.ts @@ -0,0 +1,632 @@ +import { cloneDeep } from 'lodash'; + +import { musicDB } from '@/hooks/MusicHook'; +import { useSettingsStore } from '@/store'; +import type { SongResult } from '@/types/music'; +import { isElectron } from '@/utils'; +import requestMusic from '@/utils/request_music'; + +import { searchAndGetBilibiliAudioUrl } from './bilibili'; +import type { ParsedMusicResult } from './gdmusic'; +import { parseFromGDMusic } from './gdmusic'; +import { parseFromCustomApi } from './parseFromCustomApi'; + +const { saveData, getData, deleteData } = musicDB; + +/** + * 音乐解析结果接口 + */ +export interface MusicParseResult { + data: { + code: number; + message: string; + data?: { + url: string; + [key: string]: any; + }; + }; +} + +/** + * 缓存配置 + */ +const CACHE_CONFIG = { + // 音乐URL缓存时间:30分钟 + MUSIC_URL_CACHE_TIME: 30 * 60 * 1000, + // 失败缓存时间:5分钟 + FAILED_CACHE_TIME: 5 * 60 * 1000, + // 重试配置 + MAX_RETRY_COUNT: 2, + RETRY_DELAY: 1000 +}; + +/** + * 缓存管理器 + */ +class CacheManager { + /** + * 获取缓存的音乐URL + */ + static async getCachedMusicUrl(id: number): Promise { + try { + const cached = await getData('music_url_cache', id); + if ( + cached?.createTime && + Date.now() - cached.createTime < CACHE_CONFIG.MUSIC_URL_CACHE_TIME + ) { + console.log(`使用缓存的音乐URL: ${id}`); + return cached.data; + } + // 清理过期缓存 + if (cached) { + await deleteData('music_url_cache', id); + } + } catch (error) { + console.warn('获取缓存失败:', error); + } + return null; + } + + /** + * 缓存音乐URL + */ + static async setCachedMusicUrl(id: number, result: MusicParseResult): Promise { + try { + await saveData('music_url_cache', { + id, + data: result, + createTime: Date.now() + }); + console.log(`缓存音乐URL成功: ${id}`); + } catch (error) { + console.error('缓存音乐URL失败:', error); + } + } + + /** + * 检查是否在失败缓存期内 + */ + static async isInFailedCache(id: number, strategyName: string): Promise { + try { + const cacheKey = `${id}_${strategyName}`; + const cached = await getData('music_failed_cache', cacheKey); + if (cached?.createTime && Date.now() - cached.createTime < CACHE_CONFIG.FAILED_CACHE_TIME) { + console.log(`策略 ${strategyName} 在失败缓存期内,跳过`); + return true; + } + // 清理过期缓存 + if (cached) { + await deleteData('music_failed_cache', cacheKey); + } + } catch (error) { + console.warn('检查失败缓存失败:', error); + } + return false; + } + + /** + * 添加失败缓存 + */ + static async addFailedCache(id: number, strategyName: string): Promise { + try { + const cacheKey = `${id}_${strategyName}`; + await saveData('music_failed_cache', { + id: cacheKey, + createTime: Date.now() + }); + console.log(`添加失败缓存成功: ${strategyName}`); + } catch (error) { + console.error('添加失败缓存失败:', error); + } + } +} + +/** + * 重试工具 + */ +class RetryHelper { + /** + * 带重试的异步执行 + */ + static async withRetry( + fn: () => Promise, + maxRetries = CACHE_CONFIG.MAX_RETRY_COUNT, + delay = CACHE_CONFIG.RETRY_DELAY + ): Promise { + let lastError: Error; + + for (let i = 0; i <= maxRetries; i++) { + try { + return await fn(); + } catch (error) { + lastError = error as Error; + if (i < maxRetries) { + console.log(`重试第 ${i + 1} 次,延迟 ${delay}ms`); + await new Promise((resolve) => setTimeout(resolve, delay)); + delay *= 2; // 指数退避 + } + } + } + + throw lastError!; + } +} + +/** + * 从Bilibili获取音频URL + * @param data 歌曲数据 + * @returns 解析结果 + */ +const getBilibiliAudio = async (data: SongResult) => { + const songName = data?.name || ''; + const artistName = + Array.isArray(data?.ar) && data.ar.length > 0 && data.ar[0]?.name ? data.ar[0].name : ''; + const albumName = data?.al && typeof data.al === 'object' && data.al?.name ? data.al.name : ''; + + const searchQuery = [songName, artistName, albumName].filter(Boolean).join(' ').trim(); + console.log('开始搜索bilibili音频:', searchQuery); + + const url = await searchAndGetBilibiliAudioUrl(searchQuery); + return { + data: { + code: 200, + message: 'success', + data: { url } + } + }; +}; + +/** + * 从GD音乐台获取音频URL + * @param id 歌曲ID + * @param data 歌曲数据 + * @returns 解析结果,失败时返回null + */ +const getGDMusicAudio = async (id: number, data: SongResult): Promise => { + try { + const gdResult = await parseFromGDMusic(id, data, '999'); + if (gdResult) { + return gdResult; + } + } catch (error) { + console.error('GD音乐台解析失败:', error); + } + return null; +}; + +/** + * 使用unblockMusic解析音频URL + * @param id 歌曲ID + * @param data 歌曲数据 + * @param sources 音源列表 + * @returns 解析结果 + */ +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)); +}; + +/** + * 统一的解析结果适配器 + */ +const adaptParseResult = (result: any): MusicParseResult | null => { + if (!result) return null; + + // 如果已经是标准格式 + if (result.data?.code !== undefined && result.data?.message !== undefined) { + return result; + } + + // 适配GD音乐台的返回格式 + if (result.data?.data?.url) { + return { + data: { + code: 200, + message: 'success', + data: { + url: result.data.data.url, + ...result.data.data + } + } + }; + } + + // 适配其他格式 + if (result.url) { + return { + data: { + code: 200, + message: 'success', + data: { + url: result.url, + ...result + } + } + }; + } + + return null; +}; + +/** + * 音源解析策略接口 + */ +interface MusicSourceStrategy { + name: string; + priority: number; + canHandle: (sources: string[], settingsStore?: any) => boolean; + parse: ( + id: number, + data: SongResult, + quality?: string, + sources?: string[] + ) => Promise; +} + +/** + * 自定义API解析策略 + */ +class CustomApiStrategy implements MusicSourceStrategy { + name = 'custom'; + priority = 1; + + canHandle(sources: string[], settingsStore?: any): boolean { + return sources.includes('custom') && Boolean(settingsStore?.setData?.customApiPlugin); + } + + async parse(id: number, data: SongResult, quality = 'higher'): Promise { + // 检查失败缓存 + if (await CacheManager.isInFailedCache(id, this.name)) { + return null; + } + + try { + console.log('尝试使用自定义API解析...'); + const result = await RetryHelper.withRetry(async () => { + return await parseFromCustomApi(id, data, quality); + }); + + const adaptedResult = adaptParseResult(result); + if (adaptedResult?.data?.data?.url) { + console.log('自定义API解析成功'); + return adaptedResult; + } + + // 解析失败,添加失败缓存 + await CacheManager.addFailedCache(id, this.name); + return null; + } catch (error) { + console.error('自定义API解析失败:', error); + await CacheManager.addFailedCache(id, this.name); + return null; + } + } +} + +/** + * Bilibili解析策略 + */ +class BilibiliStrategy implements MusicSourceStrategy { + name = 'bilibili'; + priority = 2; + + canHandle(sources: string[]): boolean { + return sources.includes('bilibili'); + } + + async parse(id: number, data: SongResult): Promise { + // 检查失败缓存 + if (await CacheManager.isInFailedCache(id, this.name)) { + return null; + } + + try { + console.log('尝试使用Bilibili解析...'); + const result = await RetryHelper.withRetry(async () => { + return await getBilibiliAudio(data); + }); + + const adaptedResult = adaptParseResult(result); + if (adaptedResult?.data?.data?.url) { + console.log('Bilibili解析成功'); + return adaptedResult; + } + + // 解析失败,添加失败缓存 + await CacheManager.addFailedCache(id, this.name); + return null; + } catch (error) { + console.error('Bilibili解析失败:', error); + await CacheManager.addFailedCache(id, this.name); + return null; + } + } +} + +/** + * GD音乐台解析策略 + */ +class GDMusicStrategy implements MusicSourceStrategy { + name = 'gdmusic'; + priority = 3; + + canHandle(sources: string[]): boolean { + return sources.includes('gdmusic'); + } + + async parse(id: number, data: SongResult): Promise { + // 检查失败缓存 + if (await CacheManager.isInFailedCache(id, this.name)) { + return null; + } + + try { + console.log('尝试使用GD音乐台解析...'); + const result = await RetryHelper.withRetry(async () => { + return await getGDMusicAudio(id, data); + }); + + const adaptedResult = adaptParseResult(result); + if (adaptedResult?.data?.data?.url) { + console.log('GD音乐台解析成功'); + return adaptedResult; + } + + // 解析失败,添加失败缓存 + await CacheManager.addFailedCache(id, this.name); + return null; + } catch (error) { + console.error('GD音乐台解析失败:', error); + await CacheManager.addFailedCache(id, this.name); + return null; + } + } +} + +/** + * UnblockMusic解析策略 + */ +class UnblockMusicStrategy implements MusicSourceStrategy { + name = 'unblockMusic'; + priority = 4; + + canHandle(sources: string[]): boolean { + const unblockSources = sources.filter( + (source) => !['custom', 'bilibili', 'gdmusic'].includes(source) + ); + return unblockSources.length > 0; + } + + async parse( + id: number, + data: SongResult, + _quality?: string, + sources?: string[] + ): Promise { + // 检查失败缓存 + if (await CacheManager.isInFailedCache(id, this.name)) { + return null; + } + + try { + const unblockSources = (sources || []).filter( + (source) => !['custom', 'bilibili', 'gdmusic'].includes(source) + ); + console.log('尝试使用UnblockMusic解析:', unblockSources); + + const result = await RetryHelper.withRetry(async () => { + return await getUnblockMusicAudio(id, data, unblockSources); + }); + + const adaptedResult = adaptParseResult(result); + if (adaptedResult?.data?.data?.url) { + console.log('UnblockMusic解析成功'); + return adaptedResult; + } + + // 解析失败,添加失败缓存 + await CacheManager.addFailedCache(id, this.name); + return null; + } catch (error) { + console.error('UnblockMusic解析失败:', error); + await CacheManager.addFailedCache(id, this.name); + return null; + } + } +} + +/** + * 音源策略工厂 + */ +class MusicSourceStrategyFactory { + private static strategies: MusicSourceStrategy[] = [ + new CustomApiStrategy(), + new BilibiliStrategy(), + new GDMusicStrategy(), + new UnblockMusicStrategy() + ]; + + /** + * 获取可用的解析策略 + * @param sources 音源列表 + * @param settingsStore 设置存储 + * @returns 排序后的可用策略列表 + */ + static getAvailableStrategies(sources: string[], settingsStore?: any): MusicSourceStrategy[] { + return this.strategies + .filter((strategy) => strategy.canHandle(sources, settingsStore)) + .sort((a, b) => a.priority - b.priority); + } +} + +/** + * 获取音源配置 + * @param id 歌曲ID + * @param settingsStore 设置存储 + * @returns 音源列表和音质设置 + */ +const getMusicConfig = (id: number, settingsStore?: any) => { + const songId = String(id); + let musicSources: string[] = []; + let quality = 'higher'; + + try { + // 尝试获取歌曲自定义音源 + const savedSourceStr = localStorage.getItem(`song_source_${songId}`); + if (savedSourceStr) { + try { + const customSources = JSON.parse(savedSourceStr); + if (Array.isArray(customSources)) { + musicSources = customSources; + console.log(`使用歌曲 ${id} 自定义音源:`, musicSources); + } + } catch (error) { + console.error('解析自定义音源设置失败:', error); + } + } + + // 如果没有自定义音源,使用全局设置 + if (musicSources.length === 0) { + musicSources = settingsStore?.setData?.enabledMusicSources || []; + console.log('使用全局音源设置:', musicSources); + } + + quality = settingsStore?.setData?.musicQuality || 'higher'; + } catch (error) { + console.error('读取音源配置失败,使用默认配置:', error); + musicSources = []; + quality = 'higher'; + } + + return { musicSources, quality }; +}; + +/** + * 音乐解析器主类 + */ +export class MusicParser { + /** + * 解析音乐URL + * @param id 歌曲ID + * @param data 歌曲数据 + * @returns 解析结果 + */ + static async parseMusic(id: number, data: SongResult): Promise { + const startTime = performance.now(); + + try { + // 非Electron环境直接使用API请求 + if (!isElectron) { + console.log('非Electron环境,使用API请求'); + return await requestMusic.get('/music', { params: { id } }); + } + + // 检查缓存 + console.log(`检查歌曲 ${id} 的缓存...`); + const cachedResult = await CacheManager.getCachedMusicUrl(id); + if (cachedResult) { + const endTime = performance.now(); + console.log(`✅ 命中缓存,歌曲 ${id},耗时: ${(endTime - startTime).toFixed(2)}ms`); + return cachedResult; + } + console.log(`❌ 未命中缓存,歌曲 ${id},开始解析...`); + + // 获取设置存储 + let settingsStore: any; + try { + settingsStore = useSettingsStore(); + } catch (error) { + console.error('无法获取设置存储,使用后备方案:', error); + return await requestMusic.get('/music', { params: { id } }); + } + + // 检查音乐解析功能是否启用 + if (!settingsStore?.setData?.enableMusicUnblock) { + console.log('音乐解析功能已禁用'); + return { + data: { + code: 404, + message: '音乐解析功能已禁用', + data: undefined + } + }; + } + + // 获取音源配置 + const { musicSources, quality } = getMusicConfig(id, settingsStore); + + if (musicSources.length === 0) { + console.warn('没有配置可用的音源,使用后备方案'); + return await requestMusic.get('/music', { params: { id } }); + } + + // 获取可用的解析策略 + const availableStrategies = MusicSourceStrategyFactory.getAvailableStrategies( + musicSources, + settingsStore + ); + + if (availableStrategies.length === 0) { + console.warn('没有可用的解析策略,使用后备方案'); + return await requestMusic.get('/music', { params: { id } }); + } + + console.log( + `开始解析歌曲 ${id},可用策略:`, + availableStrategies.map((s) => s.name) + ); + + // 按优先级依次尝试解析策略 + for (const strategy of availableStrategies) { + try { + const result = await strategy.parse(id, data, quality, musicSources); + if (result?.data?.data?.url) { + const endTime = performance.now(); + console.log( + `解析成功,使用策略: ${strategy.name},耗时: ${(endTime - startTime).toFixed(2)}ms` + ); + + // 缓存成功结果 + await CacheManager.setCachedMusicUrl(id, result); + + return result; + } + console.log(`策略 ${strategy.name} 解析失败,继续尝试下一个策略`); + } catch (error) { + console.error(`策略 ${strategy.name} 解析异常:`, error); + // 继续尝试下一个策略 + } + } + + console.warn('所有解析策略都失败了,使用后备方案'); + } catch (error) { + console.error('MusicParser.parseMusic 执行异常,使用后备方案:', error); + } + + // 后备方案:使用API请求 + try { + console.log('使用后备方案:API请求'); + const result = await requestMusic.get('/music', { params: { id } }); + + // 如果后备方案成功,也进行缓存 + if (result?.data?.data?.url) { + console.log('后备方案成功,缓存结果'); + await CacheManager.setCachedMusicUrl(id, result); + } + + return result; + } catch (apiError) { + console.error('API请求也失败了:', apiError); + const endTime = performance.now(); + console.log(`总耗时: ${(endTime - startTime).toFixed(2)}ms`); + return { + data: { + code: 500, + message: '所有解析方式都失败了', + data: undefined + } + }; + } + } +} diff --git a/src/renderer/hooks/MusicHook.ts b/src/renderer/hooks/MusicHook.ts index 43a0f2d..80c856d 100644 --- a/src/renderer/hooks/MusicHook.ts +++ b/src/renderer/hooks/MusicHook.ts @@ -55,11 +55,17 @@ export const textColors = ref(getTextColors()); export let playMusic: ComputedRef; export let artistList: ComputedRef; -export const musicDB = await useIndexedDB('musicDB', [ - { name: 'music', keyPath: 'id' }, - { name: 'music_lyric', keyPath: 'id' }, - { name: 'api_cache', keyPath: 'id' } -]); +export const musicDB = await useIndexedDB( + 'musicDB', + [ + { name: 'music', keyPath: 'id' }, + { name: 'music_lyric', keyPath: 'id' }, + { name: 'api_cache', keyPath: 'id' }, + { name: 'music_url_cache', keyPath: 'id' }, + { name: 'music_failed_cache', keyPath: 'id' } + ], + 2 +); // 键盘事件处理器,在初始化后设置 const setupKeyboardListeners = () => { diff --git a/src/renderer/hooks/MusicListHook.ts b/src/renderer/hooks/MusicListHook.ts index 56dcc70..c3901d9 100644 --- a/src/renderer/hooks/MusicListHook.ts +++ b/src/renderer/hooks/MusicListHook.ts @@ -19,13 +19,15 @@ export const getSongUrl = async (id: any, songData: any, isDownloaded: boolean = const { data } = await getMusicUrl(id, !unlimitedDownload); let url = ''; - let songDetail = null; + let songDetail: any = null; try { if (data.data[0].freeTrialInfo || !data.data[0].url) { const res = await getParsingMusicUrl(id, cloneDeep(songData)); - url = res.data.data.url; - songDetail = res.data.data; + if (res.data.data?.url) { + url = res.data.data.url; + songDetail = res.data.data; + } } else { songDetail = data.data[0] as any; }