From 4ae90819147b7529cc6ac00ff4aa791f86e72a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B2=E6=82=A6?= Date: Thu, 8 Jan 2026 17:54:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=9F=B3=E4=B9=90=E5=B9=B3=E5=8F=B0):=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=AD=8C=E6=9B=B2=E5=8F=AF=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E6=80=A7=E6=A3=80=E6=9F=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用 check_music API 批量检查歌曲是否可播放,优化了歌曲锁定状态的判断逻辑。当 API 检查失败时回退到基于 privileges 字段的判断,并添加了请求批处理和延迟以避免过快请求。 --- .../service/music_platform/wycloud/index.js | 60 +++++++++++++++++-- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/backend/src/service/music_platform/wycloud/index.js b/backend/src/service/music_platform/wycloud/index.js index 7775296..9b03c5d 100644 --- a/backend/src/service/music_platform/wycloud/index.js +++ b/backend/src/service/music_platform/wycloud/index.js @@ -2,7 +2,7 @@ const logger = require('consola'); const { cloud, cloudsearch, cloud_match, song_detail, user_playlist, playlist_detail, user_account, playlist_track_all, - login_qr_check, login_qr_create, login_qr_key, + login_qr_check, login_qr_create, login_qr_key, check_music, } = require('NeteaseCloudMusicApi'); const fs = require('fs'); const path = require('path'); @@ -201,12 +201,61 @@ async function getSongsFromPlaylist(uid, source, playlistId) { songsMap[song.id] = song; }); - const isBlockedSong = (song, songInfo) => { + // 使用 check_music API 检查歌曲是否可播放 + // 注意:由于需要批量检查,这里优化为分批处理 + const checkSongsPlayable = async (songIds) => { + const batchSize = 50; // 每批检查50首 + const results = {}; + + for (let i = 0; i < songIds.length; i += batchSize) { + const batch = songIds.slice(i, i + batchSize); + const batchPromises = batch.map(async (songId) => { + try { + const response = await safeRequest(uid, check_music, { id: songId, br: 999000 }, false); + if (response && response.success === true) { + return { id: songId, playable: true }; + } else { + return { id: songId, playable: false }; + } + } catch (error) { + logger.warn(`check music failed for ${songId}: ${error.message}`); + // 如果检查失败,回退到基于 privileges 字段的判断 + return { id: songId, playable: null }; // null 表示未知,需要回退判断 + } + }); + + const batchResults = await Promise.all(batchPromises); + batchResults.forEach(result => { + results[result.id] = result.playable; + }); + + // 添加延迟,避免请求过快 + if (i + batchSize < songIds.length) { + await new Promise(resolve => setTimeout(resolve, 100)); + } + } + + return results; + }; + + // 检查所有歌曲的可播放性 + const songIds = songsResponse.songs.map(song => song.id); + logger.info(`[getSongsFromPlaylist] Checking playable status for ${songIds.length} songs...`); + const playableResults = await checkSongsPlayable(songIds); + logger.info(`[getSongsFromPlaylist] Checked ${Object.keys(playableResults).length} songs`); + + const isBlockedSong = (song, songInfo, playable) => { // the song has been added to cloud if the pc field is present if (songInfo.pc) { return false; - } - + } + + // 优先使用 check_music API 的结果 + if (playable !== null) { + return !playable; // playable === true 表示不锁定 + } + + // 回退到基于 privileges 字段的判断 // 收费歌曲 if (song.fee === 1) { if (song.realPayed === 1 || song.payed === 1) { @@ -229,7 +278,8 @@ async function getSongsFromPlaylist(uid, source, playlistId) { return; } - const isBlocked = isBlockedSong(song, songInfo); + const playable = playableResults[song.id] ?? null; + const isBlocked = isBlockedSong(song, songInfo, playable); const isCloud = !!songInfo.pc; info.songs.push({ songId: songInfo.id,