mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-09 09:26:55 +08:00
fix(classification): upgrade missing music defaults
This commit is contained in:
@@ -94,11 +94,7 @@ def _build_uncategorized_classification_policy() -> ClassificationPolicy:
|
|||||||
|
|
||||||
def with_default_music_classification(policy: ClassificationPolicy) -> ClassificationPolicy:
|
def with_default_music_classification(policy: ClassificationPolicy) -> ClassificationPolicy:
|
||||||
"""为尚未配置音乐分类的策略追加安全、结构化的常用专辑分类。"""
|
"""为尚未配置音乐分类的策略追加安全、结构化的常用专辑分类。"""
|
||||||
music_categories = [
|
if not needs_default_music_classification(policy):
|
||||||
item for item in policy.categories if item.media_type == "音乐"
|
|
||||||
]
|
|
||||||
music_rules = [item for item in policy.rules if "音乐" in item.media_types]
|
|
||||||
if music_rules or any(item.id != "music.uncategorized" for item in music_categories):
|
|
||||||
return cast(ClassificationPolicy, policy.model_copy(deep=True))
|
return cast(ClassificationPolicy, policy.model_copy(deep=True))
|
||||||
|
|
||||||
categories = [
|
categories = [
|
||||||
@@ -174,15 +170,23 @@ def with_default_music_classification(policy: ClassificationPolicy) -> Classific
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def is_untouched_legacy_default_policy(state: ClassificationPolicyState) -> bool:
|
def needs_default_music_classification(policy: ClassificationPolicy) -> bool:
|
||||||
"""判断状态是否为旧版本自动创建且从未编辑的 revision 1 默认策略。"""
|
"""判断音乐侧是否仍为旧版原始兜底,未包含任何用户分类。"""
|
||||||
if state.active.revision != 1 or state.history:
|
music_rules = [item for item in policy.rules if "音乐" in item.media_types]
|
||||||
|
music_categories = [
|
||||||
|
item for item in policy.categories if item.media_type == "音乐"
|
||||||
|
]
|
||||||
|
if music_rules or len(music_categories) != 1:
|
||||||
return False
|
return False
|
||||||
normalized = state.active.model_copy(
|
category = music_categories[0]
|
||||||
deep=True,
|
return bool(
|
||||||
update={"revision": 0, "updated_at": None},
|
category.id == "music.uncategorized"
|
||||||
|
and category.name == "未分类"
|
||||||
|
and category.path == ["未分类"]
|
||||||
|
and category.enabled
|
||||||
|
and not category.labels
|
||||||
|
and policy.fallbacks.get("音乐") == category.id
|
||||||
)
|
)
|
||||||
return bool(normalized == _build_uncategorized_classification_policy())
|
|
||||||
|
|
||||||
|
|
||||||
def build_default_classification_policy() -> ClassificationPolicy:
|
def build_default_classification_policy() -> ClassificationPolicy:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from pydantic import ValidationError
|
|||||||
from app.application.classification.configuration import (
|
from app.application.classification.configuration import (
|
||||||
ClassificationPolicyConfigurationService,
|
ClassificationPolicyConfigurationService,
|
||||||
ClassificationPolicyValidationError,
|
ClassificationPolicyValidationError,
|
||||||
is_untouched_legacy_default_policy,
|
needs_default_music_classification,
|
||||||
with_default_music_classification,
|
with_default_music_classification,
|
||||||
)
|
)
|
||||||
from app.application.classification.contract import (
|
from app.application.classification.contract import (
|
||||||
@@ -159,8 +159,8 @@ async def compose_classification(
|
|||||||
ClassificationRuntime(service, diagnostics=(issue,)),
|
ClassificationRuntime(service, diagnostics=(issue,)),
|
||||||
migrated=False,
|
migrated=False,
|
||||||
)
|
)
|
||||||
if stored_state is not None and is_untouched_legacy_default_policy(
|
if stored_state is not None and needs_default_music_classification(
|
||||||
stored_state
|
stored_state.active
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
await service.async_publish(
|
await service.async_publish(
|
||||||
@@ -177,7 +177,7 @@ async def compose_classification(
|
|||||||
migrated=False,
|
migrated=False,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.info("已为未编辑的默认分类策略补充常用音乐分类 revision 2")
|
logger.info("已为仅有旧版兜底的分类策略补充常用音乐分类")
|
||||||
return finish(
|
return finish(
|
||||||
ClassificationRuntime(service),
|
ClassificationRuntime(service),
|
||||||
migrated=True,
|
migrated=True,
|
||||||
|
|||||||
@@ -210,6 +210,66 @@ async def test_untouched_legacy_default_policy_gains_music_rules_once(
|
|||||||
assert store.write_count == 1
|
assert store.write_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio # type: ignore[misc]
|
||||||
|
async def test_custom_movie_policy_with_legacy_music_fallback_gains_music_rules(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
"""其它媒体已自定义时,仍应只升级完全未编辑的旧版音乐兜底。"""
|
||||||
|
state = _published_legacy_default_state()
|
||||||
|
categories = [
|
||||||
|
category.model_copy(
|
||||||
|
deep=True,
|
||||||
|
update={"name": "我的电影", "path": ["我的电影"]},
|
||||||
|
)
|
||||||
|
if category.id == "movie.uncategorized"
|
||||||
|
else category.model_copy(deep=True)
|
||||||
|
for category in state.active.categories
|
||||||
|
]
|
||||||
|
state = ClassificationPolicyState(
|
||||||
|
active=state.active.model_copy(
|
||||||
|
deep=True,
|
||||||
|
update={"categories": categories},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
store = _MemoryPolicyStore(state)
|
||||||
|
system_config = _SystemConfig(
|
||||||
|
{
|
||||||
|
SystemConfigKey.MediaClassificationPolicy.value: state.model_dump(
|
||||||
|
mode="json"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
classification_composition,
|
||||||
|
"SystemConfigClassificationPolicyStore",
|
||||||
|
lambda *_args: store,
|
||||||
|
)
|
||||||
|
|
||||||
|
composition = await classification_composition.compose_classification(
|
||||||
|
executor=cast(Any, _InlineExecutor()),
|
||||||
|
settings=cast(Any, SimpleNamespace(CONFIG_PATH=tmp_path)),
|
||||||
|
system_config=cast(Any, system_config),
|
||||||
|
)
|
||||||
|
|
||||||
|
policy = composition.runtime.require_policy()
|
||||||
|
assert composition.migrated is True
|
||||||
|
assert policy.revision == 2
|
||||||
|
assert next(
|
||||||
|
category for category in policy.categories if category.id == "movie.uncategorized"
|
||||||
|
).path == ["我的电影"]
|
||||||
|
assert [
|
||||||
|
category.id for category in policy.categories if category.media_type == "音乐"
|
||||||
|
] == [
|
||||||
|
"music.uncategorized",
|
||||||
|
"music.album",
|
||||||
|
"music.compilation",
|
||||||
|
"music.ep",
|
||||||
|
"music.single",
|
||||||
|
]
|
||||||
|
assert store.write_count == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio # type: ignore[misc]
|
@pytest.mark.asyncio # type: ignore[misc]
|
||||||
async def test_edited_legacy_default_policy_is_not_automatically_changed(
|
async def test_edited_legacy_default_policy_is_not_automatically_changed(
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
|||||||
Reference in New Issue
Block a user