Libation asked GitHub for a newer release every time it started, with no way
to stop it. That is noise for anyone whose install is updated by something
else - a package manager, or an AppImage updater - because the prompt it
raises is one they can do nothing useful with.
Add CheckForUpgradesAtStartup, on by default so nothing changes for people
who rely on the prompt. Only the automatic check is optional: the About
window's "Check for Upgrade" button and the CLI's `version --check` ask for a
check outright, so they run either way. That is why the setting is read in a
new CheckForUpgradeAtStartupAsync rather than inside CheckForUpgradeAsync,
which the startup path and the About button share.
The new setting takes the slot of BetaOptIn, which is deleted here. It was
declared, described and logged, but no axaml or designer ever bound it and
nothing read the value: GetLatestRelease only ever asks for the stable
release, so there was no beta channel for it to select. A stale BetaOptIn key
in an existing Settings.json needs no migration, since PersistentDictionary
ignores keys with no matching property.
Closes#1999
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
The status this flag sets is now called 'Download Pending', so -p /
--download-pending is the name the help offers.
-n / --not-downloaded keeps working. It is what years of scripts, forum
answers and issue comments tell people to run, so breaking it would cost
more than the inconsistency is worth. It is hidden from --help so the new
name is the only one advertised, and both flags feed one SetPending
property that the verb acts on.
All three names stay in the 'Download Status' option group, so 'at least
one status flag is required' still holds. That error message does
enumerate the group, which is the one place the legacy name still
surfaces.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
'Not Downloaded' reads as a claim about the file on disk, but the status
it names is really an instruction about the future. Telling someone with
a finished audiobook to set it 'Not Downloaded' asks them to assert
something they know is false, which is why the question keeps coming up.
'Download Pending' says the same thing about intent without saying
anything untrue about the file, and stands alone in a dropdown or a
support reply where bare 'Pending' would not.
The context menu carriers move from "Set Download status to 'X'" to
"Mark as 'X'" so the word does not land twice in one breath, with the
accelerator on P to stay clear of Downloaded's D. The persisted enum,
the --not-downloaded CLI flag and the IsLiberated search tags are
unchanged, so scripts and saved quick filters keep working.
WinForms status combo boxes grow from 121 to 150px and the
better-quality Mark button from 210 to 240px to fit the longer label.
Docs carry 'previously "Not Downloaded"' on first mention, since years
of Reddit and GitHub answers use the old name.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Replaced with Stephen Fry, who the search and naming-template docs already use
as a narrator and who writes and narrates his own books, so the author-narrator
examples still say what they meant to.
The one code mention is a comment describing how Audible builds a contributor
URL. It used his real author page and id; the point being made is that the name
in the path is decorative, so a placeholder id makes it better, not worse.
Two things fell out of editing the narrator format line. Its template printed
'\) ' - a backslash inside quotes that already make the paren literal - so it
produced "Arthur (Conan\) Doyle", not the output printed beside it. And the
line is raw HTML rather than backticks, so markdown ate the backslash before
the opening paren, leaving the website showing a different template from the
markdown. Both spellings produce the documented output; the line now uses the
one that survives rendering, so source and page agree.
The screenshot for the author-narrator search showed a Gaiman query, so it is
replaced by the query inline. The now-unreferenced image is removed with it.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
It was a generic primer whose examples used title, body and mod_date. Only one
of those exists here, so half the guide could not be typed into the search box.
Every example is now a real field and was run against an index before being
written down, including the exclusive range and the leading-wildcard error the
old text described but never showed.
Covers the two forms that reach the new number handling and had no test: a
wildcard on a numeric title, and a curly-brace range.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Both are newly worth explaining. Searching a field for a word that names another
field used to raise "Bad filter string", and a numeric title could not be found
at all, so there was nothing to document until now.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
The page said auto-scroll only moves "when you were already looking at the bottom
of the queue". The condition in both UIs is that the item above the one starting is
still on screen, which is not the same claim - you can be well short of the bottom
and still be scrolled along with the queue.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
"(4 on this machine)" sits in whatever the two spinners leave of a fixed 400px
pane. That fit on one machine and ellipsed to "(4 on this machi..." on another,
which left the hint existing only in the tooltip on exactly the machines it was
written for.
Now "(4 at a time)": six characters shorter, and short enough to fit that column
with room over rather than by a hair. Why the number is smaller than the setting
is what the tooltip is for; the number itself is what has to be legible. Classic
reads the same string into its tooltip, so both UIs move together.
Layout geometry is untouched on purpose - the spinner widths and the row structure
are the part that was reported as clean.
docs/features/ has a page per user-facing feature, including the daily
download limit this interacts with. Records the two things that are not
discoverable from the UI: that above 3 concurrent downloads Audible starts
denying content licenses, which arrives as ordinary download failures with
nothing identifying the cause, and that books already downloading run to
completion when the daily limit pauses the queue - the limit stops new
books starting, it does not interrupt work in progress.
The refused-downloads page said the opposite - that a --pdf run is never held
back because a PDF is a different request - and neither page mentioned that a
bulk run now leaves alone the titles the last scan did not find, or what happens
when Audible lists a PDF it will not deliver.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
The script writes rows straight to SQLite, which leaves Libation's Lucene
index untouched, so every filter on a seeded book came back empty and the
empty-grid states could not be tested: nothing was findable, so every filter
produced 'No books match' whether or not that was the right answer.
Deleting the SearchEngine folder is enough. A missing index is a state
Libation already handles - performSafeQuery catches the FileNotFoundException,
re-indexes from the database and retries - and that rebuild filters IsDeleted,
so trashed books stay out of the index exactly as in a normal session.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
A marker property would be tidier, but AudibleUtilities.Account serializes a
fixed set of members and has no [JsonExtensionData], so Newtonsoft ignores an
extra property on the way in and never writes it back. Confirmed by adding
IsDemo, renaming the account in the app and letting it save: the property was
gone, and anything that dirties an account does the same, a token refresh during
a scan included. --clean would then strand demo accounts with no way to find
them. Adding a demo flag to the shipping Account class to serve a dev script is
not a trade worth making.
The id is the better anchor anyway: immutable, always persisted, and example.com
is reserved by RFC 2606 so no real Audible login can collide with it. Verified
that --clean still matches an account after the app renamed it.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Quite a lot of Libation only appears once an account exists, and it is not one
switch but two: nothing, one account, or several. The one/many split changes the
Import and Remove menus, and the new empty-library panel offers 'Add Account' or
'Scan Library' depending on it. None of that was reachable in a test environment
without signing in to Audible, which is how I ended up writing this by hand to
check the empty-library states.
seed-demo-accounts.cs writes structurally valid but meaningless identities, so
Libation counts and lists the accounts and enables everything gated on having
one. Any actual scan is refused by Audible, which the script and the docs both
say plainly.
It appends rather than overwrites, and --clean removes only what it added -
the accounts matching demo*@example.com - because this is the file holding real
Audible tokens. The first account is demo@example.com, the same one
seed-demo-library.cs assigns its books to, so seeding both describes one library
instead of two unrelated ones.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Removing a book was undocumented, so nothing told a user that a missing book
might simply be in the trash - which is what #1925 turned out to be.
Add docs/features/trash-bin.md covering how books get removed, how to find and
restore them, and what the log records. It leads with the two things people
actually get wrong: removing never touches your audio files, and 'Permanently
Delete from Libation' keeps a book away less effectively than the trash does,
because deleting Libation's record of a book means the next scan imports it
again as something it has never seen.
Also add a troubleshooting entry for a book that is in your Audible library but
not in Libation. It starts with the trash bin and an active filter, since those
account for most reports and neither leaves anything in the log, then moves on
to the import settings and the scan tally that would explain the rest.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
seed-demo-library.cs covered every grid state except the one that made #1925
hard to answer: a book that is in the database but in the trash, and therefore
absent from the grid, the search index and every status count at once.
Seed three - a purchased title, a Plus title, and an episode whose series is
still in the library so the trash bin's nesting is exercised too - and report
them under their own heading, since unlike every other seeded row they are
supposed to be missing from the grid.
Document what they are for in docs/development/testing.md: the status bar count,
the menu count, the empty-search hint, and the nested episode.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Libation has applied to the SignPath Foundation. Turning Smart App Control off
is irreversible, so pointing users at it as one of only two ways out reads
differently when signed builds may be close. The docs now list waiting first and
carry the status, hedged on approval since there is no date.
The dialog says nothing about signing progress and defers to the docs instead:
a shipped string cannot be corrected if the application falls through. It no
longer nudges toward turning Smart App Control off without mentioning that
the page has the current options.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
The troubleshooting section told users to run 'Unblock-File -Path ... -Recurse',
which is not valid PowerShell (the cmdlet has no -Recurse parameter) and would
not help even in its correct form: Smart App Control gates on the signature and
on cloud reputation, not on Mark-of-the-Web. It also said Evaluation mode
triggers blocks, and advised turning Smart App Control off temporarily and back
on again. Evaluation never blocks, and Windows cannot re-enable Smart App
Control without a reset or reinstall.
Say instead that the Windows builds are unsigned, that Windows has no per-app
exception, and what the two real options are.
Split the section in three, since it served two unrelated failures and the
'extract to a new folder' fix only applies to one of them: a Smart App Control
block, an incomplete in-app upgrade, and installing into a synced folder. Keep
the original anchor on the Smart App Control section so links in released builds
still land somewhere useful.
Refs #1967, #1873, #1876
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Account.ToString() returned "id - locale", so interpolating an account or
logging a non-destructured {Account} published the address. It now returns
the masked entry, with a DebuggerDisplay keeping the real values visible
while debugging. Nothing in the UI relied on it: both scan dialogs build
their own labels.
For structured logging, an ILogMasked type is reduced to its masked entry
by a destructuring policy, which covers the {@DebugInfo} shape most of
Libation's logging uses. And DecryptKey - the activation bytes - is now a
SecretString, so it has no plaintext for a reflective dump to find at all.
Its JSON stays the bare string it always was, so existing settings files
load unchanged.
A registered policy that nobody notices is missing protects nothing, so
the tests write through a logger built by ConfigureLogging itself rather
than a hand-made one. Deleting either registration fails them: the masked
object comes out whole, and a destructured secret renders as
{"HasValue":true} instead of its length.
The contribute guide now states the rule, since the reason for all of
this is invisible from the code alone: log files get attached to public
issues, so treat what goes in them as published.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
<title short> stops at the first colon, so it shortens Audible titles that
contain one just as readily as it drops Audible's subtitle, and distinct books
then collapse onto the same name. A colon cannot be searched for: the analyzer
discards punctuation and Lucene reads a colon in a query as a field separator.
Two bool index fields find the affected books instead.
Document how the two title tags differ, since <audible title> already drops
Audible's subtitle without ever cutting a title, and how to audit for names
that actually collide in a spreadsheet export.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
This reverts 6daaf33d. Master is 13.7.8 and the next release is the 0.0.1
increment from it, so the original 13.7.9 references were correct.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Troubleshooting gains an entry for missing and misplaced PDFs, including the
naming-template cause, since a library with no <id> in its folder and file
templates is one Libation cannot recognise the output of at all. The CLI
reference notes what a plain liberate run now covers, and that the
Audiobookshelf upload is deliberately not part of the PDF back-fill.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Adds a Features page covering the wait schedule, what clears it, and what the
CLI and app show, plus cross-links from the CLI reference, the daily download
limit page and troubleshooting. Troubleshooting also gains an entry for a log
too large to attach to a bug report, which is how the reporter in issue #1947
found this.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
The default Serilog config set rollingInterval only, so Serilog's own defaults
applied: no size-based roll and a 1 GB ceiling after which the sink silently
stops writing. A busy install (many accounts scanned several times an hour)
reaches tens of MB in a month, past the point where the log can be attached to
a bug report.
Add fileSizeLimitBytes, rollOnFileSizeLimit and retainedFileCountLimit to the
default File sink, and fill in whichever of the three an existing Settings.json
is missing so installs that already have a Serilog section benefit too. Only
absent keys are written, so a hand-tuned config is left alone.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Reflect community testing: once the subscription is inactive, Libation
cannot obtain a license and will not download those books.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Gives the three liberate options their own section on the command line page,
and cross-references them from the daily download limit page so the two
limits are not confused for each other.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Adds a feature page covering the rolling window, what counts, the MB/GB
estimate and the Docker/CLI keys, plus a one-line pointer from getting started
where a new user with a large Plus library clicks Begin Book Backups.
Scripts/seed-download-history.cs seeds fake completed downloads so the limit
can be exercised without downloading, including dating rows just under 24 hours
old to turn the multi-day pause-and-resume behavior into a one minute test.
Also shortens the queue status text: the process queue column clips rather than
wrapping, so the resume time was being cut off.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
The conditional open tag requires the -> delimiter. Written as
<has series#>, the engine does not recognize a conditional, so the
later <-has> closes the enclosing <if series-> instead and the template
reports "Missing <-if series> closing conditional." while leaking the
literal text "<has series#>" into the name.
Also add a nested-conditional example, since the reference table only
shows each conditional in isolation, plus regression tests for
<has series#-> nested inside <if series-> when a book is in a series
but has no series number.
Co-authored-by: rmcrackan <rmcrackan@gmail.com>
Auto-upload only fires when a book is liberated. Books liberated before
Audiobookshelf was configured, or while it was disabled, had no path to the
server short of re-downloading the whole library.
'libationcli upload' backfills them from the files already on disk. Bulk or
targeted by ASIN. Nothing is re-downloaded and no local file is deleted.
Also fixes a latent defect this exposes. Validate() reads a database status
(Book.AudioExists) while GetFilesToUpload() read only FilePathCache. A book
liberated long ago passes validation but has no cache entry, so the upload
found no files and returned success having sent nothing. File lookup now uses
AudibleFileStorage.Audio.GetPaths, which unions the cache with a live scan of
the Books directory.
Other changes:
- Validate() now requires LiberatedStatus.Liberated. It previously accepted
Error too, whose partial files should not be uploaded.
- New OutcomeDetermined event classifies each book as Uploaded, AlreadyExists,
NoFilesFound or Failed. Failures travel on this event rather than through
StatusHandler: the GUI process queue treats a non-success StatusHandler as a
bad book and raises the Abort/Retry/Ignore dialog, and an upload problem must
never fail a liberation.
- The verb prints an end-of-run summary and exits 0, matching other verbs.
No database migration. Duplicate detection already runs server-side inside
UploadBookAsync, so repeat runs are safe without local upload state.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>