diff --git a/backend/src/handler/sync_jobs.js b/backend/src/handler/sync_jobs.js index 41637fc..75e86e5 100644 --- a/backend/src/handler/sync_jobs.js +++ b/backend/src/handler/sync_jobs.js @@ -92,6 +92,20 @@ async function createJob(req, res) { } } + // 如果前端没有传递 songId,尝试从 URL 中提取 + if (!songId) { + logger.info(`[sync_jobs] No songId provided, trying to extract from URL: ${url}`); + // 支持网易云音乐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]) { + songId = match[1]; + logger.info(`[sync_jobs] Extracted netease songId from URL: ${songId}`); + } + } + // TODO: 支持其他音乐平台 (QQ音乐等) + } + if (songId) { // 直接传递songId给后续处理,避免冗余的网易云匹配 // 如果后续需要,可以根据songId进行精确匹配 diff --git a/backend/src/service/media_fetcher/index.js b/backend/src/service/media_fetcher/index.js index e190f6d..8b36a79 100644 --- a/backend/src/service/media_fetcher/index.js +++ b/backend/src/service/media_fetcher/index.js @@ -212,45 +212,63 @@ async function getMetaWithUrl(url) { // 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']; - try { - 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; + // 增加重试机制和超时控制 + const maxRetries = 2; + for (let attempt = 1; attempt <= maxRetries; attempt++) { try { - meta = JSON.parse(message); - } catch (e) { - logger.error(`[getMetaWithUrl] Failed to parse media-get response: ${e}`, message) - return false; - } + logger.info(`[getMetaWithUrl] Attempt ${attempt}/${maxRetries} for ${url}`); + const {code, message} = await cmd(getBinPath(), ['-u', `"${url}"`, '-m', '--infoFormat=json', '-l=silence', '--timeout=30']); + logger.info('-------') + logger.info(code); + // logger.info(message); + logger.info('-------') + if (code != 0) { + logger.error(`getMetaWithUrl failed with ${url}, err: ${message}`); + if (attempt === maxRetries) { + return false; + } + // 等待一段时间后重试 + await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); + continue; + } - 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); + if (attempt === maxRetries) { + return false; + } + await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); + continue; + } + + 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); + if (attempt === maxRetries) { + return false; + } + // 等待一段时间后重试 + await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); } - } catch (error) { - logger.error(`[getMetaWithUrl] media-get crashed or timed out for ${url}`, error); - return false; } + return false; } async function searchSongFromAllPlatform({