Repository navigation
The Library shows a folder scan's books while it is open - #60
Conversation
In local mode the Library loaded its list once and only looked again when a server sync record arrived, which local mode never writes. A first-run folder scan creates the folder's library row and imports its books at the very end, so a Library opened mid-scan read an empty database and kept saying "The Archive Stands Empty" until the tab was left and re-entered. The Library now watches the local catalog in Room: which folders exist and which books each holds, live or archived. A changed folder list reloads the list the way a server sync already does, and any other change re-filters the shelf. Playback progress stays out of the watch, so listening does not re-query the shelf every few seconds. Two edges are covered. Loads wait for the watch's first snapshot before reading, so a scan that lands between a load and the watch starting still arrives. A reload re-saves a library the user picked on this screen whose own settings write it cancelled, and leaves any other saved choice alone. The instrumented test sources compile again. Two fixtures had fallen behind constructor and saveAuthToken signature changes, so nothing under androidTest could build or run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fedba69245
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| * table changes. Progress columns stay out so a caller that drops repeats | ||
| * hears about imports and archives, not position saves. | ||
| */ | ||
| @Query("SELECT Id AS id, LibraryId AS libraryId, ArchivedAt IS NOT NULL AS isArchived FROM AudioBooks WHERE IsLocal = 1") |
There was a problem hiding this comment.
Track shelf metadata changes during rescans
When a rescan updates an existing file at the same path—for example after its title, author, cover, duration, or tracks change—the imported row keeps the same ID, library, and archive status. Room re-emits this query, but it produces the same LocalBookMembership set, so the downstream distinctUntilChanged() suppresses the event and the open shelf retains stale metadata and filter/group results until it is reopened. Include the shelf-relevant metadata in the observed snapshot, or otherwise distinguish rescan upserts from playback-only updates.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 3004cdf. The watched row (now LocalCatalogEntry) also carries title, author, narrator, covers, duration, added date, series, and genres, so a rescan that retags a book re-filters the open shelf. Progress columns stay out. New instrumented case aRescanThatRetitlesABookUpdatesTheOpenShelf failed before the change and passes on a physical device after it.
The catalog watch only compared which books existed and whether they were archived. A rescan that re-reads the same file with a new title, author, cover, or duration keeps the id, the folder, and the archive state, so the open shelf kept the old details until it was reopened. The watched row (now LocalCatalogEntry) also carries the fields the shelf shows, searches, sorts, and groups on. Progress and position still stay out, so playback saves do not re-query the shelf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3004cdfbb2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| DurationSeconds AS durationSeconds, AddedAt AS addedAt, | ||
| SeriesName AS seriesName, SeriesSequence AS seriesSequence, GenresJson AS genresJson |
There was a problem hiding this comment.
Include chapter data in catalog snapshots
When a rescan changes the track/chapter layout without changing the projected metadata—for example, splitting one track into two with the same total duration—ChaptersJson changes but this snapshot remains equal, so distinctUntilChanged() suppresses the refilter and the open Library card keeps displaying the old chapter index/count until reopened. Fresh evidence after the earlier metadata fix is that this expanded projection still omits ChaptersJson; add it to the watched entry as another shelf-visible field.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 86e2617, with one difference from the suggestion: the watched row carries length(ChaptersJson) rather than the JSON itself. The chapter list can be large, and the query re-runs on every playback progress write while the Library is open, so the length stands in for it. Any re-split that changes the chapter count changes the length. New instrumented case aRescanThatSplitsAChapterUpdatesTheOpenShelf (same total duration, one chapter becomes two) failed before and passes after on a physical device.
Library cards show "Ch N/M", and a rescan can change the track layout without changing the title or total duration, so the open card kept the old chapter count. The watched row now carries the length of the chapter list. The length stands in for the list itself, which can be large, so playback saves still do not read every chapter blob. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 86e2617a43
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| CoverPath AS coverPath, LocalCoverPath AS localCoverPath, | ||
| DurationSeconds AS durationSeconds, AddedAt AS addedAt, | ||
| SeriesName AS seriesName, SeriesSequence AS seriesSequence, GenresJson AS genresJson, | ||
| length(ChaptersJson) AS chaptersJsonLength |
There was a problem hiding this comment.
Compare chapter content instead of only its length
When a rescan keeps the chapter count and total duration but moves a boundary between equal-width timestamps—for example, two 600-second tracks changing from a 300/300 split to 200/400—the serialized chapter JSON can retain exactly the same length. Fresh evidence after the earlier thread is this follow-up's length(ChaptersJson) projection: every projected field then remains equal, so distinctUntilChanged() suppresses the refilter and the open shelf continues calculating Ch N/M from the old boundaries until it is reopened. Observe a collision-resistant chapter signature or the chapter data needed by the card rather than its string length.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Declining this one. The case needs a rescan that keeps the chapter count, the total duration, and the exact serialized length while moving a boundary, and the only effect is the open card's "Ch N/M" index until the Library is reopened. Observing the chapter list itself means re-reading every local book's chapter blob on each playback progress write while the Library is open, which is the cost this watch is built to avoid, and SQLite on Android has no hash function to make a cheap signature. Length covers the realistic re-split (the chapter count changes).
What was broken
In local mode the Library tab loaded its list once and only looked again when a server sync record arrived, which local mode never writes. A first-run folder scan creates the folder's library row and imports its books at the very end. So if you opened Library while the scan ran, it read an empty database and kept saying "The Archive Stands Empty" until you left the tab and came back.
What changed
LibraryViewModelnow watches the local catalog in Room (which folders exist, and which books each holds, live or archived). A changed folder list reloads the list the same way a server sync does (shouldReloadLibrariesAfterSyncis reused). Any other change re-filters the shelf.observeLocalCatalogquery returns only id, library, and archived flag), so listening does not re-query the shelf every few seconds. Server mode does not subscribe at all.LibraryViewModelFilterRaceTestandHomeViewModelStartupInstrumentedTesthad fallen behind constructor andsaveAuthTokenchanges, so nothing under androidTest could build.Tests
LibraryLocalCatalogLiveUpdateTest(instrumented, real ViewModel on an in-memory Room database): a first scan while the Library is open, a rescan that adds a book, and a rescan that archives one. All three failed on master at the step after the scan's writes and pass with this change.LibraryLocalCatalogChangeTest(unit): reload versus re-filter decisions and the pending-pick rules.ExampleInstrumentedTest, which expects the package name without the.debugsuffix and is unrelated.Evidence
Emulator, debug build of this commit, the reported repro (clear data, onboarding, pick the Audiobooks folder, open Library while "Scanning folder..." shows). In both Noir and Bright themes the Library opens on the empty state and the nine books appear about a second later without leaving the tab.
Review
Codex (gpt-6.1-sol, high), three local rounds. Rounds 1 and 2 each raised two P2 timing issues, all fixed (the baseline ordering, the mode-switch first snapshot, and the two selection cases above). Round 3: no material findings.
Found along the way, filed separately
🤖 Generated with Claude Code