mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-05-11 00:39:22 +08:00
relax local install python requirement to 3.11
This commit is contained in:
@@ -168,43 +168,74 @@ detect_package_manager() {
|
||||
esac
|
||||
}
|
||||
|
||||
find_python() {
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
command -v python3
|
||||
return 0
|
||||
fi
|
||||
if command -v python >/dev/null 2>&1; then
|
||||
command -v python
|
||||
python_version_ok() {
|
||||
local python_bin="$1"
|
||||
"$python_bin" - <<'PY' >/dev/null 2>&1
|
||||
import sys
|
||||
raise SystemExit(0 if sys.version_info >= (3, 11) else 1)
|
||||
PY
|
||||
}
|
||||
|
||||
try_python_candidate() {
|
||||
local candidate="$1"
|
||||
local python_path=""
|
||||
|
||||
python_path="$(command -v "$candidate" 2>/dev/null || true)"
|
||||
if [[ -n "$python_path" ]] && python_version_ok "$python_path"; then
|
||||
printf '%s\n' "$python_path"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
python_version_ok() {
|
||||
local python_bin="$1"
|
||||
"$python_bin" - <<'PY' >/dev/null 2>&1
|
||||
import sys
|
||||
raise SystemExit(0 if sys.version_info >= (3, 12) else 1)
|
||||
PY
|
||||
find_python() {
|
||||
local minor=""
|
||||
for minor in 20 19 18 17 16 15 14 13 12 11; do
|
||||
if try_python_candidate "python3.$minor"; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
if try_python_candidate python3; then
|
||||
return 0
|
||||
fi
|
||||
if try_python_candidate python; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
find_uv_python() {
|
||||
local uv_bin="$1"
|
||||
local minor=""
|
||||
local python_path=""
|
||||
|
||||
for minor in 20 19 18 17 16 15 14 13 12 11; do
|
||||
python_path="$("$uv_bin" python find "3.$minor" 2>/dev/null || true)"
|
||||
if [[ -n "$python_path" ]] && python_version_ok "$python_path"; then
|
||||
printf '%s\n' "$python_path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
python_install_hint() {
|
||||
case "$OS_NAME" in
|
||||
macOS)
|
||||
echo "脚本已尝试自动安装 Git、curl 和 Python 3.12+。" >&2
|
||||
echo "如果自动安装失败,请先安装 Homebrew,或手动执行:brew install git curl python@3.12" >&2
|
||||
echo "脚本已尝试自动安装 Git、curl 和 Python 3.11+。" >&2
|
||||
echo "如果自动安装失败,请先安装 Homebrew,或手动执行:brew install git curl python@3.11" >&2
|
||||
;;
|
||||
Linux*)
|
||||
echo "脚本已尝试自动安装 Git、curl 和 Python 3.12+。" >&2
|
||||
echo "如果自动安装失败,请先安装 Git、curl 和 Python 3.12,并确保包含 venv 模块。" >&2
|
||||
echo "例如 Debian/Ubuntu: sudo apt install git curl python3.12 python3.12-venv" >&2
|
||||
echo "例如 Fedora/RHEL: sudo dnf install git curl python3.12" >&2
|
||||
echo "脚本已尝试自动安装 Git、curl 和 Python 3.11+。" >&2
|
||||
echo "如果自动安装失败,请先安装 Git、curl 和 Python 3.11+,并确保包含 venv 模块。" >&2
|
||||
echo "例如 Debian/Ubuntu: sudo apt install git curl python3.11 python3.11-venv" >&2
|
||||
echo "例如 Fedora/RHEL: sudo dnf install git curl python3.11" >&2
|
||||
;;
|
||||
Windows)
|
||||
echo "推荐在 WSL、Linux 或 macOS 终端中运行此脚本。" >&2
|
||||
;;
|
||||
*)
|
||||
echo "请先安装 Git、curl 和 Python 3.12。" >&2
|
||||
echo "请先安装 Git、curl 和 Python 3.11 或更高版本。" >&2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -352,7 +383,7 @@ ensure_uv() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "==> 自动安装 uv,用于拉取 Python 3.12+"
|
||||
echo "==> 自动安装 uv,用于补齐 Python 3.11+ 运行时"
|
||||
env UV_INSTALL_DIR="$HOME/.local/bin" sh -c "$(curl -LsSf https://astral.sh/uv/install.sh)"
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
hash -r
|
||||
@@ -369,12 +400,18 @@ ensure_python() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "==> 未找到可用的 Python 3.12+,开始自动安装独立 Python 运行时"
|
||||
ensure_uv
|
||||
uv python install 3.12
|
||||
PYTHON_BIN="$(uv python find 3.12 || true)"
|
||||
|
||||
PYTHON_BIN="$(find_uv_python "$(command -v uv)" || true)"
|
||||
if [[ -n "$PYTHON_BIN" ]] && python_version_ok "$PYTHON_BIN"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "==> 未找到可用的 Python 3.11+,开始自动安装独立 Python 运行时"
|
||||
uv python install 3.11
|
||||
PYTHON_BIN="$(find_uv_python "$(command -v uv)" || true)"
|
||||
if [[ -z "$PYTHON_BIN" ]] || ! python_version_ok "$PYTHON_BIN"; then
|
||||
echo "自动安装 Python 3.12+ 失败。" >&2
|
||||
echo "自动安装 Python 3.11+ 失败。" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ PUBLIC_DIR = ROOT / "public"
|
||||
RUNTIME_DIR = ROOT / ".runtime"
|
||||
NODE_DIR = RUNTIME_DIR / "node"
|
||||
INSTALL_ENV_FILE = ROOT / ".moviepilot.env"
|
||||
MIN_PYTHON_VERSION = (3, 11)
|
||||
SUPPORTED_PYTHON_TEXT = f"Python {MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]} 或更高版本"
|
||||
|
||||
CONFIG_DIR = LEGACY_CONFIG_DIR
|
||||
LOG_DIR = CONFIG_DIR / "logs"
|
||||
@@ -204,6 +206,42 @@ def command_exists(name: str) -> bool:
|
||||
return shutil.which(name) is not None
|
||||
|
||||
|
||||
def get_python_version(python_bin: str) -> tuple[int, int, int]:
|
||||
version_json = capture([python_bin, "-c", "import json, sys; print(json.dumps(list(sys.version_info[:3])))"])
|
||||
version_info = json.loads(version_json)
|
||||
if not isinstance(version_info, list) or len(version_info) < 3:
|
||||
raise RuntimeError(f"无法识别 Python 版本信息:{python_bin}")
|
||||
return int(version_info[0]), int(version_info[1]), int(version_info[2])
|
||||
|
||||
|
||||
def discover_supported_python() -> Optional[str]:
|
||||
candidates = [f"python3.{minor}" for minor in range(20, MIN_PYTHON_VERSION[1] - 1, -1)]
|
||||
if sys.executable:
|
||||
candidates.append(sys.executable)
|
||||
candidates.extend(["python3", "python"])
|
||||
|
||||
seen: set[str] = set()
|
||||
for candidate in candidates:
|
||||
if not candidate or candidate in seen:
|
||||
continue
|
||||
seen.add(candidate)
|
||||
|
||||
python_path = candidate if os.sep in candidate else (shutil.which(candidate) or "")
|
||||
if not python_path:
|
||||
continue
|
||||
|
||||
try:
|
||||
version = get_python_version(python_path)
|
||||
except (OSError, subprocess.CalledProcessError, json.JSONDecodeError):
|
||||
continue
|
||||
if version >= MIN_PYTHON_VERSION:
|
||||
return python_path
|
||||
return None
|
||||
|
||||
|
||||
DEFAULT_BOOTSTRAP_PYTHON = discover_supported_python() or sys.executable
|
||||
|
||||
|
||||
def get_venv_python(venv_dir: Path) -> Path:
|
||||
if os.name == "nt":
|
||||
return venv_dir / "Scripts" / "python.exe"
|
||||
@@ -211,11 +249,10 @@ def get_venv_python(venv_dir: Path) -> Path:
|
||||
|
||||
|
||||
def ensure_supported_python(python_bin: str) -> None:
|
||||
version_json = capture([python_bin, "-c", "import json, sys; print(json.dumps(list(sys.version_info[:3])))"])
|
||||
version = tuple(json.loads(version_json))
|
||||
if version < (3, 12, 0):
|
||||
version = get_python_version(python_bin)
|
||||
if version < MIN_PYTHON_VERSION:
|
||||
raise RuntimeError(
|
||||
f"MoviePilot 本地安装需要 Python 3.12 或更高版本,当前解释器为 {python_bin} "
|
||||
f"MoviePilot 本地安装需要 {SUPPORTED_PYTHON_TEXT},当前解释器为 {python_bin} "
|
||||
f"({version[0]}.{version[1]}.{version[2]})"
|
||||
)
|
||||
|
||||
@@ -1359,6 +1396,7 @@ def install_deps(*, python_bin: str, venv_dir: Path, recreate: bool) -> Path:
|
||||
ensure_supported_python(python_bin)
|
||||
venv_dir = venv_dir.expanduser().resolve()
|
||||
venv_python = get_venv_python(venv_dir)
|
||||
print_step(f"使用 Python 解释器:{python_bin}")
|
||||
|
||||
if recreate and venv_dir.exists():
|
||||
print_step(f"删除已有虚拟环境:{venv_dir}")
|
||||
@@ -1512,7 +1550,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
install_parser = subparsers.add_parser("install-deps", help="创建虚拟环境并安装后端依赖")
|
||||
install_parser.add_argument("--python", default=sys.executable, help="用于创建虚拟环境的 Python 解释器")
|
||||
install_parser.add_argument("--python", default=DEFAULT_BOOTSTRAP_PYTHON, help="用于创建虚拟环境的 Python 解释器,默认自动选择本地 3.11+ 版本")
|
||||
install_parser.add_argument("--venv", default=str(ROOT / "venv"), help="虚拟环境目录")
|
||||
install_parser.add_argument("--recreate", action="store_true", help="删除并重建虚拟环境")
|
||||
install_parser.add_argument("--config-dir", help="配置目录,默认使用程序目录外的系统配置目录")
|
||||
@@ -1536,7 +1574,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
init_parser.add_argument("--config-dir", help="配置目录,默认使用程序目录外的系统配置目录")
|
||||
|
||||
setup_parser = subparsers.add_parser("setup", help="执行 install-deps、install-frontend、install-resources 和 init")
|
||||
setup_parser.add_argument("--python", default=sys.executable, help="用于创建虚拟环境的 Python 解释器")
|
||||
setup_parser.add_argument("--python", default=DEFAULT_BOOTSTRAP_PYTHON, help="用于创建虚拟环境的 Python 解释器,默认自动选择本地 3.11+ 版本")
|
||||
setup_parser.add_argument("--venv", default=str(ROOT / "venv"), help="虚拟环境目录")
|
||||
setup_parser.add_argument("--recreate", action="store_true", help="删除并重建虚拟环境")
|
||||
setup_parser.add_argument("--frontend-version", default="latest", help="前端版本,默认 latest")
|
||||
@@ -1560,7 +1598,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
update_parser.add_argument("--ref", default="latest", help="后端 Git 版本,默认 latest")
|
||||
update_parser.add_argument("--frontend-version", default="latest", help="前端版本,默认 latest")
|
||||
update_parser.add_argument("--node-version", default=DEFAULT_NODE_VERSION, help="本地 Node 运行时版本")
|
||||
update_parser.add_argument("--python", default=sys.executable, help="用于安装后端依赖的 Python 解释器")
|
||||
update_parser.add_argument("--python", default=DEFAULT_BOOTSTRAP_PYTHON, help="用于安装后端依赖的 Python 解释器,默认自动选择本地 3.11+ 版本")
|
||||
update_parser.add_argument("--venv", default=str(ROOT / "venv"), help="虚拟环境目录")
|
||||
update_parser.add_argument("--recreate", action="store_true", help="删除并重建虚拟环境")
|
||||
update_parser.add_argument("--skip-resources", action="store_true", help="更新 all 时跳过资源同步")
|
||||
|
||||
Reference in New Issue
Block a user