diff --git a/backend/src/service/media_fetcher/index.js b/backend/src/service/media_fetcher/index.js index 455f6c5..6c1dd95 100644 --- a/backend/src/service/media_fetcher/index.js +++ b/backend/src/service/media_fetcher/index.js @@ -38,18 +38,23 @@ function parsePageUrlParams(url) { if (!url) { return null; } + // 支持网易云音乐PC版和手机版 if (url.indexOf('music.163.com') >= 0 || url.indexOf('m.music.163.com') >= 0) { 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] }; } } + // 支持QQ音乐 if (url.indexOf('y.qq.com') >= 0) { 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] }; } } + logger.info(`[parsePageUrlParams] URL not recognized: ${url}`); return null; } @@ -115,27 +120,59 @@ async function fetchWithUrl(url, { return false; } + // Try TuneHub first for supported URLs + const params = parseTunehubParams(url) || parsePageUrlParams(url); + if (params) { + logger.info(`[fetchWithUrl] Trying TuneHub first for ${params.source}:${params.id}`); + try { + const downloadUrl = buildSongUrl(params.source, params.id); + if (downloadUrl) { + logger.info(`[fetchWithUrl] TuneHub download URL: ${downloadUrl}`); + const downloadPath = `${fileBasePath}/${songName ? songName : requestHash}.mp3`; + const isSucceed = await downloadFile(downloadUrl, downloadPath); + if (isSucceed && fs.existsSync(downloadPath)) { + logger.info(`[fetchWithUrl] Download success via TuneHub: ${downloadPath}`); + return downloadPath; + } + logger.warn(`[fetchWithUrl] TuneHub download failed, will try media-get as fallback`); + } + } catch (error) { + logger.error(`[fetchWithUrl] TuneHub download failed for ${params.source}:${params.id}`, error); + } + } else { + logger.info(`[fetchWithUrl] URL not recognized as TuneHub-supported format, using media-get`); + } + + // Fallback to media-get addMediaTag = false; // todo: 等到 media-get fix 偶现的 添加 addMediaTag 后 panic 的问题,再移除这行代码 const downloadPath = `${fileBasePath}/${songName ? songName : requestHash}.mp3`; - logger.info(`start parse and download from ${url}`); + logger.info(`[fetchWithUrl] Falling back to media-get for ${url} (may be unstable)`); let args = ['-u', `"${url}"`, '--out', `${downloadPath}`, '-t', 'audio', `${addMediaTag ? '--addMediaTag' : ''}`]; logger.info(`${getBinPath()} ${args.join(' ')}`); - const {code, message} = await cmd(getBinPath(), args); - logger.info('-------') - logger.info(code); - logger.info(message); - logger.info('-------') - if (code != 0) { - return false; - } + try { + const {code, message} = await cmd(getBinPath(), args); + logger.info('-------') + logger.info(code); + logger.info(message); + logger.info('-------') + if (code != 0) { + logger.error(`[fetchWithUrl] media-get failed with code ${code}: ${message}`); + return false; + } - if (!fs.existsSync(downloadPath)) { + if (!fs.existsSync(downloadPath)) { + logger.error(`[fetchWithUrl] media-get succeeded but file not found: ${downloadPath}`); + return false; + } + logger.info(`[fetchWithUrl] media-get download success: ${downloadPath}`); + return downloadPath; + } catch (error) { + logger.error(`[fetchWithUrl] media-get crashed or timed out for ${url}`, error); return false; } - return downloadPath; } async function getMetaWithUrl(url) { @@ -143,45 +180,57 @@ async function getMetaWithUrl(url) { const params = parseTunehubParams(url) || parsePageUrlParams(url); if (params) { + logger.info(`[getMetaWithUrl] Trying TuneHub first for ${params.source}:${params.id}`); const tunehubInfo = await getSongInfo(params.source, params.id); if (tunehubInfo) { + logger.info(`[getMetaWithUrl] TuneHub success for ${params.source}:${params.id}`); return buildMetaFromTunehub(params.source, tunehubInfo, url, params.id); } + 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`); } + // Fallback to media-get only if TuneHub fails or URL format not supported + logger.warn(`[getMetaWithUrl] Falling back to media-get for ${url} (may be unstable)`); let args = ['-u', `"${url}"`, '-m', '--infoFormat=json', '-l=silence']; - const {code, message} = await cmd(getBinPath(), args); - logger.info('-------') - logger.info(code); - // logger.info(message); - logger.info('-------') - if (code != 0) { - logger.error(`getMetaWithUrl failed with ${url}, err: ${message}`); - return false; - } - - let meta; try { - meta = JSON.parse(message); - } catch (e) { - logger.error(e, message) - return false; - } + const {code, message} = await cmd(getBinPath(), args); + logger.info('-------') + logger.info(code); + // logger.info(message); + logger.info('-------') + if (code != 0) { + logger.error(`getMetaWithUrl failed with ${url}, err: ${message}`); + return false; + } - return { - songName: meta.title, - artist: meta.artist, - album: meta.album, - duration: meta.duration, - coverUrl: meta.cover_url, - publicTime: meta.public_time, - isTrial: meta.is_trial, - resourceType: meta.resource_type, - audios: meta.audios, - fromMusicPlatform: meta.from_music_platform, - resourceForbidden: meta.resource_forbidden, - source: meta.source + let meta; + try { + meta = JSON.parse(message); + } catch (e) { + logger.error(`[getMetaWithUrl] Failed to parse media-get response: ${e}`, message) + return false; + } + + return { + songName: meta.title, + artist: meta.artist, + album: meta.album, + duration: meta.duration, + coverUrl: meta.cover_url, + publicTime: meta.public_time, + isTrial: meta.is_trial, + resourceType: meta.resource_type, + audios: meta.audios, + fromMusicPlatform: meta.from_music_platform, + resourceForbidden: meta.resource_forbidden, + source: meta.source + } + } catch (error) { + logger.error(`[getMetaWithUrl] media-get crashed or timed out for ${url}`, error); + return false; } }