music / src /utils /musicPicCache.js
ahutchen's picture
perf: 优化专辑封面图片尺寸
3b9298b
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<string>} 图片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<string>} 图片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
}