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,