Owner ruling that closed the scan-semantics discussion: the code is
correct (per-library scan IS a force scan — hx-vals force:true since
706be09d), but the label hid that. Now the naming invariant holds
everywhere: anything with Force/Rescan in its name re-reads everything
in its scope; plain 'Scan' only exists at folder scope ('Scan this
folder', shallow) and in auto mode. Book-level Rescan verified as the
per-item force path (extractMetadata re-reads sidecars/tags regardless
of file size; recomputeHashInfo follows) — it is the documented answer
to 'I changed this book's sidecar'.
Glossary Scan entry replaced with Force Rescan (old label kept in also:),
scanning.md table + prose pick up the new label. Generated file
regenerated with the Dockerfile-pinned templ version from inside
templates/ so the diff is exactly the label strings.
90 lines
5.0 KiB
Markdown
90 lines
5.0 KiB
Markdown
<!-- Ground truth: internal/handlers/scanner.go (ScanLibrary, StartScanner,
|
||
per-folder validation, coalescing), internal/services/media_scanner.go
|
||
(forceRescan semantics, steady-skip path, watcher/poll/initial
|
||
triggers, enqueueLibraryScan force:false), internal/services/worker.go
|
||
(SetForce), templates/admin.templ + web/src/admin.ts (dashboard
|
||
button, one job per library), templates/admin_library.templ +
|
||
templates/utils.go (per-library Scan force:true, per-folder scan),
|
||
templates/admin_settings.templ (Auto-Scan, Scan Interval).
|
||
Re-verify against these when scanning behavior changes. -->
|
||
|
||
# Scanning
|
||
|
||
Scanning is how Bookhoard's server reads your library folders and keeps the
|
||
catalog in step with the files on disk. This page covers every scan an
|
||
admin can trigger, what each one actually does, and what it costs — so you
|
||
can pick the right one before clicking.
|
||
|
||
## The one distinction: shallow vs. force
|
||
|
||
Every scan is one of two kinds:
|
||
|
||
- **Shallow** (the default). The scanner walks the folder and skips any
|
||
file whose size and modification time are unchanged since it was last
|
||
seen. New and changed files get processed; untouched files cost nothing.
|
||
Fast, and the right choice for day-to-day keeping-up.
|
||
- **Force**. Nothing is skipped. Every file is re-read and re-extracted
|
||
with the scanner's current parsing, and each item's hash identifiers
|
||
(the SHA-256 fingerprints used for duplicate detection and device book
|
||
matching) are recomputed. This is the backfill tool: run it after
|
||
scanner improvements you want applied to already-scanned books, after
|
||
mass edits in Calibre or on disk, or when metadata looks stale anywhere
|
||
in a library. It is deliberately slow on big libraries.
|
||
|
||
## Every scan control, and what it does
|
||
|
||
| Control | Where | Kind | Scope |
|
||
| --- | --- | --- | --- |
|
||
| **Force Rescan All Libraries** | Admin dashboard → Quick Actions | Force | Every library |
|
||
| **Force Rescan** (library row) | Admin → Libraries | Force | That library |
|
||
| **Scan this folder** (folder-row icon) | Admin → Libraries → a library's panel | Shallow | That folder's subtree |
|
||
| **Auto-Scan** + **Scan Interval (seconds)** | Admin → Settings → Scanning | Shallow | Every library |
|
||
| **Retry** / **Retry all** | Admin → Libraries → Processing Issues | Per-item | Failed items only |
|
||
| **Rescan** / **Reset to Scanned** | A book's detail page | Per-item | That one item |
|
||
|
||
The details behind the table:
|
||
|
||
- **Force Rescan All Libraries** loops over every library and starts a
|
||
force scan job for each. Libraries that already have a scan running are
|
||
left alone — the button reports which libraries started and which were
|
||
skipped because a scan was already in flight.
|
||
- **Force Rescan** on a library row is the same force rescan, scoped to
|
||
that one library. It shares the dashboard button's semantics exactly.
|
||
- **Scan this folder** is the only folder-sized scan, and the only shallow
|
||
one an admin triggers by hand: unchanged files are skipped, and only
|
||
that folder's subtree is walked. Because a partial scan can't vouch for
|
||
what it didn't look at, it never marks items from the library's *other*
|
||
folders as missing. Podcast download folders refuse this button — they
|
||
are managed by the feed system, not the scanner.
|
||
- **Auto-Scan** arms the two automatic triggers, both shallow: a
|
||
filesystem watcher that fires as files change, and a periodic poll that
|
||
runs every **Scan Interval (seconds)** (default 60, allowed 1–3600).
|
||
The same shallow pass also runs once at startup when auto-scan is on.
|
||
- **Retry** and **Retry all** on a library's Processing Issues page send
|
||
failed items back through the scanner one at a time — deliberately not
|
||
a library-wide scan, so fixing a few bad files never re-reads the whole
|
||
library.
|
||
- **Rescan** on a book's page re-extracts that item from its file and
|
||
recomputes its hashes. **Reset to Scanned** clears your manual metadata
|
||
edits first, so what lands is purely what the scanner derives from the
|
||
file.
|
||
|
||
## Rules worth knowing
|
||
|
||
- **One scan per library at a time.** Starting a second scan for a library
|
||
that already has one pending or running is refused — the dashboard
|
||
tells you a scan is active rather than stacking a duplicate. The
|
||
automatic triggers skip silently instead.
|
||
- **Scans run in the background.** The dashboard's Scanning panel shows
|
||
per-library progress while jobs run; **Cancel all scans** stops the ones
|
||
still queued or running, and you can keep working meanwhile.
|
||
- **Missing files archive after two consecutive scans.** When a
|
||
library-wide scan (shallow or force) can't find a file twice in a row,
|
||
the item is archived — reading history is kept in case the file comes
|
||
back. The Archived Items page in this section manages the results.
|
||
- **Metadata comes from the files themselves.** The scanner reads sidecar
|
||
files (Calibre's `metadata.opf` in particular), embedded tags, and
|
||
filenames — [Calibre Integration](../user/calibre-integration.md) covers
|
||
the sidecar story. What a scan extracts is exactly what a force rescan
|
||
re-extracts.
|