Repository navigation
2.1.7 fixes: letter rail, accent search, safer downloads, Wi-Fi only - #90
Conversation
The reload check compared library ids only, so a rename kept the old name until the list itself changed. It now also notices a changed name for the same id. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…led to the current one The chapter menu built every chapter up front. It is now a lazy list in a bottom sheet that only composes the rows on screen and opens at the chapter that is playing. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ooks The Dossier was one scrolling column, so Show all built every book card and cover at once. It is now a lazy list that only builds the cards on screen. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The glow around a cover no longer draws or animates at zero progress, so a shelf of unstarted books runs no per-row animation. With reduced motion on (system animation scale at zero, or the in-app toggle) it is drawn once and held at a middle brightness. Its paints, paths and path effects are built once and reused, so a frame only changes alphas, instead of allocating four paints, three filters and two paths every frame. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
BookCoverImage now uses AsyncImage with the vintage placeholder composed underneath it, instead of SubcomposeAsyncImage. The placeholder still shows while a cover loads and stays when the cover fails, but each row no longer runs a subcomposition for it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Each row used to read its title twice (cover and text) and left the progress ring and downloaded dot unlabeled. A row now reads as one sentence, like "Dune, by Frank Herbert, 42 percent listened, chapter 3 of 10, downloaded", and the search box has a steady label instead of depending on its flavor hint. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Delete used to wipe whatever folder the book row pointed at, recursively. Two editions with the same author and title share one folder, so deleting either one took the other's audio too, and a stale path could point anywhere. Delete now runs through the same fences as cancel cleanup: the folder must be a plain direct child of the download root, a user-picked root is left alone, and nothing is ever deleted recursively. When another downloaded book shares the folder, only this book's own tracks go and the cover stays for the other one. A partial download of another book writing there keeps everything. Delete also holds the row lock like cancel, and treats the engine being on the book as downloading. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pause save on a server book goes through its listening session. The session sync carries the position but no finished flag, yet its success deleted the queued progress row, including a finished one. A finished pause save now also pushes the finished flag after the session sync and keeps its queued row until that push lands. Unfinished saves are delivered by the session sync alone, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the Library is sorted by title or author, a strip of letters runs down the right edge. Touch or drag it to jump to that letter, with a big letter shown while a finger is down. It works for the flat list and for the grouped Series, Author and Genre lists (by group name), and TalkBack gets it as one adjustable "Jump to letter" control. Each letter's position is worked out once per list change off the main thread, so dragging only looks one up. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A new Download on Wi-Fi only setting, on by default. Settings saved before it existed decode to the default, so upgrades get it on too. The single drain worker now carries the network rule: it waits for an unmetered network while the setting is on. Sending one download over mobile data runs the drain on any connection without touching the setting, and that drain takes only the books sent that way. When it runs out, the rest go to a follow-up drain appended behind it that waits for Wi-Fi. A drain request from before this change reads as one on any connection, so with the setting on it starts nothing on its own. Flipping the setting applies to work already queued. Turning it on stops a book streaming over mobile data and makes it wait, unless the user sent it that way. Turning it off starts a queue that was waiting, and frees a drain still bound to Wi-Fi. Every entry point goes through the same enqueue, so Book Detail, Downloads, the paused notification and entitlement changes all follow the rule. The per-download choice lives in device-local prefs, so there is no schema change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…79) Downloads shows Waiting for Wi-Fi instead of Queued, or instead of a Downloading bar frozen after Wi-Fi dropped, plus a Use mobile data button that starts that one book without changing the setting. Book Detail says the same when a book queues on mobile data. Settings gets the Download on Wi-Fi only switch in the Data group, and flipping it applies to downloads already queued. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A search for "Emile" now finds "Émile", "dune" finds "DUNE", and "nesbo" finds "Nesbø", in the Library and in Android Auto. Each book row keeps a folded copy of its title, author, series and narrator in a new SearchText column, and search compares a folded query against it. "%" and "_" still match themselves. The database moves to version 10. The upgrade fills the column for every book already on the phone, a page at a time, so an upgraded library searches right away with no resync. Every later write fills it too. The letter rail files titles with the same folding, so "Øystein" sits under O. Android Auto search used to read title and author only. It now reads the same column as the Library, so series and narrator match there too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each series now reads by its sequence number whatever the shelf sort, so book 2 comes before book 10 and a book 1.5 sits between 1 and 2. A book with no sequence, or one that is not a plain number, goes last, by title. The series themselves are still ordered by the shelf sort, so Recently Added still lists the series with the newest book first. Books with no series keep the shelf order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Audiobookshelf's library list sends a co-authored book's authors as one line, "Terry Pratchett, Neil Gaiman", and Author view filed it under a group of that whole line. It is now split on the ", " Audiobookshelf joins with, so the book is listed under Terry Pratchett and under Neil Gaiman, and the row still shows the full line. A suffix piece like "Jr.", "III" or "PhD" stays with the name before it. Local books keep their author tag whole, since a tag like "Tolkien, J.R.R." is one person. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stopping the drain gives up after ten seconds, and WorkManager calls a cancelled drain finished long before its engine unwinds. A read stuck on a stalled server only gives up at the 60 second read timeout, and its cleanup then deleted the .part file the replacement drain was writing, so finalizing failed and the book downloaded again and again. Any restart could hit this: the Wi-Fi only flip, pause, cancel and delete. Engine runs now hold one process-wide lock from start to the end of cleanup, so a replacement drain waits until the old engine has really exited. The book recorded for cancel cleanup is set only once the lock is held, and the row is read again after the wait so a book cancelled or finished meanwhile is skipped rather than written back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…udio The engine writes Completed, then waits on the cover, and only then saves the book's localPath. Deleting another edition that shares the folder in that window missed the finishing book on both counts: no stored path made it a sharer, and its Completed row did not count as unfinished. So every plain file went, and the finishing book was then marked downloaded with its tracks gone. A Completed row now counts as still owning its folder until the book row holds a saved local copy, the book the engine is on always counts, and the folder the engine is really writing to is protected too, in case the title changed under it. Any doubt keeps the files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…book's files Delete, cancel and the engine each decided who owned a download folder from several separate reads while the others kept changing it, and each earlier fix narrowed one interleaving without closing the next. Three were still open: deleting one edition while the other saved its finish wiped the other's audio, cancelling a paused book deleted the folder another book's engine was still writing after a rename moved its row elsewhere, and a queued book cancelled as the engine picked it came back and downloaded. Now one lock covers every change to what the engine owns (claiming a row, saving the finish, letting the folder go) and every delete, cancel or pause from its decision through its last deleted file. The engine claims a row only while it still exists and is still waiting, and saves a finish only while the row still exists, with the Completed row and the book's local copy written together. The folder the engine is really writing is never deleted, whichever book it belongs to. No network call ever runs under the lock, so a stalled read cannot freeze delete, cancel or the screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a big library the shelf read spans several cursor windows, and each
window was read afresh. A sync page saved in between gives the re-saved
books new rowids, so they came back again at the end of the read (and as
many other books went missing). A book twice in one group is two list rows
with one key, and the grouped list crashed when they scrolled into view.
The shelf now reads in one transaction, so it is one snapshot. The shelf
also keeps each book once whatever the read returns, the grouped list drops
any repeated row instead of crashing, and a row key can no longer run two
group and book names together. Author, series and genre names that differ
only in capitals or spacing ("John Smith, john smith") are one group, so a
co-authored book is listed once per author and no two groups share a key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…der removes the book's files New downloads go to "Author - Title [id]", where id is the last eight letters and digits of the ABS item id, so two editions of one title never share a folder: a cancelled partial of one edition can no longer become the other's audio, and deleting one while the other downloads takes all of its files. The name is capped at 200 UTF-8 bytes with the id always kept. Existing downloads keep their stored path and are not moved. A download that started before this (bytes on record, no folder of its own yet) resumes in its old "Author - Title" folder. Delete and cancel find the folder another download may be writing with the same rule, and the shared-folder checks stay in place for the old folders. Deleting a download in a user-picked download folder used to leave its files on disk while dropping the record. It now removes this book's own named tracks, their .part leftovers and its cover, and the folder only once empty. When the files cannot be proven this book's, the book stays downloaded and the delete says so instead of pretending it worked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…groups The duplicate-row fix merged group names that differ only in capitals, which joined distinct series like Saga and SAGA (interleaving their reading order) and named the merged group by whichever spelling sorted first, so a collapsed group could reopen on a sort change. Keys are now the name with runs of spaces collapsed, nothing else. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When storage turns read only mid-delete, the files stayed on disk but the record was cleared, orphaning the audio. A file still present after the delete now returns Keep(REMOVE_FAILED), so the book stays downloaded and the refusal notice shows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The earlier #81 change fixed the Library screen's reload check, but the visible selector lives in Settings, which loaded its list only at start and on connect. Settings now follows the cached server libraries and refreshes the names of the ones it shows. Which libraries appear and which is selected still come from loadLibraries(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings' loadLibraries() could publish the cached snapshot it read before a concurrent sync renamed the library, undoing the name the collector had just applied. The collector now records the latest cached libraries before updating state, and loadLibraries() applies those names inside its own state update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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: 5d11d5b079
ℹ️ 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".
| */ | ||
| internal fun splitServerAuthorNames(authorName: String): List<String> { | ||
| val names = ArrayList<String>() | ||
| for (piece in authorName.split(", ")) { |
There was a problem hiding this comment.
Preserve last-name-first server authors
When a server book uses a single author name containing a comma, such as Tolkien, J.R.R., splitting every ", " creates separate Tolkien and J.R.R. author groups. The local-book exception does not help server libraries, whose editable authorName is mapped into this same unstructured string. Use the API's structured authors data or otherwise avoid treating every comma as a co-author boundary.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Declined for this PR, tracked as #87. Audiobookshelf rewrites "Last, First" into "First Last" when it scans (server/utils/parsers/parseNameString.js), so a comma inside one author's name needs a hand-typed name on the server. The minified library list carries no structured authors array, so the real fix is one /api/libraries/{id}/authors call per sync plus stored author ids, which is out of scope for this release.
Why
Eleven follow-ups from the big-library release (#83), plus a crash in the grouped Library that the evidence pass for this build turned up on master.
Library
Downloads
Author - Title [id], so two editions with the same title never share files again. Existing downloads stay where they are, and a download paused before the update finishes in its old folder.Player and smaller fixes
Evidence
Headless emulator, mock server with 8,000 books:
Review
Unit tests 1,243 to 1,425, all passing. Independent review: Sol 6.1 high on every part until clean, then the whole diff against master, clean.
Closes #49 #61 #63 #64 #67 #72 #75 #76 #79 #81 #82
Follow-ups filed: #85 #86 #87 #88 #89.
🤖 Generated with Claude Code