From a15b8542eaf8787cac71d054a35ddb666a05f5cc Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 10 Aug 2026 23:30:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(music):=20=E8=AF=86=E5=88=AB=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=94=AF=E6=8C=81=20MusicBrainz=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 选择 musicbrainz 数据源时非音频标题按音乐元数据解析 - 支持 艺术家 - 曲名 格式拆分艺术家后检索 MusicBrainz --- app/api/endpoints/media.py | 5 ++++- tests/test_media_recognize_endpoint.py | 29 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/app/api/endpoints/media.py b/app/api/endpoints/media.py index c3d921077..27e81fc23 100644 --- a/app/api/endpoints/media.py +++ b/app/api/endpoints/media.py @@ -11,7 +11,7 @@ from app.chain.tmdb import TmdbChain from app.core.config import settings from app.core.context import Context, MusicInfo from app.core.event import eventmanager -from app.core.meta import MetaBase +from app.core.meta import MetaBase, MetaMusic from app.core.metainfo import MetaInfo, MetaInfoPath from app.core.security import verify_token, verify_apitoken from app.db.models import User @@ -122,6 +122,9 @@ async def recognize( """ # 识别媒体信息,传入临时识别词时优先于系统配置的识别词生效 metainfo = _build_recognize_metainfo(title, subtitle, custom_words) + # MusicBrainz 仅支持音乐识别,非音频后缀的标题统一按音乐元数据解析 + if source == "musicbrainz" and not isinstance(metainfo, MetaMusic): + metainfo = MusicChain.parse_query(title) mediainfo = await MediaChain().async_recognize_by_meta( metainfo, source=source, diff --git a/tests/test_media_recognize_endpoint.py b/tests/test_media_recognize_endpoint.py index af485299b..765ec2210 100644 --- a/tests/test_media_recognize_endpoint.py +++ b/tests/test_media_recognize_endpoint.py @@ -5,6 +5,7 @@ import pytest from app.api.endpoints.media import recognize from app.core.context import MediaInfo +from app.core.meta import MetaMusic from app.schemas.types import MediaType @@ -58,3 +59,31 @@ def test_recognize_does_not_treat_non_path_title_as_file_path(title: str) -> Non asyncio.run(recognize(title=title, _=Mock())) meta_info_path.assert_not_called() + + +def test_recognize_musicbrainz_source_parses_music_meta() -> None: + """MusicBrainz 数据源下普通标题应按音乐元数据解析并拆分艺术家。""" + chain = Mock() + chain.async_recognize_by_meta = AsyncMock(return_value=None) + + with patch("app.api.endpoints.media.MediaChain", return_value=chain): + asyncio.run(recognize(title="周杰伦 - 晴天", source="musicbrainz", _=Mock())) + + metainfo = chain.async_recognize_by_meta.await_args.args[0] + assert isinstance(metainfo, MetaMusic) + assert metainfo.title == "晴天" + assert metainfo.artists == ["周杰伦"] + assert chain.async_recognize_by_meta.await_args.kwargs.get("source") == "musicbrainz" + + +def test_recognize_audio_file_title_uses_music_meta_without_source() -> None: + """音频文件名无需指定数据源即应进入音乐识别分支。""" + chain = Mock() + chain.async_recognize_by_meta = AsyncMock(return_value=None) + + with patch("app.api.endpoints.media.MediaChain", return_value=chain): + asyncio.run(recognize(title="周杰伦 - 晴天.flac", _=Mock())) + + metainfo = chain.async_recognize_by_meta.await_args.args[0] + assert isinstance(metainfo, MetaMusic) + assert metainfo.audio_format