fix(api): preserve startup import budget

This commit is contained in:
jxxghp
2026-09-04 19:04:08 +08:00
parent 8b74063908
commit 0d0bcf8a3b
11 changed files with 34 additions and 18 deletions
+4 -1
View File
@@ -44,7 +44,6 @@ from app.application.transfer.execution import (
TransferExecutionRepository, TransferExecutionRepository,
TransferRetryRequestResult, TransferRetryRequestResult,
) )
from app.runtime.errors import public_error_message
from app.runtime.log import logger from app.runtime.log import logger
from app.runtime.loop import main_loop_registry from app.runtime.loop import main_loop_registry
from app.runtime.progress import AsyncProgressHelper from app.runtime.progress import AsyncProgressHelper
@@ -90,6 +89,8 @@ def _format_retry_rejections(
rejections: list[tuple[int, TransferRetryRequestResult]], rejections: list[tuple[int, TransferRetryRequestResult]],
) -> str: ) -> str:
"""把批量整理重试拒绝原因格式化为前端可直接理解的提示。""" """把批量整理重试拒绝原因格式化为前端可直接理解的提示。"""
from app.runtime.errors import public_error_message
return "".join( return "".join(
f"{history_id} 条:{public_error_message(result.message, context='transfer')}" f"{history_id} 条:{public_error_message(result.message, context='transfer')}"
for history_id, result in rejections for history_id, result in rejections
@@ -461,6 +462,8 @@ async def ai_redo_transfer_history(
""" """
手动触发单条历史记录的 AI 重新整理,并返回进度键。 手动触发单条历史记录的 AI 重新整理,并返回进度键。
""" """
from app.runtime.errors import public_error_message
runtime_config = resolve_api_runtime_config(runtime_config) runtime_config = resolve_api_runtime_config(runtime_config)
history = await query.get_transfer(history_id) history = await query.get_transfer(history_id)
if not history: if not history:
+10 -4
View File
@@ -61,7 +61,6 @@ from app.application.subscription.status import SubscriptionExecutionStatusServi
from app.chain.subscribe.facade import SubscribeChain from app.chain.subscribe.facade import SubscribeChain
from app.domain.context import MediaInfo from app.domain.context import MediaInfo
from app.domain.metainfo import MetaInfo from app.domain.metainfo import MetaInfo
from app.runtime.errors import public_error_message
from app.runtime.execution import run_in_threadpool from app.runtime.execution import run_in_threadpool
from app.runtime.log import logger from app.runtime.log import logger
from app.runtime.tasks import TaskRegistry from app.runtime.tasks import TaskRegistry
@@ -87,6 +86,13 @@ from app.schemas.workflow import Subscribe as _SchemaSubscribe
router = ResponseAPIRouter() router = ResponseAPIRouter()
def _public_subscription_message(message: Optional[object]) -> str:
"""延迟加载订阅错误转换器,避免启动阶段增加宿主模块。"""
from app.runtime.errors import public_error_message
return public_error_message(message, context="subscription")
async def _attach_execution_status( async def _attach_execution_status(
subscribes: list[_SchemaSubscribe], subscribes: list[_SchemaSubscribe],
status_service: object, status_service: object,
@@ -292,7 +298,7 @@ async def create_subscribe(
return _SchemaResponse( return _SchemaResponse(
success=bool(sid), success=bool(sid),
message=( message=(
public_error_message(message, context="subscription") _public_subscription_message(message)
if message if message
else "" else ""
), ),
@@ -806,7 +812,7 @@ async def subscribe_share(
return _SchemaResponse( return _SchemaResponse(
success=state, success=state,
message=( message=(
public_error_message(errmsg, context="subscription") _public_subscription_message(errmsg)
if errmsg if errmsg
else "" else ""
), ),
@@ -824,7 +830,7 @@ async def subscribe_share_delete(
return _SchemaResponse( return _SchemaResponse(
success=state, success=state,
message=( message=(
public_error_message(errmsg, context="subscription") _public_subscription_message(errmsg)
if errmsg if errmsg
else "" else ""
), ),
+2 -1
View File
@@ -26,7 +26,6 @@ from app.application.transfer.execution import (
) )
from app.chain.media import MediaChain from app.chain.media import MediaChain
from app.chain.transfer.facade import TransferChain from app.chain.transfer.facade import TransferChain
from app.runtime.errors import public_error_message
from app.runtime.log import logger from app.runtime.log import logger
from app.runtime.stop import runtime_stop_state from app.runtime.stop import runtime_stop_state
from app.schemas.common import NameData as _SchemaNameData from app.schemas.common import NameData as _SchemaNameData
@@ -55,6 +54,8 @@ def _public_transfer_message(message: Optional[object]) -> Optional[str]:
"""把整理链返回的错误转换为前端可直接展示的文案。""" """把整理链返回的错误转换为前端可直接展示的文案。"""
if message is None or not str(message).strip(): if message is None or not str(message).strip():
return None return None
from app.runtime.errors import public_error_message
return public_error_message(message, context="transfer") return public_error_message(message, context="transfer")
+2 -1
View File
@@ -18,7 +18,6 @@ from app.chain.subscribe.facade import SubscribeChain
from app.chain.tvdb import TvdbChain from app.chain.tvdb import TvdbChain
from app.domain.context import MediaInfo from app.domain.context import MediaInfo
from app.domain.metainfo import MetaInfo from app.domain.metainfo import MetaInfo
from app.runtime.errors import public_error_message
from app.runtime.version import get_app_version from app.runtime.version import get_app_version
from app.schemas.response import Response as _SchemaResponse from app.schemas.response import Response as _SchemaResponse
from app.schemas.servarr import RadarrMovie, SonarrSeries from app.schemas.servarr import RadarrMovie, SonarrSeries
@@ -37,6 +36,8 @@ arr_router = APIRouter(tags=["servarr"], responses=ERROR_RESPONSES)
def _subscribe_error_message(error: Optional[object]) -> str: def _subscribe_error_message(error: Optional[object]) -> str:
"""将 Servarr 订阅失败转换为调用方能理解的提示。""" """将 Servarr 订阅失败转换为调用方能理解的提示。"""
from app.runtime.errors import public_error_message
return public_error_message( return public_error_message(
error or "订阅操作失败", error or "订阅操作失败",
context="subscription", context="subscription",
+2 -1
View File
@@ -5,7 +5,6 @@ from dataclasses import dataclass
from typing import Optional, Protocol from typing import Optional, Protocol
from app.application.subscription.execution import SearchBatchSnapshot, SearchTaskSnapshot from app.application.subscription.execution import SearchBatchSnapshot, SearchTaskSnapshot
from app.runtime.errors import public_error_message
@dataclass(frozen=True, slots=True) @dataclass(frozen=True, slots=True)
@@ -219,4 +218,6 @@ class SubscriptionExecutionStatusService:
"""压平并限制内部错误文本,避免把堆栈或超长响应暴露给界面。""" """压平并限制内部错误文本,避免把堆栈或超长响应暴露给界面。"""
if not error: if not error:
return None return None
from app.runtime.errors import public_error_message
return public_error_message(error, context="subscription")[:500] return public_error_message(error, context="subscription")[:500]
+1 -1
View File
@@ -8,7 +8,6 @@ from app.chain.media import MediaChain
from app.chain.transfer.contract import _TransferOwnerBase from app.chain.transfer.contract import _TransferOwnerBase
from app.domain.context import MediaInfo, MusicInfo from app.domain.context import MediaInfo, MusicInfo
from app.domain.meta.metabase import MetaBase from app.domain.meta.metabase import MetaBase
from app.runtime.errors import public_error_message
from app.runtime.log import logger from app.runtime.log import logger
from app.schemas.message import Message from app.schemas.message import Message
from app.schemas.tmdb import TmdbEpisode from app.schemas.tmdb import TmdbEpisode
@@ -36,6 +35,7 @@ class TransferHistoryOwner(_TransferOwnerBase):
""" """
远程重新整理,参数为历史记录 ID,或媒体来源、原生 ID 与类型。 远程重新整理,参数为历史记录 ID,或媒体来源、原生 ID 与类型。
""" """
from app.runtime.errors import public_error_message
def args_error(): def args_error():
self.post_message( self.post_message(
+3 -1
View File
@@ -17,7 +17,6 @@ from app.chain.media import MediaChain
from app.chain.storage import StorageChain from app.chain.storage import StorageChain
from app.chain.transfer.contract import _TransferOwnerBase from app.chain.transfer.contract import _TransferOwnerBase
from app.domain.context import MusicInfo from app.domain.context import MusicInfo
from app.runtime.errors import public_error_message
from app.runtime.log import logger from app.runtime.log import logger
from app.runtime.loop import main_loop_registry from app.runtime.loop import main_loop_registry
from app.runtime.tasks import get_task_registry from app.runtime.tasks import get_task_registry
@@ -155,6 +154,8 @@ class FailedRetryMixin(_TransferOwnerBase):
""" """
立即重新整理一条失败的整理记录。 立即重新整理一条失败的整理记录。
""" """
from app.runtime.errors import public_error_message
self.post_message( self.post_message(
Message( Message(
channel=channel, channel=channel,
@@ -206,6 +207,7 @@ class FailedRetryMixin(_TransferOwnerBase):
""" """
由智能助手接管一条失败的整理记录。 由智能助手接管一条失败的整理记录。
""" """
from app.runtime.errors import public_error_message
history = self.transfer_history_repository.get(history_id) history = self.transfer_history_repository.get(history_id)
if not history: if not history:
+2 -3
View File
@@ -39,7 +39,6 @@ from app.chain.transfer.contract import _TransferOwnerBase
from app.domain import episode as episode_rules from app.domain import episode as episode_rules
from app.domain.context import MediaInfo, MusicInfo from app.domain.context import MediaInfo, MusicInfo
from app.domain.meta.metabase import MetaBase from app.domain.meta.metabase import MetaBase
from app.runtime.errors import public_error_message
from app.runtime.log import logger from app.runtime.log import logger
from app.runtime.loop import main_loop_registry from app.runtime.loop import main_loop_registry
from app.schemas.message import Message from app.schemas.message import Message
@@ -524,11 +523,11 @@ class TransferSettlementOwner(_TransferOwnerBase):
manual_identity: bool = False, manual_identity: bool = False,
) -> None: ) -> None:
"""按配置逐条发送或按媒体聚合整理失败通知,供第三方整理补丁复用。""" """按配置逐条发送或按媒体聚合整理失败通知,供第三方整理补丁复用。"""
from app.runtime.errors import public_error_message
notification = TransferFailureNotification( notification = TransferFailureNotification(
media_title=( media_title=(
task.mediainfo.title_year task.mediainfo.title_year
if task.mediainfo if task.mediainfo else task.fileitem.name if task.fileitem else "未知媒体"
else task.fileitem.name if task.fileitem else "未知媒体"
), ),
season_episode=getattr(task.meta, "season_episode", "") or "", season_episode=getattr(task.meta, "season_episode", "") or "",
reason=public_error_message(transferinfo.message, context="transfer") or "整理失败", reason=public_error_message(transferinfo.message, context="transfer") or "整理失败",
+2 -1
View File
@@ -23,7 +23,6 @@ from app.application.plugin.routes import configure_plugin_routes
from app.application.plugin.runtime import get_plugin_manager from app.application.plugin.runtime import get_plugin_manager
from app.application.security.token import create_access_token, decode_access_token from app.application.security.token import create_access_token, decode_access_token
from app.runtime.correlation import get_correlation_id from app.runtime.correlation import get_correlation_id
from app.runtime.errors import public_error_message
from app.runtime.localization import LocaleHelper from app.runtime.localization import LocaleHelper
from app.runtime.log import configure_correlation_id_provider, logger from app.runtime.log import configure_correlation_id_provider, logger
from app.runtime.loop import main_loop_registry from app.runtime.loop import main_loop_registry
@@ -203,6 +202,8 @@ async def localized_http_exception_handler(
:param exc: FastAPI HTTP 异常 :param exc: FastAPI HTTP 异常
:return: 统一 JSON 错误响应 :return: 统一 JSON 错误响应
""" """
from app.runtime.errors import public_error_message
message = _localize_exception_message( message = _localize_exception_message(
request, request,
public_error_message(_get_http_exception_message(exc.detail)), public_error_message(_get_http_exception_message(exc.detail)),
+3 -2
View File
@@ -3,7 +3,6 @@ from typing import List, Literal, Optional
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from pydantic import field_validator as _field_validator from pydantic import field_validator as _field_validator
from app.runtime.errors import public_error_message as _public_error_message
from app.schemas.common import JsonData from app.schemas.common import JsonData
from app.schemas.media import OptionalMediaIdentityMixin from app.schemas.media import OptionalMediaIdentityMixin
from app.schemas.types import MediaSource from app.schemas.types import MediaSource
@@ -157,7 +156,9 @@ class TransferHistory(OptionalMediaIdentityMixin, BaseModel):
"""历史接口只返回可理解的整理失败原因,数据库原文仍用于诊断。""" """历史接口只返回可理解的整理失败原因,数据库原文仍用于诊断。"""
if value is None or not str(value).strip(): if value is None or not str(value).strip():
return None return None
return _public_error_message(value, context="transfer") from app.runtime.errors import public_error_message
return public_error_message(value, context="transfer")
class BatchTransferHistoryRedoRequest(BaseModel): class BatchTransferHistoryRedoRequest(BaseModel):
+3 -2
View File
@@ -2,7 +2,6 @@ from typing import Any, Generic, Optional, TypeVar
from pydantic import BaseModel, ConfigDict, field_validator from pydantic import BaseModel, ConfigDict, field_validator
from app.runtime.errors import public_error_message as _public_error_message
from app.runtime.localization import LocaleHelper from app.runtime.localization import LocaleHelper
DataT = TypeVar("DataT") DataT = TypeVar("DataT")
@@ -32,7 +31,9 @@ class Response(BaseModel, Generic[DataT]):
raw_message = str(value) raw_message = str(value)
if not raw_message.strip(): if not raw_message.strip():
return "" return ""
message = _public_error_message(raw_message) from app.runtime.errors import public_error_message
message = public_error_message(raw_message)
if not message: if not message:
return "" return ""
return LocaleHelper.translate_text( return LocaleHelper.translate_text(