Skip to content

fix(scan): clear stale legacy file columns when a book's folder is gone#711

Open
kevinheneveld wants to merge 1 commit into
Listenarrs:canaryfrom
kevinheneveld:fix/scan-clear-stale-legacy-file-columns
Open

fix(scan): clear stale legacy file columns when a book's folder is gone#711
kevinheneveld wants to merge 1 commit into
Listenarrs:canaryfrom
kevinheneveld:fix/scan-clear-stale-legacy-file-columns

Conversation

@kevinheneveld

Copy link
Copy Markdown
Contributor

Problem

Audiobooks can show "Downloaded" status with zero files and silently stop being re-searched.

LibraryListService derives file presence from a backward-compat fallback:

var hasTrackedFiles      = files != null && files.Count > 0;          // real AudiobookFiles rows
var hasLegacyFileSummary = !string.IsNullOrWhiteSpace(a.FilePath);   // legacy single-file column
var hasAnyFile = hasTrackedFiles || hasLegacyFileSummary;

So a book with a stale legacy FilePath (and no tracked files) reads as hasAnyFile == true → "Downloaded", while the UI's FileCount (from the real AudiobookFiles rows) is 0. It's also treated as complete by AudiobookWantedEvaluator, so it never re-searches.

Root cause

ScanJobProcessor.ProcessJobAsync has a dedicated branch for when a book's BasePath directory has vanished: it deletes the tracked AudiobookFile rows and sets audiobook.BasePath = null, then returns — but it never clears the legacy FilePath/FileSize columns. The later "legacy filePath migration" block that would clear them is downstream of this early return, so it's skipped.

Result: a book whose whole folder disappeared keeps its legacy columns set → permanent phantom "Downloaded".

Fix

Clear FilePath/FileSize alongside BasePath in the missing-folder branch:

audiobook.BasePath = null;
audiobook.FilePath = null;
audiobook.FileSize = null;
await audiobookRepository.UpdateAsync(audiobook);

The synchronous/manual scan path (LibraryManualScanWorkflow.MigrateLegacyFilePathAsync) already clears these for a missing file; this brings the background worker in line.

Test

Added ProcessJobAsync_BasePathMissing_ClearsLegacyFileColumns: a book with a non-existent BasePath and populated legacy columns is scanned, and FilePath/FileSize are asserted null afterward (read through a fresh DI scope to bypass the test context's identity map). Full suite green (1016 tests).

🤖 Generated with Claude Code

When a scan finds an audiobook's BasePath directory missing, ScanJobProcessor
removes the tracked AudiobookFile rows and nulls BasePath — but leaves the legacy
single-file columns (FilePath/FileSize) populated. LibraryListService then derives
hasAnyFile = hasTrackedFiles || !string.IsNullOrWhiteSpace(FilePath), so the book
keeps reporting "Downloaded" with zero files forever and, being treated as
complete, never gets re-searched.

Clear FilePath/FileSize alongside BasePath in the missing-folder branch so the
book correctly reverts to "no file" / wanted. The manual scan path already does
this via MigrateLegacyFilePathAsync; this brings the background worker in line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kevinheneveld kevinheneveld requested a review from a team June 29, 2026 18:52
@therobbiedavis therobbiedavis added the patch patch version bump - backward compatible bug fixes label Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch patch version bump - backward compatible bug fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants