fix legacy library scan and empty pages

This commit is contained in:
ShukeBta
2026-07-01 22:51:33 +08:00
parent e18f73ed3c
commit 0493823b99
17 changed files with 536 additions and 33 deletions
+61
View File
@@ -39,6 +39,14 @@ func resolveAccessibleLibraryPath(path string) (string, error) {
return filepath.Clean(candidate), nil
}
}
// 兜底:旧库把宿主机绝对路径整段写进来时 /media 出现在路径中段(moviepilot 布局),
// 常规候选覆盖不到。只在这里按 isAccessibleDir 校验后采用,避免污染不校验存在性的
// 目的地解析(resolveMappedDestinationPath)。见 embeddedContainerMarkerCandidates。
for _, candidate := range embeddedContainerMarkerCandidates(input) {
if isAccessibleDir(candidate) {
return filepath.Clean(candidate), nil
}
}
abs, err := filepath.Abs(input)
if err != nil {
return "", fmt.Errorf("invalid path: %w", err)
@@ -46,6 +54,52 @@ func resolveAccessibleLibraryPath(path string) (string, error) {
return "", fmt.Errorf("path is not an accessible directory: %s", abs)
}
// embeddedContainerMarkerCandidates 处理旧库把宿主机绝对路径整段写进来的情况:
// /media 或 /downloads 出现在路径中段(如 moviepilot 的 /vol1/.../media/电视剧/国产剧),
// 取最后一个该段之后的尾巴拼到容器目录(默认 /media、/downloads,可用 *_CONTAINER_DIR 覆盖)。
//
// 这个启发式偏激进,只能在**按存在性校验**的读/扫描解析里使用;绝不能并入
// mappedPathCandidates——resolveMappedDestinationPath 不校验存在性、会返回首个候选,
// 那样会把形如 <tmp>/media/... 的合法目的地错误重写到容器根,破坏整理/硬链接。
func embeddedContainerMarkerCandidates(input string) []string {
normalized := cleanPathForVolumeMapping(input)
markerPath := pathAfterWindowsDrivePrefix(normalized)
var candidates []string
for _, marker := range []struct {
part string
container string
}{
{part: "/media", container: envOrDefault("MEDIASTATION_MEDIA_CONTAINER_DIR", "/media")},
{part: "/downloads", container: envOrDefault("MEDIASTATION_DOWNLOAD_CONTAINER_DIR", "/downloads")},
} {
part := strings.TrimRight(marker.part, "/")
container := strings.TrimRight(filepath.ToSlash(marker.container), "/")
if idx := strings.LastIndex(markerPath, part+"/"); idx > 0 {
candidates = append(candidates, filepath.Clean(filepath.FromSlash(container+markerPath[idx+len(part):])))
}
}
return candidates
}
// describeUnresolvedLibraryPath 把「路径解析失败」变成可操作的诊断信息:列出尝试过的
// 候选路径与当前的宿主机→容器映射状态。旧库存的是宿主机路径,在新版/容器内常因为缺少
// MEDIASTATION_MEDIA_DIR 映射而扫不出媒体——这条诊断帮助用户直接定位到底哪一步断了。
// 仅用于日志与扫描错误提示,不改变解析逻辑本身。
func describeUnresolvedLibraryPath(rawPath string) string {
rawPath = strings.TrimSpace(rawPath)
candidates := mappedPathCandidates(rawPath)
mediaHost := strings.TrimSpace(os.Getenv("MEDIASTATION_MEDIA_DIR"))
mediaContainer := envOrDefault("MEDIASTATION_MEDIA_CONTAINER_DIR", "/media")
var b strings.Builder
fmt.Fprintf(&b, "媒体库路径无法解析为可访问目录:%s(已尝试候选:%s)", rawPath, strings.Join(candidates, " | "))
if mediaHost == "" {
b.WriteString(";未配置 MEDIASTATION_MEDIA_DIR / MEDIASTATION_MEDIA_CONTAINER_DIR。若为 Docker 部署且此库为旧宿主机路径,请设置这两个变量把宿主机路径映射到容器内路径,并确认对应 volume 已挂载。")
} else {
fmt.Fprintf(&b, ";当前映射 %s → %s。请确认该库路径位于此宿主机目录下,或补充对应的 volume 与路径映射。", mediaHost, mediaContainer)
}
return b.String()
}
func resolveAccessibleMappedPath(path string) (string, os.FileInfo, error) {
input := strings.TrimSpace(path)
if input == "" {
@@ -57,6 +111,13 @@ func resolveAccessibleMappedPath(path string) (string, os.FileInfo, error) {
return filepath.Clean(candidate), info, nil
}
}
// 兜底同 resolveAccessibleLibraryPath:中段 /media|/downloads 的旧宿主机路径,
// 仅在 os.Stat 校验存在后采用。
for _, candidate := range embeddedContainerMarkerCandidates(input) {
if info, err := os.Stat(candidate); err == nil {
return filepath.Clean(candidate), info, nil
}
}
abs, err := filepath.Abs(input)
if err != nil {
return "", nil, fmt.Errorf("invalid path: %w", err)