Files
MoviePilot/app/startup/lifecycle.py
2026-07-16 06:24:31 +08:00

132 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import asyncio
import inspect
from contextlib import asynccontextmanager
from typing import Callable
from fastapi import FastAPI
# urllib3-future 覆盖 urllib3 命名空间后删除了 format_header_param导致 telebot 崩溃,需在加载模块前打补丁
try:
import urllib3.fields as _urllib3_fields
if not hasattr(_urllib3_fields, "format_header_param") and hasattr(
_urllib3_fields, "format_header_param_rfc2231"
):
_urllib3_fields.format_header_param = (
_urllib3_fields.format_header_param_rfc2231
)
except Exception:
pass
from app.chain.system import SystemChain
from app.core.config import global_vars, settings
from app.helper.server import MoviePilotServerHelper
from app.helper.system import SystemHelper
from app.log import logger, LoggerManager
from app.startup.command_initializer import init_command, stop_command, restart_command
from app.startup.modules_initializer import init_modules, stop_modules
from app.startup.monitor_initializer import stop_monitor, init_monitor
from app.startup.plugins_initializer import init_plugins, stop_plugins, sync_plugins
from app.startup.routers_initializer import init_routers
from app.startup.scheduler_initializer import (
stop_scheduler,
init_scheduler,
init_plugin_scheduler,
)
from app.startup.workflow_initializer import init_workflow, stop_workflow
from app.utils.http import aclose_shared_async_transports
async def init_extra():
"""
同步插件及重启相关依赖服务
"""
if settings.MOVIEPILOT_SAFE_MODE:
SystemHelper().set_system_modified()
SystemChain().restart_finish()
return
if await sync_plugins():
# 重新注册插件定时服务
init_plugin_scheduler()
# 重新注册命令
restart_command()
# 设置系统已修改标志
SystemHelper().set_system_modified()
# 重启完成
SystemChain().restart_finish()
# 上报当前安装版本
await MoviePilotServerHelper.async_report_usage()
async def run_shutdown_step(name: str, callback: Callable[[], object]) -> None:
"""隔离单个关闭阶段的异常,确保后续资源仍有机会释放"""
try:
result = callback()
if inspect.isawaitable(result):
await result
except Exception as err:
logger.error(f"关闭{name}失败:{err}")
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
定义应用的生命周期事件
"""
print("Starting up...")
# 存储当前循环
global_vars.set_loop(asyncio.get_event_loop())
# 初始化路由
init_routers(app)
# 初始化模块
init_modules()
if settings.MOVIEPILOT_SAFE_MODE:
print("MoviePilot safe mode enabled: skip plugins, scheduler, monitor, commands and workflow.")
else:
# 恢复插件备份
SystemChain().restore_plugins()
# 初始化插件
init_plugins()
# 初始化定时器
init_scheduler()
# 初始化监控器
init_monitor()
# 初始化命令
init_command()
# 初始化工作流
init_workflow()
# 插件同步到本地
sync_plugins_task = asyncio.create_task(init_extra())
try:
# 在此处 yield表示应用已经启动控制权交回 FastAPI 主事件循环
yield
finally:
print("Shutting down...")
global_vars.stop_system()
# 取消同步插件任务
try:
sync_plugins_task.cancel()
await sync_plugins_task
except asyncio.CancelledError:
pass
except Exception as e:
print(str(e))
try:
if not settings.MOVIEPILOT_SAFE_MODE:
await run_shutdown_step(
"插件备份", lambda: SystemChain().backup_plugins()
)
await run_shutdown_step("工作流", stop_workflow)
await run_shutdown_step("命令服务", stop_command)
await run_shutdown_step("监控器", stop_monitor)
await run_shutdown_step("定时器", stop_scheduler)
await run_shutdown_step("插件", stop_plugins)
await run_shutdown_step("模块服务", stop_modules)
await run_shutdown_step(
"共享异步 HTTP 连接池",
aclose_shared_async_transports,
)
finally:
# 日志最后关闭,确保其他组件的收尾信息已写入文件
LoggerManager.shutdown()