Commit Graph
14 Commits
Author SHA1 Message Date
Robert McRackan 19a867488b Update dependencies 2026-08-30 14:15:35 -04:00
rmcrackan 52325e7ae8 Merge pull request #2003 from rmcrackan/cursor/fix-widevine-metadata-tags-2f5b
Fix metadata tags on the Widevine download path (#2002)
2026-08-26 09:52:25 -04:00
Cursor Agentandrmcrackan bc72aad30c refactor(metadata): flatten Audible's HTML at the API boundary, not on read
Parsing HTML belongs before the data reaches the data layer, not in it. Move the
flattening to AudibleUtilities, where the rest of the Item translation lives, so
Book.Description holds plain text and DataLayer no longer references
HtmlAgilityPack.

Import now updates a description as well as creating one, which it never did
before: books imported while descriptions were stored as raw HTML are still
holding markup, and a re-scan is what clears it. A scan that reports no summary
leaves the stored one alone.

This also cleans up the CSV/Excel export, which was emitting raw HTML too.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-26 13:15:02 +00:00
Cursor Agentandrmcrackan 5c5d1bd946 Replace CsvHelper with a minimal internal CSV writer
CsvHelper was only used to write headers and flat DTO rows in the csv
exports. The new internal writer reproduces its output byte-for-byte:
the delimiter is the culture's list separator, records end with CRLF,
fields containing the delimiter, quotes, newlines, or leading/trailing
spaces are quoted per RFC 4180, and values are formatted with the
culture. The [Name]/[Ignore] attributes it replaced are now two tiny
local attributes.

Verified byte-identical output against CsvHelper 33.1.0 for both
exporters in en-US, de-DE (';' delimiter), and invariant cultures, and
for a seeded 30-book demo library export before/after.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-26 12:48:13 +00:00
Cursor Agentandrmcrackan 1e81c86f65 Merge remote-tracking branch 'origin/master' into cursor/fix-pdf-download-retry-storm-a274
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-19 19:31:14 +00:00
Cursor Agentandrmcrackan 5ce147ef83 test: keep the supplement sync tests out of a project of their own
A new DtoImporterService.Tests pinned MSTest 4.2.2, copied from a project file
master has since moved to 4.3.3, so the merge held nine test projects at 4.3.3
and one at 4.2.2. NuGet lifted the shared MSTest.TestFramework to 4.3.3 while the
4.2.2 metapackage kept supplying the adapter, and the adapter refuses to run
against a framework of a different version: 'Zero tests ran' on every platform.

ApplicationServices references DtoImporterService, so its test project already
sees the importers and needs no package reference of its own. One fewer project
file to keep in step is the point.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-19 19:31:08 +00:00
Cursor Agentandrmcrackan afd970c672 Update MSTest to 4.3.3
Libation already runs its tests on Microsoft.Testing.Platform via the MSTest
metapackage; this only moves the version off 4.2.2 so all three repos name the
same MSTest release.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-19 18:17:57 +00:00
Cursor Agentandrmcrackan 240454efa8 bugfix: don't offer to remove books based on a scan that failed
scanAccountsAsync swallows a per-account failure so the remaining accounts can
still scan. That is right for importing, which only adds and updates, but
FindInactiveBooks treats whatever came back as the whole library and reports
every un-liberated book missing from it as no longer in the account. For a
single-account user a login or network failure therefore yields an empty scan,
and 'Remove Books' pre-checks the entire un-liberated library. ImportAccountAsync
already guards itself with 'if (totalCount == 0) return default;'; this path had
no equivalent.

Track which accounts failed and refuse to identify inactive books when any did,
or when a scan returns nothing while books are at stake. Both grids already
catch and report a failed scan with 'Error scanning library. You may still
manually select books to remove', so throwing lands in the right place and
leaves nothing checked.

Also log how many books the scan found absent, so the count behind a removal
prompt is visible after the fact.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-18 17:58:51 +00:00
rmcrackan a122a6917b Merge pull request #1951 from rmcrackan/cursor/heal-corrupt-lucene-search-index-3c5b
Heal a search index Lucene cannot open, and say which account needs a login
2026-08-16 14:44:17 -04:00
Cursor Agentandrmcrackan 79bdbe6d76 Tell the user how to delete the search index when repair fails
Ported from #1949, which surfaces the manual recovery steps the maintainer had
been giving out by hand instead of leaving the user with a raw Lucene error.

Adapted to the failure now being contained: with the exception no longer escaping
into the library change, the scan-failure catch blocks #1949 hooked would never
see it, and hooking only those would still miss every other trigger -- removing
books is what crashed the GUI. So the guard moves from AppScaffolding into
SearchEngineCommands next to the update commands it protects, and raises
UpdateFailed from there. Both GUIs subscribe, so any trigger is covered, and the
event carries the exception rather than needing #1949's StackTrace string sniffing
to find it. The dialog is shown once per session: a damaged index fails on every
library change and these steps only need following once.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-16 16:44:58 +00:00
Cursor Agentandrmcrackan 854cb280a0 test: cover the retry backoff, the store and the failure classifier
The classifier's inputs are the actual denials from the log attached to issue
#1947: owned titles on an inactive account, a Plus title no longer in the
catalog, and an unreleased preorder Audible has no audio for.

The backoff tests also caught a real overflow: first * 2^n exceeds a TimeSpan
long before the cap matters.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-16 16:23:34 +00:00
Cursor Agentandrmcrackan 59066b7139 feat(limit): add settings UI in both dialogs, throttling suggestion and tests
Avalonia and WinForms both get a Daily download limit group on the
Download/Decrypt tab: scope drop-down, and when a limit is chosen a quantity
spinner (minimum 1, no practical maximum) plus a Books/MB/GB unit, with the
MB/GB approximation note shown only for those units.

A license denial that looks like Audible throttling now suggests turning the
limit on, quoting the real number of downloads Libation recorded in the last 24
hours. Audible reports no distinct throttling reason, so the suggestion stays
silent unless that record makes throttling plausible.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-14 17:27:09 +00:00
Robert McRackan 1c5119e24d Share Audiobookshelf connect orchestration across Classic and Chardonnay 2026-07-31 14:08:57 -04:00
Robert McRackan a6d637c718 Harden Audiobookshelf Server URL handling and connection errors 2026-07-31 14:05:36 -04:00