feat(filter): 支持音乐优先级规则组

This commit is contained in:
jxxghp
2026-08-25 16:34:07 +08:00
parent c727d9c9f1
commit 605bf8174a
5 changed files with 92 additions and 15 deletions
+7 -7
View File
@@ -4,15 +4,13 @@ import copy
import re
from typing import Any, Dict, Iterable, Optional
from app.runtime.events import eventmanager
from app.application.agentdata import get_agent_subscribe_port
from app.application.configuration import get_configured_system_config
from app.application.rules import RuleHelper
from app.application.rules import RuleParser
from app.application.rules import BUILTIN_RULE_SET
from app.application.rules import BUILTIN_RULE_SET, RuleHelper, RuleParser
from app.runtime.events import eventmanager
from app.schemas.event import ConfigChangeEventData
from app.schemas.rule import CustomRule
from app.schemas.system import FilterRuleGroup
from app.schemas.event import ConfigChangeEventData
from app.schemas.types import EventType, SystemConfigKey
RULE_ID_PATTERN = re.compile(r"^[A-Za-z0-9]+$")
@@ -27,8 +25,10 @@ MEDIA_TYPE_ALIASES = {
"tv": "电视剧",
"series": "电视剧",
"show": "电视剧",
"music": "音乐",
"电影": "电影",
"电视剧": "电视剧",
"音乐": "音乐",
}
RULE_STRING_SYNTAX = {
@@ -76,9 +76,9 @@ def normalize_media_type(value: Optional[str]) -> Optional[str]:
if not value:
return None
normalized = MEDIA_TYPE_ALIASES.get(value.lower(), value)
if normalized not in {"电影", "电视剧"}:
if normalized not in {"电影", "电视剧", "音乐"}:
raise ValueError(
"media_type 仅支持 '电影''电视剧''movie''tv'"
"media_type 仅支持 '电影''电视剧''音乐''movie''tv''music'"
)
return normalized
+2 -2
View File
@@ -6,7 +6,6 @@ from typing import Optional, Type
from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.agent.tools.impl._filter_rule_utils import (
build_custom_rule_map,
collect_rule_group_usages,
@@ -17,6 +16,7 @@ from app.agent.tools.impl._filter_rule_utils import (
save_system_config,
serialize_rule_group,
)
from app.agent.tools.tags import ToolTag
from app.runtime.log import logger
from app.schemas.types import SystemConfigKey
@@ -35,7 +35,7 @@ class AddRuleGroupInput(BaseModel):
)
media_type: Optional[str] = Field(
None,
description="Optional media type scope: '电影', '电视剧', 'movie', or 'tv'.",
description="Optional media type scope: '电影', '电视剧', '音乐', 'movie', 'tv', or 'music'.",
)
category: Optional[str] = Field(
None,
+14 -5
View File
@@ -6,7 +6,18 @@
import threading
from typing import Dict, List, Optional
from pyparsing import Forward, Literal, Word, alphas, infix_notation, opAssoc, alphanums, Combine, nums, ParseResults
from pyparsing import (
Combine,
Forward,
Literal,
ParseResults,
Word,
alphanums,
alphas,
infix_notation,
nums,
opAssoc,
)
from app.adapters.system import rust as rust_accel
from app.application.configuration import get_configured_system_config
@@ -53,10 +64,8 @@ class RuleHelper:
if not group.media_type
or (
media
and (
(not group.category and group.media_type == media.type.value)
or group.category == media.category
)
and group.media_type == media.type.value
and (not group.category or group.category == media.category)
)
]
+1 -1
View File
@@ -31,7 +31,7 @@ class FilterRuleGroup(BaseModel):
name: Optional[str] = None
# 规则串
rule_string: Optional[str] = None
# 适用媒体类型 None-全部 电影/电视剧
# 适用媒体类型 None-全部 电影/电视剧/音乐
media_type: Optional[str] = None
# 适用媒体类别 None-全部 对应二级分类
category: Optional[str] = None
+68
View File
@@ -0,0 +1,68 @@
from app.agent.tools.impl._filter_rule_utils import normalize_media_type
from app.application.rules import RuleHelper
from app.domain.context import MediaInfo, MusicInfo, TorrentInfo
from app.modules.filter import FilterModule
from app.schemas.rule import FilterRuleGroup
from app.schemas.types import MediaType
def test_agent_rule_group_media_type_accepts_music_aliases():
"""Agent 规则组写入入口应统一接受中英文音乐类型。"""
assert normalize_media_type("music") == MediaType.MUSIC.value
assert normalize_media_type("音乐") == MediaType.MUSIC.value
def test_music_rule_group_matches_music_media(monkeypatch):
"""音乐规则组应在音乐搜索和订阅的过滤上下文中生效。"""
helper = RuleHelper()
groups = [
FilterRuleGroup(name="music", rule_string="FLAC", media_type=MediaType.MUSIC.value),
FilterRuleGroup(name="movie", rule_string="BLURAY", media_type=MediaType.MOVIE.value),
]
monkeypatch.setattr(helper, "get_rule_groups", lambda: groups)
matched = helper.get_rule_group_by_media(
media=MusicInfo(title="Example"),
group_names=["music", "movie"],
)
assert [group.name for group in matched] == ["music"]
def test_music_rule_group_filters_music_torrents(monkeypatch):
"""音乐专属规则组被选中后应实际过滤音乐资源。"""
helper = RuleHelper()
groups = [
FilterRuleGroup(name="music", rule_string="FLAC", media_type=MediaType.MUSIC.value)
]
monkeypatch.setattr(helper, "get_rule_groups", lambda: groups)
module = FilterModule()
module.rulehelper = helper
module.rule_set = {"FLAC": {"include": "FLAC"}}
lossless = TorrentInfo(title="Artist Album FLAC", description="")
lossy = TorrentInfo(title="Artist Album MP3 320kbps", description="")
filtered = module.filter_torrents(
rule_groups=["music"],
torrent_list=[lossless, lossy],
mediainfo=MusicInfo(title="Album"),
)
assert filtered == [lossless]
def test_rule_group_category_cannot_cross_media_types(monkeypatch):
"""二级分类相同时仍必须先匹配规则组的主媒体类型。"""
helper = RuleHelper()
groups = [
FilterRuleGroup(
name="movie-category",
rule_string="BLURAY",
media_type=MediaType.MOVIE.value,
category="shared",
)
]
monkeypatch.setattr(helper, "get_rule_groups", lambda: groups)
media = MediaInfo(type=MediaType.TV, category="shared")
assert helper.get_rule_group_by_media(media=media, group_names=["movie-category"]) == []