Files
shelfmark/tests/config/test_generate_env_docs.py
T
Vinicius GabrielandClaude Sonnet 5 c576003319 feat(naming): add {FirstAuthor} template token (#1322)
Closes #930.

## What

New `{FirstAuthor}` naming-template token. It renders only the first
author when metadata lists several ("Author1, Author2, Author3"), so
multi-author books can be filed alongside the rest of that author's work
instead of getting their own "Author1, Author2, ..." folder.

```
{Author}       -> Terry Pratchett, Neil Gaiman
{FirstAuthor}  -> Terry Pratchett
```

## How

- Added to `KNOWN_TOKENS` in `shelfmark/core/naming.py`, positioned
before `author` so `{FirstAuthor}` isn't parsed as literal `First` +
`{Author}`.
- Derived inside `parse_naming_template` from the existing `Author`
value (split on `,` / `;`), so every caller — folder transfer, rename,
the settings preview — picks it up with no extra wiring. An explicit
`FirstAuthor` key in the metadata still wins if one is ever passed.
- `{Author}` behaviour is unchanged.
- Frontend `namingTemplatePreview.ts` token list + `KNOWN_TOKENS` kept
in lockstep (there's a test enforcing that), with a matching
`firstAuthor` helper.
- Settings field descriptions + `docs/environment-variables.md` list the
new token.

## Known limitation

A lone author written `Last, First` is split on the comma too and
renders as `Last` — the source metadata doesn't mark which form it is.
Called out in the token help text and covered by a test. `{Author}`
remains available for anyone who wants the raw string.

## Checks

- `make python-test` — 2963 passed
- `make python-lint` / `make python-format` / `make python-typecheck` /
vulture — clean
- `make frontend-test` — 187 passed · `frontend-lint` /
`frontend-format` / `frontend-typecheck` — clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 00:47:37 -04:00

61 lines
1.9 KiB
Python

from scripts.generate_env_docs import generate_env_docs
def test_generated_env_docs_use_canonical_mirror_env_vars() -> None:
docs = generate_env_docs()
for canonical_var in (
"AA_BASE_URL",
"AA_MIRROR_URLS",
"LIBGEN_MIRROR_URLS",
"ZLIB_MIRROR_URLS",
"WELIB_MIRROR_URLS",
):
assert f"`{canonical_var}`" in docs
for legacy_var in (
"AA_ADDITIONAL_URLS",
"LIBGEN_ADDITIONAL_URLS",
"ZLIB_PRIMARY_URL",
"ZLIB_ADDITIONAL_URLS",
"WELIB_PRIMARY_URL",
"WELIB_ADDITIONAL_URLS",
):
assert f"`{legacy_var}`" not in docs
assert "https://annas-archive.gl" not in docs
def test_generated_env_docs_describe_mirror_lists_as_comma_separated_strings() -> None:
docs = generate_env_docs()
assert (
"| `AA_MIRROR_URLS` | List the Anna's Archive mirror URLs you want Shelfmark to use. "
"Type a URL and press Enter to add it. Order matters when Auto is selected. | "
"string (comma-separated) | _empty list_ |"
) in docs
assert (
"| `LIBGEN_MIRROR_URLS` | Mirrors are tried in the order you add them until one works. | "
"string (comma-separated) | _empty list_ |"
) in docs
def test_generated_env_docs_include_custom_component_value_fields() -> None:
docs = generate_env_docs()
for env_var in (
"TEMPLATE_RENAME",
"TEMPLATE_ORGANIZE",
"TEMPLATE_AUDIOBOOK_RENAME",
"TEMPLATE_AUDIOBOOK_ORGANIZE",
):
assert f"`{env_var}`" in docs
assert (
"| `TEMPLATE_AUDIOBOOK_ORGANIZE` | Use / to create folders. Variables: "
"{Author}, {FirstAuthor} (first of several authors), {Title}, {Year}, {Language}, "
"{User}, {OriginalName} "
"(source filename without extension), {Series}, {SeriesPosition}, {Subtitle}, "
"{PrimaryTitle}, {PartNumber}. Use arbitrary prefix/suffix:"
) in docs