mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-05-13 07:26:45 +00:00
relax local install python requirement to 3.11
This commit is contained in:
11
docs/cli.md
11
docs/cli.md
@@ -11,7 +11,7 @@ curl -fsSL https://raw.githubusercontent.com/jxxghp/MoviePilot/v2/scripts/bootst
|
||||
脚本会自动:
|
||||
|
||||
- 检测操作系统
|
||||
- 自动检查并尽量安装 `git`、`curl`、`Python 3.12+`
|
||||
- 自动检查并尽量安装 `git`、`curl`、`Python 3.11+`
|
||||
- 克隆 `MoviePilot`
|
||||
- 安装后端依赖
|
||||
- 下载 `MoviePilot-Frontend` 最新 release 的 `dist.zip`
|
||||
@@ -24,7 +24,8 @@ curl -fsSL https://raw.githubusercontent.com/jxxghp/MoviePilot/v2/scripts/bootst
|
||||
|
||||
说明:
|
||||
|
||||
- 如果系统里没有可用的 `Python 3.12+`,脚本会优先尝试自动补齐运行环境,再继续安装
|
||||
- 如果系统里已经有可用的 `Python 3.11+`,脚本会优先直接复用本地解释器
|
||||
- 如果系统里没有可用的 `Python 3.11+`,脚本会再尝试自动补齐运行环境
|
||||
- Linux 下安装系统依赖时通常需要 `sudo`
|
||||
- 复用已有仓库时,脚本现在只会因为已跟踪源码改动而阻止自动更新,不会再被 `.DS_Store` 之类未跟踪文件卡住
|
||||
|
||||
@@ -141,12 +142,16 @@ moviepilot commands
|
||||
|
||||
```shell
|
||||
moviepilot install deps
|
||||
moviepilot install deps --python python3.12
|
||||
moviepilot install deps --python python3.11
|
||||
moviepilot install deps --venv /path/to/venv
|
||||
moviepilot install deps --recreate
|
||||
moviepilot install deps --config-dir /path/to/moviepilot-config
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 默认会自动选择本地已安装的 `Python 3.11+` 解释器
|
||||
|
||||
安装前端 release:
|
||||
|
||||
```shell
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
在开始之前,请确保您的系统已安装以下软件:
|
||||
|
||||
- **Python 3.12 或更高版本** (暂时兼容 3.11 ,推荐使用 3.12+)
|
||||
- **Python 3.11 或更高版本**
|
||||
- **pip** (Python 包管理器)
|
||||
- **Git** (用于版本控制)
|
||||
|
||||
@@ -119,4 +119,4 @@ safety check -r requirements.txt --policy-file=safety.policy.yml > safety_report
|
||||
### 5. 参考资源
|
||||
|
||||
- [pip-tools 官方文档](https://github.com/jazzband/pip-tools)
|
||||
- [safety 官方文档](https://pyup.io/safety/)
|
||||
- [safety 官方文档](https://pyup.io/safety/)
|
||||
|
||||
94
moviepilot
94
moviepilot
@@ -91,7 +91,7 @@ Usage:
|
||||
|
||||
Options:
|
||||
deps:
|
||||
--python PYTHON 用于创建虚拟环境的 Python 解释器
|
||||
--python PYTHON 用于创建虚拟环境的 Python 解释器,默认自动选择本地 3.11+ 版本
|
||||
--venv PATH 虚拟环境目录,默认 ./venv
|
||||
--recreate 删除并重建虚拟环境
|
||||
--config-dir PATH 指定配置目录
|
||||
@@ -128,7 +128,7 @@ show_setup_help() {
|
||||
Usage: moviepilot setup [OPTIONS]
|
||||
|
||||
Options:
|
||||
--python PYTHON 用于创建虚拟环境的 Python 解释器
|
||||
--python PYTHON 用于创建虚拟环境的 Python 解释器,默认自动选择本地 3.11+ 版本
|
||||
--venv PATH 虚拟环境目录,默认 ./venv
|
||||
--recreate 删除并重建虚拟环境
|
||||
--frontend-version TAG 前端版本,默认 latest
|
||||
@@ -152,7 +152,7 @@ Options:
|
||||
--ref REF 后端 Git 版本,默认 latest
|
||||
--frontend-version TAG 前端版本,默认 latest
|
||||
--node-version VER 本地 Node 运行时版本,默认 20.12.1
|
||||
--python PYTHON 用于安装后端依赖的 Python 解释器
|
||||
--python PYTHON 用于安装后端依赖的 Python 解释器,默认自动选择本地 3.11+ 版本
|
||||
--venv PATH 虚拟环境目录,默认 ./venv
|
||||
--recreate 删除并重建虚拟环境
|
||||
--skip-resources 更新 all 时跳过资源同步
|
||||
@@ -179,39 +179,60 @@ 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)
|
||||
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
|
||||
}
|
||||
|
||||
find_system_python() {
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
local python3_bin
|
||||
python3_bin="$(command -v python3)"
|
||||
if python_version_ok "$python3_bin"; then
|
||||
printf '%s\n' "$python3_bin"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
if command -v python >/dev/null 2>&1; then
|
||||
local python_bin
|
||||
python_bin="$(command -v python)"
|
||||
if python_version_ok "$python_bin"; then
|
||||
printf '%s\n' "$python_bin"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
local minor
|
||||
local uv_bin
|
||||
local uv_python
|
||||
|
||||
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
|
||||
for uv_bin in "$(command -v uv 2>/dev/null || true)" "$HOME/.local/bin/uv"; do
|
||||
if [ -n "$uv_bin" ] && [ -x "$uv_bin" ]; then
|
||||
if "$uv_bin" python find 3.12 >/dev/null 2>&1; then
|
||||
"$uv_bin" python find 3.12
|
||||
return 0
|
||||
fi
|
||||
for minor in 20 19 18 17 16 15 14 13 12 11; do
|
||||
uv_python="$("$uv_bin" python find "3.$minor" 2>/dev/null || true)"
|
||||
if [ -n "$uv_python" ] && python_version_ok "$uv_python"; then
|
||||
printf '%s\n' "$uv_python"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
require_bootstrap_python() {
|
||||
if [ -n "$BOOTSTRAP_PYTHON" ]; then
|
||||
return 0
|
||||
fi
|
||||
echo "未找到可用的 Python 3.11+ 解释器,请先安装 Python 3.11 或更高版本" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
default_config_dir() {
|
||||
case "$(uname -s)" in
|
||||
Darwin)
|
||||
@@ -342,10 +363,7 @@ case "${1:-}" in
|
||||
;;
|
||||
install)
|
||||
shift
|
||||
if [ -z "$BOOTSTRAP_PYTHON" ]; then
|
||||
echo "未找到可用的 Python 解释器,请先安装 Python 3" >&2
|
||||
exit 1
|
||||
fi
|
||||
require_bootstrap_python
|
||||
case "${1:-}" in
|
||||
deps)
|
||||
shift
|
||||
@@ -367,34 +385,22 @@ case "${1:-}" in
|
||||
;;
|
||||
init)
|
||||
shift
|
||||
if [ -z "$BOOTSTRAP_PYTHON" ]; then
|
||||
echo "未找到可用的 Python 解释器,请先安装 Python 3" >&2
|
||||
exit 1
|
||||
fi
|
||||
require_bootstrap_python
|
||||
exec "$BOOTSTRAP_PYTHON" "$SETUP_SCRIPT" init "$@"
|
||||
;;
|
||||
setup)
|
||||
shift
|
||||
if [ -z "$BOOTSTRAP_PYTHON" ]; then
|
||||
echo "未找到可用的 Python 解释器,请先安装 Python 3" >&2
|
||||
exit 1
|
||||
fi
|
||||
require_bootstrap_python
|
||||
exec "$BOOTSTRAP_PYTHON" "$SETUP_SCRIPT" setup "$@"
|
||||
;;
|
||||
update)
|
||||
shift
|
||||
if [ -z "$BOOTSTRAP_PYTHON" ]; then
|
||||
echo "未找到可用的 Python 解释器,请先安装 Python 3" >&2
|
||||
exit 1
|
||||
fi
|
||||
require_bootstrap_python
|
||||
exec "$BOOTSTRAP_PYTHON" "$SETUP_SCRIPT" update "$@"
|
||||
;;
|
||||
agent)
|
||||
shift
|
||||
if [ -z "$BOOTSTRAP_PYTHON" ]; then
|
||||
echo "未找到可用的 Python 解释器,请先安装 Python 3" >&2
|
||||
exit 1
|
||||
fi
|
||||
require_bootstrap_python
|
||||
exec "$BOOTSTRAP_PYTHON" "$SETUP_SCRIPT" agent "$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -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