Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
405 commits
Select commit Hold shift + click to select a range
b212c47
docs: correct how the wait applies to a title's PDF
cursoragent Aug 16, 2026
4409fb6
Make the lock conflict test deterministic on Windows
cursoragent Aug 16, 2026
77bbc1b
Treat a Windows sharing violation on write.lock as a lock conflict
cursoragent Aug 16, 2026
d7a6ef0
test(pdf): compare paths the way Libation produces them
cursoragent Aug 16, 2026
bb55fb4
Stop the filter box looping on dialogs when the search index is at fault
cursoragent Aug 16, 2026
6daaf33
docs: the PDF and log fixes ship in 13.7.10, not 13.7.9
cursoragent Aug 16, 2026
a297715
Revert "docs: the PDF and log fixes ship in 13.7.10, not 13.7.9"
cursoragent Aug 16, 2026
1999307
Merge pull request #1952 from rmcrackan/cursor/no-rights-retry-backof…
rmcrackan Aug 16, 2026
9afcb09
Keep account addresses out of the auto-scan pause log line
cursoragent Aug 16, 2026
a122a69
Merge pull request #1951 from rmcrackan/cursor/heal-corrupt-lucene-se…
rmcrackan Aug 16, 2026
209fd3a
Bump version to 13.7.9
cursoragent Aug 16, 2026
bb0ece9
Merge pull request #1953 from rmcrackan/cursor/release-13-7-9-bf7c
rmcrackan Aug 16, 2026
d93d325
Added Multi Threading for downloads and Auto Scroll to view current d…
SirBiggin Jun 16, 2026
e90850f
Fix thread-safety crash in setLiberatedVisibleMenuItem during paralle…
SirBiggin Jun 21, 2026
e11186d
Review #1885: keep the machine-specific installer change out of the PR
dmatlock171 Aug 12, 2026
ad59032
Review #1885: cancel in-flight downloads on abort and disk full
dmatlock171 Aug 14, 2026
2db63a8
Review #1885: one concurrency setting with a safe default and a numer…
dmatlock171 Aug 14, 2026
986dda5
Review #1885: Avalonia parity, and cap concurrency by processor count
dmatlock171 Aug 14, 2026
5ca4389
Fix overlapping controls in the WinForms process queue panel
dmatlock171 Aug 14, 2026
9383a04
Fill available slots when books are queued after the loop starts
dmatlock171 Aug 14, 2026
e49f410
Fix TrackedQueue notification and enumeration under concurrency
dmatlock171 Aug 16, 2026
ac19e9e
Make the daily download limit work with a concurrent queue
dmatlock171 Aug 16, 2026
325af3a
Address the remaining review points on concurrent processing
dmatlock171 Aug 16, 2026
2b7214e
Take a copy of the active books before applying the speed limit
dmatlock171 Aug 16, 2026
524e1ce
Credit SirBiggin and dmatlock171 as contributors
dmatlock171 Aug 16, 2026
d7a794e
Pin down TrackedQueue ordering and notifications under concurrency
dmatlock171 Aug 16, 2026
381cf30
Stop the spinner's bound from overwriting the stored concurrency
dmatlock171 Aug 16, 2026
4187712
Add HasSubtitle and TitleHasColon search fields
cursoragent Aug 16, 2026
6a6e1ce
fix(metadata): keep a title's metadata when Audible's catalog has none
cursoragent Aug 16, 2026
537f53d
fix(pdf): do not save a non-PDF response as a title's PDF
cursoragent Aug 16, 2026
14ec480
feat(grid): set a title's PDF status without touching its audiobook
cursoragent Aug 16, 2026
3a3ff42
Merge pull request #1957 from rmcrackan/cursor/subtitle-search-fields…
rmcrackan Aug 16, 2026
78a0446
Merge pull request #1956 from rmcrackan/cursor/empty-metadata-and-pdf…
rmcrackan Aug 17, 2026
383d768
Lay the queue settings out so they stop colliding
dmatlock171 Aug 17, 2026
6377813
Stack Chardonnay's queue settings so Clear Finished fits
dmatlock171 Aug 17, 2026
337cb31
Adopt Dinah.Core 10.2.2.1 and AudibleApi 11.0.0.1
cursoragent Aug 17, 2026
625c0f2
Keep the account out of the failure that reports it
cursoragent Aug 17, 2026
d0cf459
fix(config): serialize PersistentDictionary cache and file access
cursoragent Aug 17, 2026
742e58d
Mask the account on the paths that do not go through an exception
cursoragent Aug 17, 2026
4ac94d1
Say AsScalar instead of restating ToString
cursoragent Aug 17, 2026
f472334
fix(config): replace Settings.json atomically instead of truncating it
cursoragent Aug 17, 2026
c813d6e
Drop AsScalar; SecretString reports its own shape now
cursoragent Aug 17, 2026
2b3325e
Point at Dinah.Core 10.2.3.1
cursoragent Aug 17, 2026
e8d215e
Point at Dinah.Core.WindowsDesktop 10.2.3.1
cursoragent Aug 17, 2026
9ea66dd
Point at AudibleApi 11.0.2.1
cursoragent Aug 17, 2026
bbb401e
fix(config): outlast a brief lock on Settings.json, and scope the rea…
cursoragent Aug 17, 2026
14c56e4
Merge pull request #1960 from rmcrackan/cursor/adopt-secretstring-pac…
rmcrackan Aug 17, 2026
c11268c
fix(test): clear CA1416 platform warnings on the unix file mode calls
cursoragent Aug 17, 2026
b80cf1b
Merge pull request #1961 from rmcrackan/cursor/fix-persistentdictiona…
rmcrackan Aug 17, 2026
1e2c945
docs: name the PDF-only control that already existed
cursoragent Aug 17, 2026
e6f8c6f
Merge pull request #1962 from rmcrackan/cursor/pdf-status-comment-acc…
cursoragent Aug 17, 2026
5beaad0
Run FileLiberator's tests serially
cursoragent Aug 17, 2026
b9aa51c
Widen the guard so it can see every exception it should
cursoragent Aug 17, 2026
d27445a
Stop a disposing file watcher from crashing the process
cursoragent Aug 17, 2026
2e49f0d
docs(faq): use ## for audible standard question
tippfehlr Aug 17, 2026
4825acc
Merge pull request #1963 from tippfehlr/faq-heading-levels
rmcrackan Aug 17, 2026
e6e8ba7
Upgrade to Dinah.Core 10.2.4.1 and AudibleApi 11.0.3.1
cursoragent Aug 17, 2026
0d13200
Merge pull request #1964 from rmcrackan/cursor/test-hygiene-guards-d9d1
rmcrackan Aug 17, 2026
294608d
Bump version to 13.7.10
cursoragent Aug 17, 2026
6fba3d6
Merge pull request #1966 from rmcrackan/cursor/release-13-7-10-8397
rmcrackan Aug 17, 2026
c827b9b
docs: correct the Smart App Control guidance
cursoragent Aug 18, 2026
6fa6523
Apply the same Smart App Control correction to the in-app dialog
cursoragent Aug 18, 2026
cc90740
docs: fix a garbled sentence in the cloud-sync section
cursoragent Aug 18, 2026
d240ab1
Present waiting for signed builds as an option now that signing is in…
cursoragent Aug 18, 2026
a0ca574
Detect Application Control enforcement and stop upgrading into a brok…
cursoragent Aug 18, 2026
0efe50b
Give Classic a one-line reason that fits its dialog
cursoragent Aug 18, 2026
af163df
Merge pull request #1968 from rmcrackan/cursor/fix-smart-app-control-…
rmcrackan Aug 18, 2026
099b01a
Ask Windows which folders are synced instead of guessing from OneDriv…
cursoragent Aug 18, 2026
240454e
bugfix: don't offer to remove books based on a scan that failed
cursoragent Aug 18, 2026
a42c332
Merge pull request #1969 from rmcrackan/cursor/gate-in-app-upgrade-un…
rmcrackan Aug 18, 2026
4df08d3
Show when the trash is not empty
cursoragent Aug 18, 2026
12cbc7e
Name the caller in the book and PDF count logs
cursoragent Aug 18, 2026
d000a8e
Merge pull request #1971 from rmcrackan/cursor/tag-book-count-log-wit…
rmcrackan Aug 18, 2026
6288bc5
Log trash changes, and reveal a searched-for book that is in the trash
cursoragent Aug 18, 2026
bf9b853
Seed books in the trash for manual testing
cursoragent Aug 18, 2026
cc50f18
docs: document the trash bin
cursoragent Aug 18, 2026
b959d87
Offer a way forward when the library is empty
cursoragent Aug 18, 2026
bc29733
Add a script that seeds fake Audible accounts
cursoragent Aug 18, 2026
2aa6d8c
Record why demo accounts are identified by id, not a flag
cursoragent Aug 18, 2026
fc973d4
Keep MaskedLogEntry out of AccountsSettings.json
cursoragent Aug 18, 2026
cb01b9b
Keep pointing at the trash when the library itself is empty
cursoragent Aug 18, 2026
0934701
Merge pull request #1970 from rmcrackan/cursor/1925-guard-inactive-bo…
rmcrackan Aug 18, 2026
955c5ca
bugfix: show the trash line the first time the library goes empty
cursoragent Aug 19, 2026
d23b87d
docs: AGENTS.md was wrong that WinForms cannot build on Linux
cursoragent Aug 19, 2026
0ca01c3
theme the new empty-state links and the status bar trash count
cursoragent Aug 19, 2026
8824382
seed script: drop the search index so filters find the seeded books
cursoragent Aug 19, 2026
3b33ee2
Merge pull request #1972 from rmcrackan/cursor/1925-winforms-trash-li…
rmcrackan Aug 19, 2026
4a138bb
Move the .NET 10 package references to 10.0.11
cursoragent Aug 19, 2026
afd970c
Update MSTest to 4.3.3
cursoragent Aug 19, 2026
e97c682
fix(pdf): fetch a supplement from the license the audiobook download …
cursoragent Aug 19, 2026
a60299e
fix(queue): wait on a title that needs only its PDF, like any other
cursoragent Aug 19, 2026
f0d3440
fix(scan): keep a book's supplement in step with what the last scan says
cursoragent Aug 19, 2026
d4b5a48
test: cover the supplement download's license, status and selection r…
cursoragent Aug 19, 2026
cb8c1b3
docs: a PDF is the same license request as the audiobook
cursoragent Aug 19, 2026
71e8d8e
Adopt Dinah.Core 10.2.5.1 and AudibleApi 11.0.4.1
cursoragent Aug 19, 2026
316099b
Merge pull request #1974 from rmcrackan/cursor/sync-net10-servicing-1…
rmcrackan Aug 19, 2026
ed20356
Merge pull request #1975 from rmcrackan/cursor/align-mstest-testing-p…
rmcrackan Aug 19, 2026
6a166b2
Merge master
cursoragent Aug 19, 2026
63dc8fc
Merge pull request #1977 from rmcrackan/cursor/adopt-dinah-10-2-5-1-a…
rmcrackan Aug 19, 2026
0cecaad
Generate real source links for the built assemblies
cursoragent Aug 19, 2026
8c1b754
Merge pull request #1978 from rmcrackan/cursor/sourcelink-github-prov…
rmcrackan Aug 19, 2026
7e89a96
fix(cli): count only the absent titles a run would have attempted
cursoragent Aug 19, 2026
5ce147e
test: keep the supplement sync tests out of a project of their own
cursoragent Aug 19, 2026
1e81c86
Merge remote-tracking branch 'origin/master' into cursor/fix-pdf-down…
cursoragent Aug 19, 2026
1d9b41c
Fix QuickFilters not loading from disk after restart (#1979)
cursoragent Aug 19, 2026
a030853
Merge pull request #1976 from rmcrackan/cursor/fix-pdf-download-retry…
rmcrackan Aug 19, 2026
49e8164
Use System.Threading.Lock for the QuickFilters locker
cursoragent Aug 19, 2026
10c1cc2
Restore MaxSampleRate clamping lost with migrate_to_v11_6_5
cursoragent Aug 19, 2026
7cb58c7
fix: one unreadable path no longer stops the file cache tracking anyt…
cursoragent Aug 19, 2026
0b03be5
Merge pull request #1980 from rmcrackan/cursor/fix-quickfilters-not-l…
rmcrackan Aug 19, 2026
819a096
Merge pull request #1981 from rmcrackan/cursor/keep-file-cache-alive-…
rmcrackan Aug 19, 2026
e9e5618
Stop abort and disk full from killing the queue loop
dmatlock171 Aug 20, 2026
b38e298
Deliver TrackedQueue notifications in mutation order
dmatlock171 Aug 20, 2026
0c66a2e
Drive TrackedQueue ordering through the posted path in tests
dmatlock171 Aug 20, 2026
059b817
Give Chardonnay's queue spinners a row of their own
dmatlock171 Aug 20, 2026
5d0ed3a
Make auto-scroll mean the same thing in both UIs
dmatlock171 Aug 20, 2026
d36369b
Make Abort mean the run, and stop it taking the queue loop with it
dmatlock171 Aug 21, 2026
84c6f6f
Scope cancelAllRequested to the run rather than to the drain
dmatlock171 Aug 21, 2026
1eddccb
Bound concurrency by the hard limit and say what the machine will do
dmatlock171 Aug 21, 2026
f816ccf
Clear the review nits
dmatlock171 Aug 21, 2026
8b16631
Pin down the dispatch loop with tests
dmatlock171 Aug 21, 2026
1f38ba8
Document parallel downloads
dmatlock171 Aug 21, 2026
29a9cb3
Fix the sidebar entry, a test race, and comments left pointing at del…
dmatlock171 Aug 21, 2026
b687559
Stop the dispatch tests measuring the CI runner instead of the loop
dmatlock171 Aug 21, 2026
492d2e9
Cancel the book, not the queue, at the daily download limit
dmatlock171 Aug 21, 2026
03dab18
test fix: give the anchored evil-regex row an input that cannot beat …
cursoragent Aug 24, 2026
5ef3f3e
fix: a directory that stops being readable no longer throws at whoeve…
cursoragent Aug 24, 2026
cc40d5e
fix: an unreadable Books folder no longer poisons AudibleFileStorage
cursoragent Aug 24, 2026
75f1d75
fix: counting the visible books can no longer close Libation
cursoragent Aug 24, 2026
1839eeb
feat: say so when the Books folder is there but cannot be read
cursoragent Aug 24, 2026
5f7df75
Merge pull request #1987 from rmcrackan/cursor/fix-flaky-anchored-bac…
rmcrackan Aug 24, 2026
edf0d32
docs: troubleshooting entry for a Books folder that returns I/O errors
cursoragent Aug 24, 2026
df97029
test: stop asserting on path spelling, which differs on Windows
cursoragent Aug 24, 2026
c6e5e37
Merge pull request #1988 from rmcrackan/cursor/keep-a-failing-books-v…
rmcrackan Aug 24, 2026
c376af9
Stop two queue tests racing the notification they assert on
dmatlock171 Aug 24, 2026
326e48c
Deliver queue notifications outside the lock a handler can block behind
dmatlock171 Aug 24, 2026
5e1ac3f
Keep the concurrency hint inside the width it has
dmatlock171 Aug 24, 2026
76e036f
Report the abort on the book the user answered for
dmatlock171 Aug 24, 2026
819193f
Merge pull request #1958 from dmatlock171/parallel-downloads-rebased
rmcrackan Aug 24, 2026
bb593fe
docs(queue): say what auto-scroll actually waits for
cursoragent Aug 24, 2026
aecffc4
refactor(queue): name the pending helpers, and log a stray completion…
cursoragent Aug 24, 2026
d424820
fix(queue): report the books an enqueue added, and where they start
cursoragent Aug 24, 2026
a0ef5c3
fix: repair a search index that is missing books
cursoragent Aug 24, 2026
7aecda4
revert: drop the searching-and-filtering note added with the index re…
cursoragent Aug 24, 2026
bf78563
Merge pull request #1990 from rmcrackan/cursor/queue-notification-cle…
rmcrackan Aug 24, 2026
0489be0
fix: searching a field for a word that names another field
cursoragent Aug 24, 2026
0e6523e
fix: a number is only zero-padded when the field it is compared again…
cursoragent Aug 24, 2026
cad813e
test: cover a book found through Hours, not just LengthInMinutes
cursoragent Aug 24, 2026
b56e55b
Merge pull request #1991 from rmcrackan/cursor/repair-a-search-index-…
rmcrackan Aug 24, 2026
5c61ebd
docs: how to tell a search keyword from the word, and a number from a…
cursoragent Aug 24, 2026
59ab316
docs: rewrite the Lucene syntax guide around Libation's own fields
cursoragent Aug 24, 2026
8402010
Merge pull request #1992 from rmcrackan/cursor/parse-a-field-name-use…
rmcrackan Aug 24, 2026
a004515
docs: drop Neil Gaiman from the examples
cursoragent Aug 24, 2026
798602c
Merge pull request #1993 from rmcrackan/cursor/drop-neil-gaiman-from-…
rmcrackan Aug 24, 2026
bf017ac
fix: restore the LibationWinForms build
cursoragent Aug 24, 2026
b1452da
ci: fail the Windows build when a publish fails
cursoragent Aug 24, 2026
5956c83
Merge pull request #1994 from rmcrackan/cursor/fix-winforms-visible-b…
rmcrackan Aug 24, 2026
1520647
Merge remote-tracking branch 'origin/master' into cursor/fail-the-win…
cursoragent Aug 24, 2026
df04be9
Merge pull request #1995 from rmcrackan/cursor/fail-the-windows-build…
rmcrackan Aug 24, 2026
04a7103
Bump version to 13.7.11
cursoragent Aug 24, 2026
c01e83f
Merge pull request #1996 from rmcrackan/cursor/release-13-7-11-c334
rmcrackan Aug 24, 2026
1431fd7
test: cover a title made of digits and punctuation
cursoragent Aug 24, 2026
2497e3a
Merge pull request #1997 from rmcrackan/cursor/cover-a-date-shaped-ti…
rmcrackan Aug 24, 2026
f870612
Rename the 'Not Downloaded' status label to 'Download Pending'
cursoragent Aug 24, 2026
e0c780c
Make --download-pending the CLI flag, with --not-downloaded as a lega…
cursoragent Aug 25, 2026
e965cc6
Merge pull request #1998 from rmcrackan/cursor/rename-not-downloaded-…
rmcrackan Aug 25, 2026
c2f9392
Let users turn off the startup update check
cursoragent Aug 25, 2026
e4dde1e
Point the update-check copy at Settings > About, not Help > About
cursoragent Aug 25, 2026
612ec05
Stop a download-only upgrade notice from starting an install
cursoragent Aug 25, 2026
f39751d
Merge pull request #2000 from rmcrackan/cursor/opt-out-update-checker…
rmcrackan Aug 26, 2026
272241b
fix(metadata): flatten Audible's HTML description before writing it t…
cursoragent Aug 26, 2026
5ff1a4f
fix(metadata): replace Audible's placeholder 01-Jan-2000 release date
cursoragent Aug 26, 2026
178b7ee
feat(metadata): carry the publisher's copyright line from the API to …
cursoragent Aug 26, 2026
1729f84
Log startup recovery without Serilog so a broken install cannot silen…
cursoragent Aug 26, 2026
23ecdf5
test(metadata): cover both shapes of file Audible delivers
cursoragent Aug 26, 2026
5c5d1bd
Replace CsvHelper with a minimal internal CSV writer
cursoragent Aug 26, 2026
c4b4fa6
Recognise any install-folder assembly failure, and let the rollback f…
cursoragent Aug 26, 2026
06356f2
Share one crash log between both UIs, and stop after a rollback
cursoragent Aug 26, 2026
1c32149
Update Dinah.Core to 10.2.6.1
cursoragent Aug 26, 2026
77cb6bf
chore: keep the two Dinah package references adjacent
cursoragent Aug 26, 2026
1ab18f5
Cover the startup paths that issue #2001 showed were untested
cursoragent Aug 26, 2026
31ad21f
Update Dinah.EntityFrameworkCore to 10.2.6.1
cursoragent Aug 26, 2026
bc72aad
refactor(metadata): flatten Audible's HTML at the API boundary, not o…
cursoragent Aug 26, 2026
bfaab98
Fit a longer startup message in the crash dialog
cursoragent Aug 26, 2026
c22efa2
Retrigger CI: Dinah.Core and Dinah.EntityFrameworkCore 10.2.6.1 are n…
cursoragent Aug 26, 2026
0c0b72c
Update AudibleApi to 11.0.5.1 and Dinah.Core.WindowsDesktop to 10.2.6.1
cursoragent Aug 26, 2026
3fbd0e1
Merge pull request #2004 from rmcrackan/cursor/replace-csvhelper-inte…
rmcrackan Aug 26, 2026
ebb1d9f
Retrigger CI: transient corrupted ref assembly on the macOS x64 runner
cursoragent Aug 26, 2026
52325e7
Merge pull request #2003 from rmcrackan/cursor/fix-widevine-metadata-…
rmcrackan Aug 26, 2026
673f8f3
Merge pull request #2005 from rmcrackan/cursor/adopt-dinah-core-10-2-…
rmcrackan Aug 26, 2026
47980e1
Check the rollback's own work, and offer a restart when it went well
cursoragent Aug 26, 2026
3ab22a9
Report the crash log path without the Windows long-path prefix
cursoragent Aug 26, 2026
b02aedb
Merge pull request #2007 from rmcrackan/cursor/serilog-startup-resili…
rmcrackan Aug 26, 2026
7292794
Let one account scan more than one marketplace
cursoragent Aug 26, 2026
5b2c620
Add the marketplaces dialog to the accounts grid
cursoragent Aug 26, 2026
c7f6815
Add marketplaces to Classic and to list-accounts
cursoragent Aug 26, 2026
e1449c9
Document marketplaces in the docs and the tour
cursoragent Aug 26, 2026
5268e0c
Drop the registered marketplace from the extras on the way out too
cursoragent Aug 26, 2026
7538491
update dependencies
rmcrackan Aug 26, 2026
75c25fe
Merge branch 'master' into cursor/multi-marketplace-accounts-6d3e
rmcrackan Aug 26, 2026
f980600
Merge pull request #2008 from rmcrackan/cursor/multi-marketplace-acco…
rmcrackan Aug 26, 2026
5f814a5
Update README.md
rmcrackan Aug 26, 2026
eca013e
Bump version to 14.0.0
cursoragent Aug 26, 2026
9bfdaa9
Merge pull request #2009 from rmcrackan/cursor/release-14-0-0-073d
rmcrackan Aug 26, 2026
19a8674
Update dependencies
rmcrackan Aug 30, 2026
09be09b
Cull dead code. Simplify
rmcrackan Aug 30, 2026
094e207
Merge pull request #2016 from rmcrackan/rmcrackan/cull-dead-code
rmcrackan Aug 30, 2026
5503a9d
Allow the download to resume from the last flushed position when TLS …
rmcrackan Aug 31, 2026
4cd7847
avoid infinite retry loops
rmcrackan Aug 31, 2026
3e7191a
Merge pull request #2017 from rmcrackan/rmcrackan/2015-handle-bad-dow…
rmcrackan Aug 31, 2026
129486c
Warn if accounts dialog is dirty when user attempts to close it. This…
rmcrackan Aug 31, 2026
a9df4ad
Merge pull request #2019 from rmcrackan/rmcrackan/dirty-accounts
rmcrackan Aug 31, 2026
5c7515d
Implement Contemporary Cellar experience
cclements Aug 31, 2026
43ccbb1
Reconcile Contemporary Cellar delivery evidence
cclements Aug 31, 2026
2439edd
Add focused Contemporary Cellar contract tests
cclements Aug 31, 2026
2e7a5b6
Harden Contemporary Cellar shell activation
cclements Aug 31, 2026
720365b
Add Contemporary Cellar headless contracts
cclements Aug 31, 2026
512c1d6
Run test applications explicitly in CI
cclements Aug 31, 2026
e05b77b
Harden macOS release packaging
cclements Aug 31, 2026
dda0109
Complete Contemporary Cellar and harden macOS runtime
cclements Sep 1, 2026
f5fa488
S0: add isolated demo-profile script
cclements Sep 2, 2026
23f76fa
S0: generated demo library with cover placeholders
cclements Sep 2, 2026
d46ca1e
S0: capture plan model
cclements Sep 2, 2026
fb42a95
S0: in-app capture mode driven by LIBATION_CAPTURE_PLAN
cclements Sep 2, 2026
d34ad7d
S0: capture driver and standard plans
cclements Sep 2, 2026
809a1b6
S0: contact-sheet script for mockup comparison
cclements Sep 2, 2026
9a48714
S0: make macOS captures occlusion-safe
cclements Sep 2, 2026
da8821d
S0: point status ledger at the evidence-based re-plan
cclements Sep 2, 2026
f24d2ea
S1: harden the contemporary design system
cclements Sep 2, 2026
a53d421
docs: state the WinForms compile-check rule and SDK roll-forward plai…
cclements Sep 2, 2026
276b78e
S2: recompose the contemporary shell
cclements Sep 2, 2026
336a01d
Request download licenses from the marketplace the book was scanned f…
rmcrackan Sep 2, 2026
82fb235
Drop the ApiExtendedFunc test hook. Pass the book's marketplace strai…
rmcrackan Sep 2, 2026
635780a
Bump version to 14.0.1
rmcrackan Sep 2, 2026
66bf8b9
Merge pull request #2022 from rmcrackan/rmcrackan/2020-download-store…
rmcrackan Sep 2, 2026
fde09e2
S3: rebuild Library and Current Flight
cclements Sep 2, 2026
fef4813
Merge remote-tracking branch 'origin/master' into contemporary-cellar-v2
cclements Sep 2, 2026
74a028a
Show a dialog when Audible throttles a download license.
rmcrackan Sep 3, 2026
014beaf
incr ver
rmcrackan Sep 3, 2026
d27bd76
S4: rebuild Cellar and Tasting Room overviews
cclements Sep 3, 2026
3650bba
S5: rebuild Processing and Decanter
cclements Sep 3, 2026
a0adf75
Merge pull request #2023 from rmcrackan/cursor/throttling-license-ui
rmcrackan Sep 3, 2026
5272fe9
Merge remote-tracking branch 'origin/master' into contemporary-cellar-v2
cclements Sep 3, 2026
67b476e
Do not use Audible's sentinel episode numbers as podcast series order.
rmcrackan Sep 3, 2026
3d563a2
Merge pull request #2025 from rmcrackan/cursor/2024-podcast-series-nu…
rmcrackan Sep 3, 2026
e1235f3
S6: rebuild secondary destinations
cclements Sep 3, 2026
b8e16b4
S7: harden release variants
cclements Sep 3, 2026
09b6c5b
S8: add tests and evidence contracts
cclements Sep 3, 2026
0f06ce9
S9: harden cross-platform release evidence
cclements Sep 4, 2026
d78737f
S9: make platform capture evidence portable
cclements Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.sh text eol=lf
22 changes: 21 additions & 1 deletion .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,27 @@ jobs:
- name: Unit test
if: ${{ inputs.run-unit-tests }}
working-directory: ./Source
run: dotnet test
env:
LIBATION_HEADLESS_CAPTURE_OUTPUT: ${{ inputs.OS == 'Debian' && inputs.architecture == 'x64' && format('{0}/cross-platform-captures/linux-x64', github.workspace) || '' }}
run: |
shopt -s nullglob
TEST_PROJECTS=(_Tests/*.Tests/*.csproj)
if (( ${#TEST_PROJECTS[@]} == 0 )); then
echo "::error::No unit-test projects were found."
exit 1
fi
for TEST_PROJECT in "${TEST_PROJECTS[@]}"; do
dotnet run --project "${TEST_PROJECT}" --configuration Release -- --minimum-expected-tests 1
done

- name: Upload Linux contemporary captures
if: ${{ always() && inputs.run-unit-tests && inputs.OS == 'Debian' && inputs.architecture == 'x64' }}
uses: actions/upload-artifact@v7
with:
name: contemporary-captures-linux-x64
path: ./cross-platform-captures/linux-x64/*.png
if-no-files-found: error
retention-days: ${{ inputs.retention-days }}

- name: Publish
id: publish
Expand Down
27 changes: 21 additions & 6 deletions .github/workflows/build-mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ on:
sign-app:
type: boolean
description: "Wheather to sign an notorize the app bundle and dmg."
release:
type: boolean
default: false
description: "Whether this artifact is being built for a release."
architecture:
type: string
description: "CPU architecture targeted by the build."
Expand All @@ -32,7 +36,7 @@ jobs:
runs-on: macos-latest
env:
RUNTIME_ID: "osx-${{ inputs.architecture }}"
WAIT_FOR_NOTARIZE: ${{ vars.WAIT_FOR_NOTARIZE == 'true' }}
WAIT_FOR_NOTARIZE: ${{ inputs.release || vars.WAIT_FOR_NOTARIZE == 'true' }}
steps:
- uses: apple-actions/import-codesign-certs@v7
if: ${{ inputs.sign-app }}
Expand All @@ -50,7 +54,16 @@ jobs:
- name: Unit test
if: ${{ inputs.run-unit-tests }}
working-directory: ./Source
run: dotnet test
run: |
shopt -s nullglob
TEST_PROJECTS=(_Tests/*.Tests/*.csproj)
if (( ${#TEST_PROJECTS[@]} == 0 )); then
echo "::error::No unit-test projects were found."
exit 1
fi
for TEST_PROJECT in "${TEST_PROJECTS[@]}"; do
dotnet run --project "${TEST_PROJECT}" --configuration Release -- --minimum-expected-tests 1
done

- name: Publish
id: publish
Expand Down Expand Up @@ -81,14 +94,15 @@ jobs:
- name: Notarize bundle
if: ${{ inputs.sign-app }}
run: |
if [ ${{ vars.WAIT_FOR_NOTARIZE == 'true' }} ]; then
WAIT="--wait"
WAIT_ARGS=()
if [ "${WAIT_FOR_NOTARIZE}" = "true" ]; then
WAIT_ARGS+=(--wait)
fi
echo "::debug::Submitting the disk image for notarization"
# Capture stdout+stderr (2>&1). Use || true so that when notarytool fails (e.g. Apple TOS
# agreement required), the script does not exit before we can print RESPONSE—otherwise the
# job would fail with no visible error message in the workflow log.
RESPONSE=$(xcrun notarytool submit ./bundle/${{ steps.bundle.outputs.artifact }} $WAIT --no-progress --apple-id ${{ vars.APPLE_DEV_EMAIL }} --password ${{ secrets.APPLE_DEV_PASSWORD }} --team-id ${{ secrets.APPLE_TEAM_ID }} 2>&1) || true
RESPONSE=$(xcrun notarytool submit ./bundle/${{ steps.bundle.outputs.artifact }} "${WAIT_ARGS[@]}" --no-progress --apple-id ${{ vars.APPLE_DEV_EMAIL }} --password ${{ secrets.APPLE_DEV_PASSWORD }} --team-id ${{ secrets.APPLE_TEAM_ID }} 2>&1) || true
echo "$RESPONSE"
SUBMISSION_ID=$(echo "$RESPONSE" | awk '/id: / { print $2;exit; }')
echo "::notice::Notary Submission Id: $SUBMISSION_ID"
Expand All @@ -100,9 +114,10 @@ jobs:
exit 1
fi

if [ ${{ vars.WAIT_FOR_NOTARIZE == 'true' }} ]; then
if [ "${WAIT_FOR_NOTARIZE}" = "true" ]; then
echo "::debug::Stapling the notarization ticket to the disk image"
xcrun stapler staple "./bundle/${{ steps.bundle.outputs.artifact }}"
xcrun stapler validate "./bundle/${{ steps.bundle.outputs.artifact }}"
fi

- uses: actions/upload-artifact@v7
Expand Down
46 changes: 39 additions & 7 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
name: "Windows-${{ matrix.release_name }}-${{ matrix.architecture }} (${{ matrix.ui }})"
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
ui: [Avalonia]
architecture: [x64]
Expand All @@ -53,8 +54,25 @@ jobs:
- name: Unit test
if: ${{ inputs.run-unit-tests }}
working-directory: ./Source
run: dotnet test

env:
LIBATION_HEADLESS_CAPTURE_OUTPUT: ${{ matrix.ui == 'Avalonia' && matrix.architecture == 'x64' && format('{0}/cross-platform-captures/windows-x64', github.workspace) || '' }}
run: |
$testProjects = @(Get-ChildItem -Path "_Tests/*.Tests/*.csproj")
if ($testProjects.Count -eq 0) { throw "No unit-test projects were found." }
foreach ($testProject in $testProjects) {
dotnet run --project $testProject.FullName --configuration Release -- --minimum-expected-tests 1
if ($LASTEXITCODE -ne 0) { throw "Unit tests failed for $($testProject.FullName)" }
}

- name: Upload Windows contemporary captures
if: ${{ always() && inputs.run-unit-tests && matrix.ui == 'Avalonia' && matrix.architecture == 'x64' }}
uses: actions/upload-artifact@v7
with:
name: contemporary-captures-windows-x64
path: ./cross-platform-captures/windows-x64/*.png
if-no-files-found: error
retention-days: ${{ inputs.retention-days }}

- name: Publish
working-directory: ./Source
run: |
Expand All @@ -66,10 +84,20 @@ jobs:
"-p:PublishReadyToRun=${{ inputs.publish-r2r }}",
"-p:SelfContained=true")

dotnet publish "Libation${{ matrix.ui }}/Libation${{ matrix.ui }}.csproj" $PUBLISH_ARGS
dotnet publish "LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj" $PUBLISH_ARGS
dotnet publish "LibationCli/LibationCli.csproj" $PUBLISH_ARGS
dotnet publish "Hangover${{ matrix.ui }}/Hangover${{ matrix.ui }}.csproj" $PUBLISH_ARGS
# PowerShell does not stop at a native command that fails, and the step is judged by the exit code
# of the last one, so a publish that fails anywhere but the end passes as a green build. That is not
# hypothetical: LibationWinForms failed to compile for a while and every run stayed green, uploading
# a Libation-Classic zip with no Libation.exe in it. Check each one.
$projects = @(
"Libation${{ matrix.ui }}/Libation${{ matrix.ui }}.csproj",
"LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj",
"LibationCli/LibationCli.csproj",
"Hangover${{ matrix.ui }}/Hangover${{ matrix.ui }}.csproj")

foreach ($project in $projects) {
dotnet publish "$project" $PUBLISH_ARGS
if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed for $project" }
}

- name: Zip artifact
id: zip
Expand All @@ -81,6 +109,11 @@ jobs:
"WindowsConfigApp.deps.json")

foreach ($file in $delfiles){ if (test-path $file){ Remove-Item $file } }

# Both UIs publish as Libation.exe. upload-artifact's if-no-files-found only proves the zip exists,
# which it does whether or not the app is inside it, so say outright that it has to be.
if (-not (Test-Path "Libation.exe")) { throw "Libation.exe is missing from the publish output" }

$artifact="${{ matrix.artifact_stem }}.${{ inputs.libation-version }}-windows-${{ matrix.release_name }}-${{ matrix.architecture }}.zip"
"artifact=$artifact" >> $env:GITHUB_OUTPUT
Compress-Archive -Path * -DestinationPath "$artifact"
Expand Down Expand Up @@ -127,4 +160,3 @@ jobs:
path: ./bin/${{ steps.setup.outputs.setup }}
if-no-files-found: error
retention-days: ${{ inputs.retention-days }}

5 changes: 3 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:

macOS:
strategy:
fail-fast: false
matrix:
architecture: [x64, arm64]
uses: ./.github/workflows/build-mac.yml
Expand All @@ -51,10 +52,12 @@ jobs:
retention-days: ${{ inputs.retention-days }}
architecture: ${{ matrix.architecture }}
sign-app: ${{ inputs.release || vars.SIGN_MAC_APP_ON_VALIDATE == 'true' }}
release: ${{ inputs.release }}
secrets: inherit

linux:
strategy:
fail-fast: false
matrix:
OS: [Redhat, Debian]
architecture: [x64, arm64]
Expand All @@ -68,5 +71,3 @@ jobs:
architecture: ${{ matrix.architecture }}
OS: ${{ matrix.OS }}



6 changes: 4 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

- name: Login to Docker Hub
if: ${{ inputs.release }}
uses: docker/login-action@v4.5.2
uses: docker/login-action@v4.6.0
with:
username: ${{ secrets.docker_username }}
password: ${{ secrets.docker_token }}
Expand All @@ -59,7 +59,9 @@ jobs:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ steps.metadata.outputs.tags != ''}}
# Always read cache. Only write on release so PR validate cannot fail
# when GHA cache is full or the token cannot write cache.
cache-from: type=gha
cache-to: type=gha,mode=max
cache-to: ${{ inputs.release && 'type=gha,mode=max,ignore-error=true' || '' }}
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
libation-version: ${{ needs.prerelease.outputs.version }}
publish-r2r: true
release: true
run-unit-tests: true

release:
needs: [prerelease, build]
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ on:
branches: [master]
paths:
- Source/**
- Scripts/**
- .github/workflows/**
pull_request:
branches: [master]
paths:
- Source/**
- Scripts/**
- .github/workflows/**

jobs:
Expand Down
14 changes: 14 additions & 0 deletions .vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,19 @@ export default defineConfig({
text: "Audiobookshelf Auto-Upload",
link: "/docs/features/audiobookshelf",
},
{
text: "Daily Download Limit",
link: "/docs/features/daily-download-limit",
},
{ text: "Naming Templates", link: "/docs/features/naming-templates" },
{
text: "Parallel Downloads",
link: "/docs/features/parallel-downloads",
},
{
text: "Retrying Refused Downloads",
link: "/docs/features/retrying-refused-downloads",
},
{
text: "Searching & Filtering",
link: "/docs/features/searching-and-filtering",
Expand All @@ -103,6 +115,7 @@ export default defineConfig({
text: "Easy guide to searching",
link: "/docs/features/lucene",
},
{ text: "Trash Bin", link: "/docs/features/trash-bin" },
],
},
{
Expand All @@ -124,6 +137,7 @@ export default defineConfig({
link: "/docs/development/getting-started",
},
{ text: "Contribute", link: "/docs/development/contribute" },
{ text: "Testing Changes", link: "/docs/development/testing" },
{ text: "Website & Docs", link: "/docs/development/website" },
{ text: "Linux Setup (Nix)", link: "/docs/development/nix-linux-setup" },
],
Expand Down
75 changes: 75 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# AGENTS.md

## Cursor Cloud specific instructions

Libation is a cross-platform .NET desktop/CLI app for downloading and de-DRMing Audible
audiobooks. The core product is the Avalonia GUI (`LibationAvalonia`, assembly name `Libation`)
plus a headless CLI (`LibationCli`) that shares the same config and SQLite library database.

### Toolchain / environment
- Requires the **.NET 10 SDK**. `global.json` sets a `10.0.101` floor with `rollForward: latestFeature`,
so any newer 10.0.x SDK is used as-is. It is preinstalled at
`~/.dotnet`; `~/.bashrc` exports `DOTNET_ROOT` and adds `~/.dotnet` (and `~/.dotnet/tools`) to
`PATH`. New non-login shells may not have it — use `dotnet` after a login shell, or call
`~/.dotnet/dotnet` directly.
- The startup update script runs `dotnet tool restore` (restores `dotnet-ef`) and
`dotnet restore Source/Libation.slnx`. No other setup is needed.
- The solution file is the new XML format: `Source/Libation.slnx` (there is no classic `.sln`).

### Building — everything compiles on Linux, only some of it runs
- `dotnet build Source/Libation.slnx` succeeds on Linux with 0 errors. So do
`LibationWinForms`, `HangoverWinForms` and the `LoadByOS/{Windows,MacOS}ConfigApp` helpers,
individually: they target `net10.0-windows7.0` but set `EnableWindowsTargeting`, which is what makes
a non-Windows SDK build them. Compile-check every change to the WinForms projects here; a Linux
build is the only automated check those projects get before a human on Windows sees them.
- Compiling is not running. WinForms has no Linux runtime, so behaviour and layout in
`LibationWinForms` / `HangoverWinForms` still need a human on Windows. Say so explicitly rather than
implying a WinForms change was tested.
- Build the runnable cross-platform apps directly:
- `dotnet build Source/LibationAvalonia/LibationAvalonia.csproj`
- `dotnet build Source/LibationCli/LibationCli.csproj`
- There is no dedicated lint step; the repo's `.editorconfig` is minimal and CI
(`.github/workflows/validate.yml`) only builds and tests. The compiler/analyzer warnings from a
normal build serve as the static-analysis check. (A known transitive `NU1903` SQLite
vulnerability warning is expected and harmless.)

### Testing
- Test projects live under `Source/_Tests/` and use MSTest on **Microsoft.Testing.Platform**
(configured via `global.json` `test.runner`). Because of this runner, `dotnet test` requires
`--project` for a single project (a positional project path is rejected):
`dotnet test --project Source/_Tests/FileManager.Tests/FileManager.Tests.csproj`
- CI runs `dotnet test` from `Source/` on every platform, Linux included, and it works here too: the
restore covers the whole solution but only the test projects and their references are compiled, so
the Windows-only projects never break the run. Naming one project is simply quicker.
- **Manual UI testing:** `dotnet run Scripts/seed-demo-library.cs` fills the library with fake books
covering every Liberate-column icon and prints the expected result for each row
(`-- --clean` removes them). Read `docs/development/testing.md` before seeding library state by
hand: the yellow lamp is an `.aaxc` file on disk rather than a stored status, `AudioExists` is a
database check so green/error need no files, and a podcast's series is keyed off the parent
book's own ASIN or the grid silently drops the parent row.
- **GNOME Keyring / OS secret store:** Libation's default `TokenStorageMethod` is `Encrypted`,
and the AES-GCM master key is stored via the OS secret store (`OsSecretStore` /
`IdentityTokenStorageWiring`). On Linux that is GNOME Keyring (Secret Service), which **blocks
indefinitely** here: the login keyring is locked with a password nobody has, and the D-Bus call
hangs even with no prompt on screen. Probing availability does not help - the probe is the
blocking call.
Every test project should finish in about a second. If one runs for minutes, assume something
reached the OS secret store; do not sit through it and do not re-run it hoping for a different
result. `ResolveSecretStore` short-circuits on `LIBATION_MASTER_KEY_FILE`, an existing
`libation-master.key` under the Libation files dir, or `LIBATION_MASTER_KEY`, so setting one of
those keeps a test off the OS store entirely.
The tests that deliberately exercise the real store are opt-in via
`LIBATION_TEST_OS_SECRET_STORE=1` and are skipped otherwise. Leave them skipped on Linux; they
will hang if enabled. Always run tests under `timeout` so a regression here cannot stall a session.

### Running the apps
- GUI: a display is available on `DISPLAY=:1`. Run with
`cd Source/LibationAvalonia && dotnet run`. First launch shows a Welcome/walkthrough and creates
config under `~/.local/share/Libation/` (`Settings.json`, `AccountsSettings.json`,
`LibationContext.db`). The same keyring note above applies when the GUI/CLI first encrypts
account tokens.
- CLI: `cd Source/LibationCli && dotnet run -- <command>` (e.g. `version`, `get-setting`,
`list-accounts`, `scan`, `liberate`). The CLI reads the same `~/.local/share/Libation/` config
and database as the GUI.
- Actually scanning/downloading a library requires signing into a real Audible account, so full
end-to-end liberation cannot be exercised without credentials.
8 changes: 8 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,12 @@
<PropertyGroup>
<EnableMSTestRunner>true</EnableMSTestRunner>
</PropertyGroup>

<ItemGroup>
<!-- The SDK supplies Microsoft.SourceLink.Common and the git tasks, which is enough to find the
repository but not to turn its URL into fetchable source URLs. Without a host provider every
project warns that the generated source link is empty and ships PDBs that cannot locate their
sources. -->
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.301" PrivateAssets="All" />
</ItemGroup>
</Project>
Loading
Loading