From 8044e1cbfe0850d16d612d6636af009d666ad528 Mon Sep 17 00:00:00 2001 From: KodeStar Date: Wed, 14 Oct 2026 09:50:09 +0100 Subject: [PATCH 1/3] Health: duplicate reasons (same_id, id_length_differs) and the length rule for confident matches (server #129) --- docs-developers/server/api/reference.md | 24 +++++++++++++++---- docs-users/admin/health.md | 32 ++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/docs-developers/server/api/reference.md b/docs-developers/server/api/reference.md index 6dc0bce..2c9b26a 100644 --- a/docs-developers/server/api/reference.md +++ b/docs-developers/server/api/reference.md @@ -4624,7 +4624,7 @@ Each book's **outcome**: | Outcome | Meaning | |---|---| -| `auto` | Confident: the best candidate scores **90** or more, at least **10** ahead of the next, and its recording has an ASIN or ISBN. Applied unless the admin leaves it out. A repick's offers are all `auto`. | +| `auto` | Confident: the best candidate scores **90** or more, at least **10** ahead of the next, its recording has an ASIN or ISBN, and, when both lengths are known, the recording's runtime is within **10%** of the book's (the closest recording of a work can still be another edition). Applied unless the admin leaves it out. A repick's offers are all `auto`. | | `review` | A candidate that isn't confident. Applied only if the admin puts it in. | | `none` | No candidate. | | `error` | The service failed for this book (`detail: "metadata_unavailable"`). Five in a row stop the run. | @@ -5102,13 +5102,27 @@ different libraries are deliberate and never grouped). } ``` -- `reason` - `same_files` when the copies' audio is identical (same - fingerprint and size), otherwise `same_book` (the same ASIN or ISBN, or the - same author, title and narrator with lengths within a minute or 2%). +- `reason` - why the books were grouped, from the joins that formed the group. + "Lengths match" means within a minute or 2%, an unknown length (no ffprobe) + matching only another unknown one. + + | `reason` | Meaning | + |---|---| + | `id_length_differs` | Two of the books share an ASIN or ISBN but their lengths don't match (every pair of an identifier's books is compared): one may be matched to the wrong recording, or a copy is incomplete. Not plain copies: the console marks none as the one to keep. | + | `same_files` | Two of the books have identical audio (same fingerprint and size). | + | `same_id` | Two of the books share an ASIN or ISBN and their lengths match. | + | `same_book` | The books share author, title and narrator and their lengths match. | + + A group joined in several ways takes the first reason of this table that + any of its joins gives: `id_length_differs` outranks even `same_files`, as + such a group holds a different recording beside its identical copies. + An older server says `same_book` for both identifier reasons; a client should + read a `reason` it doesn't know like `same_book`. - `books` - each copy as a [`GET /admin/books`](#get-apiv1adminbooks) row plus `listeners` (people with progress on it), the copy worth keeping **first** (better format, then a single file, then the higher bitrate, then more - listeners). + listeners). Under `id_length_differs` the order says nothing about which to + keep. - `ignored` - every copy in the group is ignored for `duplicate`. Without `?ignored=true` only groups with `ignored: false` are returned; with it, only the ignored ones. An ignored group becomes open again by itself when a new diff --git a/docs-users/admin/health.md b/docs-users/admin/health.md index 988b8ca..a000b01 100644 --- a/docs-users/admin/health.md +++ b/docs-users/admin/health.md @@ -125,7 +125,10 @@ what it found and apply it. ![Reviewing the matches Match automatically found](/img/screenshots/admin/health-bulk-match.png) A match is confident when it scores 90% or more, at least 10 points ahead of -the next candidate, and its recording has an ASIN or ISBN to attach. Whatever a +the next candidate, its recording has an ASIN or ISBN to attach, and its +recording's length is within 10% of the book's (when both are known: the +closest recording can still be another edition, such as a dramatization of an +unabridged book). Whatever a run writes shows as a **Community** value on the book's page, and you can revert it like any other. To undo every community match at once and start again, use [Clear community matches](server.md#community-metadata) in Server @@ -178,8 +181,31 @@ over several, then the higher bitrate, then the copy more people listen to. ![Comparing likely duplicates](/img/screenshots/admin/health-duplicates.png) -The line under the title says why they look like copies: identical audio -files, or the same title, author and length in different files. Each column +The line under the title says why they look like copies: + +| Line | Why they were grouped | +|---|---| +| **Identical audio files** | The audio is the same, byte for byte. | +| **Sharing an ASIN or ISBN, of the same length** | The books carry the same identifier and their lengths match (within a minute or 2%). | +| **Sharing an ASIN or ISBN, but of different lengths** | The books carry the same identifier, but their lengths don't match. See below: these may not be copies at all. | +| **The same title, author and length, in different files** | Title, author and narrator agree, and so do the lengths. | + +**Sharing an ASIN or ISBN, but of different lengths** usually means one of the +books is matched to the wrong recording (an unabridged book given a dramatized +edition's ASIN, say), or one copy is incomplete. These groups have no **Worth +keeping** or **Another copy** marks, and a note under the line says what to +check. Don't delete either until you know which is which: + +- Compare the **Length** of each with the recording it should be. If one is + matched to the wrong recording, open it, click **Match**, and pick the + recording that fits its length (the match dialog shows how far each one is + off). If the community catalogue doesn't have that recording, clear the + book's ASIN on its page instead. +- If one copy is missing files, replace it with a complete copy. +- If both are right (two editions that really share an identifier), click + **They're different books**. + +Each column shows the copy's **Path**, **Format**, **Length**, **Size**, **Chapters**, whether it is **Matched**, how many **Listeners** have progress on it, and when it was **Added**. **Open book** opens a copy's page. From 006cef1010edd12cfce06d132ba381b2d101a670 Mon Sep 17 00:00:00 2001 From: KodeStar Date: Wed, 14 Oct 2026 10:19:48 +0100 Subject: [PATCH 2/3] API reference: an unknown length never makes an identifier's holders differ; refresh holds another edition --- docs-developers/server/api/reference.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs-developers/server/api/reference.md b/docs-developers/server/api/reference.md index 2c9b26a..41b6694 100644 --- a/docs-developers/server/api/reference.md +++ b/docs-developers/server/api/reference.md @@ -4608,7 +4608,8 @@ the manager's enrichment: each is looked up, and when its recording now sells in the preferred marketplace under another ASIN, that is offered) or a `refresh` run (every book with an ASIN or ISBN, whoever set it: each is looked up by its own identifier alone, the ISBN when the ASIN finds nothing, and the -record it names is the book, `auto` whatever its score; an identifier the +record it names is the book, `auto` whatever its score unless its recording's +length is more than 10% from the book's, which makes it `review`; an identifier the community doesn't know is `none` with `detail: "identifier_unknown"`, never a fuzzy match by title). A refresh fills in, or with `overwrite` replaces, the details an identifier alone didn't bring (the manager's enrichment, an `ids` @@ -5103,8 +5104,9 @@ different libraries are deliberate and never grouped). ``` - `reason` - why the books were grouped, from the joins that formed the group. - "Lengths match" means within a minute or 2%, an unknown length (no ffprobe) - matching only another unknown one. + "Lengths match" means within a minute or 2%. For `same_book` an unknown length + (no ffprobe) matches only another unknown one; for a shared ASIN or ISBN an + unknown length is no evidence either way, so it never makes the lengths differ. | `reason` | Meaning | |---|---| From 4bc63e2144a7e1d32c9447914cc1e85e04e7ce1f Mon Sep 17 00:00:00 2001 From: KodeStar Date: Wed, 14 Oct 2026 10:32:09 +0100 Subject: [PATCH 3/3] API reference: id_length_differs compares the whole group's lengths --- docs-developers/server/api/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-developers/server/api/reference.md b/docs-developers/server/api/reference.md index 41b6694..29a618f 100644 --- a/docs-developers/server/api/reference.md +++ b/docs-developers/server/api/reference.md @@ -5110,7 +5110,7 @@ different libraries are deliberate and never grouped). | `reason` | Meaning | |---|---| - | `id_length_differs` | Two of the books share an ASIN or ISBN but their lengths don't match (every pair of an identifier's books is compared): one may be matched to the wrong recording, or a copy is incomplete. Not plain copies: the console marks none as the one to keep. | + | `id_length_differs` | The books were grouped by a shared ASIN or ISBN but their lengths don't match (the group's shortest and longest known lengths are compared): one may be matched to the wrong recording, or a copy is incomplete. Not plain copies: the console marks none as the one to keep. | | `same_files` | Two of the books have identical audio (same fingerprint and size). | | `same_id` | Two of the books share an ASIN or ISBN and their lengths match. | | `same_book` | The books share author, title and narrator and their lengths match. |