fix(ebooks): model targeted scans as file-only

This commit is contained in:
rxwatcher
2026-07-22 13:43:57 +02:00
parent 7f3b93d1b4
commit f094d70996
2 changed files with 18 additions and 6 deletions
+15 -3
View File
@@ -53,6 +53,7 @@ func (s *Scanner) ScanEbookFolder(ctx context.Context, folder *models.MediaFolde
type ebookRootScan struct {
root string
files []string
fileOnly bool // explicit single-file scan; index it but never reconcile a subtree
rootErr error // root stat failed, or the root is not a directory
walkFailures int // entries within the subtree the walk could not read or resolve
}
@@ -88,7 +89,11 @@ func collectEbookRootScans(ctx context.Context, folderID int, roots []string) ([
// Unmounted/missing/permission-broken root: failed, not empty.
scan.rootErr = fmt.Errorf("stat root: %w", statErr)
case !info.IsDir():
scan.rootErr = fmt.Errorf("root is not a directory after symlink resolution")
if info.Mode().IsRegular() && SupportsEbookFile(cleanRoot) {
scan.fileOnly = true
} else {
scan.rootErr = fmt.Errorf("root is not a directory after symlink resolution")
}
}
if statErr == nil {
if err := walkLogicalTree(ctx, cleanRoot, cleanRoot, walkModeEbook, visitedPhysicalDirs, &scan.files, &scan.walkFailures); err != nil {
@@ -115,7 +120,7 @@ func splitEbookReconcileRoots(scans []ebookRootScan) (reconcileRoots []string, s
reconcileRoots = make([]string, 0, len(scans))
for i := range scans {
scan := &scans[i]
if scan.failed() {
if scan.failed() || scan.fileOnly {
continue
}
if len(scan.files) > 0 {
@@ -370,7 +375,14 @@ func (s *Scanner) reconcileEbookScan(ctx context.Context, folder *models.MediaFo
}
reconcileRoots, _ := splitEbookReconcileRoots(scans)
if len(reconcileRoots) == 0 {
if len(scans) > 0 {
allFailed := len(scans) > 0
for i := range scans {
if !scans[i].failed() {
allFailed = false
break
}
}
if allFailed {
slog.WarnContext(ctx, "ebook scan: every root walk failed; skipping missing-file reconciliation", "component", "scanner",
"folder_id", folder.ID,
)
+3 -3
View File
@@ -1455,7 +1455,7 @@ func TestCollectEbookRootScansExcludesUnmountedRootFromReconciliation(t *testing
}
}
func TestCollectEbookRootScansTreatsNonDirectoryRootAsFailed(t *testing.T) {
func TestCollectEbookRootScansTreatsSingleFileRootAsNonReconciling(t *testing.T) {
dir := t.TempDir()
fileRoot := filepath.Join(dir, "book.epub")
if err := os.WriteFile(fileRoot, []byte("x"), 0o644); err != nil {
@@ -1466,8 +1466,8 @@ func TestCollectEbookRootScansTreatsNonDirectoryRootAsFailed(t *testing.T) {
if err != nil {
t.Fatalf("collectEbookRootScans: %v", err)
}
if len(scans) != 1 || !scans[0].failed() {
t.Fatalf("scans = %+v, want one failed scan for a non-directory root", scans)
if len(scans) != 1 || scans[0].failed() || !scans[0].fileOnly {
t.Fatalf("scans = %+v, want one healthy file-only scan", scans)
}
// The file itself is still indexed; only reconciliation is withheld.
if len(scans[0].files) != 1 {