refactor(config): retire RuntimeSettingsCompat host usage

This commit is contained in:
jxxghp
2026-08-26 15:55:21 +08:00
parent cdab54254d
commit 9dbe424c3d
162 changed files with 1966 additions and 1745 deletions
+4 -6
View File
@@ -20,9 +20,7 @@ from app.db.engine import get_engine
from app.db.health import probe_database
from app.db.maintenance import DatabaseCleanupRepository
from app.db.session import SessionFactory
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.runtime.settings import get_runtime_setting
def build_database_governance() -> DatabaseGovernance:
@@ -57,7 +55,7 @@ def configure_database() -> None:
def read_backup_policy() -> BackupPolicy:
"""读取一次可热更新的数据库备份目录与保留策略。"""
return BackupPolicy(
root=settings.DATABASE_BACKUP_PATH,
retention_days=settings.DB_BACKUP_RETENTION_DAYS,
max_count=settings.DB_BACKUP_MAX_COUNT,
root=get_runtime_setting('DATABASE_BACKUP_PATH'),
retention_days=get_runtime_setting('DB_BACKUP_RETENTION_DAYS'),
max_count=get_runtime_setting('DB_BACKUP_MAX_COUNT'),
)
+2 -4
View File
@@ -16,11 +16,9 @@ from app.agent.runtime_loader import (
)
from app.application.agent import register_agent_service_providers
from app.application.messaging.skill import register_skill_catalog_provider
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.runtime.events import Event, eventmanager
from app.runtime.log import logger
from app.runtime.settings import get_runtime_setting
from app.schemas.types import EventType
AGENT_BLOCKING_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS = 10.0
@@ -127,7 +125,7 @@ class AgentInitializer:
self._shutdown_started = False
self._shutdown_complete = False
if agent_manager is not None:
if not settings.AI_AGENT_ENABLE:
if not get_runtime_setting('AI_AGENT_ENABLE'):
logger.info("AI智能体功能未启用")
return True
self._manager = agent_manager
+4 -6
View File
@@ -10,13 +10,11 @@ from alembic.util import CommandError
from sqlalchemy import inspect
from sqlalchemy.engine import Engine
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.db.base import Base
from app.db.engine import get_engine
from app.db.models import load_all_models
from app.runtime.log import logger
from app.runtime.settings import get_runtime_setting
from app.startup.composition.database import build_database_governance
@@ -27,7 +25,7 @@ def _build_alembic_config(engine: Engine | None = None) -> Config:
alembic_cfg.file_config = _ConfigParser(interpolation=None)
alembic_cfg.set_main_option(
'script_location',
str(settings.ROOT_PATH / 'database'),
str(get_runtime_setting('ROOT_PATH') / 'database'),
)
alembic_cfg.set_main_option(
'sqlalchemy.url',
@@ -105,8 +103,8 @@ def prepare_database(*, before_alembic: Callable[[], None] | None = None) -> Non
)
if (
requires_migration
and settings.DB_BACKUP_ENABLE
and settings.DB_BACKUP_ON_UPGRADE
and get_runtime_setting('DB_BACKUP_ENABLE')
and get_runtime_setting('DB_BACKUP_ON_UPGRADE')
):
current_version = current_heads[0] if current_heads else "未标记"
target_version = target_heads[0]
+8 -10
View File
@@ -1,4 +1,5 @@
from app.adapters.system import rust as rust_accelerator
from app.application.recognition import RecognitionRuleService
from app.domain.context import configure_tmdb_image_url_builder
from app.domain.media import configure_search_source_provider
from app.domain.meta.customization import configure_customization_provider
@@ -6,10 +7,7 @@ from app.domain.meta.releasegroup import configure_release_groups_provider
from app.domain.meta.runtime import configure_recognition_runtime
from app.domain.meta.words import configure_custom_words_provider
from app.domain.metainfo import clear_rust_parse_options_cache
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.application.recognition import RecognitionRuleService
from app.runtime.settings import get_runtime_setting
def configure_domain_dependencies() -> None:
@@ -18,15 +16,15 @@ def configure_domain_dependencies() -> None:
configure_customization_provider(rule_service.get_customization)
configure_release_groups_provider(rule_service.get_release_groups)
configure_custom_words_provider(rule_service.get_custom_words)
configure_search_source_provider(lambda: settings.SEARCH_SOURCE)
configure_tmdb_image_url_builder(settings.TMDB_IMAGE_URL)
configure_search_source_provider(lambda: get_runtime_setting('SEARCH_SOURCE'))
configure_tmdb_image_url_builder(get_runtime_setting('TMDB_IMAGE_URL'))
configure_recognition_runtime(
media_extensions_provider=lambda: (
*settings.RMT_MEDIAEXT,
*settings.RMT_SUBEXT,
*settings.RMT_AUDIOEXT,
*get_runtime_setting('RMT_MEDIAEXT'),
*get_runtime_setting('RMT_SUBEXT'),
*get_runtime_setting('RMT_AUDIOEXT'),
),
audio_extensions_provider=lambda: settings.RMT_AUDIOEXT,
audio_extensions_provider=lambda: get_runtime_setting('RMT_AUDIOEXT'),
accelerator=rust_accelerator,
)
clear_rust_parse_options_cache()
+30 -22
View File
@@ -22,18 +22,12 @@ except ImportError as e:
print(error_message, file=sys.stderr)
sys.exit(1)
from app.adapters.system.host import SystemUtils
from app.runtime.config import settings as legacy_settings
from app.runtime.log import logger
from app.runtime.settings import RuntimeSettingsCompat
from app.runtime.stop import runtime_stop_state
settings = RuntimeSettingsCompat()
from app.adapters.external.server import (
MoviePilotServerHelper,
configure_server_application_services,
)
from app.adapters.network.doh import DohHelper
from app.adapters.system.host import SystemUtils
from app.adapters.system.resource import (
ResourceHelper,
configure_resource_version_provider,
@@ -147,6 +141,7 @@ from app.db.uow import (
)
from app.db.worker import DatabaseWorker
from app.runtime.cache import AsyncFileCache, FileCache
from app.runtime.config import settings as legacy_settings
from app.runtime.events import EventHandlerBinding, EventManager
from app.runtime.execution import run_in_threadpool_to_completion
from app.runtime.extensions.module.dispatcher import ModuleInvocationDispatcher
@@ -156,9 +151,15 @@ from app.runtime.extensions.service_config import (
ServiceConfigHelper,
configure_service_config_reader,
)
from app.runtime.log import logger
from app.runtime.observability import record_metric
from app.runtime.settings import configure_runtime_setting_provider
from app.runtime.settings import (
configure_runtime_setting_provider,
configure_runtime_setting_updater,
get_runtime_setting,
)
from app.runtime.state import SystemHelper
from app.runtime.stop import runtime_stop_state
from app.runtime.tasks import get_task_registry
from app.runtime.thread import ThreadHelper
from app.schemas.message import Message, MessageType
@@ -226,7 +227,7 @@ async def _initialize_configuration_services(
def _build_runtime_settings_service() -> RuntimeSettingsService:
"""将可变部署配置实现注入管理服务,避免把兼容代理再次包装"""
"""唯一可变部署配置实现注入管理服务。"""
return RuntimeSettingsService(legacy_settings)
@@ -254,7 +255,7 @@ def _build_chain_runtime_context() -> ChainRuntimeContext:
send_callback=callback
),
module_dispatcher_factory=ModuleInvocationDispatcher,
configuration=build_chain_runtime_config(settings),
configuration=build_chain_runtime_config(legacy_settings),
data_ports=get_chain_data_ports(),
durable_event_writer=TransactionalChainDurableEventWriter(SessionFactory),
stop_state=runtime_stop_state,
@@ -512,11 +513,11 @@ def start_frontend():
or not SystemUtils.is_windows():
return
# 临时Nginx目录
nginx_path = settings.ROOT_PATH / 'nginx'
nginx_path = get_runtime_setting("ROOT_PATH") / 'nginx'
if not nginx_path.exists():
return
# 配置目录下的Nginx目录
run_nginx_dir = settings.CONFIG_PATH.with_name('nginx')
run_nginx_dir = get_runtime_setting("CONFIG_PATH").with_name('nginx')
if not run_nginx_dir.exists():
# 移动到配置目录
SystemUtils.move(nginx_path, run_nginx_dir)
@@ -543,9 +544,15 @@ def clear_temp():
清理临时文件和图片缓存
"""
# 清理临时目录中3天前的文件
SystemUtils.clear(settings.TEMP_PATH, days=settings.TEMP_FILE_DAYS)
SystemUtils.clear(
get_runtime_setting("TEMP_PATH"),
days=get_runtime_setting("TEMP_FILE_DAYS"),
)
# 清理图片缓存目录中7天前的文件
SystemUtils.clear(settings.CACHE_PATH / "images", days=settings.GLOBAL_IMAGE_CACHE_DAYS)
SystemUtils.clear(
get_runtime_setting("CACHE_PATH") / "images",
days=get_runtime_setting("GLOBAL_IMAGE_CACHE_DAYS"),
)
# 清理 pip/uv 包下载缓存,不接管整个 .cache 目录。
clear_package_tool_cache()
@@ -554,10 +561,10 @@ def clear_package_tool_cache():
"""
清理 pip/uv 包下载缓存,只处理 MoviePilot 管理的工具子目录。
"""
days = settings.PACKAGE_CACHE_DAYS
days = get_runtime_setting("PACKAGE_CACHE_DAYS")
if days <= 0:
return
tool_cache_root = settings.PACKAGE_CACHE_PATH
tool_cache_root = get_runtime_setting("PACKAGE_CACHE_PATH")
for child in ("pip", "uv"):
cache_path = tool_cache_root / child
try:
@@ -593,7 +600,7 @@ def check_auth():
mtype=MessageType.Manual,
title="MoviePilot用户认证",
text=err_msg,
link=settings.MP_DOMAIN('#/site')
link=get_runtime_setting("MP_DOMAIN")('#/site')
)
)
@@ -776,9 +783,9 @@ async def init_modules() -> HostRuntime:
},
)
runtime_configuration = RuntimeConfiguration(
api=lambda: build_api_runtime_config(settings),
scheduler=lambda: build_scheduler_runtime_config(settings),
chain=lambda: build_chain_runtime_config(settings),
api=lambda: build_api_runtime_config(legacy_settings),
scheduler=lambda: build_scheduler_runtime_config(legacy_settings),
chain=lambda: build_chain_runtime_config(legacy_settings),
)
runtime_settings = _build_runtime_settings_service()
agent_chat_persistence = AgentChatPersistenceService(
@@ -831,7 +838,8 @@ async def init_modules() -> HostRuntime:
configure_runtime_configuration(host_runtime.configuration)
configure_runtime_settings(host_runtime.settings)
configure_runtime_setting_provider(lambda key: getattr(legacy_settings, key))
configure_token_runtime_config(lambda: build_token_runtime_config(settings))
configure_runtime_setting_updater(host_runtime.settings.update)
configure_token_runtime_config(lambda: build_token_runtime_config(legacy_settings))
# 旧 app.api.data 导入只保留 ABI 转发,正式 API 依赖全部读取 HostRuntime。
configure_api_data_runtime(api_data)
configure_runtime_data_providers()
@@ -856,7 +864,7 @@ async def init_modules() -> HostRuntime:
configure_outbox_dispatcher(_build_outbox_dispatcher)
configure_transfer_retry_config(
lambda: TransferRetryConfig(
max_failed_retries=settings.TRANSFER_MAX_FAILED_RETRIES,
max_failed_retries=get_runtime_setting("TRANSFER_MAX_FAILED_RETRIES"),
)
)
configure_database_governance(build_database_governance())
+25 -27
View File
@@ -3,7 +3,24 @@ import uuid
from datetime import datetime, timezone
from pathlib import Path
from app.adapters.external.market import (
LOCAL_REPO_PREFIX,
VERSION_BACKWARD_COMPATIBLE_FLAGS,
PluginHelper,
configure_installed_plugins_provider,
configure_plugin_install_gateway,
split_plugin_market_repo_urls,
)
from app.adapters.external.plugin.client import PluginMarketClient
from app.adapters.external.server import MoviePilotServerHelper
from app.adapters.system.host import SystemUtils
from app.adapters.system.plugin.dependency import PluginDependencyInstaller
from app.adapters.system.plugin.manifest import dependency_manifest_status
from app.adapters.system.plugin.package import PluginPackageManager
from app.application.commands import init_commands
from app.application.configuration import get_configured_system_config
from app.application.plugin.catalog import PluginCatalogService
from app.application.plugin.data import DeletePluginDataCommand
from app.application.plugin.gateway import (
PluginInstallGateway,
configure_plugin_install_service,
@@ -32,37 +49,17 @@ from app.application.plugin.transaction import (
get_plugin_persistence,
)
from app.application.scheduling import update_plugin_job
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
from app.db.oper.plugindata import PluginDataOper
from app.db.session import SessionFactory
from app.db.uow import SqlAlchemyUnitOfWork
from app.foundation.version import compare_version
from app.runtime.compat.diagnostics import (
configure_legacy_import_diagnostics,
scan_plugin_legacy_imports,
)
from app.runtime.compat.resource_imports import scan_plugin_resource_imports
from app.runtime.config import global_vars
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.adapters.external.market import (
LOCAL_REPO_PREFIX,
VERSION_BACKWARD_COMPATIBLE_FLAGS,
PluginHelper,
configure_installed_plugins_provider,
configure_plugin_install_gateway,
split_plugin_market_repo_urls,
)
from app.adapters.external.plugin.client import PluginMarketClient
from app.adapters.external.server import MoviePilotServerHelper
from app.adapters.system.host import SystemUtils
from app.adapters.system.plugin.dependency import PluginDependencyInstaller
from app.adapters.system.plugin.manifest import dependency_manifest_status
from app.adapters.system.plugin.package import PluginPackageManager
from app.application.configuration import get_configured_system_config
from app.application.plugin.catalog import PluginCatalogService
from app.application.plugin.data import DeletePluginDataCommand
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
from app.db.oper.plugindata import PluginDataOper
from app.db.session import SessionFactory
from app.db.uow import SqlAlchemyUnitOfWork
from app.foundation.version import compare_version
from app.runtime.execution import run_in_threadpool_to_completion
from app.runtime.extensions.plugin.dependency import PluginDependencyInstallResult
from app.runtime.extensions.plugin.storage import (
@@ -83,6 +80,7 @@ from app.runtime.extensions.plugin_manager import (
)
from app.runtime.log import logger
from app.runtime.managed_resources import acquire_managed_resource
from app.runtime.settings import get_runtime_setting
from app.schemas.exception import PluginMutationRejectedError
from app.schemas.plugin import PluginRuntimeStatus
from app.schemas.types import SystemConfigKey
@@ -130,7 +128,7 @@ def configure_plugin_services() -> None:
async def load_inventory(force: bool):
"""读取本轮配置市场和本地仓库的完整候选事实。"""
return await inventory_reader.async_load(
split_plugin_market_repo_urls(settings.PLUGIN_MARKET),
split_plugin_market_repo_urls(get_runtime_setting('PLUGIN_MARKET')),
force=force,
)
@@ -263,7 +261,7 @@ def configure_plugin_services() -> None:
installed_plugins_provider=lambda: get_configured_system_config().get(
SystemConfigKey.UserInstalledPlugins
) or [],
plugin_dir=Path(settings.ROOT_PATH) / "app" / "plugins",
plugin_dir=Path(get_runtime_setting('ROOT_PATH')) / "app" / "plugins",
),
dependency_manifest_status=dependency_manifest_status,
compatible_flags=lambda flag: (
+14 -16
View File
@@ -26,25 +26,23 @@ try:
except Exception:
pass
from app.application.plugin.lifecycle import plugin_lifecycle
from app.application.plugin.runtime import get_plugin_manager
from app.chain.system import SystemChain
from app.foundation.environment import is_free_threaded_runtime, is_gil_enabled
from app.runtime.config import global_vars
from app.runtime.settings import RuntimeSettingsCompat
from app.runtime.stop import runtime_stop_state
settings = RuntimeSettingsCompat()
from app.adapters.external.server import MoviePilotServerHelper
from app.adapters.network.http import (
aclose_shared_async_transports,
configure_default_user_agent,
)
from app.application.plugin.lifecycle import plugin_lifecycle
from app.application.plugin.runtime import get_plugin_manager
from app.chain.system import SystemChain
from app.db.engine import check_connection_budget, get_engine, get_global_async_engine
from app.foundation.environment import is_free_threaded_runtime, is_gil_enabled
from app.runtime.config import global_vars
from app.runtime.execution import run_in_threadpool_to_completion
from app.runtime.health import get_application_health
from app.runtime.log import LoggerManager, logger
from app.runtime.settings import get_runtime_setting
from app.runtime.state import SystemHelper
from app.runtime.stop import runtime_stop_state
from app.runtime.tasks import TaskRegistry, configure_task_registry
from app.runtime.topology import validate_process_topology
from app.startup.initializers.agent import stop_agent
@@ -90,7 +88,7 @@ async def init_extra():
"""
同步插件及重启相关依赖服务
"""
if settings.MOVIEPILOT_SAFE_MODE:
if get_runtime_setting('MOVIEPILOT_SAFE_MODE'):
SystemHelper().set_system_modified()
SystemChain().restart_finish()
_log_runtime_gil_status()
@@ -355,7 +353,7 @@ def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]:
),
LifecycleComponent(
name="HTTP 基础能力",
start=lambda: configure_default_user_agent(settings.USER_AGENT),
start=lambda: configure_default_user_agent(get_runtime_setting('USER_AGENT')),
stop=aclose_shared_async_transports,
start_order=20,
stop_order=80,
@@ -386,7 +384,7 @@ def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]:
LifecycleComponent(
name="路由",
dependencies=("数据库连接预算",),
start=lambda: init_routers(app, settings.API_V1_STR),
start=lambda: init_routers(app, get_runtime_setting('API_V1_STR')),
start_order=60,
start_timeout_seconds=30,
),
@@ -584,8 +582,8 @@ async def lifespan(app: FastAPI):
active_start_component: LifecycleComponent | None = None
try:
validate_process_topology(
workers=settings.API_WORKERS,
safe_mode=settings.MOVIEPILOT_SAFE_MODE,
workers=get_runtime_setting('API_WORKERS'),
safe_mode=get_runtime_setting('MOVIEPILOT_SAFE_MODE'),
)
print("Starting up...")
main_loop_owner = global_vars.set_loop(main_loop)
@@ -593,7 +591,7 @@ async def lifespan(app: FastAPI):
enabled_components = tuple(
component
for component in components
if component.enabled(settings.MOVIEPILOT_SAFE_MODE)
if component.enabled(get_runtime_setting('MOVIEPILOT_SAFE_MODE'))
)
logger.info(
"启用生命周期组件:%s",
@@ -611,7 +609,7 @@ async def lifespan(app: FastAPI):
)
started_component_names.add(component.name)
active_start_component = None
if settings.MOVIEPILOT_SAFE_MODE:
if get_runtime_setting('MOVIEPILOT_SAFE_MODE'):
print("MoviePilot safe mode enabled: skip plugins, scheduler, monitor, commands and workflow.")
health.mark_ready()
except BaseException: