Multi-value genres for FLAC/Ogg/MP4, readable tinted and error text, no missing-track seeds #143

Merged
bvandeusen merged 5 commits from dev into main 2026-10-07 23:53:09 -04:00
Owner

Five commits from dev. CI is green on the head commit 37d3a5bc (run 8701).

Library

  • #2500: FLAC, Ogg Vorbis, Opus and MP4 files now keep every genre they declare. dhowden/tag kept only the last of a repeated field. On the operator's library, 3,658 of 4,092 FLACs carry more than one genre. tagReadVersion goes 3 → 4, so the startup scan re-reads tags on files already indexed. Unchanged files keep their duration and fingerprint, so the scan costs tag reads only. Expect noticeably higher genre counts in browse.

Recommendations

  • #2701: For You's liked-track seeds and the Songs-like fallback seed now skip tracks whose files are missing.

Web

  • #3150: text on a tint of its own hue uses the house -fg tokens. That is 23 sites, including the player bar's active buttons, the admin count pills, the status pills and the accent chips. Worst-case contrast goes from about 2:1 to 4.75–5.03:1.
  • #3150: error text uses error-fg on every surface (19 sites), going from 2.64–3.63:1 to at least 5.30:1. A CI check now fails on any new hue-on-own-tint text and on raw error text.
  • #3943: the web tests stub SvelteKit's $app modules suite-wide, which removes the __SVELTEKIT_PAYLOAD__ module-load flake.

No migrations.

🤖 Generated with Claude Code

