Bindery on a phone, kinder metadata provider limits, and a long list of import and library fixes
This release is mostly about using Bindery on a phone. Dialogs, lists, Settings and the Queue were reworked after two rounds of testing in real mobile browsers, the back button now closes a dialog instead of leaving the page, a list keeps your page and filters when you come back to it, and Bindery can be installed to a home screen. Metadata providers are treated more gently: every provider now waits and retries when asked to slow down, a bulk import pauses all its requests to a provider at once, and repeated searches are cached. Downloads your NZB client reports as broken are now blocklisted instead of grabbed again on every sweep, proxy auth finally works behind Cloudflare Tunnel, Fix match no longer moves your files unless you ask, and series can be merged. Nasjonalbiblioteket, the National Library of Norway, joins as an opt in metadata provider. Two database migrations run on their own at startup. Please read the upgrade notes if you use proxy auth, NZBGet or SABnzbd, or script Fix match or library scans through the API.
Upgrade notes
- Using proxy auth? Make sure your proxy owns the identity header (#3096, #3097). Bindery now decides whether to trust the identity header by the address of the proxy that connects to it, not the visitor address the proxy forwards, so a header arriving through a trusted proxy is believed. Your proxy must remove or overwrite that header on every route to Bindery, or a visitor could send their own and sign in as anyone. With Cloudflare Access, every hostname and path that reaches Bindery must be covered by an Access application with no Bypass policy. Bindery does not check
Cf-Access-Jwt-Assertion. docs/auth-proxy.md has the details for nginx, Traefik, Caddy and Cloudflare. - NZBGet and SABnzbd failures about the release itself now blocklist it (#3024, #3049). Missing articles, a repair or unpack that cannot succeed, and corrupt or password protected archives add the release to the Blocklist so the next search picks another, instead of retrying the same release every six hours. For failures that may be NZBGet's own problem (
FAILURE/UNPACK,FAILURE/PAR,FAILURE/SCAN), Bindery reads the job's log first and only blocklists on a sign the release is bad. As a safety catch, if the log says nothing and three different releases fail the same step on one client within two hours, Bindery stops blocklisting that step for that client until NZBGet completes it again, and shows a warning on the download client in Settings. If you see that warning, check that NZBGet has UnRAR, free disk space and a writable folder. Releases that failed before the upgrade are not blocklisted retroactively, and a SABnzbd set to a language other than English keeps the old behaviour. - Fix match now leaves your file where it is by default, in the web UI only (#2055, #3107). The dialog asks what to do with the file and defaults to Correct the match only. Scripts calling the API are unaffected: a request without the new
relocatefield still moves and renames the file as before, so send"relocate": falseif you want the new behaviour from a script. - A library scan requested during a scan is queued instead of refused (#3014).
POST /library/scanused to answer 409 while a scan was running. It now answers 202 with"queued": true, and the scan status endpoint reportsrunningandqueued. Scripts that treated 409 as "try again later" can drop that. - Two database migrations run automatically at startup: 101 adds the table that keeps a merged series' old ids as aliases (#2554), and 106 records how an Audiobookshelf item was linked to a book (#1691). Both are additive and need nothing from you.
Fixed
- Proxy auth works behind Cloudflare Tunnel and any other proxy that sets
X-Forwarded-For(#3096, #3097). Bindery checked the visitor's address againstBINDERY_TRUSTED_PROXYinstead of the proxy's own, so every proxied sign in was rejected and the login page offered nothing to click. The rejection log now shows the connecting proxy and the forwarded visitor separately. See the upgrade note above about the identity header. Thanks to Leftastic for the report and the exact fix. - A signed in user is no longer sent to the login page when one session check fails (#3068). A database hiccup or an abandoned request used to make the status check report you as signed out, and the web app took any failed status check as a logout. The server now answers those with a retryable error, the web app keeps your session, and if nothing has loaded yet it offers a Retry button instead of the login page.
- A release your download client reports as broken is blocklisted instead of grabbed again (#3024, #3049). Since v1.38.0 a failed download is retried six hours after it failed, which suits a failure that was about the network or the indexer. It did not suit one about the release itself: NZBGet or SABnzbd failing it for missing articles, a repair or unpack that could not succeed, or a corrupt or password protected archive. That release failed the same way every time, so it was sent to your client again on every sweep and filled its download folder with copies of the same failed job. Now those failures add the release to the Blocklist with a reason such as
downloadFailed: FAILURE/HEALTH, and the next search picks a different one. NZBGet'sFAILURE/HEALTHandFAILURE/BADand the matching SABnzbd failure messages always do. NZBGet also reports problems on its own machine, such as a missing unrar, a full disk while unpacking or a folder it cannot write, asFAILURE/UNPACK,FAILURE/PARorFAILURE/SCAN, so for those Bindery first reads the job's log in NZBGet: a CRC error or a missing volume blocklists the release, while a sign of a problem on NZBGet's side does not, and shows a warning on the download client under Settings. On NZBGet 24 and later Bindery also asks NZBGet whether it can find UnRAR when it needs to know, and shows an error on the client when it cannot. When the log says nothing and three different releases fail the same step within two hours, Bindery stops blocklisting that step for that client until NZBGet completes it again. Other failures, such as NZBGet'sFAILURE/MOVEor SABnzbd running out of disk space, keep the six hour retry. SABnzbd messages are matched in English, so a SABnzbd set to another language keeps the old behaviour. Releases that failed before you upgrade are not blocklisted retroactively; blocklist them from History if they keep coming back. Thanks to Broomfundel for the report and the numbers. - Grab anyway for a release Bindery already imported (#2289, #3108). When a release was marked imported but that import left nothing usable, every Grab answered "already grabbed: this release has already been imported", and the only ways out were deleting the book or deleting the torrent in your download client, which on a ratio tracked tracker throws away its seeding history. Grab now asks whether to grab it anyway, on the Search page, a book's search results and the Wanted page. Confirming sends the release to your download client again and reuses the old Queue entry. Nothing is removed from the download client and the grab does not touch the files the book already has. It is for an import whose files are missing: while they are still there, the new download is skipped as a duplicate. A release that is still downloading or importing is refused as before. API callers send the grab again with
"force": true. Thanks to schmitzkr for the report and Terebi42 for confirming it. - Removing a queue item no longer removes a download another queue item still uses (#3051). When two grabs of the same release end up sharing one torrent in the download client (from two indexers, or by two users), removing one of them, asking for its files to be deleted, a stalled download being cleared, or remove on import now leaves the torrent and its data in place while the other queue item still needs it. The last one to go removes it as before.
- Download client Test and health check look where grabs actually land (#2664, #3108). For rTorrent they checked the client's own default directory, but every grab is sent its own folder built from your download directory, so a seedbox whose default folder Bindery cannot see was reported as unable to import on a setup that imports fine. For a qBittorrent category with an empty save path, Test checked the default save path itself, and the health check failed outright, although qBittorrent saves those downloads to the default save path plus the category name. A qBittorrent client with no category failed the health check as "category is empty" while Test passed it. Both now check the same folder Diagnose reports, a relative category save path is resolved under the default save path, and a category folder qBittorrent has not created yet passes when the folder it will be created in is readable.
- Fix match no longer moves your file unless you ask it to (#2055, #3107). Fixing a match used to run the full import, so the file was moved into the chosen book's folder and renamed from your naming template, replacing your own layout. After you pick the book, Fix match now asks what should happen to the file, and the default, Correct the match only, leaves it exactly where it is under its current name and only changes which book it belongs to. Also move and rename the file is still there for when you want Bindery's layout, and still shows the destination before you confirm. API callers that leave out the new
relocatefield get the old move behaviour. - Edits made during an import are no longer undone (#2375, #3107). When a file was recorded against a book, Bindery rewrote the whole book from a copy it had read a moment earlier, so unmonitoring, renaming or changing the media type of a book while its import was running could quietly come back. Recording a file now only updates the book's status and file paths, and works them out again if you changed the media type at the same moment.
- Audiobooks side by side in an author folder are each matched (#1985, #3107). In a flat
Author/Title.mp3layout, one tracked audiobook made the library scan count every other audiobook in that author folder as already tracked, so a wanted one was never attached and never listed as unmatched either. The scan now only treats files that are tracks of the tracked audiobook (the same name with a part or track number, or a bare number) as belonging to it. - A library scan requested while one is running is queued instead of refused (#3014, #3107). Asking for a scan during a scan used to answer 409 and drop the request, so a file copied into a folder the scan had already walked past was missed until the next scheduled scan. The request is now accepted with
{"queued": true}, and one more scan runs as soon as the current one finishes, however many requests came in meanwhile. The Settings page says so instead of reporting a failure. Thanks to foobarbigtime for the report. - The book page refreshes itself while its download is importing (#2423, #3107). The page was meant to reload every few seconds while a book's file was on its way in, but it waited on a book status Bindery never sets, so it never did and you had to reload by hand. It now follows the book's own download: while one is grabbed, downloading or importing, the page reloads the book every 5 seconds, and stops once the file lands, refreshing the history with it.
- Audiobookshelf import matches a book by its files before its title (#1691, #3106). When Bindery already had a book with the item's file tracked, for example one it downloaded itself, and the Audiobookshelf title was spelled differently ("Chapter House Dune" against "Chapterhouse: Dune"), the import created a second book that could never take the file and so stayed wanted beside the one you own. The item is now matched to the book that already holds its file, as long as both credit the same author. That book keeps its own author and title. Retitling an item in Audiobookshelf and importing again already updates the same book. Thanks to gavinmcfall for the report.
- Review duplicates finds titles filed with the article behind a comma (#1691, #3106). "Trace of Death, A" and "A Trace of Death" are now offered as one group. As before, nothing is excluded until you confirm it. Thanks to gavinmcfall for the report.
- Authors whose middle initials differ are no longer matched as one person (#2881, #3106). "J. R. Smith" and "J. T. Smith" used to count as a confident match, so an import or a provider lookup could file one author's books under the other. Names whose initials disagree at the same position now need a person to confirm them. A name that only leaves initials out, whether the first name is an initial ("J. Rowling" against "J.K. Rowling") or written in full ("George Martin" against "George R. R. Martin"), is not matched on the name alone, but does match wherever the book's title already agrees. The usual spellings of one author still match on their own: "J.R.R. Tolkien", "J. R. R. Tolkien", "JRR Tolkien", "Jrr Tolkien" and "John Ronald Reuel Tolkien" are still the same person.
- Re-running an author import no longer duplicates authors linked to another provider (#2117, #3106). The Readarr import and the pasted or uploaded author list only recognised an existing author by provider id, so an author linked to DNB during an OpenLibrary outage, or added on a Hardcover primary, was added a second time once OpenLibrary answered with its own id. An author already in your library under the same name is now skipped, whichever provider it is linked to. Nothing is merged or changed; use Merge on the author page for duplicates you already have. Thanks to TheOpsMgr for the report.
- Authors added from DNB or Hardcover are labelled with the right provider (#2117, #3106). When the provider could not be asked for the author's details (DNB has no lookup by id at all), adding a book or an author, or mapping an Audiobookshelf author by hand, labelled the new author as an OpenLibrary author whatever its id said.
- Merging two authors keeps the moved books' timestamps in the standard format (#2792, #3098). Moving books over during an author merge stored their
updated_atin a different shape from every other write. Reads tolerated it, but anything comparing or sorting that column as text saw two formats on the merged books. Thanks to magrhino for the report and kragent66-glitch for the fix. - Series fill respects the author's metadata profile (#2208, #3109). Fill gaps and Add all on a series no longer create box sets, undated works or anything else the profile filters out, and a fill no longer puts stored books the profile rejects back on Wanted. The result line says how many the profile skipped. Adding a single book from a series row still works for anything you pick by hand. Thanks to ianepreston for the report.
- Split edition parts of a book you own are left alone (#3048, #3109). Parts like "The Way of Kings, Part 1" and "Part 2" next to The Way of Kings itself are no longer put back on Wanted by Fill gaps or searched by the scheduled wanted search, so Bindery stops downloading parts of a book on your shelf. The series card marks them as split parts, leaves them out of the missing count, and offers to unmonitor any a fill from an older version left monitored. Nothing is deleted.
- Every metadata provider now waits and retries when asked to slow down (#2369, #3100). Google Books, DNB, Nasjonalbiblioteket, Audible and audnex used to give up on the first "too many requests" answer. They now wait and retry the way OpenLibrary does, honouring the provider's own
Retry-After, so an Audiobookshelf import or a Google Books quota blip no longer drops a lookup that a second try would have answered. A refusal that outlasts the retries is reported as a rate limit, so scheduled discovery pauses instead of walking on through its queue. A Google Books key whose daily quota has run out is not retried, since that only resets the next day. - A provider that asks Bindery to slow down now pauses every request to it (#2075, #3100). During a bulk author import each request used to back off on its own, so the other catalogue fetches and edition lookups in flight kept hitting OpenLibrary while it was throttling, and the 429s turned into timeouts and refused connections. A search whose wait would run past its own time limit now fails straight away rather than sitting out the timeout, while CSV, Readarr and Goodreads imports wait the pause out and carry on instead of failing rows. If the provider keeps refusing, the import stops after a few rows and tells you to try again later, rather than waiting and asking for every row. The ISBN and ASIN lookups in the Add Book dialog now give up after 20 seconds with an error instead of hanging until the connection drops. Thanks to TheOpsMgr for the report and the patient retesting.
- A Hardcover rate limit sent as an error message now slows Bindery down (#2791, #3100). A limit that arrived as a normal answer carrying an error message used to count as a healthy request, which relaxed the pacing earlier refusals had set up. It is now retried and paced like any other refusal, and other query errors no longer count toward relaxing the pacing either.
- ISBN and Audible ASIN lookups no longer hand out a stale or shared record (#2869, #3102). An ISBN or ASIN answer was cached for a day under a key that ignored the metadata provider settings, so changing the primary provider or the Hardcover token kept serving the old answer, and every caller was handed the same cached book. Each lookup now gets its own copy, a settings change fetches a fresh answer, and a record built while an enrichment provider was failing is kept for five minutes instead of a day so it is retried. Thanks to magrhino for the report.
- Translations from Hardcover no longer slip past the language filter when OpenLibrary is the primary provider (#3091, #3102). Hardcover's edition languages were only checked when Hardcover was the primary provider, so with OpenLibrary primary its translated works arrived with no language and the default "pass" setting let them in, often monitored and wanted. They are now checked whenever a Hardcover token is set, and a work whose only known editions are in other languages is skipped. A work whose title is in a different script from the author's other titles no longer inherits the author's language, and OpenLibrary no longer spends a request per Hardcover work looking for editions it does not have. Thanks to magrhino for the report.
- OpenLibrary works take their language and cover from the edition OpenLibrary features (#1779, #3102). When a work had no language or cover of its own, Bindery read a handful of editions in whatever order OpenLibrary returned them, so a work whose first few happened to be translations could be filed as foreign language or shown with a translated cover. It now uses the work's featured edition, the one its openlibrary.org page shows, and only samples other editions for what that edition lacks, preferring a cover in the language it settled on. A single book fetched from OpenLibrary, as on Re-bind, also takes the featured edition's cover when the work has none. This costs up to two extra OpenLibrary requests per work that needs sampling, and one extra request for a single book only when its work has no cover.
- Re-binding a book drops the old record's editions (#2781, #3102). A book re-bound from the wrong metadata record kept that record's provider editions, so searches could keep using the wrong work's ISBN. A re-bind to a different record now removes them. Editions imported from Calibre or Audiobookshelf, the edition you selected, and editions a download or Calibre delivery points at are kept. Thanks to magrhino for the report.
- Books from a Hardcover list sync reach Calibre with their ISBN and publisher (#1853, #3108). Since list sync stopped fetching every book's editions up front, a list synced book had no editions on record, so when it was later downloaded and handed to Calibre it went without an ISBN, publisher, publish date or edition language. Now a Hardcover book with no editions has them fetched from Hardcover when it is delivered to Calibre, for that one book only, and they are kept for next time. It needs a Hardcover API token. At most five books are fetched per delivery run or pull listing, so a large backlog arrives over a few minutes, and the book still goes to Calibre without them if Hardcover cannot answer.
- calibredb mode says so when there is no calibredb (#1940, #3108). The official Bindery image does not include Calibre, so with the write integration set to calibredb CLI nothing ever reached Calibre, while every import looked successful and the only trace was one line in the log. Now saving that mode answers with a warning when calibredb cannot be found, or is there but can't run in the image, as a Calibre install mounted into it can't, and the Calibre settings tab shows it whenever it opens in that mode, with a button to switch to the Calibre Bridge plugin. Test connection gives the same reason. Books imported in the meantime wait in the delivery queue without using up their retries, and its status line names the missing calibredb, so they go to Calibre once calibredb is installed or the Bridge plugin is set up. Thanks to drcrypt for the report.
- Searching indexers from a book page shows that something is happening (#1636, #3103). The button spins while the search runs, and when the results arrive the page scrolls down to them, so a slow indexer no longer leaves you looking at a button that seems to do nothing. Thanks to SturmB for the report.
- Automatic searches are easier to follow from scripts (#2154, #3103). A bulk search through the API now marks each book whose search is actually on its way with
"queued": true, because"ok": trueonly ever meant the request was accepted, not that the search had finished. When no search will run, for example because the book already has every format it wants, the entry says why insearchSkipped.GET /api/v1/search/last-debugnow also shows scheduled, bulk and other automatic searches, says which kind of search it is, which book it was for and what came of it, and a request made with the API key sees the search a user just ran in the browser. Thanks to SturmB for the report. - Modals on phones (#3052, #3053). On an iPhone the buttons at the bottom of a dialog could end up at the very edge of the screen or under the browser toolbar once the dialog filled with search results, where a tap shows the toolbar instead of pressing the button. Dialogs now size themselves to the visible part of the screen and keep a gap below their buttons, and the page behind an open dialog no longer scrolls on touch devices. Tapping into a text field no longer zooms the page in, and the book details in Add to library stay inside their card on narrow screens. Thanks to johnistheman for the report and the fix.
- Back button with a dialog open (#3052, #3074). On Android the back gesture or button used to leave the page behind an open dialog, throwing away whatever you had typed into Edit metadata, Edit author or Add to library. Back now closes the dialog and leaves you on the page, one dialog at a time when one is open on top of another, and closing a dialog any other way no longer leaves an extra step in your back history. Every dialog now closes with Escape, keeps Tab inside itself, returns focus to the button that opened it, and is announced as a dialog by screen readers. With a phone in landscape, Reconcile catalogue and Edit author now scroll as a whole instead of squeezing their lists into a sliver, and Edit metadata no longer lets the page behind it scroll.
- Going back to a list keeps your place (#3052, #3064). Opening a book from page 3 of Books and going back, whether with the browser button, the Android back gesture or a swipe on iPhone, used to land on page 1 with the search cleared. Books, Authors and Wanted now keep the page, the search, the filters and the sort order in the address bar, so back returns to the same list scrolled to where you left it, and a copied link opens the same view. Opening a book or author now always starts at the top of its page instead of at the list's old scroll position.
- Pages on phones (#3052, #3065). A round of fixes for screens that broke on a phone:
- The menu behind the hamburger button now scrolls, so the admin menu no longer runs off the bottom of the screen with its last entries out of reach.
- Wanted keeps the book title readable: the format picker and the Unmonitor and Search buttons move to a line under the title instead of squeezing it to nothing.
- Users scrolls sideways when the table is wider than the screen, so Reset password and Delete are no longer cut off.
- The Calibre rollback and Push all dialogs keep their buttons on screen and scroll their content instead.
- Logs shows each entry as a short block on a phone, so the message is readable instead of squeezed into a sliver.
- The bar that appears when you select books or authors stays on one row, with the less common actions under More, so it no longer covers the list and the page buttons.
- Settings picks a section from a dropdown on a phone, so choosing one shows it right away, and the back button now returns to the section you were on before instead of leaving Settings.
- The update notice, the Fix match dialog, the search page, the library search results, the filter menu and the download client form all fit narrow screens.
- Keyboards, password managers and touch targets on phones (#3052, #3062). Phones no longer capitalise or autocorrect usernames, hosts, URLs and API keys, show a number pad for download client ports, and label the keyboard's return key Search in search boxes. Password managers on iOS and Android stop filling your Bindery login into API key, token and download client credential fields, and stop offering to save those keys as your password. File paths, rename previews and import errors wrap on small screens instead of hiding the rest behind a hover tooltip, and an import row's short reason can be tapped to show the full explanation. The Links menu on a book now closes on a second tap on touch screens, and settings switches have a larger tap area.
- More phone fixes (#3052, #3070). On a narrow phone the Queue's buttons now sit on their own line under a download instead of squeezing its error text into a thin column, and the Match to book search fits the screen. Long author names, book titles and series names wrap instead of widening the page or getting cut to a few letters, and the full release or book name shows on Queue and Wanted. Dropdowns no longer draw their text under the arrow on touch screens, and Save buttons in Settings stay inside their card. Things that only showed on hover can now be reached by tapping: the exact time behind "6m ago" in the Queue, what each file naming token means, and why Enrich is unavailable without an ASIN. View toggles, filter chips, the Library tabs, book checkboxes, the History and Indexer actions and Sign out are easier to hit with a finger. In the dark theme, date pickers, dropdown lists and scrollbars are dark too. Failure reasons in History and the Blocklist are easier to read in the light theme. On the phone calendar, tapping a day with releases lists just that day. A tab left open across an upgrade now reloads itself when it opens a page instead of showing an error.
- Phone follow ups (#3052, #3078). Settings no longer jumps as it loads: the Security section used to appear a moment late and push everything under it down the screen. On a 320px phone the Default monitor mode dropdown in Settings, Metadata, the date fields in Logs and the Queue's Match to book search all fit the screen again, with nothing cut off. The More menu on an author page opens upward when it is near the bottom of the screen instead of running off it. On a phone each user on the Users page is a card with its Reset password and Delete buttons in view, instead of a table that hid them off to the side. Pagination, Sort and Filters, the Download link on Books, the Series actions, Retry all failed and Clear all failed, the Queue's time toggle, Add Indexer and the selection bar's buttons are easier to hit with a finger. The page now tells the browser and screen readers which language the interface is in, so they no longer read a translated interface as English.
- An empty rollback preview no longer crashes Settings (#3076). Opening the rollback preview for a Calibre import with nothing to undo, such as a dry run, now says there is nothing to roll back and keeps Apply disabled. The Audiobookshelf rollback preview gets the same fix.
- A tab left open across an upgrade reloads on iPhones and iPads too (#3076). It now reloads itself once instead of showing the error page. Bindery answers a request for a missing script or stylesheet with a 404 rather than the app page, and the reload check recognises the Safari, Chrome and Firefox messages for a script that came back as a web page.
Changed
- Fewer requests to metadata providers (#2594, #2615). Identical searches within five minutes reuse the earlier answer, and identical lookups in flight at the same moment share one request instead of each asking the provider. Cached answers stay separate per account and provider settings, every caller gets its own copy, edition results stay complete, and failures are not cached so they are retried. Refresh Metadata still fetches the author and catalogue fresh. Thanks to magrhino for the request and the work.
- Review duplicates shows its evidence (#2999, #3004). Every row now says whether it has files (ebook or audiobook, with the format), its status, year, language, ISBN, ASIN and series position, and each group says where the rows agree (a shared ISBN, ASIN or series position) or disagree (different series positions, years more than a year apart, different languages). When exactly one row has files it is marked as the one to keep. If nothing disagrees and something ties each empty row to it (a shared ISBN, ASIN or series position, or a title that is the same apart from punctuation, an article or an edition marker), the group offers to exclude the empty rows in one confirmed click. That offer never includes a row with files, a row imported after the page loaded is skipped rather than excluded, and nothing is excluded until you confirm. Thanks to foobarbigtime for the request.
- Pages load faster, especially on phones (#3052, #3063). Languages other than English are now downloaded only when selected, which cuts the main script by about a third, and cover and author images in lists and grids load as they scroll into view.
- Test databases now match production settings (#3042). A follow up to the faster test suite from v1.40.2: each test database copy now gets the same SQLite settings a normal open does. Test only, no runtime change. Thanks francisrath.
Added
- Merge series (#2554, #3057). One series that ended up as several (a provider naming it two ways, or renaming it) can be folded into one from the series page, with a preview first. Books keep the kept series' positions and fill its gaps, and the merged series' provider ids stay as aliases, so a later refresh no longer recreates them. Admin only. Thanks to francisrath for building it and magrhino for the request.
- Nasjonalbiblioteket metadata provider (#2979, #2995). The National Library of Norway can be chosen as the primary metadata provider in Settings, Metadata Profiles. It covers Norwegian publications by legal deposit under their original Norwegian titles, so a Norwegian library's files match the author's catalogue instead of the English translations other providers list. Books carry the author's series and their number in it, and the library's genres. Audiobooks carry their narrator and running time where the library records them. An author's other spellings, such as the name without its Norwegian letters, become aliases so release names still match. It is opt in: an install that does not select it never contacts the National Library. Thanks francisrath.
- Duplicates across the whole library (#2999, #3004). Books has a new Review duplicates page that lists every author's duplicate title groups a page at a time, with the same evidence and buttons as an author's window. It finds exactly the groups each author's window would, and a group drops off once it no longer has two included rows. Thanks to foobarbigtime for the request.
- Install Bindery as an app (#3052, #3063). Bindery can be added to a phone or desktop home screen: it now has a web app manifest, an Apple touch icon and a browser toolbar colour that follows the light or dark theme, including when you override the system theme in Bindery. Installed, it keeps the header and bottom bars clear of the notch and home indicator, and it works under a URL base.