From e1ac2ba55fa6ac72f5de217569e0d70ba3a1d9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B2=E6=82=A6?= Date: Thu, 8 Jan 2026 13:13:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(media=5Ffetcher):=20=E6=B7=BB=E5=8A=A0=20T?= =?UTF-8?q?uneHub=20=E4=BD=9C=E4=B8=BA=E4=B8=BB=E8=A6=81=E9=9F=B3=E4=B9=90?= =?UTF-8?q?=E6=BA=90=E5=B9=B6=E4=BC=98=E5=8C=96=E6=90=9C=E7=B4=A2=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优先使用 TuneHub 进行音乐搜索,仅在失败时回退到 media-get 移除对 media-get 的强制依赖检查 添加更完善的错误处理逻辑 --- backend/src/init_app.js | 15 +--- backend/src/service/media_fetcher/index.js | 95 +++++++++++++++------- 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/backend/src/init_app.js b/backend/src/init_app.js index 77438dd..3923606 100644 --- a/backend/src/init_app.js +++ b/backend/src/init_app.js @@ -5,8 +5,6 @@ const process = require('process'); initDir(); initAccountFileIfNotExisted(); -const mediaGet = require('./service/media_fetcher/media_get'); - function initDir() { // make sure all dir has been created const dirList = [ @@ -33,16 +31,7 @@ function initAccountFileIfNotExisted() { } module.exports = async function() { - // check if media-get is installed - const mediaGetInfo = await mediaGet.getMediaGetInfo(); - if (mediaGetInfo === false) { - process.exit(-1); - } - if (!mediaGetInfo.hasInstallFFmpeg) { - logger.error('please install FFmpeg and FFprobe first'); - process.exit(-1); - } - logger.info(`[media-get] Version: ${mediaGetInfo.versionInfo}`); + logger.info('Melody 服务启动成功,使用 TuneHub 作为主要音乐源'); - // TODO check media-get latest version + // TODO: 可以在这里添加对 TuneHub 连接性的检查 } \ No newline at end of file diff --git a/backend/src/service/media_fetcher/index.js b/backend/src/service/media_fetcher/index.js index 6c1dd95..2f7d8d6 100644 --- a/backend/src/service/media_fetcher/index.js +++ b/backend/src/service/media_fetcher/index.js @@ -242,8 +242,38 @@ async function searchSongFromAllPlatform({ const globalConfig = await configManager.getGlobalConfig(); - let searchParams = keyword - ? ['-k', `"${keyword}"`] + // Try TuneHub first + const searchKeyword = keyword || `${songName} ${artist}`.trim(); + if (searchKeyword) { + logger.info(`[searchSongFromAllPlatform] Using TuneHub to search: ${searchKeyword}`); + const tunehubResult = await require('../music_platform/tunehub').searchSongs(searchKeyword, { + aggregate: true, + }); + if (tunehubResult && tunehubResult.result && tunehubResult.result.songs) { + logger.info(`[searchSongFromAllPlatform] TuneHub search success, found ${tunehubResult.result.songs.length} songs`); + return tunehubResult.result.songs.map(song => { + return { + songName: song.name || '', + artist: song.artist || '', + album: song.album || '', + duration: song.duration || 0, + url: song.url || '', + resourceForbidden: song.resourceForbidden || false, + source: song.source || '', + fromMusicPlatform: true, + score: song.score || 0, + } + }); + } + logger.warn(`[searchSongFromAllPlatform] TuneHub search failed or no results, will try media-get as fallback`); + } else { + logger.info(`[searchSongFromAllPlatform] No search keyword provided, using media-get`); + } + + // Fallback to media-get only if TuneHub fails + logger.warn(`[searchSongFromAllPlatform] Falling back to media-get (may be unstable)`); + let searchParams = keyword + ? ['-k', `"${keyword}"`] : ['--searchSongName', `"${songName}"`, '--searchArtist', `"${artist}"`, '--searchAlbum', `"${album}"`]; searchParams = searchParams.concat([ '--searchType="song"', @@ -255,37 +285,42 @@ async function searchSongFromAllPlatform({ logger.info(`cmdStr: ${getBinPath()} ${searchParams.join(' ')}`); - const {code, message} = await cmd(getBinPath(), searchParams); - logger.info('-------') - logger.info(code); - // logger.info(message); - logger.info('-------') - if (code != 0) { - logger.error(`searchSong failed with ${arguments}, err: ${message}`); - return false; - } - - let jsonResponse; try { - jsonResponse = JSON.parse(message); - } catch (e) { - logger.error(e, message) + const {code, message} = await cmd(getBinPath(), searchParams); + logger.info('-------') + logger.info(code); + // logger.info(message); + logger.info('-------') + if (code != 0) { + logger.error(`searchSong failed with ${arguments}, err: ${message}`); + return false; + } + + let jsonResponse; + try { + jsonResponse = JSON.parse(message); + } catch (e) { + logger.error(`[searchSongFromAllPlatform] Failed to parse media-get response: ${e}`, message) + return false; + } + + return jsonResponse.map(searchItem => { + return { + songName: searchItem.Name, + artist: searchItem.Artist, + album: searchItem.Album, + duration: searchItem.Duration, + url: searchItem.Url, + resourceForbidden: searchItem.ResourceForbidden, + source: searchItem.Source, + fromMusicPlatform: searchItem.FromMusicPlatform, + score: searchItem.Score, + } + }) + } catch (error) { + logger.error(`[searchSongFromAllPlatform] media-get crashed or timed out`, error); return false; } - - return jsonResponse.map(searchItem => { - return { - songName: searchItem.Name, - artist: searchItem.Artist, - album: searchItem.Album, - duration: searchItem.Duration, - url: searchItem.Url, - resourceForbidden: searchItem.ResourceForbidden, - source: searchItem.Source, - fromMusicPlatform: searchItem.FromMusicPlatform, - score: searchItem.Score, - } - }) } module.exports = {