diff --git a/docs-developers/architecture/cross-repo-contract.md b/docs-developers/architecture/cross-repo-contract.md index 3a5495f2..23b937e0 100644 --- a/docs-developers/architecture/cross-repo-contract.md +++ b/docs-developers/architecture/cross-repo-contract.md @@ -760,8 +760,10 @@ player. **Server:** - `internal/meta` resolves a book's ASIN or ISBN against `metaserve` and composes an - enrichment envelope. `GET /libraries/{id}/meta` returns it, behind an admin - off-switch and a bounded cache. + enrichment envelope. A book with neither, or one `metaserve` does not know, is + resolved by its recorded match instead (see + [Matches by work id](#matches-by-work-id)). `GET /libraries/{id}/meta` returns + it, behind an admin off-switch and a bounded cache. - `GET /meta/work?id=…` serves one work document straight through, from the same cache, for the earlier books of a series the caller does not own. It is authed but **not** library-scoped, since a work id names nothing on this server. @@ -864,9 +866,18 @@ From 2.6.0 the server records each match by its work id (`book_matches`). So a s listing that `metaserve` holds with no ASIN or ISBN counts as matched. A refresh looks such a book up by `works/{id}`, following a retired slug's `301`. The rule a run applies to such a book is in -[Bulk community matching](../server/api/reference.md#bulk-community-matching). The -player's `/meta` stays identifier-keyed, so it shows no community content for such a -book yet. +[Bulk community matching](../server/api/reference.md#bulk-community-matching). + +From 2.7.0 the server's `/meta` answers such a book from its recorded match. It +tries the book's identifiers first, then its `book_matches` `work_id` and +`recording_id` when they find nothing. The envelope is the same, so the wire does +not change. + +The player's book page asks `/meta` for every loaded book that is not an episode +(`bookPageMetaEnabled`), so a book matched with neither identifier shows its +community content there. Most other surfaces keep the identifier gate +(`metaEnabledFor`). [Enriched book metadata](../frontend/state-and-data.md#enriched-book-metadata) +lists which surface uses which gate. ### Community chapters from the meta side @@ -934,6 +945,10 @@ These are all additive: writes the legal text.** A client must render it beside that content and never compose it. - `recording.chapter_count`. +- `recording.librivox` `{id, url?, zip_url?}`, only on a LibriVox recording (see + [`GET /libraries/{id}/meta`](../server/api/reference.md#get-apiv1librariesidmeta)). + The server checks each URL, and the player checks `url` again before About + links it. - `local` `{library_id, path}` on each rail entry the caller owns, in the main view and the orderings. The server resolves `local` **per request, after the shared cache, on a copy**, and never stores it there. A failure placing the caller's books diff --git a/docs-developers/bridge/attachments.md b/docs-developers/bridge/attachments.md index dbd09ee2..1b6a7381 100644 --- a/docs-developers/bridge/attachments.md +++ b/docs-developers/bridge/attachments.md @@ -393,7 +393,8 @@ book's signature names a characters, recaps or description file. chapters, and a map is how its text lines up; neither is generated content. - It is read from the narrow column, so a list never joins `book_attachments`. - Every book object carries it: lists, search, `item`, recent, and the next book's - `book`. The player uses it to ask `/meta` for a book with no ASIN or ISBN. + `book`. The player's identifier-gated surfaces (`metaEnabledFor`) use it to ask + `/meta` for a book with no ASIN or ISBN. Off the wire, `Book.GeneratedParts` says which parts the signature names. So `/meta` and the console's book page read only the columns a book has. diff --git a/docs-developers/frontend/book-page.md b/docs-developers/frontend/book-page.md index d35edff9..3fc7e8b7 100644 --- a/docs-developers/frontend/book-page.md +++ b/docs-developers/frontend/book-page.md @@ -403,6 +403,17 @@ hero and the tabs. (`openLibraryRoot`) on `libraryBooksHref({ genre | tag })`. - A matched book without community text keeps "View on AudioSilo Meta". Long text folds past six lines with Show more. + - A LibriVox recording adds "Also free on LibriVox" (`book.about.librivox`) under + the other links out. It comes after the credit and **Improve this**, else + after "View on AudioSilo Meta". It opens `librivoxUrl(recording)` through + `openExternalUrl`. + `librivoxUrl` (`book-details-model.ts`) reads the matched recording's + `librivox.url` and keeps it only when it is an absolute http(s) URL with a host + (`urlEndpoint`). It lower-cases the scheme, because the in-app browser on iOS + (`expo-web-browser`) refuses `HTTPS://` and `openExternalUrl` swallows the + error. The server checks the URL too. The player checks again because the + link leaves the app. With no `librivox.url`, or on an older server, there is + no link. - **Other versions** - `BookVersions`: every other copy (`useBookCopies`) with where it lives and its quality, each opening that copy's page. Nothing shows when there is none. diff --git a/docs-developers/frontend/offline.md b/docs-developers/frontend/offline.md index bac787bf..c4fa1816 100644 --- a/docs-developers/frontend/offline.md +++ b/docs-developers/frontend/offline.md @@ -989,9 +989,11 @@ a marker (`DownloadManifest.meta = { savedAt }`). write the registry once (`persistSoon`). If the download was removed or replaced while the file was written, the marker isn't set, and a removed book's file is deleted again. -- *What `captureOfflineMeta` skips.* A book that can't match because it has no ASIN - or ISBN (`canMatch`, which reads the cached full item first, since the manifest's - book can be the list shape). Also a server without `metadata`. +- *What `captureOfflineMeta` skips.* A book with no ASIN or ISBN and no generated + content (`canMatch`, `metaEnabledFor` on the cached full item first, since the + manifest's book can be the list shape). That includes a book matched only in the + console. Online its page shows the match. Offline it reads as unmatched. Also a + server without `metadata`. - *How it reads.* It reads through the screens' own option factories (`bookMetaQuery`, `metaWorkQuery`), so it reuses a fresh answer they hold. It never throws and never retries. diff --git a/docs-developers/frontend/podcasts.md b/docs-developers/frontend/podcasts.md index a1fd6f9c..857240de 100644 --- a/docs-developers/frontend/podcasts.md +++ b/docs-developers/frontend/podcasts.md @@ -193,8 +193,9 @@ audio changed and the place is kept. Nothing is rescaled. There is no Generate, community metadata, rating or series. The player's companion chips and tabs are off for an episode (`useCompanionData` reads `book.episode` as `off`). The player never asks for community metadata for an -episode: `metaEnabledFor` in `meta-gating.ts` is false for `book.episode`, because -a feed's tags can carry an ISBN-like value. +episode: `bookPageMetaEnabled` in `meta-gating.ts`, which `metaEnabledFor` builds +on, is false for `book.episode`, because a feed's tags can carry an ISBN-like +value. ### Wording in the player and on Home diff --git a/docs-developers/frontend/state-and-data.md b/docs-developers/frontend/state-and-data.md index b8b23d73..4a751aa0 100644 --- a/docs-developers/frontend/state-and-data.md +++ b/docs-developers/frontend/state-and-data.md @@ -614,6 +614,9 @@ older server (React Query rejects it instead), and the query stays pending. it; the series page and Search's character group do. `community_description` feeds the book page's About card (`aboutContent`). - `BookMetaRecording.chapter_count`, which has no reader yet. + - `BookMetaRecording.librivox` (`BookMetaLibrivox { id, url?, zip_url? }`), only + on a LibriVox recording. The About card links `url` + ([The aside](book-page.md#the-aside)); nothing reads `id` or `zip_url` yet. - `local` (`BookRef`) on rail entries. It is how the series page and Next in your series tell an owned book from a ghost. - `previous` on a matched `BookMeta`. The @@ -983,10 +986,24 @@ Each field is read through one of two readers, never both: [generated work](../server/api/reference.md#generated-content) (`matched: false` with a `work`). So an unmatched book's generated description reaches About too. -`metaEnabledFor` asks `/meta` for a book with an ASIN or ISBN **or** -`generated: true`. An answer that is only generated content goes stale after -2 minutes (`META_GENERATED_ONLY_STALE_MS`), rather than the hour a community -answer keeps. +Two gates in `meta-gating.ts` decide whether a screen asks `/meta`: + +- `bookPageMetaEnabled`: the `metadata` capability and a loaded book that is not + an episode, identifiers or not. Only the book page uses it. A newer server + answers a book with no ASIN or ISBN from its console match (a LibriVox + download), and an older one answers `matched: false` without asking upstream, + so the one request is cheap either way. +- `metaEnabledFor`: that gate and an ASIN, an ISBN **or** `generated: true`. The + companion and reveal toast, Home's Now card and Previously on, the end credits + and the offline copy (`canMatch` in `offline-meta.ts`) use it, because they ask + for a book in passing or for every download. + +The series page's anchor (`metadataAnchor`, an ASIN or ISBN) and Search's +characters (`characterBooksToLoad`, the started books, identifiers or not) pick +their books by their own rules. + +An answer that is only generated content goes stale after 2 minutes +(`META_GENERATED_ONLY_STALE_MS`), rather than the hour a community answer keeps. `work.generated` drives the "Generated on this server" chip (`GeneratedChip`). `GeneratedMark` heads a Characters panel and the Previously On card's recap, and diff --git a/docs-developers/server/api/reference.md b/docs-developers/server/api/reference.md index 88c95f67..fa7e16bc 100644 --- a/docs-developers/server/api/reference.md +++ b/docs-developers/server/api/reference.md @@ -2322,8 +2322,9 @@ its other series (`series_list`) in their order. The community rails answer firs but only when they can **place** the next entry on one of the caller's books. Otherwise the local steps decide, in order, and the first one with an answer wins: -1. **`community`**: community metadata is on and the book matches a work with at - least one series rail. +1. **`community`**: community metadata is on and the book's ASIN or ISBN matches + a work with at least one series rail. Unlike `/meta`, this step never reads + the book's recorded match. - **Rail order.** The rails are taken in the book's own order. First comes a rail named like its main series (by the rail's name or one of its orderings', ignoring case, accents, punctuation and spacing). Then come rails named like @@ -2421,13 +2422,27 @@ path is authorized against the caller's share scope exactly like `/item`. The ro is gated by the `metadata` [capability](#get-apiv1server). When it is false the route returns 404, so a client that honours the flag never calls this. -The server resolves the book's `asin` or `isbn`. These are the effective fields on -the indexed book: stored in `book_enrichment`, or set by an admin edit, which wins. -It looks the recording up upstream and folds the matched recording plus up to three +The server finds the book's work in two steps (`bookEnvelope`): + +1. **Its identifiers.** The server looks the book's `asin` or `isbn` up upstream. + These are the effective fields on the indexed book: stored in + `book_enrichment`, or set by an admin edit, which wins. A book its identifiers + match never reads its recorded match. +2. **Its recorded match.** When the book has neither identifier, or the upstream + does not know them, the server reads the book's recorded community match. That + is its `book_matches` row, shown as + [`community_match`](#get-apiv1adminlibrariesidbook) on the console's book page: + the `work_id` and `recording_id` an admin accepted or a match run applied. The + server fetches that work (`works/{id}`, `meta.Service.EnrichMatch`) and uses + the match's recording as the envelope's recording, as a lookup would. When + the match names no recording, or one the work no longer has, that is the + work's first. A book with no match row is unmatched. + +Either way the server folds the matched recording plus up to three series rails into one envelope, one rail per reading-order family (see [Reading-order families](#reading-order-families)). Every series-rail entry carries its own `web_url`, so a client links to the metadata site without ever building a URL itself. Answers are cached in -memory and in the server's database (see +memory and in the server's database, keyed by identifier or by the recorded match (see [Configuration](../configuration.md#community-metadata-metadata)), so a restart serves them warm and a known book keeps its enrichment through a metadata-service outage. @@ -2523,6 +2538,27 @@ the upstream has none. Every `omitempty` string or number field (`subtitle`, - `recording.chapter_count` is the matched recording's chapter count, omitted when the metadata service does not know it. +- `recording.librivox` (2.7.0, additive) says where a LibriVox recording is + published. It is present only when the community gives the recording a LibriVox + project id (`librivox_id` above 0), and absent on every other recording and on + older servers: + + ```json + "librivox": { + "id": 47, + "url": "https://librivox.org/…", + "zip_url": "https://archive.org/compress/…" + } + ``` + + `id` is the LibriVox project id. `url` is the recording's page on librivox.org + and `zip_url` the whole recording as one zip, from the community's + `links.librivox` and `links.zip` (see + [The `links` field](../../meta/data-model.md#the-links-field)). Each is + optional, and the server keeps it only + when it is an absolute `http` or `https` URL with a host (`librivoxOf`). It + never builds either from `id`, because LibriVox's pages are named by slug. A + cached envelope gains the field once its entry is next refreshed. - `series[].position` is this work's position in that series. Positions are strings ("1", "2.5", "1-3.5"). - `series[].works` is the full ordered rail, **including the current work**. The @@ -2822,8 +2858,8 @@ this param: it gates on the device against its live position, which can be ahead of the last saved one. `spoilers=hide` is for clients that cannot gate themselves. -When the book has neither an `asin` nor an `isbn`, or the upstream reports no -match, the response is `200 { "matched": false }`. That is a normal, non-error result +When neither the book's identifiers nor its recorded match (if any) find a work +upstream, the response is `200 { "matched": false }`. That is a normal, non-error result that the client treats as "nothing to show": ```json @@ -2869,10 +2905,11 @@ content wins **per dimension**: `sidecars.describe` job shows its own time. - The whole object is omitted when nothing was generated, or the community has it all. -- **A book with no match** (no `asin`/`isbn`, or none upstream) but generated - content answers `matched: false` **with a work** of its own: `id` empty, - `title` the book's, `authors` `[]`, the generated `characters`, `recaps`, - `recap_summary` and `generated_description`, and `generated`: +- **A book with no match** (neither its identifiers nor its recorded match find + a work) but generated content answers `matched: false` **with a work** of its + own: `id` empty, `title` the book's, `authors` `[]`, the generated + `characters`, `recaps`, `recap_summary` and `generated_description`, and + `generated`: ```json { "matched": false, "work": { "id": "", "title": "The Book", "authors": [], "language": "", @@ -2937,7 +2974,7 @@ generated description, because it is a field they don't know. | `400` | missing `path` / invalid library id | | `403` | path outside the caller's share scope | | `404` | `no book at that path`, **or** metadata lookup is disabled on this server (`metadata` capability false) | -| `500` | with `spoilers=hide`, `could not load progress` - the caller's progress could not be read; the ungated envelope is never sent instead | +| `500` | `could not load book` - the book or its recorded match could not be read. With `spoilers=hide`, also `could not load progress` - the caller's progress could not be read; the ungated envelope is never sent instead | | `502` | `metadata service unavailable` - the upstream was unreachable or errored, the server holds no earlier answer for the book, and it has no generated content | ### `GET /api/v1/libraries/{id}/meta/cover` @@ -2996,7 +3033,8 @@ reach the client. | `400` | `url is required`; `size must be one of [96 160 320 640]` (an empty `size=` too); `path is required` / `invalid library id` | | `401` | missing/invalid token | | `403` | path outside the caller's share scope | -| `404` | `no such cover` - the book is unmatched (no `asin`/`isbn`, or no match upstream) or its envelope hands out no such URL; `no cover` - the image isn't one the server can decode, or is over 40 megapixels; `no book at that path`; `metadata lookup not enabled` (`meta_covers` false) | +| `404` | `no such cover` - the book is unmatched (neither its identifiers nor its recorded match find a work) or its envelope hands out no such URL; `no cover` - the image isn't one the server can decode, or is over 40 megapixels; `no book at that path`; `metadata lookup not enabled` (`meta_covers` false) | +| `500` | `could not load book` - the book or its recorded match could not be read | | `502` | `metadata service unavailable` - the book's envelope couldn't be had; `code: "cover_unavailable"` - the image couldn't be fetched (an address that isn't public, a timeout, too many redirects, an answer other than `200`), or its fetch failed less than a minute ago | ### `GET /api/v1/meta/work` @@ -6421,8 +6459,9 @@ is a `400` too (`too many format or codec values`). duplicates groups' copies and the book page's `book` carry it too. - `matched` - the book has an ASIN or ISBN, or a recorded community match (a store listing with neither, from 2.6.0): the same rule as the `matched` filter - (`catalog.matchedExpr`). The player's `/meta` and the community chapter check find - a book by its ASIN or ISBN alone, so check `asin` / `isbn` for those, not `matched`. + (`catalog.matchedExpr`). The community chapter check finds a book by its ASIN or + ISBN alone, so check `asin` / `isbn` for it, not `matched`. `/meta` also reads + the recorded match, so it can answer a `matched` book with neither identifier. - `abridged` - `true`, `false`, or `null` while nothing says (always present, unlike the player's `Book`, which omits it). `genres` and `tags` are `[]` for none. diff --git a/docs-developers/server/configuration.md b/docs-developers/server/configuration.md index a6cebb21..a8358308 100644 --- a/docs-developers/server/configuration.md +++ b/docs-developers/server/configuration.md @@ -422,7 +422,8 @@ saving (see [`GET /admin/settings`](api/reference.md#get-apiv1adminsettings)). ### Community metadata (`metadata.*`) The server can enrich a book with a description, production details and its -series. It resolves the book's ASIN or ISBN against the community metadata API +series. It resolves the book's ASIN or ISBN, else the book's recorded community +match, against the community metadata API ([meta.audiosilo.app](https://meta.audiosilo.app)) and serves the result at `GET /libraries/{id}/meta`. Players draw it as an "About this book" block plus the recaps, characters and series tabs. The same config also gates @@ -681,17 +682,19 @@ database, in the `meta_cache` table (migration `0024`; see makes the cache survive a restart and an outage: - **What it holds:** - - A book's enrichment, keyed by its ASIN or ISBN: a match, a "no match", or an - envelope missing a series rail. An envelope missing a rail is kept for 2 - minutes only, and never over a stored answer, which stays the outage fallback. + - A book's enrichment, keyed by its ASIN or ISBN, or by its recorded community + match's work and recording ids for a book its identifiers don't match: a + match, a "no match", or an envelope missing a series rail. An envelope + missing a rail is kept for 2 minutes only, and never over a stored answer, + which stays the outage fallback. - Works fetched by id (`/meta/work`, and the previous books `/meta` adds), matches only. - It never holds a service error, and never a row for an unknown work id. An id - is the caller's choice, so storing misses would let any user grow the table. A - `404` from the service for a work already stored replaces its row, so a dropped - work isn't served again in a later outage. In mirror mode a `404` the local copy - gave never does (see [Mirror mode](#mirror-mode-metadatamode-mirror)). + It never holds a service error, and never a row for a work id that `/meta/work` + or `previous` found unknown. That id is the caller's choice, so storing misses + would let any user grow the table. A `404` from the service for a work already + stored replaces its row, so a dropped work isn't served again in a later + outage. In mirror mode a `404` the local copy gave never does (see [Mirror mode](#mirror-mode-metadatamode-mirror)). - **How it is read:** after a memory miss. A row still within its TTL is served and warms memory for the rest of that TTL. A match past its TTL is not served while the service answers, but when the service fails it is served anyway diff --git a/docs-developers/server/data-model.md b/docs-developers/server/data-model.md index 5f1eeb56..a29b1c9b 100644 --- a/docs-developers/server/data-model.md +++ b/docs-developers/server/data-model.md @@ -1621,8 +1621,9 @@ metadata cache (`internal/meta.Store`, `catalog/metacache.go`). It holds the answers the in-memory cache holds, so a restart serves them warm, and a known book keeps its enrichment through a metadata-service outage. Columns: -- `key`: the primary key. A book's enrichment is `a:` or `i:`, and - a work fetched by id is `w:`. +- `key`: the primary key. A book's enrichment is `a:` or `i:`, the + enrichment of a recorded community match is `m:/` (each + id path-escaped), and a work fetched by id is `w:`. - `version`: the payload's format. A row of another version is ignored. - `source`: the metadata service's base URL the answer came from. A row from another URL is ignored. @@ -1631,7 +1632,8 @@ book keeps its enrichment through a metadata-service outage. Columns: Index `idx_meta_cache_stored` on `stored_at`. -It holds community data only, keyed by identifier, never by a book or a user. +It holds community data only, keyed by identifier or community id, never by a +book or a user. Nothing moves it with a book or purges it with an account, and dropping it costs one upstream lookup per book. @@ -1835,7 +1837,13 @@ this row alone. `catalog.matchedExpr` What needs an identifier tests `asin` or `isbn` itself: community chapters, the unknown-ASIN report, upcoming releases, a repick, the Bridge's estimate and the -player's `/meta`. +community step of `/next`. + +`/meta` reads the row only when the book's identifiers find nothing. A book with +neither, or one the upstream does not know, answers from its recorded match +(see [`GET /libraries/{id}/meta`](api/reference.md#get-apiv1librariesidmeta)). +Which player surfaces ask `/meta` for such a book is in +[Enriched book metadata](../frontend/state-and-data.md#enriched-book-metadata). The row moves with the book (`moveBookState`, as one set with its edits), is copied from the first part on a join, is kept through a rescan or rebuild, and diff --git a/docs-developers/server/web-ui.md b/docs-developers/server/web-ui.md index b22c44b0..2a25d481 100644 --- a/docs-developers/server/web-ui.md +++ b/docs-developers/server/web-ui.md @@ -556,8 +556,8 @@ Undo. metadata is off: - The gaps come from the `GET /libraries/{id}/meta` of each series' first book - with an ASIN or ISBN (`metaCandidate`, `hasIdentifier`), because `/meta` is - identifier-keyed. + with an ASIN or ISBN (`metaCandidate`, `hasIdentifier`). A book matched with + neither is skipped, although `/meta` would answer it from its recorded match. - The card offers "Match a book" when that `/meta` found no match. When no book has an ASIN or ISBN, it offers it when none of the series is matched. - Each card resolves its books with an ASIN or ISBN to community work ids diff --git a/docs-users/admin/books.md b/docs-users/admin/books.md index 9ccd6ef6..5c4888e6 100644 --- a/docs-users/admin/books.md +++ b/docs-users/admin/books.md @@ -87,8 +87,10 @@ Its **Community metadata** card says "No ASIN or ISBN: matched to its store listing". A few things need an ASIN or ISBN, so they leave such a book out: the -[community chapter check](#community-chapters), the missing books on the -[Series](#series) page, and the players' community "About this book". +[community chapter check](#community-chapters) and the missing books on the +[Series](#series) page. In the players, such a book shows its community details +on its book page and in Search's characters, but not offline (see +[Recaps and characters](../listening/recaps-and-characters.md#good-to-know)). ### Unlinking a community match diff --git a/docs-users/admin/server.md b/docs-users/admin/server.md index c6808dfa..0ef73b6e 100644 --- a/docs-users/admin/server.md +++ b/docs-users/admin/server.md @@ -134,14 +134,15 @@ servers leave it empty. **Look up community metadata** turns on extra book details from the free community database at [AudioSilo Meta](https://meta.audiosilo.app). Books -with an ASIN or ISBN gain an "About this book" section in the player: a -description, series, and (where written) characters and recaps. See +with an ASIN or ISBN, and books you matched in the console, gain an "About this +book" section in the player: a description, series, and (where written) +characters and recaps. See [A book's page](../listening/book-page.md#about-this-book). What is sent: -- **Looking up a book** sends only its ASIN or ISBN, never file paths or who - is listening. +- **Looking up a book** sends only its ASIN or ISBN, or the community entry + you matched it to, never file paths or who is listening. - **Matching a book** in the console sends search terms from its tags, folder names and length, only when you match. See [What matching sends](books.md#what-matching-sends). diff --git a/docs-users/faq.md b/docs-users/faq.md index c7ca5488..4f3c1f98 100644 --- a/docs-users/faq.md +++ b/docs-users/faq.md @@ -65,7 +65,7 @@ Three names come up across these docs: Only what a lookup needs: -- **Community metadata** sends a book's ASIN or ISBN to meta.audiosilo.app. Matching a book also sends details like its title and author (see [Server settings](./admin/server.md#community-metadata)). The server never sends listener details or full file paths. A match can send up to three names from the book's path (its author, series and book folders) as search terms. +- **Community metadata** sends meta.audiosilo.app a book's ASIN or ISBN, or the community entry an admin matched it to. Matching a book also sends details like its title and author (see [Server settings](./admin/server.md#community-metadata)). The server never sends listener details or full file paths. A match can send up to three names from the book's path (its author, series and book folders) as search terms. - **Help complete the community database** (off by default) sends, once a day, the ASINs the database doesn't know. See [Helping complete the community database](./admin/server.md#helping-complete-the-community-database). - **Podcast search** sends the search text to Apple Podcasts or Podcast Index. See [Searching podcast directories](./admin/podcasts.md#searching-podcast-directories). - **The update check** asks GitHub once a day for the newest version. See [What the update check sends](./admin/server.md#what-the-update-check-sends). diff --git a/docs-users/listening/book-page.md b/docs-users/listening/book-page.md index 1c0312de..7d595835 100644 --- a/docs-users/listening/book-page.md +++ b/docs-users/listening/book-page.md @@ -49,7 +49,7 @@ A finished book shows **Finished** with the date. If your server keeps ratings, ## About this book -The **About** card describes the book. The description comes from the community database, [AudioSilo Meta](../community/meta-site.md), when your server has that switched on and the book is matched. Otherwise it's the one your server holds. +The **About** card describes the book. The description comes from the community database, [AudioSilo Meta](../community/meta-site.md), when your server has that switched on and the book is matched. A book is matched by its ASIN or ISBN, or by an admin in the console. Otherwise it's the one your server holds. If neither has one, your server's [Bridge](../admin/generating-characters-and-recaps.md#a-description-for-the-book) may have written a spoiler-free one, marked **Generated on this server**. @@ -57,6 +57,8 @@ If neither has one, your server's [Bridge](../admin/generating-characters-and-re Under the description you'll find the publisher, release date, language and whether it's abridged, when known. Then come the book's **Genres** and **Tags**. Tap one to open the [Library](browsing.md#books) filtered to it. +When the community database matches the book to a free LibriVox recording, the card ends with **Also free on LibriVox**. It opens that recording's page on librivox.org. + Below the About card: - **Other versions** lists copies of the same book in your other libraries or servers. diff --git a/docs-users/listening/offline-downloads.md b/docs-users/listening/offline-downloads.md index ae329671..4060c15c 100644 --- a/docs-users/listening/offline-downloads.md +++ b/docs-users/listening/offline-downloads.md @@ -92,7 +92,7 @@ When your device has no network, downloaded books play and everything else waits ## Community notes offline -When your server has [community metadata](book-page.md#about-this-book) switched on, a downloaded book keeps its description, [recaps and characters](recaps-and-characters.md) and Series tab. They still [hide what you haven't reached](recaps-and-characters.md#nothing-gets-spoiled-before-you-reach-it). They're refreshed when the copy is over a week old and the app can reach your server. +When your server has [community metadata](book-page.md#about-this-book) switched on, a downloaded book keeps its description, [recaps and characters](recaps-and-characters.md) and Series tab. They still [hide what you haven't reached](recaps-and-characters.md#nothing-gets-spoiled-before-you-reach-it). They're refreshed when the copy is over a week old and the app can reach your server. A book with no ASIN or ISBN, matched only by an admin in the console, doesn't keep them. ## Downloads in a web browser diff --git a/docs-users/listening/recaps-and-characters.md b/docs-users/listening/recaps-and-characters.md index e2d1599f..2bdb3506 100644 --- a/docs-users/listening/recaps-and-characters.md +++ b/docs-users/listening/recaps-and-characters.md @@ -49,6 +49,7 @@ Material from the community database always comes first. The Bridge also writes ## Good to know -- A book only gets material from the community database when it can be matched (it has an ASIN or ISBN) and someone has written it. Many books have a description and a Series tab, but no characters or recaps yet. +- A book only gets material from the community database when it is matched and someone has written it. Many books have a description and a Series tab, but no characters or recaps yet. +- A book matched only by an admin in the console, with no ASIN or ISBN, shows this material on its page and in Search. The companion, Home's cards, the end credits and downloaded copies need an ASIN or ISBN, so they leave it out. - An admin switches community metadata on or off under **Server > Settings > Community metadata** in the admin console. See [Server settings](../admin/server.md#community-metadata). - A [downloaded](offline-downloads.md#community-notes-offline) book keeps this material, so it works offline.