From e2fe2058e911dbca856634595411c697bec20fbd Mon Sep 17 00:00:00 2001 From: mtvpls Date: Mon, 2 Feb 2026 01:15:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=9F=B3=E4=B9=90=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/music/route.ts | 15 ++- src/app/music/page.tsx | 261 ++++++++++++++++++++----------------- 2 files changed, 155 insertions(+), 121 deletions(-) diff --git a/src/app/api/music/route.ts b/src/app/api/music/route.ts index 18f5035..b496a2e 100644 --- a/src/app/api/music/route.ts +++ b/src/app/api/music/route.ts @@ -388,6 +388,16 @@ export async function POST(request: NextRequest) { ); } + // 添加缓存支持 + const qualityKey = quality || '320k'; + const idsKey = Array.isArray(ids) ? ids.join(',') : ids; + const cacheKey = `parse-${platform}-${idsKey}-${qualityKey}`; + const cached = serverCache.proxyRequests.get(cacheKey); + + if (cached && Date.now() - cached.timestamp < serverCache.CACHE_DURATION) { + return NextResponse.json(cached.data); + } + try { const response = await proxyRequest(`${baseUrl}/v1/parse`, { method: 'POST', @@ -398,7 +408,7 @@ export async function POST(request: NextRequest) { body: JSON.stringify({ platform, ids, - quality: quality || '320k', + quality: qualityKey, }), }); @@ -414,6 +424,9 @@ export async function POST(request: NextRequest) { }); } + // 缓存成功的解析结果 + serverCache.proxyRequests.set(cacheKey, { data, timestamp: Date.now() }); + return NextResponse.json(data); } catch (error) { console.error('解析歌曲失败:', error); diff --git a/src/app/music/page.tsx b/src/app/music/page.tsx index e6fbc16..a3a1d9d 100644 --- a/src/app/music/page.tsx +++ b/src/app/music/page.tsx @@ -40,6 +40,18 @@ interface Playlist { updateFrequency?: string; } +interface DbRecord { + platform: 'netease' | 'qq' | 'kuwo'; + id: string; + play_time: number; + duration: number; + save_time: number; + name: string; + artist: string; + album?: string; + pic?: string; +} + export default function MusicPage() { const router = useRouter(); const [currentSource, setCurrentSource] = useState<'netease' | 'qq' | 'kuwo'>('netease'); @@ -115,63 +127,119 @@ export default function MusicPage() { localStorage.setItem('musicPlayState', JSON.stringify(playState)); }; - // 从 localStorage 恢复播放状态 + // 从 localStorage 恢复播放状态(已废弃,现在统一使用数据库) const restorePlayState = async () => { - try { - const saved = localStorage.getItem('musicPlayState'); - if (!saved) return; + // 此函数已不再使用,所有状态恢复都在 initializePlayState 中完成 + }; - const playState = JSON.parse(saved); + // 页面加载时恢复播放状态和数据库记录 + useEffect(() => { + const initializePlayState = async () => { + try { + console.log('=== 开始同步加载数据库记录 ==='); - setCurrentSong(playState.currentSong); - setCurrentSongIndex(playState.currentSongIndex); - setSongs(playState.songs || []); - setCurrentPlaylistTitle(playState.currentPlaylistTitle || ''); - setCurrentSource(playState.currentSource || 'netease'); - setCurrentView(playState.currentView || 'playlists'); - setQuality(playState.quality || '320k'); - setPlayMode(playState.playMode || 'loop'); - setVolume(playState.volume || 100); - setLyrics(playState.lyrics || []); - setPlayRecords(playState.playRecords || []); - setPlaylist(playState.playlist || []); // 恢复播放列表 + // 1. 直接从 API 同步加载播放记录(阻塞等待,不使用缓存) + const response = await fetch('/api/music/playrecords'); + const dbRecords = await response.json(); - // 恢复 playlistIndex,如果没有则设置为 -1 - const restoredIndex = playState.playlistIndex ?? -1; - setPlaylistIndex(restoredIndex); + console.log('=== 数据库原始数据 ==='); + console.log('dbRecords:', dbRecords); - // 保存需要恢复的时间点 - restoredTimeRef.current = playState.currentTime || 0; + // 将数据库记录转换为前端格式 + const records: PlayRecord[] = []; + const songs: Song[] = []; - if (playState.currentSong) { - setShowPlayer(true); + Object.entries(dbRecords).forEach(([key, record]) => { + const dbRecord = record as DbRecord; + console.log('处理记录:', key, dbRecord); + records.push({ + platform: dbRecord.platform, + id: dbRecord.id, + playTime: dbRecord.play_time, + duration: dbRecord.duration, + timestamp: dbRecord.save_time, + }); - // 记录歌曲开始播放的时间(恢复时也需要设置) - songStartTimeRef.current = Date.now(); + songs.push({ + id: dbRecord.id, + name: dbRecord.name, + artist: dbRecord.artist, + album: dbRecord.album, + pic: dbRecord.pic, + platform: dbRecord.platform, + }); + }); - // 获取歌曲的平台信息 - const platform = playState.currentSong.platform || playState.currentSource || 'netease'; + // 按 save_time 倒序排序 + const sortedIndices = records + .map((record, index) => ({ record, index })) + .sort((a, b) => b.record.timestamp - a.record.timestamp); - // 重新解析歌曲获取新的播放链接 - try { - const response = await fetch('/api/music', { + const sortedRecords = sortedIndices.map(item => records[item.index]); + const sortedSongs = sortedIndices.map(item => songs[item.index]); + + // 2. 更新播放列表 + if (sortedRecords.length > 0) { + setPlayRecords(sortedRecords); + setPlaylist(sortedSongs); + } + + // 3. 获取 localStorage 配置(只获取配置,不获取歌曲信息) + const savedPlayState = localStorage.getItem('musicPlayState'); + const playState = savedPlayState ? JSON.parse(savedPlayState) : {}; + + // 恢复配置状态(不包括歌曲) + setSongs(playState.songs || []); + setCurrentPlaylistTitle(playState.currentPlaylistTitle || ''); + setCurrentSource(playState.currentSource || 'netease'); + setCurrentView(playState.currentView || 'playlists'); + setQuality(playState.quality || '320k'); + setPlayMode(playState.playMode || 'loop'); + setVolume(playState.volume || 100); + + // 4. 使用数据库的最新记录(歌曲和进度都从数据库获取) + if (sortedRecords.length > 0) { + const latestDbRecord = sortedRecords[0]; + const latestDbSong = sortedSongs[0]; + + console.log('=== 从数据库加载歌曲 ==='); + console.log('数据库最新歌曲:', latestDbSong); + console.log('数据库播放进度:', latestDbRecord.playTime); + console.log('数据库保存时间:', new Date(latestDbRecord.timestamp).toLocaleString()); + + // 使用数据库的歌曲信息 + setCurrentSong(latestDbSong); + setPlaylistIndex(0); + setShowPlayer(true); + + // 从数据库恢复播放进度 + const dbPlayTime = latestDbRecord.playTime || 0; + songStartTimeRef.current = Date.now(); + + // 5. 同步解析歌曲获取播放链接(阻塞等待) + const platform = latestDbSong.platform || 'netease'; + + console.log('开始解析歌曲:', platform, latestDbSong.id); + + const parseResponse = await fetch('/api/music', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'parse', platform: platform, - ids: playState.currentSong.id, + ids: latestDbSong.id, quality: playState.quality || '320k', }), }); - const data = await response.json(); + const data = await parseResponse.json(); + + console.log('解析结果:', data); if (data.code === 0 && data.data?.data && data.data.data.length > 0) { const songData = data.data.data[0]; if (songData.url && songData.success) { - // 对于酷我音乐,使用代理 let playUrl = songData.url; if (platform === 'kuwo') { playUrl = `/api/music/proxy?url=${encodeURIComponent(songData.url)}`; @@ -179,87 +247,27 @@ export default function MusicPage() { setCurrentSongUrl(songData.url); - // 延迟设置音频源,等待 audio 元素加载 - setTimeout(() => { - if (audioRef.current) { - audioRef.current.src = playUrl; + if (songData.lyrics) { + const parsedLyrics = parseLyric(songData.lyrics); + setLyrics(parsedLyrics); + } - // 监听多个事件以确保进度恢复 - const restoreTime = () => { - if (audioRef.current && restoredTimeRef.current > 0) { - audioRef.current.currentTime = restoredTimeRef.current; - restoredTimeRef.current = 0; - } - }; + console.log('设置音频源:', playUrl); + console.log('恢复播放进度:', dbPlayTime); - // 监听加载完成事件 - audioRef.current.addEventListener('loadedmetadata', restoreTime, { once: true }); - audioRef.current.addEventListener('canplay', restoreTime, { once: true }); + // 6. 等待所有数据准备好后,再设置音频源和进度 + if (audioRef.current) { + audioRef.current.src = playUrl; - // 调用 load() 触发音频加载 - audioRef.current.load(); - } - }, 100); - } - } - } catch (error) { - console.error('重新解析歌曲失败:', error); - } - } - } catch (error) { - console.error('恢复播放状态失败:', error); - } - }; + const restoreTime = () => { + if (audioRef.current && dbPlayTime > 0) { + audioRef.current.currentTime = dbPlayTime; + console.log('播放进度已恢复:', audioRef.current.currentTime); + } + }; - // 页面加载时恢复播放状态和数据库记录 - useEffect(() => { - const initializePlayState = async () => { - // 先恢复 localStorage 中的播放状态 - restorePlayState(); - - // 从数据库加载播放记录 - try { - const dbRecords = await getAllMusicPlayRecords(); - - // 将数据库记录转换为前端格式 - const records: PlayRecord[] = []; - const songs: Song[] = []; - - Object.entries(dbRecords).forEach(([key, record]) => { - records.push({ - platform: record.platform, - id: record.id, - playTime: record.play_time, - duration: record.duration, - timestamp: record.save_time, - }); - - songs.push({ - id: record.id, - name: record.name, - artist: record.artist, - album: record.album, - pic: record.pic, - platform: record.platform, // 添加平台信息 - }); - }); - - // 更新状态 - if (records.length > 0) { - setPlayRecords(records); - setPlaylist(songs); - - // 如果当前有正在播放的歌曲,找到它在记录中的索引 - const savedPlayState = localStorage.getItem('musicPlayState'); - if (savedPlayState) { - const playState = JSON.parse(savedPlayState); - if (playState.currentSong) { - const platform = playState.currentSong.platform || playState.currentSource || 'netease'; - const currentIndex = records.findIndex( - r => r.platform === platform && r.id === playState.currentSong.id - ); - if (currentIndex >= 0) { - setPlaylistIndex(currentIndex); + audioRef.current.addEventListener('loadedmetadata', restoreTime, { once: true }); + audioRef.current.load(); } } } @@ -380,7 +388,7 @@ export default function MusicPage() { setPlayRecords(prev => { const existingIndex = prev.findIndex(r => r.platform === record.platform && r.id === record.id); if (existingIndex >= 0) { - // 记录已存在,更新时间戳 + // 记录已存在,更新时间戳但不重置播放时间 const updated = [...prev]; updated[existingIndex] = { ...updated[existingIndex], @@ -544,8 +552,9 @@ export default function MusicPage() { // 上一曲 const playPrev = () => { // 优先从播放列表切换 - if (playlist.length > 0 && playlistIndex > 0) { - const prevIndex = playlistIndex - 1; + if (playlist.length > 0) { + // 如果已经是第一首,循环到最后一首 + const prevIndex = playlistIndex > 0 ? playlistIndex - 1 : playlist.length - 1; setPlaylistIndex(prevIndex); playSong(playlist[prevIndex], -1); } else if (currentSongIndex > 0) { @@ -556,8 +565,9 @@ export default function MusicPage() { // 下一曲 const playNext = () => { // 优先从播放列表切换 - if (playlist.length > 0 && playlistIndex < playlist.length - 1) { - const nextIndex = playlistIndex + 1; + if (playlist.length > 0) { + // 如果已经是最后一首,循环到第一首 + const nextIndex = playlistIndex < playlist.length - 1 ? playlistIndex + 1 : 0; setPlaylistIndex(nextIndex); playSong(playlist[nextIndex], -1); } else if (currentSongIndex < songs.length - 1) { @@ -732,8 +742,15 @@ export default function MusicPage() { audio.currentTime = 0; audio.play(); } else if (playMode === 'random') { - const randomIndex = Math.floor(Math.random() * songs.length); - playSong(songs[randomIndex], randomIndex); + // 优先从播放列表中随机选择 + if (playlist.length > 0) { + const randomIndex = Math.floor(Math.random() * playlist.length); + setPlaylistIndex(randomIndex); + playSong(playlist[randomIndex], -1); + } else if (songs.length > 0) { + const randomIndex = Math.floor(Math.random() * songs.length); + playSong(songs[randomIndex], randomIndex); + } } else { playNext(); } @@ -1290,7 +1307,11 @@ export default function MusicPage() { )} {/* 音量控制 */} -
+
setShowVolumeSlider(true)} + onMouseLeave={() => setShowVolumeSlider(false)} + > {/* 垂直音量条 - 桌面悬浮/移动端点击 */}
e.stopPropagation()} >