Six places read the Supported Book Formats and Supported Audiobook
Formats settings on their own: post-processing, the post-process scan,
IRC, slskd, Prowlarr and direct download. For ebooks they already
agreed, since they all fall back to `DEFAULT_EBOOK_FORMATS`. For
audiobooks they did not. The settings registry and slskd fall back to
every audio format plus zip and rar, while post-processing and IRC fell
back to m4b and mp3 only.
They now share `shelfmark/core/formats.py`, with
`get_supported_formats`, `get_supported_audiobook_formats` and a
content-type aware `get_supported_formats_for`, and a shared
`DEFAULT_AUDIOBOOK_FORMATS` in `core/utils.py` next to the ebook one.
`policy.py` re-exports the same functions, so nothing that imports them
from there changes. It is not in `policy.py` itself because importing
the `postprocess` package pulls in the router, which imports the release
sources, so a source could not import from it without a cycle.
One behaviour difference, and only in the fallback: with the audiobook
setting unset, or emptied, post-processing and IRC used to accept m4b
and mp3 and now accept the full default list like slskd and the settings
page already did. A configured list is read exactly as before. Direct
download's own helper used the raw configured list and now reads the
same normalized one, which is identical for any list the settings page
can save.
Also public now: `parse_size` in the Prowlarr source (slskd and Newznab
imported it with the underscore) and
`coerce_completed_path_timeout_seconds` in the client base handler
(slskd imported that too).
Not changed on purpose: slskd's `_coerce_int` looks like a copy of
`coerce_int` but is not. It reads `"12.0"` as 12 and `True` as 0, where
`coerce_int` gives 0 and 1. And the `_parse_size` in the IRC source is a
different function, string to bytes.
Tests: a new set checks that post-processing, IRC and slskd all fall
back to the shared defaults and that the settings default is the shared
one, and the scan-archive and IRC tests now patch the shared config
instead of the per-module copies. Full suite passes.
The default for Supported Book Formats (`epub, mobi, azw3, fb2, djvu,
cbz, cbr`) was written out in five places: the settings registry,
`postprocess/policy.py`, `irc/parser.py`, Prowlarr's `source.py` (added
in #1490) and slskd's `source.py` (added in #1428). Both of those two
arrived in 1.5.0, and both are mine, so this is cleaning up after myself
as much as anything.
The setting was also read three different ways. Post-processing split on
commas only. slskd split on commas and spaces and stripped a leading
dot. IRC split on commas and kept the dot. Prowlarr split on commas and
kept `""` as a format when the value was an empty string. So `.epub` or
`epub mobi` worked in one source and not another.
This puts `DEFAULT_EBOOK_FORMATS` and `normalize_format_list` in
`core/utils.py` next to `AUDIOBOOK_FORMATS`, and the registry,
post-processing, IRC, Prowlarr and slskd all use them. I took the most
lenient reading of the three (comma or space split, leading dot dropped,
empties ignored, default when nothing is left), so no existing config
gets stricter. I put the reader in `core/utils.py` and not in
`policy.py` because the release sources can't import `policy` without
pulling in the whole post-processing package.
A config with a sensible list reads exactly as before. The only
differences are for malformed values: an emptied setting or an empty
string now falls back to the default in every place, where IRC used to
filter everything out and Prowlarr treated `""` as a format.
The audiobook default is a separate story and I left it alone: the
registry and slskd default to every processable audiobook format plus
the archives, while post-processing and IRC fall back to `m4b, mp3`.
They only differ when the setting is unset or emptied, but they do
differ, and I'd rather ask which one you want than pick one here.
Ruff, basedpyright on the changed files and the backend suite pass (4400
passed, e2e not run). New tests cover the reader and the two fallbacks.