Summary
Audnexus reports a series position as a string, and it is not always a number. Squeezing it through a decimal loses it in two different ways, and the loss reaches the filename.
Evidence
Real positions from the Audible catalogue (all public domain, all verifiable against api.audnex.us):
| Book |
ASIN |
position |
| The Father Brown Collection: Books 1-4 |
B0F84DFZ66 |
"1-4" — one ASIN, four books |
| The Thirty-Nine Steps |
B002V1PLZK |
"1-2" — the product also ships Greenmantle |
| She and Allan |
B00CQ5WAXW |
"0" — a prequel slot |
| a novella between two books |
— |
"1.5" |
1. The parse uses the server's culture
Audiobook.CreateBasicAudioMetadata calls decimal.TryParse(SeriesNumber, out var sp) with no CultureInfo, so it uses CurrentCulture — while FileNamingService formats the result back with InvariantCulture. The source value always uses . as the decimal separator, so where . is the group separator the number silently changes:
en-US "1.5" -> 1.5 "0.5" -> 0.5
de-DE "1.5" -> 15 "0.5" -> 5
fr-FR "1.5" -> null "0.5" -> null
invariant "1.5" -> 1.5 "0.5" -> 0.5
On a German-locale server a novella at position 1.5 becomes book 15.
DownloadImportService is worse — it formats with a bare .ToString(), which writes 1,5 (with a comma) into a filename. The two naming sites don't agree with each other.
2. A position that doesn't parse is silently discarded
A position that fails the parse becomes null, which is indistinguishable from a book with no series position. Naming then falls through to the track number:
{ "SeriesNumber", FirstNonEmpty(metadata.SeriesPosition?.ToString(...),
metadata.TrackNumber?.ToString()) },
So a book at position "1-4" is renamed using an unrelated track number — no error, nothing in the log, wrong name on disk.
Expected
A real series position should survive to the filename, whatever the server's locale, and whether or not it happens to be a decimal. A book with genuinely no position should still fall back to the track number, as it does today.
Notes
PR #763 implements this: parses are pinned to InvariantCulture, and AudioMetadata carries the position as the source gave it so a non-numeric-but-real position is not lost. Opened after the fact — my apologies, I should have raised the issue first per CONTRIBUTING.md.
Summary
Audnexus reports a series position as a string, and it is not always a number. Squeezing it through a
decimalloses it in two different ways, and the loss reaches the filename.Evidence
Real positions from the Audible catalogue (all public domain, all verifiable against
api.audnex.us):positionB0F84DFZ66"1-4"— one ASIN, four booksB002V1PLZK"1-2"— the product also ships GreenmantleB00CQ5WAXW"0"— a prequel slot"1.5"1. The parse uses the server's culture
Audiobook.CreateBasicAudioMetadatacallsdecimal.TryParse(SeriesNumber, out var sp)with noCultureInfo, so it usesCurrentCulture— whileFileNamingServiceformats the result back withInvariantCulture. The source value always uses.as the decimal separator, so where.is the group separator the number silently changes:On a German-locale server a novella at position 1.5 becomes book 15.
DownloadImportServiceis worse — it formats with a bare.ToString(), which writes1,5(with a comma) into a filename. The two naming sites don't agree with each other.2. A position that doesn't parse is silently discarded
A position that fails the parse becomes
null, which is indistinguishable from a book with no series position. Naming then falls through to the track number:So a book at position
"1-4"is renamed using an unrelated track number — no error, nothing in the log, wrong name on disk.Expected
A real series position should survive to the filename, whatever the server's locale, and whether or not it happens to be a decimal. A book with genuinely no position should still fall back to the track number, as it does today.
Notes
PR #763 implements this: parses are pinned to
InvariantCulture, andAudioMetadatacarries the position as the source gave it so a non-numeric-but-real position is not lost. Opened after the fact — my apologies, I should have raised the issue first perCONTRIBUTING.md.