Commit Graph
49 Commits
Author SHA1 Message Date
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 178b7ee1d8 feat(metadata): carry the publisher's copyright line from the API to the tag
Libation never had a copyright of its own to fall back on: it only cleaned up
whatever the .aaxc file already embedded. Widevine downloads carry no cprt at
all, so they came out blank.

The copyright line is only returned by the product_details response group, so
request it on the full library scan, store it on Book, and use it as the tag
fallback. Existing libraries pick it up on their next scan, and Audible still
returns null for plenty of titles.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-26 12:40:04 +00:00
Cursor Agentandrmcrackan a0045156c1 docs: drop Neil Gaiman from the examples
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>
2026-08-24 20:31:29 +00:00
Cursor Agentandrmcrackan f0d344099a fix(scan): keep a book's supplement in step with what the last scan says
Only a newly imported book ever recorded a supplement, so a title that gained a
PDF after its first import never got one, and a title that lost its PDF went on
claiming one - which in issue #1973 is why three titles Audible has no PDF for
were still being asked for.

Sync from updateBook as well, and give Book set-semantics for the one supplement
Audible reports per title. The duplicate guard compared the incoming url to
itself, so it happened to mean 'this book already has a supplement' and a url
that had changed was silently ignored.

A supplement is dropped only when Audible says outright that no supplement url is
available. A missing url says nothing by itself: episodes come from the catalog,
which is never asked for pdf_url, so there it means 'not asked'. A PDF already
downloaded is left alone either way, since the file is on disk and the library
should go on saying so.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-19 18:36:02 +00:00
Cursor Agentandrmcrackan 824ff10dd9 fix(download): stop asking Audible for a license it just refused
A license denial left no trace: BookStatus stayed NotLiberated, so every
liberate run asked again. Only the GUI's bad-book dialog could mark a title
Error, and license denials take their own path and never reach that dialog, so
a headless install had no way at all to stop the retries. A cron schedule then
re-requested the same refused licenses every run and printed the same warning
block for each one, which is both wasted API traffic and the log noise reported
in issue #1947.

Record the refusal instead, with a wait that doubles per consecutive failure:
one day for an eligibility denial (up to 30), six hours for a title Audible has
no audio for yet such as an unreleased preorder (up to 7 days), one hour when
the denial names GenericError, which the GUI already reads as an outage
(up to 12). Nothing is permanent - every kind is attempted again on its own.

Only failures attributable to Audible are recorded. A dropped connection, a
decrypt error or a full disk keeps being retried on the next run as before.

Naming a title, --force, and setting a download status all clear the record:
asking for a title explicitly overrides the wait.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-16 16:13:17 +00:00
Cursor Agentandrmcrackan 818510d1b0 feat(limit): add opt-in daily download limit core, history store and enforcement
Records every successful audiobook download in the library database (a new
DownloadHistory table) and, when the user opts in, stops downloading once the
rolling 24 hour window is full.

The history lives in the database rather than a file under LibationFiles
because in Docker only the database is on a volume; a file there is discarded
on every container restart.

The limit is checked immediately before each book downloads, never at queueing
time, so a full queue stays full and the user can raise or disable the limit
mid-run. When nothing in the queue can proceed the queue pauses and re-checks
every 15 seconds, recomputing settings, history and clock from scratch, so a
queue left running for days drip-feeds itself as downloads age out. The CLI
never waits: it skips covered titles and reports a count.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-14 17:16:47 +00:00
Michael Bucari-Tovo b8616a6da0 Re-add Category Name Column to database
Category names were accidently removed during the last DB migration in d67692355f.

Re-add the Names column and modify CategoryImporter to upsert the names on update.
2026-04-27 14:45:13 -06:00
Jo-Be-Co b4f48cb54c Extend <language> and <locale> tags to allow different outputs like ISO-codes or names in different languages 2026-04-10 09:43:11 +02:00
Michael Bucari-Tovo 3ab1edc076 Code Cleanup
Make fields readonly
Remove unnecessary casts
Format document
Remove unnecessary usings
Sort usings
Use file-level namespaces
Order modifiers
2026-02-05 12:48:44 -07:00
Michael Bucari-Tovo d67692355f Enable project-wide nullable reference types
Added DB migration for nullable types.
2026-02-05 12:43:05 -07:00
Michael Bucari-Tovo 804bac5c4c Add LibraryBook.IsAudiblePlus property 2026-01-07 15:50:23 -07:00
MBucari e850465ec1 Add more null safety
Enable project-wide nullable on LibationUiBase and LibationAvalonia

Explicitly parallelize unit tests
2025-12-30 13:17:11 -07:00
Taylor Southwick 1b5db9b28f Add support for postgres
Supporting postgres simplifies deployments to environments such as kubernetes. Since sqlite doesn't work well on nfs shares it can be easier for databases to have a dedicated db set up that applications can connect to. Sqlite is easier for most deployments though, so this will default to that if the settings haven't been updated to support it.

This change does the following:

