A folder naming pattern containing {SeriesNumber} produces one layout when a book arrives by import and a different one when the same book is renamed, so a library can be reorganised by pressing Organize even though no setting has changed.
Same book, same setting, on current canary:
FolderNamingPattern = {Author}/{Series}/{SeriesNumber} - {Title}
import destination /audiobooks/Arthur Conan Doyle/Sherlock Holmes/The Valley of Fear
rename preview plans /audiobooks/Arthur Conan Doyle/Sherlock Holmes/7 - The Valley of Fear
The book is Sherlock Holmes #7, so the token has a value. Import drops it, rename keeps it.
As far as I can tell the cause is that the two paths build their token dictionaries separately and do not agree on what is in them. ManualImportPathPlanner has no reference to SeriesNumber anywhere in it. RenameService.Helpers.cs:204 has { "SeriesNumber", audiobook.SeriesNumber ?? string.Empty }.
The reason this is quiet rather than loud is the empty-token handling in FileNamingService. An unrecognised token resolves to the empty sentinel and the separator beside it is then cleaned up, which is the right behaviour for a book with no series, but it means an unsupplied token is indistinguishable from an absent value. So {Author}/{Series}/{SeriesNumber} - {Title} silently becomes {Author}/{Series}/{Title} on import, with nothing in the response or the logs suggesting the pattern was not fully applied.
What I think makes this worth fixing rather than documenting is the consequence rather than the token. A book imported today sits in one folder. Press Organize tomorrow and the rename preview reports a pending change and moves it, and from the outside that looks like Listenarr deciding to reorganise a library on its own. Nothing has changed but which code path last touched the book.
I have not tried to work out which of the two is the intended behaviour, and I do not think that is mine to decide. Whether the series position belongs in a folder name is a product call. The two paths disagreeing about it is the part that looks like a defect.
If it is useful, the check is public and takes one command:
git clone https://github.com/m4bard/listenarr-testdata
cd listenarr-testdata
python3 -m venv .venv && .venv/bin/pip install -e .
./tools/validate_naming_parity.sh --image ghcr.io/listenarrs/listenarr:canary
It puts one book and one pattern through both paths and prints what each produced. --pattern probes a different one, since SeriesNumber is only the token I happened to find; anything supplied by one path and not the other would show up the same way. I did not audit the full token list against both dictionaries, so there may be others.
This is separate from #764 and #795, which are about parsing a series position that is not a plain number. Here the value parses fine and one of the two paths simply never asks for it.
A folder naming pattern containing
{SeriesNumber}produces one layout when a book arrives by import and a different one when the same book is renamed, so a library can be reorganised by pressing Organize even though no setting has changed.Same book, same setting, on current canary:
The book is Sherlock Holmes #7, so the token has a value. Import drops it, rename keeps it.
As far as I can tell the cause is that the two paths build their token dictionaries separately and do not agree on what is in them.
ManualImportPathPlannerhas no reference toSeriesNumberanywhere in it.RenameService.Helpers.cs:204has{ "SeriesNumber", audiobook.SeriesNumber ?? string.Empty }.The reason this is quiet rather than loud is the empty-token handling in
FileNamingService. An unrecognised token resolves to the empty sentinel and the separator beside it is then cleaned up, which is the right behaviour for a book with no series, but it means an unsupplied token is indistinguishable from an absent value. So{Author}/{Series}/{SeriesNumber} - {Title}silently becomes{Author}/{Series}/{Title}on import, with nothing in the response or the logs suggesting the pattern was not fully applied.What I think makes this worth fixing rather than documenting is the consequence rather than the token. A book imported today sits in one folder. Press Organize tomorrow and the rename preview reports a pending change and moves it, and from the outside that looks like Listenarr deciding to reorganise a library on its own. Nothing has changed but which code path last touched the book.
I have not tried to work out which of the two is the intended behaviour, and I do not think that is mine to decide. Whether the series position belongs in a folder name is a product call. The two paths disagreeing about it is the part that looks like a defect.
If it is useful, the check is public and takes one command:
It puts one book and one pattern through both paths and prints what each produced.
--patternprobes a different one, sinceSeriesNumberis only the token I happened to find; anything supplied by one path and not the other would show up the same way. I did not audit the full token list against both dictionaries, so there may be others.This is separate from #764 and #795, which are about parsing a series position that is not a plain number. Here the value parses fine and one of the two paths simply never asks for it.