Five commits from `dev`. CI is green on the head commit 37d3a5bc (run 8701). **Library** - **#2500:** FLAC, Ogg Vorbis, Opus and MP4 files now keep every genre they declare. dhowden/tag kept only the last of a repeated field. On the operator's library, 3,658 of 4,092 FLACs carry more than one genre. `tagReadVersion` goes 3 → 4, so the startup scan re-reads tags on files already indexed. Unchanged files keep their duration and fingerprint, so the scan costs tag reads only. Expect noticeably higher genre counts in browse. **Recommendations** - **#2701:** For You's liked-track seeds and the Songs-like fallback seed now skip tracks whose files are missing. **Web** - **#3150:** text on a tint of its own hue uses the house `-fg` tokens. That is 23 sites, including the player bar's active buttons, the admin count pills, the status pills and the accent chips. Worst-case contrast goes from about 2:1 to 4.75–5.03:1. - **#3150:** error text uses `error-fg` on every surface (19 sites), going from 2.64–3.63:1 to at least 5.30:1. A CI check now fails on any new hue-on-own-tint text and on raw error text. - **#3943:** the web tests stub SvelteKit's `$app` modules suite-wide, which removes the `__SVELTEKIT_PAYLOAD__` module-load flake. No migrations. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
bvandeusen added 5 commits 2026-10-07 23:53:06 -04:00
test(web): stub SvelteKit app modules suite-wide so no test loads the client runtime (#3943)
release / govulncheck (push) Successful in 32s
release / web (push) Successful in 1m25s
release / go (push) Successful in 1m41s
release / integration (push) Successful in 4m55s
release / android (push) Successful in 6m15s
release / Build signed APK (releases and dev) (push) Successful in 6m42s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 2m4s
c17f4273c6
The home page reaches the real $app/navigation through AlbumCard and
AlbumMenu. That loads SvelteKit's client runtime, whose $app/paths reads
__SVELTEKIT_PAYLOAD__ at module load. The global is only there when the
kit plugin's define reaches the module, and under vitest that is not
reliable: page.test.ts failed to load on CI run 6576 and passed on its
re-run. #374 was the same class of failure.

vitest.setup.ts now mocks $app/navigation, $app/state and $app/paths for
every test. Per-file mocks still win. A small guard test fails if the
suite-wide mocks are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(web): text on a tint of its own hue uses the house -fg tokens (#3150)
release / govulncheck (push) Successful in 38s
release / integration (push) Successful in 5m1s
release / Build + push container image (push) Successful in 1m17s
release / Verify release artifacts (tag releases only) (push) Skipped
release / web (push) Successful in 1m22s
release / go (push) Successful in 1m48s
release / android (push) Successful in 5m41s
release / Build signed APK (releases and dev) (push) Successful in 5m11s
release / Attach APK to the Release (tag releases only) (push) Skipped
fdee77eaec
A hue painted as text on a color-mix tint of itself sits close to the
surface under it. On Minstrel's surfaces the raw accent on its 15% tint
measures 1.97:1 at worst (dark mode, hover surface), against AA's 4.5.

- tokens.json gains colors.fg: the five FabledSword -fg formulas (accent
  45%, success 45%, warning, error and info 50%), each mixed toward
  parchment so one declaration serves both modes. Success is Minstrel's
  moss. tokens-to-css emits them in :root.
- Tailwind exposes them as text-accent-fg, text-warning-fg, text-error-fg
  and text-info-fg.
- 23 sites swapped: 14 Tailwind class strings (PlayerBar and the admin
  count pills) and 9 CSS rules (StatusPill's four tones and five accent
  chips). Worst case after: accent-fg 5.03, error-fg 4.75, warning-fg
  4.92, success-fg 4.85.
- scripts/check-tint-contrast.js finds the pair in either spelling. Its
  test scans src in the web Vitest lane and fails on any new site, with
  fixture cases showing it can fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(web): error text uses error-fg on every surface, not only on tints (#3150)
release / govulncheck (push) Successful in 17s
release / web (push) Successful in 1m6s
release / go (push) Successful in 1m29s
release / integration (push) Successful in 4m30s
release / android (push) Successful in 5m18s
release / Build signed APK (releases and dev) (push) Successful in 5m32s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
743b6f5eac
Raw error red fails AA as text even with no tint behind it: in dark mode
it measures 3.63:1 on obsidian, 3.23 on iron and 2.64 on slate, against
4.5. error-fg (the house formula, 50% toward parchment) measures 5.30 at
worst across both modes.

- All 19 text-error uses become text-error-fg: the "Couldn't load"
  messages on the admin pages, the integrations form errors, the flag
  popover, and the error toast's text. The toast keeps its error border,
  since a border is a graphic with a 3:1 floor.
- check-tint-contrast flags raw error text anywhere (text-error,
  class:text-error, color: var(--fs-error)) and leaves borders and
  outlines alone. Run against the files before the swap, it finds all 19.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(playlists): a missing track never seeds For You or Songs-like (#2701)
release / govulncheck (push) Successful in 14s
release / web (push) Successful in 1m11s
release / go (push) Successful in 1m29s
release / integration (push) Successful in 4m27s
release / android (push) Successful in 5m2s
release / Build signed APK (releases and dev) (push) Successful in 5m17s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m15s
release / Verify release artifacts (tag releases only) (push) Skipped
efa3bf54ce
PickTopPlayedTracksForUser's liked tier read general_likes without
joining tracks. With no plays to seed from, For You could pick a liked
track whose file is gone. The play tiers were already safe: their
play_events join filters missing_since.

The same gap was in PickTopPlayedTrackForArtistByUser's fallback, which
seeds Songs-like from the artist's newest album when there are no recent
plays. It could pick a missing track, and since #5296 a missing track is
never fetched for similarity, so that seed has no edges either. The
caller already skips an empty seed, so an artist whose tracks are all
missing gets no Songs-like mix instead of one aimed at nothing.

Integration tests cover both cases: a liked-but-missing track is not a
seed, and the Songs-like fallback moves to the next album once the
newest one's track goes missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(library): read every genre of FLAC, Ogg, Opus and MP4 files (#2500)
release / govulncheck (push) Successful in 50s
release / web (push) Successful in 2m3s
release / go (push) Successful in 2m18s
release / integration (push) Successful in 5m42s
release / android (push) Successful in 6m30s
release / Build signed APK (releases and dev) (push) Successful in 6m0s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m20s
release / Verify release artifacts (tag releases only) (push) Skipped
37d3a5bcd3
Vorbis comments repeat a field to give it several values (GENRE=Boom Bap,
GENRE=Downtempo, ...). dhowden/tag keeps comments in a map keyed by field
name, so each repeat overwrote the previous one and only the last genre
was stored. MP4 has the same gap: several data atoms in one ©gen atom, or
repeated ©gen atoms, collapse to one value.

On the operator's library 3,658 of 4,092 FLACs declare more than one
genre. Kupla's Life Forms carries eight and was stored as "Instrumental
Hip Hop" alone, so browse and the taste profile never saw the other seven.

- vorbisgenre.go reads the comment block directly: FLAC's metadata block
  (including FLACs behind an ID3v2 tag), and the comment packet of Ogg
  Vorbis and Opus, reassembled across pages when cover art makes it span
  several.
- mp4genre.go walks moov > udta > meta > ilst and returns every ©gen text
  value. It handles ISO and QuickTime meta layouts and a moov placed after
  mdat. A file with only the numeric gnre atom still falls back to
  dhowden, which resolves it.
- extractGenres routes VORBIS and MP4 through them, as #2499 did for
  ID3v2.
- tagReadVersion 3 -> 4, so the next scan re-reads the tags of files
  already indexed. Unchanged files keep their duration and fingerprint,
  so the pass costs tag reads only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bvandeusen merged commit 97e84a79eb into main 2026-10-07 23:53:09 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/minstrel#143