From 6baa2c4868b1a8a8c8db148fcb9d6d9673645e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B2=E6=82=A6?= Date: Wed, 7 Jan 2026 17:20:28 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E9=9F=B3=E4=B9=90=E6=9C=8D=E5=8A=A1):?= =?UTF-8?q?=20=E9=87=8D=E6=9E=84=E7=BD=91=E6=98=93=E4=BA=91=E9=9F=B3?= =?UTF-8?q?=E4=B9=90=E5=8C=B9=E9=85=8D=E5=92=8C=E6=AD=8C=E6=9B=B2=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将网易云音乐API调用迁移至tunehub服务 - 新增歌曲信息解析和URL处理工具函数 - 改进歌曲匹配逻辑,支持更多来源的URL解析 - 优化下载流程,增加tunehub下载支持 --- .../find_the_best_match_from_wycloud.js | 41 +++++++-- .../sync_music/sync_single_song_with_url.js | 89 +++++++++++++++++-- 2 files changed, 113 insertions(+), 17 deletions(-) diff --git a/backend/src/service/search_songs/find_the_best_match_from_wycloud.js b/backend/src/service/search_songs/find_the_best_match_from_wycloud.js index 638aa97..a12d0ab 100644 --- a/backend/src/service/search_songs/find_the_best_match_from_wycloud.js +++ b/backend/src/service/search_songs/find_the_best_match_from_wycloud.js @@ -1,14 +1,27 @@ -const { searchSong, getSongInfo } = require('../music_platform/wycloud'); +const { getSongInfo, searchSongs } = require('../music_platform/tunehub'); const logger = require('consola'); +function splitArtists(artist) { + if (!artist) { + return []; + } + return artist + .split(/[、/]/) + .map(item => item.trim()) + .filter(Boolean); +} + module.exports = async function findTheBestMatchFromWyCloud(uid, {songName, artist, album, musicPlatformSongId} = {}) { if (musicPlatformSongId) { - const songInfo = await getSongInfo(uid, musicPlatformSongId); - + const songInfo = await getSongInfo('netease', musicPlatformSongId); if (songInfo) { - return songInfo; + return { + songId: musicPlatformSongId, + songName: songInfo.name || songName, + artists: splitArtists(songInfo.artist), + album: songInfo.album || album, + }; } - if (songName && artist) { return { songId: musicPlatformSongId, @@ -24,12 +37,24 @@ module.exports = async function findTheBestMatchFromWyCloud(uid, {songName, arti if (songName === "" || artist === "") { return null; } - const searchLists = await searchSong(uid, songName, artist); - logger.info('searchLists', searchLists); - if (searchLists === false) { + const searchData = await searchSongs(`${songName} ${artist}`, { + source: 'netease', + limit: 20, + aggregate: false, + }); + if (searchData === false || !searchData.results) { logger.warn(`search song failed, no matter, go on`); return null; } + const searchLists = searchData.results.map(item => { + return { + songId: item.id, + songName: item.name, + artists: splitArtists(item.artist), + album: item.album, + }; + }); + logger.info('searchLists', searchLists); let matchSongAndArtist = null; for (const searchItem of searchLists) { diff --git a/backend/src/service/sync_music/sync_single_song_with_url.js b/backend/src/service/sync_music/sync_single_song_with_url.js index 60d7d2f..9b3238b 100644 --- a/backend/src/service/sync_music/sync_single_song_with_url.js +++ b/backend/src/service/sync_music/sync_single_song_with_url.js @@ -1,4 +1,4 @@ -const { fetchWithUrl, getMetaWithUrl } = require('../media_fetcher'); +const { fetchWithUrl, getMetaWithUrl, downloadViaSourceUrl } = require('../media_fetcher'); const logger = require('consola'); const sleep = require('../../utils/sleep'); const findTheBestMatchFromWyCloud = require('../search_songs/find_the_best_match_from_wycloud'); @@ -11,6 +11,57 @@ const libPath = require('path'); const utilFs = require('../../utils/fs'); const { downloadFromLocalTmpPath } = require('./download_to_local'); const uploadWithRetryThenMatch = require('./upload_to_wycloud_disk_with_retry_then_match'); +const { getSongInfo, buildSongUrl } = require('../music_platform/tunehub'); + +function parseTunehubParams(url) { + try { + const parsed = new URL(url); + if (!parsed.hostname.includes('music-dl.sayqz.com')) { + return null; + } + const source = parsed.searchParams.get('source'); + const id = parsed.searchParams.get('id'); + if (!source || !id) { + return null; + } + return { source, id }; + } catch (err) { + return null; + } +} + +function parsePageUrlParams(url) { + if (!url) { + return null; + } + if (url.indexOf('music.163.com') >= 0) { + const match = url.match(/id=(\d+)/); + if (match && match[1]) { + return { source: 'netease', id: match[1] }; + } + } + if (url.indexOf('y.qq.com') >= 0) { + const match = url.match(/songDetail\/([A-Za-z0-9]+)/); + if (match && match[1]) { + return { source: 'qq', id: match[1] }; + } + } + return null; +} + +function buildSongInfoFromTunehub(source, info) { + return { + songName: info.name || '', + artist: info.artist || '', + album: info.album || '', + coverUrl: info.pic || '', + duration: 0, + fromMusicPlatform: true, + resourceForbidden: false, + source, + audios: info.url ? [{ url: info.url }] : [], + }; +} module.exports = async function syncSingleSongWithUrl(uid, url, { songName = "", @@ -19,11 +70,25 @@ module.exports = async function syncSingleSongWithUrl(uid, url, { songFromWyCloud = null } = {}, jobId = 0, jobType = JobType.SyncSongFromUrl, playlistName = "", collectRet) { // step 1. fetch song info - const songInfo = await getMetaWithUrl(url); - logger.info(songInfo); - if (songInfo === false || songInfo.isTrial) { - logger.error(`fetch song info failed or it's a trial song. ${JSON.stringify(songInfo)}`); - return false; + let songInfo = null; + let downloadUrl = url; + let usedTunehub = false; + const tunehubParams = parseTunehubParams(url) || parsePageUrlParams(url); + if (tunehubParams) { + const tunehubInfo = await getSongInfo(tunehubParams.source, tunehubParams.id); + if (tunehubInfo) { + usedTunehub = true; + songInfo = buildSongInfoFromTunehub(tunehubParams.source, tunehubInfo); + downloadUrl = tunehubInfo.url || buildSongUrl(tunehubParams.source, tunehubParams.id); + } + } + if (!songInfo) { + songInfo = await getMetaWithUrl(url); + logger.info(songInfo); + if (songInfo === false || songInfo.isTrial) { + logger.error(`fetch song info failed or it's a trial song. ${JSON.stringify(songInfo)}`); + return false; + } } await updateJobIfNeed(uid, jobId, songInfo, jobType); @@ -36,7 +101,7 @@ module.exports = async function syncSingleSongWithUrl(uid, url, { findSongName = songName; findArtist = artist; findAlbum = album; - } else if (songInfo.fromMusicPlatform) { + } else if (songInfo && songInfo.fromMusicPlatform) { findSongName = songInfo.songName; findArtist = songInfo.artist; findAlbum = songInfo.album; @@ -54,7 +119,13 @@ module.exports = async function syncSingleSongWithUrl(uid, url, { // step 3. download // should add meta tag if not matched song on wycloud - const path = await fetchWithUrl(url, {songName: songInfo.songName, addMediaTag: songFromWyCloud ? false : true}); + let path = false; + if (usedTunehub && downloadUrl) { + path = await downloadViaSourceUrl(downloadUrl); + } + if (path === false) { + path = await fetchWithUrl(url, {songName: songInfo.songName, addMediaTag: songFromWyCloud ? false : true}); + } if (path === false) { return false; } @@ -80,4 +151,4 @@ async function updateJobIfNeed(uid, jobId, songInfo, jobType) { desc: `歌曲: ${songInfo.songName}`, tip: "任务开始", }); -} \ No newline at end of file +}