diff --git a/internal/scanner/scanner.go b/internal/scanner/scanner.go index f0c9cc2b..38395bc2 100644 --- a/internal/scanner/scanner.go +++ b/internal/scanner/scanner.go @@ -1981,6 +1981,12 @@ func (s *Scanner) ScanFile(ctx context.Context, filePath string, folder *models. } return s.syncFolderScopedAudioLibraryState(ctx, folder.ID) } + if librarykind.IsEbook(folder.Type) { + if !SupportsEbookFile(cleanFile) { + return fmt.Errorf("unrecognized ebook extension: %s", strings.ToLower(filepath.Ext(cleanFile))) + } + return s.scanEbookPaths(ctx, folder, []string{cleanFile}, false) + } // Verify the file extension is recognized. ext := strings.ToLower(filepath.Ext(cleanFile)) diff --git a/internal/scanner/scanner_test.go b/internal/scanner/scanner_test.go index 29e431ed..baabc04d 100644 --- a/internal/scanner/scanner_test.go +++ b/internal/scanner/scanner_test.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "sort" + "strings" "testing" "time" @@ -265,3 +266,21 @@ func TestScanSubtreeEbookLibraryRoutesToEbookScanner(t *testing.T) { t.Fatal("ScanSubtree ebook result = nil, want empty result") } } + +func TestScanFileEbookLibraryUsesEbookPipeline(t *testing.T) { + filePath := filepath.Join(t.TempDir(), "book.epub") + if err := os.WriteFile(filePath, []byte("not a real epub"), 0o644); err != nil { + t.Fatalf("write fake ebook: %v", err) + } + + err := (&Scanner{}).ScanFile(context.Background(), filePath, &models.MediaFolder{ID: 44, Type: "ebooks"}) + if err == nil { + t.Fatal("ScanFile returned nil, want ebook parse failure") + } + if strings.Contains(err.Error(), "unrecognized video extension") { + t.Fatalf("ScanFile used video extension gate: %v", err) + } + if !strings.Contains(err.Error(), "folder_id=44") { + t.Fatalf("error = %q, want ebook scanner aggregate failure", err) + } +}