mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 16:07:01 +08:00
refactor: complete runtime configuration migration
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
"""把可变部署设置转换成宿主各领域使用的类型化配置快照。"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from app.application.configuration import (
|
||||
ApiRuntimeConfig,
|
||||
ChainRuntimeConfig,
|
||||
SchedulerRuntimeConfig,
|
||||
)
|
||||
from app.runtime.config import Settings
|
||||
from app.schemas.types import MediaType
|
||||
|
||||
|
||||
def normalize_subscribe_rss_interval(value: object) -> int:
|
||||
"""把无效或过小的 RSS 间隔收敛为兼容的安全值。"""
|
||||
try:
|
||||
if not isinstance(value, (str, bytes, bytearray, int, float)):
|
||||
return 30
|
||||
return max(int(value), 5)
|
||||
except (TypeError, ValueError):
|
||||
return 30
|
||||
|
||||
|
||||
def build_api_runtime_config(settings: Settings) -> ApiRuntimeConfig:
|
||||
"""从可热更新的部署设置构建一次 API 请求配置快照。"""
|
||||
return ApiRuntimeConfig(
|
||||
advanced_mode=settings.ADVANCED_MODE,
|
||||
access_token_expire_minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES,
|
||||
btrfs_fsid_dedup=settings.BTRFS_FSID_DEDUP,
|
||||
ai_agent_enable=settings.AI_AGENT_ENABLE,
|
||||
api_token=settings.API_TOKEN,
|
||||
temp_path=settings.TEMP_PATH,
|
||||
media_recognize_share=settings.MEDIA_RECOGNIZE_SHARE,
|
||||
subscribe_mode=settings.SUBSCRIBE_MODE,
|
||||
search_source=settings.SEARCH_SOURCE,
|
||||
media_extensions=tuple(settings.RMT_MEDIAEXT),
|
||||
subtitle_extensions=tuple(settings.RMT_SUBEXT),
|
||||
audio_extensions=tuple(settings.RMT_AUDIOEXT),
|
||||
movie_rename_format=settings.RENAME_FORMAT(MediaType.MOVIE),
|
||||
television_rename_format=settings.RENAME_FORMAT(MediaType.TV),
|
||||
music_rename_format=settings.RENAME_FORMAT(MediaType.MUSIC),
|
||||
vapid_private_key=settings.VAPID.get("privateKey", ""),
|
||||
vapid_subject=settings.VAPID.get("subject", ""),
|
||||
cookiecloud_enable_local=bool(settings.COOKIECLOUD_ENABLE_LOCAL),
|
||||
cookiecloud_auth_header=settings.COOKIECLOUD_AUTH_HEADER,
|
||||
cookie_path=settings.COOKIE_PATH,
|
||||
root_path=settings.ROOT_PATH,
|
||||
version_flag=settings.VERSION_FLAG,
|
||||
)
|
||||
|
||||
|
||||
def build_scheduler_runtime_config(settings: Settings) -> SchedulerRuntimeConfig:
|
||||
"""从可热更新的部署设置构建一次 Scheduler 配置快照。"""
|
||||
return SchedulerRuntimeConfig(
|
||||
dev=settings.DEV,
|
||||
timezone=settings.TZ,
|
||||
scheduler_workers=settings.CONF.scheduler,
|
||||
db_backup_enable=settings.DB_BACKUP_ENABLE,
|
||||
db_backup_cron=settings.DB_BACKUP_CRON,
|
||||
cookiecloud_interval=settings.COOKIECLOUD_INTERVAL,
|
||||
mediaserver_sync_interval=settings.MEDIASERVER_SYNC_INTERVAL,
|
||||
subscribe_search=settings.SUBSCRIBE_SEARCH,
|
||||
subscribe_search_interval=settings.SUBSCRIBE_SEARCH_INTERVAL,
|
||||
subscribe_mode=settings.SUBSCRIBE_MODE,
|
||||
subscribe_rss_interval=normalize_subscribe_rss_interval(
|
||||
settings.SUBSCRIBE_RSS_INTERVAL
|
||||
),
|
||||
data_cleanup_enable=settings.DATA_CLEANUP_ENABLE,
|
||||
sitedata_refresh_interval=settings.SITEDATA_REFRESH_INTERVAL,
|
||||
memory_gc_interval=settings.MEMORY_GC_INTERVAL,
|
||||
ai_agent_enable=settings.AI_AGENT_ENABLE,
|
||||
ai_agent_job_interval=settings.AI_AGENT_JOB_INTERVAL,
|
||||
usage_statistic_share=settings.USAGE_STATISTIC_SHARE,
|
||||
site_link=settings.MP_DOMAIN("#/site"),
|
||||
)
|
||||
|
||||
|
||||
def build_chain_runtime_config(settings: Settings) -> ChainRuntimeConfig:
|
||||
"""从部署设置构建 Chain 在本次实例生命周期使用的配置快照。"""
|
||||
return ChainRuntimeConfig(
|
||||
media_extensions=tuple(
|
||||
settings.RMT_MEDIAEXT
|
||||
+ settings.DOWNLOAD_TMPEXT
|
||||
+ settings.RMT_SUBEXT
|
||||
+ settings.RMT_AUDIOEXT
|
||||
),
|
||||
video_extensions=tuple(settings.RMT_MEDIAEXT),
|
||||
subtitle_extensions=tuple(settings.RMT_SUBEXT),
|
||||
audio_extensions=tuple(settings.RMT_AUDIOEXT),
|
||||
temporary_path=settings.TEMP_PATH,
|
||||
root_path=settings.ROOT_PATH,
|
||||
config_path=settings.CONFIG_PATH,
|
||||
frontend_path=Path(settings.FRONTEND_PATH),
|
||||
superuser=settings.SUPERUSER,
|
||||
media_recognize_share=settings.MEDIA_RECOGNIZE_SHARE,
|
||||
auxiliary_auth_enable=settings.AUXILIARY_AUTH_ENABLE,
|
||||
global_image_cache=settings.GLOBAL_IMAGE_CACHE,
|
||||
download_subtitle=settings.DOWNLOAD_SUBTITLE,
|
||||
music_metadata_to_simplified=settings.MUSIC_METADATA_TO_SIMPLIFIED,
|
||||
recognize_plugin_first=settings.RECOGNIZE_PLUGIN_FIRST,
|
||||
ai_agent_enable=settings.AI_AGENT_ENABLE,
|
||||
ai_agent_global=settings.AI_AGENT_GLOBAL,
|
||||
ai_agent_retry_transfer=settings.AI_AGENT_RETRY_TRANSFER,
|
||||
llm_provider=settings.LLM_PROVIDER,
|
||||
llm_model=settings.LLM_MODEL,
|
||||
search_resource_pages=settings.SEARCH_RESOURCE_PAGES,
|
||||
ai_recommend_enabled=settings.AI_RECOMMEND_ENABLED,
|
||||
ai_recommend_max_items=settings.AI_RECOMMEND_MAX_ITEMS,
|
||||
ai_recommend_user_preference=settings.AI_RECOMMEND_USER_PREFERENCE,
|
||||
max_search_name_limit=settings.MAX_SEARCH_NAME_LIMIT,
|
||||
search_multiple_name=settings.SEARCH_MULTIPLE_NAME,
|
||||
search_threadpool_size=settings.CONF.threadpool,
|
||||
transfer_threads=settings.TRANSFER_THREADS,
|
||||
transfer_failure_notification_aggregation=(
|
||||
settings.TRANSFER_FAILURE_NOTIFICATION_AGGREGATION
|
||||
),
|
||||
transfer_task_timeout=settings.TRANSFER_TASK_TIMEOUT,
|
||||
scrape_follow_tmdb=settings.SCRAP_FOLLOW_TMDB,
|
||||
metadata_cache_ttl=settings.CONF.meta,
|
||||
auto_download_user=settings.AUTO_DOWNLOAD_USER,
|
||||
resource_url=settings.MP_DOMAIN("#/resource"),
|
||||
history_url=settings.MP_DOMAIN("#/history"),
|
||||
downloading_url=settings.MP_DOMAIN("#/downloading"),
|
||||
movie_subscribe_url=settings.MP_DOMAIN("#/subscribe/movie?tab=mysub"),
|
||||
television_subscribe_url=settings.MP_DOMAIN("#/subscribe/tv?tab=mysub"),
|
||||
music_subscribe_url=settings.MP_DOMAIN("#/subscribe/music?tab=mysub"),
|
||||
user_agent=settings.USER_AGENT,
|
||||
normal_user_agent=settings.NORMAL_USER_AGENT,
|
||||
proxy=settings.PROXY,
|
||||
proxy_server=settings.PROXY_SERVER,
|
||||
proxy_host=settings.PROXY_HOST,
|
||||
github_headers=settings.GITHUB_HEADERS,
|
||||
cookiecloud_blacklist=settings.COOKIECLOUD_BLACKLIST,
|
||||
subscribe_mode=settings.SUBSCRIBE_MODE,
|
||||
no_cache_site_key=settings.NO_CACHE_SITE_KEY,
|
||||
refresh_batch_size=settings.CONF.refresh,
|
||||
torrent_cache_size=settings.CONF.torrents,
|
||||
site_url=settings.MP_DOMAIN("#/site"),
|
||||
season_zero_names=tuple(settings.RENAME_FORMAT_S0_NAMES),
|
||||
movie_rename_format=settings.RENAME_FORMAT(MediaType.MOVIE),
|
||||
television_rename_format=settings.RENAME_FORMAT(MediaType.TV),
|
||||
music_rename_format=settings.RENAME_FORMAT(MediaType.MUSIC),
|
||||
tmdb_image_domain=settings.TMDB_IMAGE_DOMAIN,
|
||||
)
|
||||
@@ -9,7 +9,7 @@ from app.application.messaging.chat import (
|
||||
AsyncUnitOfWork,
|
||||
)
|
||||
from app.application.outbox import AsyncOutboxTransaction
|
||||
from app.application.configuration import RuntimeConfiguration
|
||||
from app.application.configuration import RuntimeConfiguration, RuntimeSettingsService
|
||||
from app.application.subscription.delete import SubscribeDeletionRepository
|
||||
from app.application.subscription.identity import SubscribeIdentityDeletionRepository
|
||||
from app.application.subscription.mutation import (
|
||||
@@ -189,3 +189,4 @@ class HostRuntime:
|
||||
subscription: SubscriptionRuntime
|
||||
workflow: WorkflowRuntime
|
||||
configuration: RuntimeConfiguration
|
||||
settings: RuntimeSettingsService
|
||||
|
||||
@@ -14,8 +14,20 @@ from app.runtime.config import settings
|
||||
from app.db.base import Base
|
||||
from app.db.engine import get_engine
|
||||
from app.db.models import load_all_models
|
||||
from app.db.session import SessionFactory, async_session_scope
|
||||
from app.db.uow import configure_transaction_runners
|
||||
from app.runtime.log import logger
|
||||
from app.startup.database import build_database_governance
|
||||
from app.startup.transaction import TransactionalWriteRunner
|
||||
|
||||
|
||||
def _configure_migration_transaction_runner() -> None:
|
||||
"""在 Alembic 调用旧无会话 Oper 前装配可独立提交的兼容事务。"""
|
||||
runner = TransactionalWriteRunner(
|
||||
sync_session=SessionFactory,
|
||||
async_session=async_session_scope,
|
||||
)
|
||||
configure_transaction_runners(sync=runner.sync, async_=runner.async_)
|
||||
|
||||
|
||||
def _build_alembic_config(engine: Engine | None = None) -> Config:
|
||||
@@ -149,6 +161,8 @@ def update_db(alembic_cfg: Config | None = None):
|
||||
更新数据库
|
||||
"""
|
||||
try:
|
||||
# 早期迁移脚本会调用 SystemConfigOper(),此时 modules_initializer 尚未执行。
|
||||
_configure_migration_transaction_runner()
|
||||
alembic_cfg = alembic_cfg or _build_alembic_config()
|
||||
upgrade(alembic_cfg, 'head')
|
||||
except Exception as error:
|
||||
|
||||
@@ -37,16 +37,20 @@ from app.application.messaging.message import (
|
||||
stop_message,
|
||||
)
|
||||
from app.application.configuration import (
|
||||
ApiRuntimeConfig,
|
||||
ChainRuntimeConfig,
|
||||
RuntimeConfiguration,
|
||||
SchedulerRuntimeConfig,
|
||||
RuntimeSettingsService,
|
||||
SystemConfigService,
|
||||
TransferRetryConfig,
|
||||
configure_runtime_configuration,
|
||||
configure_runtime_settings,
|
||||
configure_system_config,
|
||||
configure_transfer_retry_config,
|
||||
)
|
||||
from app.startup.configuration import (
|
||||
build_api_runtime_config,
|
||||
build_chain_runtime_config,
|
||||
build_scheduler_runtime_config,
|
||||
)
|
||||
from app.application.database import configure_database_governance
|
||||
from app.application.service import configure_service_directory
|
||||
from app.application.plugin.runtime import configure_plugin_runtime
|
||||
@@ -173,88 +177,12 @@ def _build_chain_runtime_context() -> ChainRuntimeContext:
|
||||
send_callback=callback
|
||||
),
|
||||
module_dispatcher_factory=ModuleInvocationDispatcher,
|
||||
configuration=_build_chain_runtime_config(),
|
||||
configuration=build_chain_runtime_config(settings),
|
||||
data_ports=get_chain_data_ports(),
|
||||
durable_event_writer=TransactionalChainDurableEventWriter(SessionFactory),
|
||||
)
|
||||
|
||||
|
||||
def _normalize_subscribe_rss_interval(value: object) -> int:
|
||||
"""把无效或过小的 RSS 间隔收敛为兼容的安全值。"""
|
||||
try:
|
||||
return max(int(value), 5)
|
||||
except (TypeError, ValueError):
|
||||
return 30
|
||||
|
||||
|
||||
def _build_api_runtime_config() -> ApiRuntimeConfig:
|
||||
"""从可热更新 settings 构建一次 API 请求配置快照。"""
|
||||
return ApiRuntimeConfig(
|
||||
advanced_mode=settings.ADVANCED_MODE,
|
||||
access_token_expire_minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES,
|
||||
btrfs_fsid_dedup=settings.BTRFS_FSID_DEDUP,
|
||||
ai_agent_enable=settings.AI_AGENT_ENABLE,
|
||||
api_token=settings.API_TOKEN,
|
||||
temp_path=settings.TEMP_PATH,
|
||||
media_recognize_share=settings.MEDIA_RECOGNIZE_SHARE,
|
||||
subscribe_mode=settings.SUBSCRIBE_MODE,
|
||||
)
|
||||
|
||||
|
||||
def _build_scheduler_runtime_config() -> SchedulerRuntimeConfig:
|
||||
"""从可热更新 settings 构建一次 Scheduler 操作配置快照。"""
|
||||
return SchedulerRuntimeConfig(
|
||||
dev=settings.DEV,
|
||||
timezone=settings.TZ,
|
||||
scheduler_workers=settings.CONF.scheduler,
|
||||
db_backup_enable=settings.DB_BACKUP_ENABLE,
|
||||
db_backup_cron=settings.DB_BACKUP_CRON,
|
||||
cookiecloud_interval=settings.COOKIECLOUD_INTERVAL,
|
||||
mediaserver_sync_interval=settings.MEDIASERVER_SYNC_INTERVAL,
|
||||
subscribe_search=settings.SUBSCRIBE_SEARCH,
|
||||
subscribe_search_interval=settings.SUBSCRIBE_SEARCH_INTERVAL,
|
||||
subscribe_mode=settings.SUBSCRIBE_MODE,
|
||||
subscribe_rss_interval=_normalize_subscribe_rss_interval(
|
||||
settings.SUBSCRIBE_RSS_INTERVAL
|
||||
),
|
||||
data_cleanup_enable=settings.DATA_CLEANUP_ENABLE,
|
||||
sitedata_refresh_interval=settings.SITEDATA_REFRESH_INTERVAL,
|
||||
memory_gc_interval=settings.MEMORY_GC_INTERVAL,
|
||||
ai_agent_enable=settings.AI_AGENT_ENABLE,
|
||||
ai_agent_job_interval=settings.AI_AGENT_JOB_INTERVAL,
|
||||
usage_statistic_share=settings.USAGE_STATISTIC_SHARE,
|
||||
site_link=settings.MP_DOMAIN("#/site"),
|
||||
)
|
||||
|
||||
|
||||
def _build_chain_runtime_config() -> ChainRuntimeConfig:
|
||||
"""构建 Chain 在本次实例生命周期内使用的部署配置快照。"""
|
||||
return ChainRuntimeConfig(
|
||||
media_extensions=tuple(
|
||||
settings.RMT_MEDIAEXT
|
||||
+ settings.DOWNLOAD_TMPEXT
|
||||
+ settings.RMT_SUBEXT
|
||||
+ settings.RMT_AUDIOEXT
|
||||
),
|
||||
superuser=settings.SUPERUSER,
|
||||
media_recognize_share=settings.MEDIA_RECOGNIZE_SHARE,
|
||||
auxiliary_auth_enable=settings.AUXILIARY_AUTH_ENABLE,
|
||||
global_image_cache=settings.GLOBAL_IMAGE_CACHE,
|
||||
auto_download_user=settings.AUTO_DOWNLOAD_USER,
|
||||
resource_url=settings.MP_DOMAIN("#/resource"),
|
||||
user_agent=settings.USER_AGENT,
|
||||
proxy=settings.PROXY,
|
||||
proxy_server=settings.PROXY_SERVER,
|
||||
proxy_host=settings.PROXY_HOST,
|
||||
cookiecloud_blacklist=settings.COOKIECLOUD_BLACKLIST,
|
||||
subscribe_mode=settings.SUBSCRIBE_MODE,
|
||||
no_cache_site_key=settings.NO_CACHE_SITE_KEY,
|
||||
refresh_batch_size=settings.CONF.refresh,
|
||||
torrent_cache_size=settings.CONF.torrents,
|
||||
site_url=settings.MP_DOMAIN("#/site"),
|
||||
)
|
||||
|
||||
|
||||
def configure_runtime_data_providers() -> None:
|
||||
"""在启动组合层装配运行时和外部服务所需的数据库读取能力。"""
|
||||
configure_service_config_reader(lambda key: SystemConfigOper().get(key))
|
||||
@@ -591,10 +519,11 @@ async def init_modules() -> HostRuntime:
|
||||
},
|
||||
)
|
||||
runtime_configuration = RuntimeConfiguration(
|
||||
api=_build_api_runtime_config,
|
||||
scheduler=_build_scheduler_runtime_config,
|
||||
chain=_build_chain_runtime_config,
|
||||
api=lambda: build_api_runtime_config(settings),
|
||||
scheduler=lambda: build_scheduler_runtime_config(settings),
|
||||
chain=lambda: build_chain_runtime_config(settings),
|
||||
)
|
||||
runtime_settings = RuntimeSettingsService(settings)
|
||||
host_runtime = HostRuntime(
|
||||
agent_chat=AgentChatRuntime(
|
||||
async_session=get_async_db,
|
||||
@@ -632,8 +561,10 @@ async def init_modules() -> HostRuntime:
|
||||
system_config=SystemConfigOper,
|
||||
),
|
||||
configuration=runtime_configuration,
|
||||
settings=runtime_settings,
|
||||
)
|
||||
configure_runtime_configuration(host_runtime.configuration)
|
||||
configure_runtime_settings(host_runtime.settings)
|
||||
# 旧 app.api.data 导入只保留 ABI 转发,正式 API 依赖全部读取 HostRuntime。
|
||||
configure_api_data_runtime(api_data)
|
||||
configure_runtime_data_providers()
|
||||
|
||||
Reference in New Issue
Block a user