feat(media_fetcher): 添加TuneHub优先下载策略并增强日志记录

为媒体获取服务添加TuneHub优先下载策略,当URL匹配时先尝试通过TuneHub下载,失败后再回退到media-get
同时增加详细的日志记录,包括URL解析、下载过程和错误处理
This commit is contained in:
史悦
2026-01-08 11:55:42 +08:00
parent e3c8a02669
commit b2f6448fad

View File

@@ -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;
}
}