修复python3找不到的bug
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user