- Separate out SQLite from the DataLayer and adds a Postgres assembly for migrations as well
- Add a configuration setting for a postgres connection string that will be used if it is there, otherwise reverts to the original sqlite string
- Add a copydb command for the cli to bootstrap the postgres db
- A convenience script to update migrations for both dbs at the same time
2025-10-27 16:30:50 -07:00
delebash fcd79c5561 InludedUntil fixes by Mbucari 2025-10-20 12:55:48 -04:00
delebash ba98820989 move code to LibraryBook 2025-10-18 02:05:56 -04:00
delebash fa8f761771 Added IncludedUntil Date 2025-10-17 13:36:01 -04:00
Michael Bucari-Tovo 0e4231906a Set IsSpatial field instead of only flipping to true (#1273) 2025-07-28 15:28:04 -06:00
MBucari 9b217a4e18 Add audio format data
- Add Book.IsSpatial property and add it to search index
- Read audio format of actual output files and store it in UserDefinedItem. Now works with MP3s.
- Store last downloaded audio file version
- Add IsSpatial, file version, and Audio Format to library exports and to template tags. Updated docs.
- Add last downloaded audio file version and format info to the Last Downloaded tab
- Migrated the DB
- Update AAXClean with some bug fixes
  - Fixed error converting xHE-AAC audio files to mp3 when splitting by chapter (or trimming the audible branding from the beginning of the file)
  - Improve mp3 ID# tags support. Chapter titles are now preserved.
  - Add support for reading EC-3 and AC-4 audio format metadata
2025-07-25 12:18:50 -06:00
Michael Bucari-Tovo 52d3b9cb67 Disable warning 2025-05-07 11:13:26 -06:00
Michael Bucari-Tovo 5f4551822b Remove Book.AudioFormat property
This property was set to the highest quality returned by the library scan. Since adding quality option settings, it is no longer guaranteed to reflect the file that is downloaded. Also, the library scan qualities don't contain spatial audio or widevine-specific qualities., only ADRM.
2025-05-02 12:39:12 -06:00
Michael Bucari-Tovo 7d806e0f3e Increase tag template options for contributor and series types
- Add template tag support for multiple series
- Add series ID and contributor ID to template tags
- <first author> and <first narrator> are now name types with name formatter support
- Properly import contributor IDs into database
- Updated docs
2025-03-25 09:34:57 -06:00
Robert McRackan c6788ccb48 typo 2024-09-25 10:12:01 -04:00
Robert McRackan 2fd8ea91e1 New search field: Finished/IsFinished.
All work complete except db migration
2024-09-11 07:45:37 -04:00
MBucari fb9b4eb77e Update audio duration on library scan (#707) 2023-08-12 17:19:56 -06:00
Mbucari 3211b2dc85 Improved Category Ladders 2023-07-18 15:51:02 -06:00
Mbucari ea6adeb58f Add category ladders 2023-07-17 16:50:45 -06:00
Mbucari 423b5312f7 Add setting to choose downloaded audio quality ((#648) 2023-07-02 19:19:28 -06:00
Mbucari 83fa73cef5 Integrate new Title and Subtitle properties into Libation 2023-06-29 21:06:54 -06:00
Alanoll 2195574422 feat: add Book subtitle capturing so TitleShort reflects titles better 2023-06-26 12:18:15 -05:00
MBucari 9ae1f0399b Add SeriesViewDialog 2023-03-22 08:28:20 -06:00
MBucari 565c84c4ab Add series # to grid display (#529) 2023-03-17 22:11:04 -06:00
Mbucari fd16e97632 When book is unavailable, check other accounts (#535) 2023-03-17 14:06:02 -06:00
Mbucari 230e014bb1 Add better AYCL detection and add verbose library scan logging 2023-03-10 13:09:59 -07:00
Mbucari f6dcc0db1d Add AbsentFromLastScan 2023-03-07 18:58:18 -07:00
Mbucari bd7e45ca3c Add last download into to database 2023-03-02 15:09:10 -07:00
Michael Bucari-Tovo ee8c0ae27b Use new .NET regular expression source generators 2023-02-10 19:45:52 -07:00
Robert McRackan 867085600c New feature #469 - <language> and <language short> template options 2023-02-01 12:12:50 -05:00
Robert McRackan 83fb2cd1d0 New feature #430 : bulk set pdf-downloaded status 2022-12-29 09:33:32 -05:00
Robert McRackan 7ad0ab566a New feature: 'Remove' now removes forever. Removed books won't be re-added on next scan 2022-12-14 16:19:55 -05:00
Robert McRackan 3982537d46 Tags no longer saved outside of database 2022-07-31 21:58:53 -04:00
Robert McRackan 77e61479cf New feature #284 : Add bitrate, sample rate, and channels to template options and to exports 2022-07-21 08:37:04 -04:00
Michael Bucari-Tovo 5815a04712 Add bitrate to Book 2022-06-24 23:09:20 -06:00
Michael Bucari-Tovo c48eacd9af Add ContentType.Parent
Import Series parent when only individual episodes are in library
2022-06-07 15:27:18 -06:00
Michael Bucari-Tovo e8a320dac9 Add grid categories 2022-05-22 20:00:41 -06:00
Robert McRackan 75c5f662dc * Batch actions for visible books: 'remove from library' complete
* refactor entity properties into extension methods
* refactor shared simple message boxes => MessageBoxLib
2022-05-12 09:53:21 -04:00
Robert McRackan c6ce814e1c Set the stage for batch processing 2022-05-10 16:18:09 -04:00
Michael Bucari-Tovo 0091245734 Revert PictureID_1215 migration and add PictureIDLarge migration 2022-05-10 12:03:47 -06:00
Michael Bucari-Tovo b320276926 Add PictureID_1215 to Book and migrate DB 2022-05-09 23:21:52 -06:00
Robert McRackan 389fbb2371 Move source code into "Source" folder 2022-05-09 10:31:45 -04:00