diff --git a/internal/handlers/scanner.go b/internal/handlers/scanner.go index bcfdda4..c7031a2 100644 --- a/internal/handlers/scanner.go +++ b/internal/handlers/scanner.go @@ -89,6 +89,7 @@ func SetupRoutes(g *echo.Group, db *database.Queries, connManager *wsync.Connect type ScanLibraryRequest struct { FolderPaths []string `json:"folder_paths,omitempty"` LibraryID string `json:"library_id,omitempty"` + Force bool `json:"force,omitempty"` } // ScanLibrary handles POST /api/scanner/scan (now runs in background) @@ -155,6 +156,7 @@ func (h *Handler) ScanLibrary(c echo.Context) error { "folders": folderPaths, "admin_id": userUUID.String(), "db": h.db, + "force": req.Force, }, Status: services.JobStatusPending, Context: h.ctx, diff --git a/internal/services/media_scanner.go b/internal/services/media_scanner.go index 1d6bcd0..ba60c0d 100644 --- a/internal/services/media_scanner.go +++ b/internal/services/media_scanner.go @@ -76,6 +76,7 @@ type MediaScanner struct { adminID pgtype.UUID defaultLibraryID pgtype.UUID libraryTypes map[string][]string + forceRescan bool totalFiles int newItems int @@ -104,6 +105,10 @@ func (s *MediaScanner) SetAdminID(adminID pgtype.UUID) { s.adminID = adminID } +func (s *MediaScanner) SetForce(force bool) { + s.forceRescan = force +} + func (s *MediaScanner) GetStats() (int, int, int) { return s.totalFiles, s.newItems, s.errors } @@ -356,14 +361,25 @@ func (s *MediaScanner) processMediaFile(ctx context.Context, path string) (bool, existingItem, err := s.getMediaItemByFilePath(ctx, path) if err == nil { fmt.Printf("Media item already exists in database: %s (size: %d vs %d)\n", path, existingItem.FileSize.Int64, info.Size()) - // Media item exists, check if file has changed (by size) - if existingItem.FileSize.Int64 != info.Size() { - fmt.Printf("File size changed, updating media item: %s\n", path) - _ = s.updateMediaItem(ctx, existingItem.ID, path, info) + + // If force rescan is enabled, always re-process + if s.forceRescan { + fmt.Printf("Force rescan enabled, re-processing existing media item: %s\n", path) + // Force update: delete existing and re-create + if err := s.db.DeleteMediaItem(ctx, existingItem.ID); err != nil { + fmt.Printf("Warning: failed to delete existing media item: %v\n", err) + } + // Continue to create new entry below + } else { + // Normal behavior: check if file has changed (by size) + if existingItem.FileSize.Int64 != info.Size() { + fmt.Printf("File size changed, updating media item: %s\n", path) + _ = s.updateMediaItem(ctx, existingItem.ID, path, info) + return false, nil + } + fmt.Printf("Media item already exists with same size, skipping: %s\n", path) return false, nil } - fmt.Printf("Media item already exists with same size, skipping: %s\n", path) - return false, nil } else if err != pgx.ErrNoRows { fmt.Printf("Database error checking media item existence: %v\n", err) return false, fmt.Errorf("failed to check if media item exists: %v", err) diff --git a/internal/services/worker.go b/internal/services/worker.go index 53e0234..fd0d1da 100644 --- a/internal/services/worker.go +++ b/internal/services/worker.go @@ -194,6 +194,11 @@ func (w *Worker) processScanJob(job *Job) (interface{}, error) { return nil, fmt.Errorf("database queries required") } + force := false + if forceVal, ok := job.Params["force"].(bool); ok { + force = forceVal + } + scanner := NewMediaScanner(db) scanner.job = job @@ -218,6 +223,7 @@ func (w *Worker) processScanJob(job *Job) (interface{}, error) { return nil, err } scanner.SetAdminID(adminUUID) + scanner.SetForce(force) if err := scanner.ScanFolders(job.Context); err != nil { return nil, err