import { urlCacheManager } from './urlCache.js' // 请求去重映射 const pendingRequests = new Map() /** * 带缓存的音乐图片URL获取函数 * @param {string} source - 音乐源 * @param {string} id - 图片ID * @param {string|number} size - 图片尺寸 * @param {boolean} skipCache - 是否跳过缓存 * @returns {Promise} 图片URL */ export async function getCachedMusicPicUrl(source, id, size = 300, skipCache = false) { if (!source || !id) return null // 强制使用 300 尺寸,不管传入什么参数 const sizeStr = '300' // 先检查缓存(除非明确跳过) if (!skipCache) { const cached = urlCacheManager.getCachedUrl(source, id, sizeStr) if (cached) { return cached } } // 检查是否有正在进行的相同请求(去重) const requestKey = `${source}+${id}+${sizeStr}` if (pendingRequests.has(requestKey)) { return pendingRequests.get(requestKey) } // 创建新的API请求 const apiUrl = `https://music-api.gdstudio.xyz/api.php?types=pic&source=${encodeURIComponent(source)}&id=${encodeURIComponent(id)}&size=${encodeURIComponent(sizeStr)}` const requestPromise = fetch(apiUrl) .then(response => { if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`) } return response.json() }) .then(data => { let resultUrl = null if (data && data.url) { resultUrl = data.url // 将结果存入缓存 urlCacheManager.setCachedUrl(source, id, sizeStr, resultUrl) } return resultUrl }) .catch(error => { console.error('获取音乐图片失败:', error) return null }) .finally(() => { // 请求完成后从去重映射中移除 pendingRequests.delete(requestKey) }) // 将请求添加到去重映射 pendingRequests.set(requestKey, requestPromise) return requestPromise } /** * 带延时的音乐图片URL获取函数(懒加载专用) * 第一个请求立即执行,后续请求间隔200ms * @param {string} source - 音乐源 * @param {string} id - 图片ID * @param {string|number} size - 图片尺寸 * @returns {Promise} 图片URL */ export async function getCachedMusicPicUrlWithDelay(source, id, size = 300) { if (!source || !id) return null // 强制使用 300 尺寸,不管传入什么参数 const sizeStr = '300' // 先检查缓存 const cached = urlCacheManager.getCachedUrl(source, id, sizeStr) if (cached) { // 缓存命中,直接返回,无需延时 return cached } // 缓存未命中,需要请求API // 如果不是第一个请求,添加200ms延时 const requestKey = `${source}+${id}+${sizeStr}` if (pendingRequests.size > 0) { await new Promise(resolve => setTimeout(resolve, 200)) } return getCachedMusicPicUrl(source, id, 300) } /** * 批量预加载音乐图片URL * @param {Array} songs - 歌曲数组,每个歌曲应包含source和pic_id字段 * @param {string|number} size - 图片尺寸 * @param {number} concurrency - 并发数量 */ export async function preloadMusicPics(songs, size = 300, concurrency = 1) { if (!Array.isArray(songs) || songs.length === 0) return // 强制使用 300 尺寸,不管传入什么参数 const sizeStr = '300' // 过滤出需要加载的歌曲(未缓存的) const toLoad = songs.filter(song => { if (!song.source || !song.pic_id) return false return !urlCacheManager.getCachedUrl(song.source, song.pic_id, sizeStr) }) if (toLoad.length === 0) return // 分批并发加载 for (let i = 0; i < toLoad.length; i += concurrency) { const batch = toLoad.slice(i, i + concurrency) const promises = batch.map(song => getCachedMusicPicUrl(song.source, song.pic_id, 300).catch(error => { console.warn(`预加载 ${song.name} 图片失败:`, error) return null }) ) await Promise.all(promises) // 批次间间隔200ms if (i + concurrency < toLoad.length) { await new Promise(resolve => setTimeout(resolve, 200)) } } } /** * 清除指定歌曲的图片缓存 * @param {string} source - 音乐源 * @param {string} id - 图片ID */ export function clearMusicPicCache(source, id) { if (!source || !id) return // 清除所有尺寸的缓存 const commonSizes = ['300', '500', '800', '1200'] commonSizes.forEach(size => { const key = urlCacheManager.getCacheKey(source, id, size) urlCacheManager.cache.delete(key) }) urlCacheManager.saveToStorage() } /** * 获取缓存统计信息 */ export function getCacheStats() { return urlCacheManager.getStats() } /** * 清空所有缓存 */ export function clearAllCache() { urlCacheManager.clear() } export default { getCachedMusicPicUrl, getCachedMusicPicUrlWithDelay, preloadMusicPics, clearMusicPicCache, getCacheStats, clearAllCache }