feat(media_fetcher): 添加TuneHub优先下载策略并增强日志记录
为媒体获取服务添加TuneHub优先下载策略,当URL匹配时先尝试通过TuneHub下载,失败后再回退到media-get 同时增加详细的日志记录,包括URL解析、下载过程和错误处理
This commit is contained in:
@@ -38,18 +38,23 @@ function parsePageUrlParams(url) {
|
|||||||
if (!url) {
|
if (!url) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// 支持网易云音乐PC版和手机版
|
||||||
if (url.indexOf('music.163.com') >= 0 || url.indexOf('m.music.163.com') >= 0) {
|
if (url.indexOf('music.163.com') >= 0 || url.indexOf('m.music.163.com') >= 0) {
|
||||||
const match = url.match(/id=(\d+)/);
|
const match = url.match(/id=(\d+)/);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
|
logger.info(`[parsePageUrlParams] Parsed Netease URL: ${url} -> netease:${match[1]}`);
|
||||||
return { source: 'netease', id: match[1] };
|
return { source: 'netease', id: match[1] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 支持QQ音乐
|
||||||
if (url.indexOf('y.qq.com') >= 0) {
|
if (url.indexOf('y.qq.com') >= 0) {
|
||||||
const match = url.match(/songDetail\/([A-Za-z0-9]+)/);
|
const match = url.match(/songDetail\/([A-Za-z0-9]+)/);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
|
logger.info(`[parsePageUrlParams] Parsed QQ URL: ${url} -> qq:${match[1]}`);
|
||||||
return { source: 'qq', id: match[1] };
|
return { source: 'qq', id: match[1] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logger.info(`[parsePageUrlParams] URL not recognized: ${url}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,27 +120,59 @@ async function fetchWithUrl(url, {
|
|||||||
return false;
|
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 的问题,再移除这行代码
|
addMediaTag = false; // todo: 等到 media-get fix 偶现的 添加 addMediaTag 后 panic 的问题,再移除这行代码
|
||||||
const downloadPath = `${fileBasePath}/${songName ? songName : requestHash}.mp3`;
|
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' : ''}`];
|
let args = ['-u', `"${url}"`, '--out', `${downloadPath}`, '-t', 'audio', `${addMediaTag ? '--addMediaTag' : ''}`];
|
||||||
|
|
||||||
logger.info(`${getBinPath()} ${args.join(' ')}`);
|
logger.info(`${getBinPath()} ${args.join(' ')}`);
|
||||||
|
|
||||||
const {code, message} = await cmd(getBinPath(), args);
|
try {
|
||||||
logger.info('-------')
|
const {code, message} = await cmd(getBinPath(), args);
|
||||||
logger.info(code);
|
logger.info('-------')
|
||||||
logger.info(message);
|
logger.info(code);
|
||||||
logger.info('-------')
|
logger.info(message);
|
||||||
if (code != 0) {
|
logger.info('-------')
|
||||||
return false;
|
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 false;
|
||||||
}
|
}
|
||||||
return downloadPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMetaWithUrl(url) {
|
async function getMetaWithUrl(url) {
|
||||||
@@ -143,45 +180,57 @@ async function getMetaWithUrl(url) {
|
|||||||
|
|
||||||
const params = parseTunehubParams(url) || parsePageUrlParams(url);
|
const params = parseTunehubParams(url) || parsePageUrlParams(url);
|
||||||
if (params) {
|
if (params) {
|
||||||
|
logger.info(`[getMetaWithUrl] Trying TuneHub first for ${params.source}:${params.id}`);
|
||||||
const tunehubInfo = await getSongInfo(params.source, params.id);
|
const tunehubInfo = await getSongInfo(params.source, params.id);
|
||||||
if (tunehubInfo) {
|
if (tunehubInfo) {
|
||||||
|
logger.info(`[getMetaWithUrl] TuneHub success for ${params.source}:${params.id}`);
|
||||||
return buildMetaFromTunehub(params.source, tunehubInfo, url, 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'];
|
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 {
|
try {
|
||||||
meta = JSON.parse(message);
|
const {code, message} = await cmd(getBinPath(), args);
|
||||||
} catch (e) {
|
logger.info('-------')
|
||||||
logger.error(e, message)
|
logger.info(code);
|
||||||
return false;
|
// logger.info(message);
|
||||||
}
|
logger.info('-------')
|
||||||
|
if (code != 0) {
|
||||||
|
logger.error(`getMetaWithUrl failed with ${url}, err: ${message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
let meta;
|
||||||
songName: meta.title,
|
try {
|
||||||
artist: meta.artist,
|
meta = JSON.parse(message);
|
||||||
album: meta.album,
|
} catch (e) {
|
||||||
duration: meta.duration,
|
logger.error(`[getMetaWithUrl] Failed to parse media-get response: ${e}`, message)
|
||||||
coverUrl: meta.cover_url,
|
return false;
|
||||||
publicTime: meta.public_time,
|
}
|
||||||
isTrial: meta.is_trial,
|
|
||||||
resourceType: meta.resource_type,
|
return {
|
||||||
audios: meta.audios,
|
songName: meta.title,
|
||||||
fromMusicPlatform: meta.from_music_platform,
|
artist: meta.artist,
|
||||||
resourceForbidden: meta.resource_forbidden,
|
album: meta.album,
|
||||||
source: meta.source
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user