mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-31 04:14:38 +08:00
* fix(media): preserve recognition and rule mutation identities * test: cover typed classification facts with alias policies --------- Co-authored-by: jxxghp <jxxghp@gmail.com>
310 lines
10 KiB
Python
310 lines
10 KiB
Python
"""
|
||
过滤规则和规则组管理 API。
|
||
"""
|
||
|
||
from typing import Annotated, Any, Optional
|
||
|
||
from fastapi import Depends, Query
|
||
|
||
from app.api.context import get_host_runtime
|
||
from app.api.dependencies.auth import (
|
||
get_current_active_superuser_async,
|
||
get_current_active_user_async,
|
||
)
|
||
from app.api.principal import ApiPrincipal
|
||
from app.api.response import ResponseAPIRouter
|
||
from app.application.filtering import FilterRuleService
|
||
from app.application.rules import RuleGroupMutationConflictError
|
||
from app.schemas.common import JsonObject as _SchemaJsonObject
|
||
from app.schemas.response import Response as _SchemaResponse
|
||
from app.schemas.rule import (
|
||
CustomFilterRuleCreateRequest as _SchemaCustomFilterRuleCreateRequest,
|
||
)
|
||
from app.schemas.rule import (
|
||
CustomFilterRuleReorderRequest as _SchemaCustomFilterRuleReorderRequest,
|
||
)
|
||
from app.schemas.rule import (
|
||
CustomFilterRuleUpdateRequest as _SchemaCustomFilterRuleUpdateRequest,
|
||
)
|
||
from app.schemas.rule import (
|
||
FilterRuleGroupCreateRequest as _SchemaFilterRuleGroupCreateRequest,
|
||
)
|
||
from app.schemas.rule import (
|
||
FilterRuleGroupReorderRequest as _SchemaFilterRuleGroupReorderRequest,
|
||
)
|
||
from app.schemas.rule import (
|
||
FilterRuleGroupUpdateRequest as _SchemaFilterRuleGroupUpdateRequest,
|
||
)
|
||
from app.startup.composition.context import HostRuntime
|
||
|
||
router = ResponseAPIRouter()
|
||
|
||
|
||
def _service(runtime: HostRuntime) -> FilterRuleService:
|
||
"""
|
||
从宿主组合根收窄出规则 API 所需端口。
|
||
"""
|
||
return FilterRuleService(
|
||
runtime.agent.subscriptions,
|
||
runtime.agent.async_rule_group_mutation_scope,
|
||
runtime.system.publish_config_changed,
|
||
)
|
||
|
||
|
||
@router.get(
|
||
"/builtin",
|
||
summary="查询内置过滤规则",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def query_builtin_rules(
|
||
rule_ids: Annotated[
|
||
Optional[list[str]],
|
||
Query(
|
||
description=(
|
||
"要返回的内置规则标识;可在查询字符串中重复传入 rule_ids,省略时列出全部内置规则。"
|
||
)
|
||
),
|
||
] = None,
|
||
_: ApiPrincipal = Depends(get_current_active_user_async),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
返回内置规则及规则串语法。
|
||
"""
|
||
return _SchemaResponse(
|
||
success=True,
|
||
data=FilterRuleService.query_builtin(rule_ids),
|
||
)
|
||
|
||
|
||
@router.get(
|
||
"/custom",
|
||
summary="查询自定义过滤规则",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def query_custom_rules(
|
||
rule_ids: Annotated[
|
||
Optional[list[str]],
|
||
Query(
|
||
description=(
|
||
"要返回的自定义规则标识;可在查询字符串中重复传入 rule_ids,省略时列出全部自定义规则。"
|
||
)
|
||
),
|
||
] = None,
|
||
include_group_refs: bool = True,
|
||
_: ApiPrincipal = Depends(get_current_active_user_async),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
返回自定义规则和可选规则组引用。
|
||
"""
|
||
return _SchemaResponse(
|
||
success=True,
|
||
data=FilterRuleService.query_custom(
|
||
rule_ids,
|
||
include_group_refs=include_group_refs,
|
||
),
|
||
)
|
||
|
||
|
||
@router.get(
|
||
"/groups",
|
||
summary="查询过滤规则组",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def query_rule_groups(
|
||
group_names: Annotated[
|
||
Optional[list[str]],
|
||
Query(
|
||
description=(
|
||
"要返回的规则组名称;可在查询字符串中重复传入 group_names,省略时列出全部规则组。"
|
||
)
|
||
),
|
||
] = None,
|
||
include_usage: bool = True,
|
||
_: ApiPrincipal = Depends(get_current_active_user_async),
|
||
runtime: HostRuntime = Depends(get_host_runtime),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
返回规则组、解析层级和可选引用位置。
|
||
"""
|
||
return _SchemaResponse(
|
||
success=True,
|
||
data=await _service(runtime).query_groups(
|
||
group_names,
|
||
include_usage=include_usage,
|
||
),
|
||
)
|
||
|
||
|
||
@router.post(
|
||
"/custom",
|
||
summary="新增自定义过滤规则",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def add_custom_rule(
|
||
payload: _SchemaCustomFilterRuleCreateRequest,
|
||
_: ApiPrincipal = Depends(get_current_active_superuser_async),
|
||
runtime: HostRuntime = Depends(get_host_runtime),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
校验并新增一条自定义过滤规则。
|
||
"""
|
||
try:
|
||
data = await _service(runtime).add_custom(**payload.model_dump())
|
||
except (ValueError, RuleGroupMutationConflictError) as error:
|
||
return _SchemaResponse(success=False, message=str(error))
|
||
return _SchemaResponse(success=True, message=data.get("message") or "", data=data)
|
||
|
||
|
||
@router.put(
|
||
"/custom/reorder",
|
||
summary="调整自定义过滤规则顺序",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def reorder_custom_rules(
|
||
payload: _SchemaCustomFilterRuleReorderRequest,
|
||
_: ApiPrincipal = Depends(get_current_active_superuser_async),
|
||
runtime: HostRuntime = Depends(get_host_runtime),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
只调整现有自定义规则顺序并拒绝过期集合覆盖。
|
||
"""
|
||
try:
|
||
data = await _service(runtime).reorder_custom(
|
||
payload.rule_ids,
|
||
expected_rule_ids=payload.expected_rule_ids,
|
||
)
|
||
except (ValueError, RuleGroupMutationConflictError) as error:
|
||
return _SchemaResponse(success=False, message=str(error))
|
||
return _SchemaResponse(success=True, message=data.get("message") or "", data=data)
|
||
|
||
|
||
@router.put(
|
||
"/custom/{rule_id}",
|
||
summary="更新自定义过滤规则",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def update_custom_rule(
|
||
rule_id: str,
|
||
payload: _SchemaCustomFilterRuleUpdateRequest,
|
||
_: ApiPrincipal = Depends(get_current_active_superuser_async),
|
||
runtime: HostRuntime = Depends(get_host_runtime),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
更新自定义规则并在改名时原子重写引用。
|
||
"""
|
||
try:
|
||
data = await _service(runtime).update_custom(
|
||
current_rule_id=rule_id,
|
||
**payload.model_dump(),
|
||
)
|
||
except (ValueError, RuleGroupMutationConflictError) as error:
|
||
return _SchemaResponse(success=False, message=str(error))
|
||
return _SchemaResponse(success=True, message=data.get("message") or "", data=data)
|
||
|
||
|
||
@router.delete(
|
||
"/custom/{rule_id}",
|
||
summary="删除自定义过滤规则",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def delete_custom_rule(
|
||
rule_id: str,
|
||
_: ApiPrincipal = Depends(get_current_active_superuser_async),
|
||
runtime: HostRuntime = Depends(get_host_runtime),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
删除一条未被规则组引用的自定义过滤规则。
|
||
"""
|
||
try:
|
||
data = await _service(runtime).delete_custom(rule_id)
|
||
except (ValueError, RuleGroupMutationConflictError) as error:
|
||
return _SchemaResponse(success=False, message=str(error))
|
||
return _SchemaResponse(success=True, message=data.get("message") or "", data=data)
|
||
|
||
|
||
@router.post(
|
||
"/groups",
|
||
summary="新增过滤规则组",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def add_rule_group(
|
||
payload: _SchemaFilterRuleGroupCreateRequest,
|
||
_: ApiPrincipal = Depends(get_current_active_superuser_async),
|
||
runtime: HostRuntime = Depends(get_host_runtime),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
新增一个已经完成语法与引用校验的规则组。
|
||
"""
|
||
try:
|
||
data = await _service(runtime).add_group(**payload.model_dump())
|
||
except (ValueError, RuleGroupMutationConflictError) as error:
|
||
return _SchemaResponse(success=False, message=str(error))
|
||
return _SchemaResponse(success=True, message=data.get("message") or "", data=data)
|
||
|
||
|
||
@router.put(
|
||
"/groups/reorder",
|
||
summary="调整过滤规则组顺序",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def reorder_rule_groups(
|
||
payload: _SchemaFilterRuleGroupReorderRequest,
|
||
_: ApiPrincipal = Depends(get_current_active_superuser_async),
|
||
runtime: HostRuntime = Depends(get_host_runtime),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
只调整现有规则组顺序并拒绝过期集合覆盖。
|
||
"""
|
||
try:
|
||
data = await _service(runtime).reorder_groups(
|
||
payload.group_names,
|
||
expected_group_names=payload.expected_group_names,
|
||
)
|
||
except (ValueError, RuleGroupMutationConflictError) as error:
|
||
return _SchemaResponse(success=False, message=str(error))
|
||
return _SchemaResponse(success=True, message=data.get("message") or "", data=data)
|
||
|
||
|
||
@router.put(
|
||
"/groups/{name:path}",
|
||
summary="更新过滤规则组",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def update_rule_group(
|
||
name: str,
|
||
payload: _SchemaFilterRuleGroupUpdateRequest,
|
||
_: ApiPrincipal = Depends(get_current_active_superuser_async),
|
||
runtime: HostRuntime = Depends(get_host_runtime),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
更新规则组并原子重写全部名称引用。
|
||
"""
|
||
try:
|
||
data = await _service(runtime).update_group(
|
||
current_name=name,
|
||
**payload.model_dump(),
|
||
)
|
||
except (ValueError, RuleGroupMutationConflictError) as error:
|
||
return _SchemaResponse(success=False, message=str(error))
|
||
return _SchemaResponse(success=True, message=data.get("message") or "", data=data)
|
||
|
||
|
||
@router.delete(
|
||
"/groups/{name:path}",
|
||
summary="删除过滤规则组",
|
||
response_model=_SchemaResponse[_SchemaJsonObject],
|
||
)
|
||
async def delete_rule_group(
|
||
name: str,
|
||
_: ApiPrincipal = Depends(get_current_active_superuser_async),
|
||
runtime: HostRuntime = Depends(get_host_runtime),
|
||
) -> _SchemaResponse[Any]:
|
||
"""
|
||
删除规则组并原子清理全部全局和订阅引用。
|
||
"""
|
||
try:
|
||
data = await _service(runtime).delete_group(name)
|
||
except (ValueError, RuleGroupMutationConflictError) as error:
|
||
return _SchemaResponse(success=False, message=str(error))
|
||
return _SchemaResponse(success=True, message=data.get("message") or "", data=data)
|