feat(music): 识别接口支持 MusicBrainz 数据源

- 选择 musicbrainz 数据源时非音频标题按音乐元数据解析
- 支持 艺术家 - 曲名 格式拆分艺术家后检索 MusicBrainz
This commit is contained in:
jxxghp
2026-08-10 23:30:57 +08:00
parent ddc45a10c0
commit a15b8542ea
2 changed files with 33 additions and 1 deletions

View File

@@ -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,

View File

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