feat(audiobookshelf): bootstrap the account, library and scan from the deployer #119
@@ -153,21 +153,37 @@ the file, not the bind: the same line count grows while the total climbs.
|
|||||||
ABS's own single-book layout; formats of one book share a folder), through the
|
ABS's own single-book layout; formats of one book share a folder), through the
|
||||||
journal-guarded mover, then delete the stale item so the old path is not kept,
|
journal-guarded mover, then delete the stale item so the old path is not kept,
|
||||||
then scan.
|
then scan.
|
||||||
- **`?force=1` is not a discovery arm.** Both arms of `POST /libraries/:id/scan`
|
- **`?force=1` is neither a discovery arm nor a repair arm.** Both arms of
|
||||||
walk the disk; force additionally re-scans every item that already exists
|
`POST /libraries/:id/scan` walk the disk, so finding new folders needs neither
|
||||||
(measured 1:33 against 0:04 on ~4,600 items). Finding new folders needs neither,
|
and the deployer's cheap trigger stays on the plain arm. Force additionally
|
||||||
so the deployer's cheap trigger stays on the plain arm.
|
re-scans every item that already exists (measured 1:33 against 0:04 on ~4,600
|
||||||
|
items) and repaired nothing on a library whose residuals were file-level: item
|
||||||
|
count, path set and media-less count identical before and after.
|
||||||
- **`DELETE /api/items/:id` is a DB row; `?hard=1` is `fs.remove()`.** In
|
- **`DELETE /api/items/:id` is a DB row; `?hard=1` is `fs.remove()`.** In
|
||||||
`LibraryItemController.delete`, `hardDelete = req.query.hard == 1` and then
|
`LibraryItemController.delete`, `hardDelete = req.query.hard == 1` and then
|
||||||
`fs.remove(libraryItemPath)`. Pointed at a covering item, that path is the whole
|
`fs.remove(libraryItemPath)`. Pointed at a covering item, that path is the whole
|
||||||
swallowed subtree — `hard=1` there deletes every book beneath it off the array.
|
swallowed subtree — `hard=1` there deletes every book beneath it off the array.
|
||||||
Assert the item's `path` first, and never pass `hard`.
|
Assert the item's `path` first, and never pass `hard`.
|
||||||
- **`Invalid data found when processing input` names the file, not the bind.** The
|
- **`Invalid data found when processing input` names the file, not the bind.** The
|
||||||
scanner logs it per file and skips the file — the book never appears — so a
|
scanner logs it per file and skips that file; a book whose files ALL fail never
|
||||||
missing title beside those lines is library debris, not a server fault. Census of
|
becomes an item at all, so the disk-vs-DB folder census shows it as a book with
|
||||||
the refusals on the first full scan: 38 of 43 files were 0 bytes, 3 were WAV
|
no item. That is corrupt media, not a lost book. Never clear a suspect file with
|
||||||
bytes wearing a `.mp3` name, 2 were unreadable. `stat -c %s` before any
|
a lenient probe: `ffprobe -show_entries format=duration` happily returns three
|
||||||
investigation of the server.
|
hours for the WAV bytes wearing a `.mp3` name that the scanner refuses. Read the
|
||||||
|
log's file paths instead, and `stat -c %s` first — 0-byte files are the common
|
||||||
|
case, and a truncated download reports `moov atom not found`.
|
||||||
|
- **A `Disc 01` / `CD 1` subfolder is part of a book, not a book.** The extension
|
||||||
|
census counts those folders; ABS deliberately does not — its grouping attaches
|
||||||
|
their files to the ancestor item (measured: 110 such folders under one item
|
||||||
|
carrying all 192 tracks). Read the basename before believing a disk-vs-DB gap.
|
||||||
|
- **`.cbr`/`.cbz` group like media and never play.** They sit in the grouping
|
||||||
|
predicate, so they hide books, but the comic parser throws `ERAR_BAD_ARCHIVE`
|
||||||
|
(`parseComicMetadata` → `CbrComicBookExtractor`) and the item lands with no
|
||||||
|
media. A media-less row beside those lines is the file, not the scanner.
|
||||||
|
- **A scan is finished when a NEW scan uuid completes.** Match
|
||||||
|
`Library scan "<uuid>" completed in ...`. `podman logs --since 30m` is a sliding
|
||||||
|
window: the set of visible completion lines changes on its own as old lines age
|
||||||
|
out, so a poller keyed on that hash reads a mid-scan number and calls it done.
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
@@ -178,3 +194,10 @@ stub of the API above: create-once, idempotent re-run, no secret in a URL, refus
|
|||||||
by URL when the server does not answer, and the deployer calling it only after the
|
by URL when the server does not answer, and the deployer calling it only after the
|
||||||
health gate) and `tests/media_overlay.bats` (only [[Plex]] and [[Jellyfin]] may
|
health gate) and `tests/media_overlay.bats` (only [[Plex]] and [[Jellyfin]] may
|
||||||
reference the metadata view).
|
reference the metadata view).
|
||||||
|
|
||||||
|
Five book folders hold media no scanner can read, so they stay out of the library:
|
||||||
|
three 2024 Nebula nominees in the `_unsorted` box (two `.m4b` with no `moov` atom,
|
||||||
|
one 36-file `.mp3` set that is not MPEG audio at all), `Anthony Heald/Bounty
|
||||||
|
Hunter Wars - Book 1` and `Star Wars Full Cast Audio Drama/Dark Empire III` — the
|
||||||
|
latter two also exist in readable form under their author folders. Fix by
|
||||||
|
re-acquiring the files; the scanner names every one of them in its log.
|
||||||
Reference in new issue
Block a user