Sixth admin page. Documents what creates conflict groups (scanner at scan time + the hash backfill retroactively), the group card anatomy (count badge, library + short SHA-256, per-copy rows with usage summary highlighted when that copy holds reading data), and both resolutions with verified semantics: Keep this copy (reparents the other copies' children into the kept copy, deletes their catalog rows, records the resolver — files on disk never touched, consistent with platform-wide delete semantics) and Keep both (mark resolved, stop flagging, nothing merged). Glossary gains Keep both and Keep this copy.
57 lines
2.4 KiB
Markdown
57 lines
2.4 KiB
Markdown
<!-- Ground truth: templates/admin_hash_conflicts.templ (cards, actions,
|
|
confirms), internal/handlers/hash_conflicts.go (ResolveHashConflict:
|
|
keep_all marks resolved; keep reparents children to the kept copy,
|
|
deletes the other catalog rows, records who resolved), internal/
|
|
services/media_scanner.go (recordHashConflictIfAny), internal/services/
|
|
hash_backfill.go (backfill-created conflicts). Re-verify against these
|
|
when the page changes. -->
|
|
|
|
# Hash Conflicts
|
|
|
|
Hash Conflicts is the duplicate-content queue: groups of catalog items
|
|
whose files are **byte-identical** (same SHA-256) but stored at more than
|
|
one path in the same library. The scanner flags these as it goes — and
|
|
the hash backfill job flags them retroactively for content that predates
|
|
hash tracking — so both fresh scans and old libraries surface their
|
|
duplicates here.
|
|
|
|
Duplicates usually mean the same book was copied into two folders, or a
|
|
library was reorganized and both the old and new copies got scanned. The
|
|
harm is quiet but real: split reading histories, double-counted library
|
|
stats, and two entries for one book.
|
|
|
|
## What a conflict looks like
|
|
|
|
One card per group: a **Duplicate content** heading, a badge with the
|
|
copy count, the library and a short fingerprint of the shared hash, and
|
|
one row per copy — title, author, full path, file size, and a usage
|
|
summary that's **highlighted when that copy holds reading data** (someone
|
|
has actually read it).
|
|
|
|
## Resolving a group
|
|
|
|
Two outcomes, both confirm-guarded:
|
|
|
|
### Keep this copy
|
|
|
|
Per-copy button. Keeps the copy you chose and folds the **other copies'
|
|
reading data into it** (their catalog rows are removed; the underlying
|
|
files on disk are never touched — this platform's deletes act on the
|
|
catalog, not your files). The row-level usage summaries exist precisely
|
|
so you can pick the copy that already carries the reading history.
|
|
|
|
### Keep both
|
|
|
|
The group-level button, for duplicates that are intentional. It records
|
|
the resolution and stops flagging this group — every copy stays in the
|
|
catalog exactly as it is, nothing merged, nothing removed.
|
|
|
|
After either choice the card is replaced with the outcome, and the group
|
|
never comes back (unless the same duplicate situation re-arises from a
|
|
*new* scan, which creates a fresh group).
|
|
|
|
## When the page is empty
|
|
|
|
"No content duplicates detected." — the queue drains from the top; groups
|
|
appear as scans and the backfill find them.
|