mirror of
https://github.com/navidrome/navidrome.git
synced 2026-06-02 07:01:36 +00:00
* test(artwork): expect shared disc art for unnumbered filenames in single-folder albums
* fix(artwork): match unnumbered disc art for every disc in single-folder albums
* test(artwork): verify shared disc art resolves for every disc number
* test(artwork): regression guard for numbered disc filter with mixed filenames
* test(artwork): verify DiscArtPriority order decides numbered vs shared disc art
* test(artwork): strengthen regression guard to exercise both disc art branches
* refactor(artwork): simplify disc art matching and drop redundant comments
- Lowercase the pattern and filename once in fromExternalFile and pass
lowered values into extractDiscNumber, eliminating the duplicate
strings.ToLower calls inside that helper.
- Drop narrating comments in reader_disc.go and reader_disc_test.go that
duplicated information already conveyed by nearby code or doc comments.
* fix(artwork): prefer numbered disc art over shared fallback within a pattern
Review feedback: with files [disc.jpg, disc1.jpg, disc2.jpg] in a single
folder, the previous single-folder fall-through returned the first match
in imgFiles order. Because compareImageFiles sorts 'disc' before 'disc1'
and 'disc2', disc.jpg would mask the per-disc numbered files for every
disc, regressing the behavior from before the shared-disc-art change.
Within a single pattern the loop now records the first viable unnumbered
candidate as a fallback and keeps scanning for a numbered match equal to
the target disc. Numbered matches still win immediately; the shared file
is only returned when no numbered match for the target disc exists.
Also drops the redundant strings.ToLower(pattern) at the top of
fromExternalFile; fromDiscArtPriority already lowercases the whole
priority string before splitting, so the function contract is now
'pattern must be lowercase' (documented on the function).
* refactor(artwork): trim disc art matching comments and table-drive tests
Doc comment on fromExternalFile is trimmed to the one non-obvious
contract (caller must pre-lowercase the pattern) plus the headline
behavior; the bulleted restatement of the branch logic went away.
Two inline comments that narrated what the code already shows are
also gone.
Hoisting a `hasWildcard := strings.ContainsRune(pattern, '*')` check
out of the loop avoids per-iteration extractDiscNumber calls for
literal patterns (e.g. `shellac.png`) and lets the loop break as
soon as a viable fallback is found, since literal patterns can never
be beaten by a numbered match. Wildcard patterns keep the original
scan-to-end-for-numbered-match behavior.
The two regression tests added in the previous commit were
structurally identical apart from discNumber/expected, so they are
collapsed into a DescribeTable with two entries — matching the
existing table style used for extractDiscNumber tests in the same
file.
* fix(artwork): support '?' and '[...]' wildcards in disc art patterns
filepath.Match understands three glob metacharacters ('*', '?', '[')
but extractDiscNumber only looked for '*'. A pattern like 'disc?.jpg'
or 'cd[12].jpg' would therefore be treated as unnumbered, and every
disc of a multi-disc album would resolve to the same (first-sorted)
file instead of the per-disc numbered art.
extractDiscNumber now finds the literal prefix of the pattern by
scanning for the first '*', '?', or '[' (via strings.IndexAny),
strips it from the filename, and parses the leading digits that
follow. The standalone filepath.Match check is dropped; HasPrefix
plus the leading-digits requirement is enough to reject non-matches,
and the caller already verifies the glob match before calling.
fromExternalFile's literal-pattern optimization is widened
correspondingly: a pattern is treated as literal only when it
contains none of '*', '?', '['. Any wildcard form now keeps the
scan-to-end behavior so a numbered match can beat a fallback.
Adds table entries for both the extractDiscNumber parser and the
fromExternalFile higher-level behavior, covering '?' and '[...]'
patterns as well as a literal-pattern baseline.
* refactor(artwork): tidy extractDiscNumber after glob-wildcard support
- Name the '*?[' charset as globMetaChars, used by both extractDiscNumber
and fromExternalFile so the two call sites can't drift.
- Trim the extractDiscNumber doc comment: keep the non-obvious caller
contract, drop the algorithm narration.
- Replace the byte-slice digit accumulator with a direct filename slice
fed to strconv.Atoi.
- Rename the four new non-'*' wildcard Entry descriptions so they read
like the existing extractDiscNumber table ('pattern, target → expected')
instead of the ambiguous 'disc 1' shorthand.
* fix(artwork): retry remaining fallbacks when the first one fails to open
Review feedback: the previous shape remembered only the first unnumbered
candidate and fell through to a generic error if os.Open failed on it,
even though other matching unnumbered files in imgFiles could have
succeeded. The pre-PR code was more resilient because it looped and
continued on open failure.
fromExternalFile now collects every viable unnumbered candidate into a
slice during the scan, then tries them in order after the loop, mirroring
the pre-PR retry-on-open-failure behavior. Numbered matches still return
immediately on first success and skip the candidate list entirely — an
open failure on a numbered match means no other file has that number
anyway.
Also:
- globMetaChars doc comment now notes that '\' escape is intentionally
excluded (filepath.Match supports it but treating it as a metachar here
would misalign extractDiscNumber's literal-prefix extraction with no
benefit for realistic config patterns).
- The 'cover.jpg doesn't match disc*.*' Entry in the extractDiscNumber
table is renamed to 'cover.jpg with disc*.* (no prefix match)' to
reflect that the test now exercises the HasPrefix defensive guard,
not the removed internal filepath.Match check.
Regression test added: a single-folder album with a deleted first
candidate file resolves to the second candidate.
* fix(artwork): scan all literal-pattern matches so fallback retry works
Review feedback: the 'break on first literal match' optimization
assumed only one file in imgFiles could match a literal basename,
but filepath.Match compares basenames only — multiple folders can
contribute files with the same basename, and the fallback-list retry
in 5d79f751c is defeated if the loop breaks after recording just
the first one.
Removing the break makes literal and wildcard patterns follow the
same scan-to-end path, preserving the retry-on-open-failure
resilience regained in 5d79f751c. The efficiency cost is negligible
— imgFiles is 5-20 entries per album and this is a cache-miss path.