修复python3找不到的bug

This commit is contained in:
lmk
2026-08-13 15:10:26 +08:00
parent 2c3ed56755
commit 66395f71b9
+70 -35
View File
@@ -248,11 +248,56 @@ func (s *sceneSplitService) detectScenes(ctx context.Context, videoPath, outputJ
}
// lookupPython 查找真实可用的 Python 可执行文件
// Windows 上 %LOCALAPPDATA%\Microsoft\WindowsApps\ 下存在 Store 占位(假 Python),
// 需要跳过该目录,优先检查常见安装路径
// 优先级:PATH 中的 python3/python(容器镜像内 venv 已置于 PATH 首位,命中带 scenedetect 的解释器)
// → 硬编码常见安装路径(Windows 用户/系统目录、/usr/bin/python3 等)
// 每个候选都必须同时通过「真实 Python」与「可导入 scenedetect」验证,两者缺一即跳过,
// 禁止只按 --version 选中解释器(否则会命中系统 python,其无 scenedetect,运行时报 ModuleNotFoundError)。
func (s *sceneSplitService) lookupPython(ctx context.Context) (string, error) {
// 1. 常见 Python 安装路径(Windows
commonPaths := []string{
for _, p := range s.collectPythonCandidates(ctx) {
if s.verifyPython(ctx, p) {
g.Log().Infof(ctx, "[场景检测] 使用 Python: %s", p)
return p, nil
}
}
return "", fmt.Errorf("未找到可用的 Python 3(需能导入 scenedetect,安装参考: pip install scenedetect[opencv,ffmpeg]")
}
// collectPythonCandidates 收集候选 Python 路径,PATH 优先,硬编码路径回退,去重
func (s *sceneSplitService) collectPythonCandidates(ctx context.Context) []string {
var candidates []string
seen := make(map[string]bool)
add := func(p string) {
if p != "" && !seen[p] {
seen[p] = true
candidates = append(candidates, p)
}
}
// 1. PATH 中的 python3 / python(容器镜像内 venv 已置于 PATH 首位)
for _, name := range []string{"python3", "python"} {
if path, err := exec.LookPath(name); err == nil {
// 跳过 Microsoft Store 假占位
if strings.Contains(path, "WindowsApps") {
g.Log().Warningf(ctx, "[场景检测] 跳过 %s(%s): Microsoft Store 占位,非真实 Python", name, path)
continue
}
add(path)
}
}
// 2. 常见安装路径(Windows/Linux/macOS),仅收集实际存在的
for _, p := range commonPythonPaths() {
if _, err := os.Stat(p); err == nil {
add(p)
}
}
return candidates
}
// commonPythonPaths 硬编码的常见 Python 安装路径,作为 PATH 查找的回退
func commonPythonPaths() []string {
return []string{
// 用户安装
filepath.Join(os.Getenv("LOCALAPPDATA"), "Programs", "Python", "Python314", "python.exe"),
filepath.Join(os.Getenv("LOCALAPPDATA"), "Programs", "Python", "Python313", "python.exe"),
@@ -275,43 +320,33 @@ func (s *sceneSplitService) lookupPython(ctx context.Context) (string, error) {
filepath.Join("C:", "Python312", "python.exe"),
filepath.Join("C:", "Python311", "python.exe"),
filepath.Join("C:", "Python310", "python.exe"),
// WSL / Git Bash 环境
// WSL / Git Bash / Linux 环境
"/usr/bin/python3",
"/usr/bin/python",
}
for _, p := range commonPaths {
if _, err := os.Stat(p); err == nil {
if s.verifyPython(ctx, p) {
return p, nil
}
}
}
// 2. 从 PATH 查找,排除 WindowsApps 目录
for _, name := range []string{"python3", "python"} {
if path, err := exec.LookPath(name); err == nil {
// 跳过 Microsoft Store 假占位
if strings.Contains(path, "WindowsApps") {
g.Log().Warningf(ctx, "[场景检测] 跳过 %s(%s): Microsoft Store 占位,非真实 Python", name, path)
continue
}
if s.verifyPython(ctx, path) {
return path, nil
}
}
}
return "", fmt.Errorf("未找到可用的 Python 3")
}
// verifyPython 执行 python --version 验证是否真实可用
func (s *sceneSplitService) verifyPython(ctx context.Context, path string) bool {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
// checkPythonImport 探测候选 Python 能否执行指定 Python 代码
func checkPythonImport(ctx context.Context, path, code string, timeout time.Duration) bool {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
verCmd := exec.CommandContext(ctx, path, "--version")
output, err := verCmd.CombinedOutput()
if err != nil {
g.Log().Warningf(ctx, "[场景检测] 尝试 %s 失败(%v): %s", path, err, string(output))
cmd := exec.CommandContext(ctx, path, "-c", code)
if err := cmd.Run(); err != nil {
g.Log().Warningf(ctx, "[场景检测] %s 执行 python -c %q 失败: %v", path, code, err)
return false
}
return true
}
// verifyPython 验证候选 Python:真实可用 且 能导入 scenedetect
func (s *sceneSplitService) verifyPython(ctx context.Context, path string) bool {
// 1. 确认是真实可用的 Python(排除 Microsoft Store 假占位等)
if !checkPythonImport(ctx, path, "import sys", 5*time.Second) {
return false
}
// 2. 确认能导入 scenedetect(场景分割实际依赖,缺失则运行时报 ModuleNotFoundError
if !checkPythonImport(ctx, path, "import scenedetect", 30*time.Second) {
g.Log().Warningf(ctx, "[场景检测] %s 无法导入 scenedetect,跳过", path)
return false
}
return true