mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-28 19:47:41 +08:00
126 lines
4.0 KiB
Python
126 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import platform
|
|
import sys
|
|
from datetime import datetime
|
|
from typing import Any, Optional
|
|
|
|
from app.runtime.settings import RuntimeSettingsCompat
|
|
|
|
settings = RuntimeSettingsCompat()
|
|
from app.doctor.checks import default_checks
|
|
from app.doctor.models import (
|
|
DoctorFinding,
|
|
DoctorFindingStatus,
|
|
DoctorReport,
|
|
DoctorSeverity,
|
|
)
|
|
from app.adapters.system.host import SystemUtils
|
|
from version import APP_VERSION
|
|
|
|
|
|
class DoctorRunner:
|
|
"""
|
|
MoviePilot 离线诊断运行器,负责组合检查项并生成报告。
|
|
"""
|
|
|
|
def __init__(self, *, fix: bool = False, deep: bool = False):
|
|
"""
|
|
初始化诊断运行器。
|
|
|
|
:param fix: 是否执行白名单安全修复
|
|
:param deep: 是否执行可能较慢的深度检查
|
|
"""
|
|
self.fix = fix
|
|
self.deep = deep
|
|
self.report = DoctorReport(
|
|
generated_at=datetime.now(),
|
|
version=APP_VERSION,
|
|
environment=self._environment(),
|
|
)
|
|
|
|
def run(self) -> DoctorReport:
|
|
"""
|
|
执行所有默认诊断检查并返回报告。
|
|
"""
|
|
for check in default_checks():
|
|
try:
|
|
check(self)
|
|
except Exception as err:
|
|
self.add(
|
|
finding_id=f"doctor.check_failed.{check.__name__.lstrip('_')}",
|
|
severity=DoctorSeverity.Error,
|
|
status=DoctorFindingStatus.Failed,
|
|
title="诊断检查自身执行失败",
|
|
detail=f"{check.__name__}: {str(err)}",
|
|
recommendation="请把该 doctor 报告附加到反馈 Issue,便于修复诊断器本身。",
|
|
)
|
|
return self.report
|
|
|
|
def add(
|
|
self,
|
|
*,
|
|
finding_id: str,
|
|
severity: DoctorSeverity,
|
|
status: DoctorFindingStatus,
|
|
title: str,
|
|
detail: str,
|
|
recommendation: str,
|
|
fixable: bool = False,
|
|
fixed: bool = False,
|
|
affects_report_status: bool = True,
|
|
context: Optional[dict[str, Any]] = None,
|
|
) -> DoctorFinding:
|
|
"""
|
|
添加诊断发现并返回该对象。
|
|
|
|
:param finding_id: 诊断项稳定标识
|
|
:param severity: 诊断严重级别
|
|
:param status: 单项诊断状态
|
|
:param title: 诊断项标题
|
|
:param detail: 诊断详情
|
|
:param recommendation: 处理建议
|
|
:param fixable: 是否支持 Doctor 自动修复
|
|
:param fixed: 本次运行是否已修复
|
|
:param affects_report_status: 是否参与整体报告状态聚合
|
|
:param context: 可选结构化上下文
|
|
:return: 新增的诊断发现
|
|
"""
|
|
finding = DoctorFinding(
|
|
id=finding_id,
|
|
severity=severity,
|
|
status=status,
|
|
title=title,
|
|
detail=detail,
|
|
recommendation=recommendation,
|
|
fixable=fixable,
|
|
fixed=fixed,
|
|
affects_report_status=affects_report_status,
|
|
context=context or {},
|
|
)
|
|
self.report.add_finding(finding)
|
|
return finding
|
|
|
|
@staticmethod
|
|
def _environment() -> dict[str, Any]:
|
|
"""收集 Doctor 报告所需的本地运行环境信息。"""
|
|
return {
|
|
"runtime": "Docker" if SystemUtils.is_docker() else platform.system(),
|
|
"platform": platform.platform(),
|
|
"python": sys.executable,
|
|
"python_version": platform.python_version(),
|
|
"root_path": str(settings.ROOT_PATH),
|
|
"config_path": str(settings.CONFIG_PATH),
|
|
"log_path": str(settings.LOG_PATH),
|
|
"temp_path": str(settings.TEMP_PATH),
|
|
"is_docker": SystemUtils.is_docker(),
|
|
"safe_mode": settings.MOVIEPILOT_SAFE_MODE,
|
|
"pid": os.getpid(),
|
|
}
|
|
|
|
|
|
def run_doctor(*, fix: bool = False, deep: bool = False) -> DoctorReport:
|
|
"""运行 MoviePilot 离线诊断并返回报告。"""
|
|
return DoctorRunner(fix=fix, deep=deep).run()
|