From b5778f8a03993ffed3dbeda6839b7afb3133241a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B2=E6=82=A6?= Date: Thu, 8 Jan 2026 13:33:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=9F=B3=E4=B9=90=E9=93=BE=E6=8E=A5):=20?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=BD=BF=E7=94=A8=20TuneHub=20=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E9=9F=B3=E4=B9=90=E9=A1=B5=E9=9D=A2=E9=93=BE?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将网易云和QQ音乐的原始链接替换为 TuneHub 格式的统一链接 在同步任务中优先获取歌曲元数据,避免显示原始URL 增加日志输出以帮助调试链接解析过程 --- backend/src/handler/playlists.js | 2 +- backend/src/handler/songs.js | 5 ++-- backend/src/handler/sync_jobs.js | 24 +++++++++++++++--- backend/src/service/media_fetcher/index.js | 25 ++++++++++++++++--- .../service/music_platform/wycloud/index.js | 2 +- 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/backend/src/handler/playlists.js b/backend/src/handler/playlists.js index 0b8e018..33094f4 100644 --- a/backend/src/handler/playlists.js +++ b/backend/src/handler/playlists.js @@ -30,7 +30,7 @@ function normalizeTunehubPlaylist(playlistId, detail) { duration: 0, album: item.album || '', cover, - pageUrl: `https://music.163.com/song?id=${item.id}`, + pageUrl: `https://music-dl.sayqz.com/?source=netease&id=${item.id}`, playUrl: item.url || '', isBlocked, isCloud: false, diff --git a/backend/src/handler/songs.js b/backend/src/handler/songs.js index 4cd1301..4e5ad8d 100644 --- a/backend/src/handler/songs.js +++ b/backend/src/handler/songs.js @@ -7,11 +7,12 @@ function buildPageUrl(source, songId) { if (!source || !songId) { return ''; } + // 使用 TuneHub 格式的页面 URL if (source === 'netease') { - return `https://music.163.com/song?id=${songId}`; + return `https://music-dl.sayqz.com/?source=${source}&id=${songId}`; } if (source === 'qq') { - return `https://y.qq.com/n/ryqq/songDetail/${songId}`; + return `https://music-dl.sayqz.com/?source=${source}&id=${songId}`; } return ''; } diff --git a/backend/src/handler/sync_jobs.js b/backend/src/handler/sync_jobs.js index 55f07dd..11dcc10 100644 --- a/backend/src/handler/sync_jobs.js +++ b/backend/src/handler/sync_jobs.js @@ -64,15 +64,31 @@ async function createJob(req, res) { let meta = {}; const songId = request.urlJob && request.urlJob.meta.songId ? request.urlJob.meta.songId : ""; - + + // 先从 TuneHub 获取歌曲信息,避免显示原始 URL if (request.urlJob.meta && (request.urlJob.meta.songName !== "" && request.urlJob.meta.artist !== "")) { meta = { songName: request.urlJob.meta.songName, artist: request.urlJob.meta.artist, album : request.urlJob.meta.album ? request.urlJob.meta.album : "", }; + } else { + // 没有前端元数据,先获取歌曲信息 + logger.info(`[sync_jobs] No meta provided, fetching song info from URL: ${url}`); + const songInfo = await require('../service/media_fetcher').getMetaWithUrl(url); + if (songInfo && songInfo.songName) { + meta = { + songName: songInfo.songName, + artist: songInfo.artist || "", + album: songInfo.album || "", + }; + logger.info(`[sync_jobs] Fetched song info: ${meta.songName} - ${meta.artist}`); + } else { + logger.warn(`[sync_jobs] Failed to fetch song info from URL, will use URL as fallback`); + meta.songName = url; // 备用方案 + } } - + if (songId) { const songFromWyCloud = await findTheBestMatchFromWyCloud(req.account.uid, { songName: meta.songName, @@ -90,7 +106,7 @@ async function createJob(req, res) { } meta.songFromWyCloud = songFromWyCloud; } - + // create job const args = `${jobType}: {"url":${url}}`; if (await JobManager.findActiveJobByArgs(uid, args)) { @@ -108,7 +124,7 @@ async function createJob(req, res) { tip: `等待${operation}`, createdAt: Date.now() }); - + // async job syncSingleSongWithUrl(req.account.uid, url, meta, jobId, jobType).then(async ret => { await JobManager.updateJob(uid, jobId, { diff --git a/backend/src/service/media_fetcher/index.js b/backend/src/service/media_fetcher/index.js index 2f7d8d6..e190f6d 100644 --- a/backend/src/service/media_fetcher/index.js +++ b/backend/src/service/media_fetcher/index.js @@ -36,25 +36,36 @@ function parseTunehubParams(url) { function parsePageUrlParams(url) { if (!url) { + logger.error(`[parsePageUrlParams] URL is empty or null`); return null; } + logger.info(`[parsePageUrlParams] Parsing URL: ${url}`); + // 支持网易云音乐PC版和手机版 if (url.indexOf('music.163.com') >= 0 || url.indexOf('m.music.163.com') >= 0) { + logger.info(`[parsePageUrlParams] Detected Netease domain in URL`); const match = url.match(/id=(\d+)/); if (match && match[1]) { logger.info(`[parsePageUrlParams] Parsed Netease URL: ${url} -> netease:${match[1]}`); return { source: 'netease', id: match[1] }; + } else { + logger.error(`[parsePageUrlParams] Failed to extract song ID from Netease URL: ${url}`); } } + // 支持QQ音乐 if (url.indexOf('y.qq.com') >= 0) { + logger.info(`[parsePageUrlParams] Detected QQ domain in URL`); const match = url.match(/songDetail\/([A-Za-z0-9]+)/); if (match && match[1]) { logger.info(`[parsePageUrlParams] Parsed QQ URL: ${url} -> qq:${match[1]}`); return { source: 'qq', id: match[1] }; + } else { + logger.error(`[parsePageUrlParams] Failed to extract song ID from QQ URL: ${url}`); } } - logger.info(`[parsePageUrlParams] URL not recognized: ${url}`); + + logger.error(`[parsePageUrlParams] URL not recognized: ${url}`); return null; } @@ -178,7 +189,14 @@ async function fetchWithUrl(url, { async function getMetaWithUrl(url) { logger.info(`getMetaWithUrl from ${url}`); - const params = parseTunehubParams(url) || parsePageUrlParams(url); + // DEBUG: 添加详细日志 + logger.info(`[getMetaWithUrl] DEBUG: URL = ${url}`); + const tunehubParams = parseTunehubParams(url); + logger.info(`[getMetaWithUrl] DEBUG: tunehubParams = ${JSON.stringify(tunehubParams)}`); + const pageParams = parsePageUrlParams(url); + logger.info(`[getMetaWithUrl] DEBUG: pageParams = ${JSON.stringify(pageParams)}`); + + const params = tunehubParams || pageParams; if (params) { logger.info(`[getMetaWithUrl] Trying TuneHub first for ${params.source}:${params.id}`); const tunehubInfo = await getSongInfo(params.source, params.id); @@ -188,7 +206,8 @@ async function getMetaWithUrl(url) { } logger.warn(`[getMetaWithUrl] TuneHub failed for ${params.source}:${params.id}, will try media-get as fallback`); } else { - logger.info(`[getMetaWithUrl] URL not recognized as TuneHub-supported format, trying media-get`); + logger.error(`[getMetaWithUrl] ERROR: URL not recognized as TuneHub-supported format: ${url}`); + return false; // 直接返回失败,不再调用 media-get } // Fallback to media-get only if TuneHub fails or URL format not supported diff --git a/backend/src/service/music_platform/wycloud/index.js b/backend/src/service/music_platform/wycloud/index.js index c77793c..7775296 100644 --- a/backend/src/service/music_platform/wycloud/index.js +++ b/backend/src/service/music_platform/wycloud/index.js @@ -239,7 +239,7 @@ async function getSongsFromPlaylist(uid, source, playlistId) { duration: songInfo.dt / 1000, album: songInfo.al.name, cover: songInfo.al.picUrl, - pageUrl: `https://music.163.com/song?id=${songInfo.id}`, + pageUrl: `https://music-dl.sayqz.com/?source=netease&id=${songInfo.id}`, playUrl: !isBlocked && !isCloud ? `http://music.163.com/song/media/outer/url?id=${songInfo.id}.mp3` : '', // 不再建议使用这个 url,建议每次都 Call API 获取 isBlocked, isCloud,