Skip to content

feat(naming): add {FirstAuthor} template token - #1322

Merged
calibrain merged 1 commit into
calibrain:mainfrom
viniciuspx:feat/firstauthor-naming-token
Sep 11, 2026
Merged

calibrain merged 1 commit into
calibrain:mainfrom
viniciuspx:feat/firstauthor-naming-token

Conversation

@viniciuspx

Copy link
Copy Markdown
Contributor

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

Closes calibrain#930.

Folders and filenames built from {Author} use the full "Author1, Author2,
Author3" string when metadata lists several authors. {FirstAuthor} renders
only the first entry (split on ',' or ';'), so multi-author books can share
a folder with the rest of that author's work.

- {Author} is unchanged.
- A single name written "Last, First" is also split on the comma and
  renders as "Last" -- the source metadata does not distinguish the two
  forms. Documented in the token help and tests.
- Derived in parse_naming_template from the existing Author value, so every
  caller (transfer, settings preview, ...) picks it up with no wiring.
- Frontend preview token list kept in lockstep.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WfHSviQ4wkyh2dseiHFYjq
@calibrain

Copy link
Copy Markdown
Owner

Looks good to me ! Thank you !

@calibrain
calibrain merged commit c576003 into calibrain:main Sep 11, 2026
doonga pushed a commit to greyrock-labs/home-ops that referenced this pull request Sep 26, 2026
…v1.4.0) (#283)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/calibrain/shelfmark](https://github.com/calibrain/shelfmark) | minor | `v1.3.15` → `v1.4.0` |

---

### Release Notes

<details>
<summary>calibrain/shelfmark (ghcr.io/calibrain/shelfmark)</summary>

### [`v1.4.0`](https://github.com/calibrain/shelfmark/releases/tag/v1.4.0)

[Compare Source](calibrain/shelfmark@v1.3.15...v1.4.0)

##### What's Changed

- build(deps): bump the python-deps group with 2 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1302](calibrain/shelfmark#1302)
- fix: keep polling queued Real-Debrid torrents by [@&#8203;mvanhorn](https://github.com/mvanhorn) in [#&#8203;1303](calibrain/shelfmark#1303)
- fix(bypass): keep Anna's Archive's aa\_ddg\_check so clearance replays by [@&#8203;jfmlima](https://github.com/jfmlima) in [#&#8203;1305](calibrain/shelfmark#1305)
- fix(postprocess): attach unmatched chaptered audio files to existing book group ([#&#8203;1176](calibrain/shelfmark#1176)) by [@&#8203;amasen02](https://github.com/amasen02) in [#&#8203;1309](calibrain/shelfmark#1309)
- build(deps): bump the gh-actions group with 3 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1310](calibrain/shelfmark#1310)
- build(deps): bump the npm-deps group in /src/frontend with 4 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1308](calibrain/shelfmark#1308)
- build(deps): bump python from `cae66f2` to `cad9a2c` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1307](calibrain/shelfmark#1307)
- build(deps): bump the docker-base-image-digests group with 2 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1306](calibrain/shelfmark#1306)
- Deep-link Search By mode via URL hash by [@&#8203;nfvelten](https://github.com/nfvelten) in [#&#8203;1311](calibrain/shelfmark#1311)
- build(deps): bump the python-deps group across 1 directory with 4 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1327](calibrain/shelfmark#1327)
- Keep default filters out of the URL hash by [@&#8203;nfvelten](https://github.com/nfvelten) in [#&#8203;1314](calibrain/shelfmark#1314)
- feat(download): add Blackhole torrent handoff by [@&#8203;atirna](https://github.com/atirna) in [#&#8203;1312](calibrain/shelfmark#1312)
- feat(naming): add {FirstAuthor} template token by [@&#8203;viniciuspx](https://github.com/viniciuspx) in [#&#8203;1322](calibrain/shelfmark#1322)
- fix: share rotating log file handlers by [@&#8203;eikopf](https://github.com/eikopf) in [#&#8203;1316](calibrain/shelfmark#1316)
- fix(prowlarr): skip indexers in Prowlarr failure back-off by [@&#8203;jfmlima](https://github.com/jfmlima) in [#&#8203;1324](calibrain/shelfmark#1324)
- fix: prevent Anna's Archive download countdown resets by preserving browser sessions by [@&#8203;broglea](https://github.com/broglea) in [#&#8203;1325](calibrain/shelfmark#1325)
- build(deps): bump the docker-base-image-digests group with 2 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1328](calibrain/shelfmark#1328)
- build(deps-dev): bump the npm-deps group in /src/frontend with 4 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1329](calibrain/shelfmark#1329)
- build(deps-dev): bump vitest from 4.1.11 to 5.0.0 in /src/frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1330](calibrain/shelfmark#1330)
- fix(irc): search by surname, and rank the answer by author ([#&#8203;1331](calibrain/shelfmark#1331)) by [@&#8203;Kukkerem](https://github.com/Kukkerem) in [#&#8203;1332](calibrain/shelfmark#1332)
- fix(irc): rank a surname-only result as partial, not wrong ([#&#8203;1332](calibrain/shelfmark#1332)) by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1334](calibrain/shelfmark#1334)
- feat(sources): add Libgen as a direct catalogue search source by [@&#8203;klaidliadon](https://github.com/klaidliadon) in [#&#8203;1326](calibrain/shelfmark#1326)
- refactor: make direct download provider-driven by [@&#8203;TomJansen](https://github.com/TomJansen) in [#&#8203;1337](calibrain/shelfmark#1337)
- fix(sources): restore Direct Download search errors and language matches by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1339](calibrain/shelfmark#1339)
- fix(sources): send a Referer when fetching libgen ads.php pages by [@&#8203;klaidliadon](https://github.com/klaidliadon) in [#&#8203;1340](calibrain/shelfmark#1340)
- Feature: Add Download counts to search result displays and Download sidebar by [@&#8203;RoninTech](https://github.com/RoninTech) in [#&#8203;1336](calibrain/shelfmark#1336)
- Extract archives when zip/rar are enabled as supported formats by [@&#8203;funkypenguin](https://github.com/funkypenguin) in [#&#8203;1343](calibrain/shelfmark#1343)
- fix(download): complete consumed Blackhole handoffs by [@&#8203;atirna](https://github.com/atirna) in [#&#8203;1345](calibrain/shelfmark#1345)
- Add configurable word separator for naming templates by [@&#8203;viniciuspx](https://github.com/viniciuspx) in [#&#8203;1333](calibrain/shelfmark#1333)
- build(deps): bump the python-deps group across 1 directory with 5 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1344](calibrain/shelfmark#1344)
- fix: unbreak main and follow up on the Blackhole handoff review by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1346](calibrain/shelfmark#1346)
- build(deps): bump python-socketio from 5.16.4 to 5.17.0 in the python-deps group by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1347](calibrain/shelfmark#1347)
- Added the ability to sort direct search results by Most downloads by [@&#8203;RoninTech](https://github.com/RoninTech) in [#&#8203;1351](calibrain/shelfmark#1351)
- feat: Add TorBox client support and settings integration by [@&#8203;marcelorodrigo](https://github.com/marcelorodrigo) in [#&#8203;1342](calibrain/shelfmark#1342)
- refactor: extract the per-source release search out of /api/releases by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1355](calibrain/shelfmark#1355)
- build(deps): bump the docker-base-image-digests group with 2 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1348](calibrain/shelfmark#1348)
- build(deps): bump the npm-deps group in /src/frontend with 9 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1349](calibrain/shelfmark#1349)
- build(deps): bump the gh-actions group with 6 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1350](calibrain/shelfmark#1350)
- feat(auth): provision proxy users as non-admin once an admin exists by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1356](calibrain/shelfmark#1356)
- fix(users): apply user updates only after the payload validates by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1360](calibrain/shelfmark#1360)
- fix(download): default is\_admin to False in the request policy guard by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1358](calibrain/shelfmark#1358)
- fix(oidc): reject backslash paths in the return\_to sanitizer by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1359](calibrain/shelfmark#1359)
- fix(queue): don't stamp CANCELLED over a finished download by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1361](calibrain/shelfmark#1361)
- fix(download): check task ownership before serving queued files by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1357](calibrain/shelfmark#1357)
- Feature: Show the AA search result stats by [@&#8203;RoninTech](https://github.com/RoninTech) in [#&#8203;1362](calibrain/shelfmark#1362)
- fix(requests): reject non-object items in the batch endpoint by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1369](calibrain/shelfmark#1369)
- fix(http): keep the host of a protocol-relative download link by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1368](calibrain/shelfmark#1368)
- fix(googlebooks): page by the capped size, not the raw limit by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1370](calibrain/shelfmark#1370)
- fix(deluge): send seeding ratio limit under Deluge's own keys by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1367](calibrain/shelfmark#1367)
- feat(auth): static API\_KEY (env) accepted as Bearer or X-Api-Key, cookie or key by [@&#8203;gavinmcfall](https://github.com/gavinmcfall) in [#&#8203;1366](calibrain/shelfmark#1366)
- fix(auth): rename the API\_KEY env var to SHELFMARK\_API\_KEY by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1374](calibrain/shelfmark#1374)
- fix: bypass recordings, welib wrong-md5 links, footer build sha ([#&#8203;1364](calibrain/shelfmark#1364)) by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1373](calibrain/shelfmark#1373)
- build(deps): bump the python-deps group with 4 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1375](calibrain/shelfmark#1375)
- ci: debounce dev image builds instead of building nightly by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1376](calibrain/shelfmark#1376)
- fix(download): stream a completed book instead of buffering it in RAM - lowering memory needs significantly by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1378](calibrain/shelfmark#1378)
- perf(docker): keep the heavy build layers cacheable across builds - save 11minutes per build by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1379](calibrain/shelfmark#1379)
- test(auth): stop proxy provisioning tests depending on run order by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1381](calibrain/shelfmark#1381)
- feat(search): add a configurable default content type by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1371](calibrain/shelfmark#1371)
- feat(library): mark search results already in a Calibre library by [@&#8203;splitsec2](https://github.com/splitsec2) in [#&#8203;1377](calibrain/shelfmark#1377)
- Default to english when no lang is slected by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1396](calibrain/shelfmark#1396)
- fix(auth): fail closed when auth prerequisites are missing ([#&#8203;1387](calibrain/shelfmark#1387)) by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1397](calibrain/shelfmark#1397)
- build(deps): bump the gh-actions group with 4 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1395](calibrain/shelfmark#1395)
- fix(audiobookbay): reuse the resolved magnet when retrying ([#&#8203;1388](calibrain/shelfmark#1388)) by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1398](calibrain/shelfmark#1398)
- build(deps): bump the npm-deps group in /src/frontend with 7 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1394](calibrain/shelfmark#1394)
- build(deps): bump seleniumbase from 4.54.9 to 4.54.10 in the python-deps group by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1382](calibrain/shelfmark#1382)
- feat: narrator, series and bitrate columns for MyAnonamouse results by [@&#8203;adman234](https://github.com/adman234) in [#&#8203;1390](calibrain/shelfmark#1390)
- build(deps): bump astral-sh/uv from 0.12.16 to 0.12.19 in the docker-base-image-digests group across 1 directory by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1392](calibrain/shelfmark#1392)
- fix(mam): keep the session ID on MAM and rerun Prowlarr's exact search by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1399](calibrain/shelfmark#1399)
- fix(ui): contain the result count added in [#&#8203;1362](calibrain/shelfmark#1362) by [@&#8203;calibrain](https://github.com/calibrain) in [#&#8203;1363](calibrain/shelfmark#1363)

##### New Contributors

- [@&#8203;amasen02](https://github.com/amasen02) made their first contribution in [#&#8203;1309](calibrain/shelfmark#1309)
- [@&#8203;atirna](https://github.com/atirna) made their first contribution in [#&#8203;1312](calibrain/shelfmark#1312)
- [@&#8203;viniciuspx](https://github.com/viniciuspx) made their first contribution in [#&#8203;1322](calibrain/shelfmark#1322)
- [@&#8203;eikopf](https://github.com/eikopf) made their first contribution in [#&#8203;1316](calibrain/shelfmark#1316)
- [@&#8203;broglea](https://github.com/broglea) made their first contribution in [#&#8203;1325](calibrain/shelfmark#1325)
- [@&#8203;klaidliadon](https://github.com/klaidliadon) made their first contribution in [#&#8203;1326](calibrain/shelfmark#1326)
- [@&#8203;TomJansen](https://github.com/TomJansen) made their first contribution in [#&#8203;1337](calibrain/shelfmark#1337)
- [@&#8203;RoninTech](https://github.com/RoninTech) made their first contribution in [#&#8203;1336](calibrain/shelfmark#1336)
- [@&#8203;marcelorodrigo](https://github.com/marcelorodrigo) made their first contribution in [#&#8203;1342](calibrain/shelfmark#1342)
- [@&#8203;splitsec2](https://github.com/splitsec2) made their first contribution in [#&#8203;1355](calibrain/shelfmark#1355)
- [@&#8203;gavinmcfall](https://github.com/gavinmcfall) made their first contribution in [#&#8203;1366](calibrain/shelfmark#1366)

**Full Changelog**: <calibrain/shelfmark@v1.3.15...v1.4.0>

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/New_York)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDUuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjEwNS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Reviewed-on: https://git.greyrock.io/todd/home-ops/pulls/283
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create {FirstAuthor} tag

2 participants