scanner: fix library isolation, file mtime, force rescan, and deletion handling

Fix 1 - File modification time for created_at:
- Get file.ModTime() in processMediaFile and pass to CreateMediaItem
- Modified SQL INSERT to include created_at column

Fix 2 - Force rescan UPDATE instead of DELETE+INSERT:
- Changed force rescan logic to call updateMediaItem instead of delete + create
- Preserves created_at timestamp on force rescan

Fix 3 - GetMediaItemByFilePath filters by library_id:
- Added library_id to WHERE clause in SQL query
- Created GetMediaItemByFilePathAnyLibrary for cross-library lookups (KOReader)
- Added SetLibraryID method to MediaScanner
- Updated handler to call SetLibraryID for watch mode

Fix 4 - File deletion handling with persistent logging:
- Added fsnotify.Remove handler in WatchChanges
- Added orphan cleanup in ScanFolders after scan completes
- Created scanner_logger.go with daily log rotation (7 days)
- Logs to /app/logs/scanner-deletes-YYYY-MM-DD.log and scanner-errors-YYYY-MM-DD.log
- Individual deletes with enhanced safety logging

Note: Integration tests can now safely scan /app/uploads because
GetMediaItemByFilePath now filters by library_id, preventing
cross-library interference.
This commit is contained in:
2026-02-26 16:39:42 -05:00
parent 56c80fbe14
commit d802236874
14 changed files with 1432 additions and 83 deletions
+2 -2
View File
@@ -264,8 +264,8 @@ func (h *KOReaderHandler) resolveBookToMediaItem(c echo.Context, deviceID pgtype
return alias.MediaItemID, alias.ConfidenceScore.Float64
}
// Try to find by file path
mediaItem, err := h.db.GetMediaItemByFilePath(ctx, book.FilePath)
// Try to find by file path (search any library)
mediaItem, err := h.db.GetMediaItemByFilePathAnyLibrary(ctx, book.FilePath)
if err == nil {
// Create new alias
confidence := 0.7
+2 -1
View File
@@ -266,6 +266,7 @@ func (h *Handler) StartWatchModeForLibrary(ctx context.Context, libraryID pgtype
}
scanner.SetAdminID(adminID)
scanner.SetLibraryID(libraryID)
scanner.WatchChanges(h.watchModeCtx)
h.watchingLibraries[libraryIDStr] = true
@@ -382,7 +383,7 @@ func (h *Handler) StartWatchModeForAllLibraries(ctx context.Context) error {
for _, library := range libraries {
libraryIDStr := fmt.Sprintf("%x", library.ID.Bytes)
if err := h.StartWatchModeForLibrary(ctx, library.ID, library.ID); err != nil {
if err := h.StartWatchModeForLibrary(ctx, library.ID, library.CreatedByAdminID); err != nil {
fmt.Printf("Warning: failed to start watch mode for library %s: %v\n", libraryIDStr, err)
continue
}