From 0fb62b633a55cca9498e8b538b53f3d5ab80f815 Mon Sep 17 00:00:00 2001 From: drondeseries Date: Mon, 9 Feb 2026 00:15:48 -0500 Subject: [PATCH 1/3] fix(health): improve broken path detection during recovery --- internal/health/library_sync.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/internal/health/library_sync.go b/internal/health/library_sync.go index cc6ecbbc9..5bfc99141 100644 --- a/internal/health/library_sync.go +++ b/internal/health/library_sync.go @@ -632,9 +632,16 @@ func (lsw *LibrarySyncWorker) SyncLibrary(ctx context.Context, dryRun bool) *Dry // Fast Path Recovery: If record exists but has no library path OR has a broken 'complete' path, // try to see if it's at the expected location even if not in filesInUse - isBrokenCompletePath := recordExists && existingRecord.LibraryPath != nil && - strings.Contains(*existingRecord.LibraryPath, "/complete/") && - libraryPath == nil + isBrokenCompletePath := false + if recordExists && existingRecord.LibraryPath != nil { + lp := *existingRecord.LibraryPath + if strings.Contains(lp, "/complete/") { + // Check if it actually exists at that path + if _, err := os.Stat(lp); os.IsNotExist(err) { + isBrokenCompletePath = true + } + } + } if recordExists && (existingRecord.LibraryPath == nil || isBrokenCompletePath) && libraryPath == nil { expectedPath := filepath.Join(cfg.MountPath, path) From f9d084b5681ee2d90cfc09428a763dda045b30f8 Mon Sep 17 00:00:00 2001 From: drondeseries Date: Mon, 9 Feb 2026 00:18:28 -0500 Subject: [PATCH 2/3] feat(health): make path recovery generic and folder-agnostic --- internal/health/library_sync.go | 61 +++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/internal/health/library_sync.go b/internal/health/library_sync.go index 5bfc99141..1ebc19b55 100644 --- a/internal/health/library_sync.go +++ b/internal/health/library_sync.go @@ -620,47 +620,56 @@ func (lsw *LibrarySyncWorker) SyncLibrary(ctx context.Context, dryRun bool) *Dry p.Go(func() { // Check if needs to be added or repaired - recordExists := false var existingRecord *database.AutomaticHealthCheckRecord if it, exists := dbPathSet[path]; exists { - recordExists = true existingRecord = &it } - // Look up library path from our map + // Look up library path from our map (found via symlinks/strm scanning) libraryPath := lsw.getLibraryPath(path, filesInUse) - // Fast Path Recovery: If record exists but has no library path OR has a broken 'complete' path, + // Path Recovery: If record exists but the physical path is broken, // try to see if it's at the expected location even if not in filesInUse - isBrokenCompletePath := false - if recordExists && existingRecord.LibraryPath != nil { - lp := *existingRecord.LibraryPath - if strings.Contains(lp, "/complete/") { - // Check if it actually exists at that path - if _, err := os.Stat(lp); os.IsNotExist(err) { - isBrokenCompletePath = true + if existingRecord != nil && libraryPath == nil { + lp := existingRecord.LibraryPath + needsRecovery := lp == nil + + if !needsRecovery { + // It has a path, but is the file actually there? + if _, err := os.Stat(*lp); os.IsNotExist(err) { + needsRecovery = true } } - } - if recordExists && (existingRecord.LibraryPath == nil || isBrokenCompletePath) && libraryPath == nil { - expectedPath := filepath.Join(cfg.MountPath, path) - if _, err := os.Stat(expectedPath); err == nil { - // Found it at the expected location! - libStr := expectedPath - libraryPath = &libStr - slog.InfoContext(ctx, "Recovered library path for record", - "path", path, "recovered_location", expectedPath) - - // Update DB immediately for this recovered path - if err := lsw.healthRepo.UpdateLibraryPath(ctx, path, expectedPath); err != nil { - slog.ErrorContext(ctx, "Failed to update recovered library path", - "path", path, "error", err) + if needsRecovery { + expectedPath := filepath.Join(cfg.MountPath, path) + if _, err := os.Stat(expectedPath); err == nil { + // Found it! Use this recovered path + libStr := expectedPath + libraryPath = &libStr + slog.InfoContext(ctx, "Recovered broken library path", + "path", path, "new_location", expectedPath) + + // Update DB immediately for this recovered path + if err := lsw.healthRepo.UpdateLibraryPath(ctx, path, expectedPath); err != nil { + slog.ErrorContext(ctx, "Failed to update recovered library path", + "path", path, "error", err) + } } } } - if !recordExists || (existingRecord.LibraryPath == nil && libraryPath != nil) { + // Determine if we need to update/add the record + needsProcess := existingRecord == nil + if !needsProcess { + // Update if library path changed (e.g. renamed or recovered) + oldLP := existingRecord.LibraryPath + if libraryPath != nil && (oldLP == nil || *oldLP != *libraryPath) { + needsProcess = true + } + } + + if needsProcess { record, err := lsw.processMetadataForSync(ctx, path, libraryPath) if err != nil { slog.ErrorContext(ctx, "Failed to read metadata", From deaa93fdf0c690bc32e40ea90d40c6d4dfa6b539 Mon Sep 17 00:00:00 2001 From: drondeseries Date: Mon, 9 Feb 2026 00:23:19 -0500 Subject: [PATCH 3/3] fix(health): enforce absolute library paths and recover relative ones --- internal/health/library_sync.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/health/library_sync.go b/internal/health/library_sync.go index 1ebc19b55..318444a2d 100644 --- a/internal/health/library_sync.go +++ b/internal/health/library_sync.go @@ -632,17 +632,21 @@ func (lsw *LibrarySyncWorker) SyncLibrary(ctx context.Context, dryRun bool) *Dry // try to see if it's at the expected location even if not in filesInUse if existingRecord != nil && libraryPath == nil { lp := existingRecord.LibraryPath + // A path needs recovery if it's nil, missing from disk, or is a relative path (buggy) needsRecovery := lp == nil if !needsRecovery { - // It has a path, but is the file actually there? - if _, err := os.Stat(*lp); os.IsNotExist(err) { + // Check if path is absolute and file exists + if !filepath.IsAbs(*lp) { + needsRecovery = true + } else if _, err := os.Stat(*lp); os.IsNotExist(err) { needsRecovery = true } } if needsRecovery { - expectedPath := filepath.Join(cfg.MountPath, path) + // Use the configured mount path to build an absolute expected path + expectedPath := pathutil.JoinAbsPath(cfg.MountPath, path) if _, err := os.Stat(expectedPath); err == nil { // Found it! Use this recovered path libStr := expectedPath