diff --git a/app/helper/message.py b/app/helper/message.py index da93dbf37..0863c9112 100644 --- a/app/helper/message.py +++ b/app/helper/message.py @@ -15,7 +15,7 @@ from jinja2 import Template from app.core.cache import TTLCache from app.core.config import global_vars -from app.core.context import MediaInfo, MusicInfo, TorrentInfo +from app.core.context import MUSIC_ENTITY_ALBUM, MediaInfo, MusicInfo, TorrentInfo from app.core.meta import MetaBase, MetaMusic from app.db.systemconfig_oper import SystemConfigOper from app.log import logger @@ -93,8 +93,13 @@ class TemplateContextBuilder: if not mediainfo: return if isinstance(mediainfo, MusicInfo): - # 专辑识别结果会被同一批次的所有曲目复用,不能覆盖每个文件自己的曲名和曲序。 - title = context.get("title") or cls.__convert_invalid_characters(mediainfo.title) + # 专辑实体的下载/整理是整批曲目共享一次通知:标题应以专辑为主 + # 题,且不展示单曲序号;单曲场景继续使用每个文件自己的曲名和曲序。 + is_album_context = mediainfo.music_type == MUSIC_ENTITY_ALBUM + if is_album_context and mediainfo.album: + title = cls.__convert_invalid_characters(mediainfo.album) + else: + title = context.get("title") or cls.__convert_invalid_characters(mediainfo.title) artists = context.get("artists") or [ cls.__convert_invalid_characters(item) for item in mediainfo.artists ] @@ -103,13 +108,21 @@ class TemplateContextBuilder: album_artist = context.get("album_artist") or cls.__convert_invalid_characters( mediainfo.album_artist ) - year = context.get("year") or mediainfo.year + year = ( + mediainfo.year + if (is_album_context and mediainfo.year) + else (context.get("year") or mediainfo.year) + ) disc_number = context.get("disc_number") or mediainfo.disc_number - track_number = context.get("track_number") or mediainfo.track_number + track_number = ( + None + if is_album_context + else (context.get("track_number") or mediainfo.track_number) + ) context.update({ "type": mediainfo.type.value, "title": title, - "name": context.get("name") or title, + "name": title if is_album_context else (context.get("name") or title), "artists": artists, "artist": artist, "album": album, diff --git a/tests/test_notification_template_render.py b/tests/test_notification_template_render.py index 3f343431b..b9b1e92fe 100644 --- a/tests/test_notification_template_render.py +++ b/tests/test_notification_template_render.py @@ -17,8 +17,10 @@ import importlib.util import pytest +from app.core.context import MUSIC_ENTITY_ALBUM, MusicInfo +from app.core.meta import MetaMusic from app.db.systemconfig_oper import SystemConfigOper -from app.helper.message import MessageTemplateHelper, TemplateHelper +from app.helper.message import MessageTemplateHelper, TemplateContextBuilder, TemplateHelper from app.schemas.message import Notification from app.schemas.types import ContentType, SystemConfigKey @@ -56,6 +58,57 @@ def notification_templates() -> SystemConfigOper: config_oper.set(SystemConfigKey.NotificationTemplates, original) +def test_album_batch_context_uses_album_title_for_notification() -> None: + """ + 专辑实体批量入库的通知上下文应以专辑名作为标题, + 不得展示批次中某个单曲的曲名和曲序。 + """ + meta = MetaMusic( + title="晴天", + artists=["周杰伦"], + album="叶惠美", + track_number=3, + year=2003, + ) + mediainfo = MusicInfo( + music_type=MUSIC_ENTITY_ALBUM, + title="晴天", + album="叶惠美", + artists=["周杰伦"], + year=2003, + ) + + context = TemplateContextBuilder().build(meta=meta, mediainfo=mediainfo) + + assert context["title"] == "叶惠美" + assert context["title_year"] == "叶惠美 (2003)" + assert context.get("track_number") is None + + rendered = TemplateHelper().render( + template_content=MUSIC_ORGANIZE_TEMPLATE, + **dict(context, file_count=11, total_size="1.2 GB"), + ) + assert isinstance(rendered, dict) + assert rendered["title"] == "叶惠美 (2003) 已入库" + assert "艺术家:周杰伦" in rendered["text"] + assert "#3" not in rendered["title"] + + +def test_single_track_context_keeps_track_title_and_number() -> None: + """ + 单曲下载/整理的通知上下文应继续使用曲目标题和曲序, + 不受专辑场景调整的影响。 + """ + context = MUSIC_CONTEXT.copy() + + rendered = TemplateHelper().render( + template_content=MUSIC_ORGANIZE_TEMPLATE, **context + ) + + assert isinstance(rendered, dict) + assert rendered["title"] == "晴天 (2003) #3 已入库" + + def test_literal_template_with_quoted_condition_renders_music() -> None: """ 带 ``{% if type == "音乐" %}`` 双引号条件的模板应能正常渲染,