Speakers fetch their own audio, so the phone cannot level it. A cast
token minted with level=true (and the client's asAlbum, which only the
queue holder knows) now returns GET /api/tracks/{id}/leveled.flac: the
track rendered by ffmpeg at the user's gain (volume=XdB, plus
alimiter at -1 dBFS for a limiter-mode boost), metadata stripped, FLAC
at 16 or 24 bits and at most 48 kHz. The gain is computed server-side
from the user's preference and the stored loudness, carried as
?g=<centi-dB>&lim=0|1 and signed into the token, so an edited URL does
not verify. Unity gains get the plain stream.
Renders are written beside the cache file and renamed in, keyed by the
source's size and mtime, coalesced per file (singleflight, detached
from the requesting speaker so a retry finds the render running),
started at mint time so the fetch finds them ready, and evicted least
recently used past leveled_cache_mb, a new admin setting (migration
0068, Loudness analysis card).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- golang.org/x/text v0.37.0 -> v0.39.0 (GO-2026-5970, infinite loop on
invalid input, reachable from pgxpool). x/sync follows to v0.21.0.
- web lockfile: in-range updates from `npm audit fix` for devalue (high)
and svelte (moderate), both of which ship in the browser bundle.
package.json is unchanged.
- Dockerfile builder golang:1.25 -> golang:1.26. CI has tested on 1.26
since the ci-go migration while the image was still compiled with 1.25,
left over from the April skeleton; the shipped binary now uses the
toolchain the tests ran on. govulncheck under golang:1.26-bookworm
(go1.26.8) reports 0 vulnerabilities reachable from our code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add Scanner.ScanFiles (watcher-driven targeted scan returning changed album
IDs) and a recursive fsnotify Watcher that debounces filesystem events and
enriches just the affected albums inline. Pure classifyEvent/drainPending
seams unit-tested; ScanFiles covered in the scanner integration test.
Direct bumps:
- golang.org/x/crypto v0.35.0 → v0.51.0 (security backports;
single highest-priority bump in the audit)
- github.com/jackc/pgx/v5 v5.7.4 → v5.9.2
- github.com/golang-migrate/migrate/v4 v4.18.2 → v4.19.1
- github.com/jackc/pgerrcode 2022-04-16 → 2025-09-07 (untagged
pseudo refresh)
Side effects (good):
- `go mod tidy` dropped three transitives that migrate v4.18
pulled in but v4.19 no longer needs: hashicorp/errwrap,
hashicorp/go-multierror, go.uber.org/atomic.
Toolchain note:
- `go.mod` `go` directive auto-bumped 1.23.0 → 1.25.0 because
x/crypto v0.51.0 declares Go 1.25 as its minimum. If the
go-ci runner image isn't on Go 1.25+, CI will bounce on
this; the runner image bump is operator infra (memory:
project_forgejo_ci.md). Tracked in Fable #464.
Reference: Fable #464 + audit note #460.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Builds the per-user daily-at-03:00-local scheduler for #392 Half B.
Uses github.com/go-co-op/gocron/v2 with WithLocation(userTZ) for
each user's job. Hourly reconciliation pass keeps the in-memory
job set in sync with the active-users query.
Start sequence: clear stale in_flight rows; register a daily job
for every active user at their stored timezone; fire a one-shot
runOnce to catch up missed schedules during downtime; start gocron.
Stop drains and shuts down the gocron loop.
Refresh(ctx, userID) removes the user's existing job (if any) and
registers a fresh one at their current timezone — wired into the
PUT /api/me/timezone handler and new-user registration in the next
commit.
Server struct gains PlaylistScheduler; main.go constructs it after
the eventbus and threads it through to api.Mount. The existing
StartSystemPlaylistCron call stays for one more commit so we don't
have a window where no scheduler is running; Task 3 deletes it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
internal/sync/changes_test.go imports github.com/stretchr/testify/require,
moving testify from indirect to direct. Also pulled in testify's own
indirects (go-spew, go-difflib) and promoted pgerrcode to direct.
Caught by CI go vet on 6fb6729.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Initial module at git.fabledsword.com/bvandeusen/minstrel pinned to go
1.23.0 to match the runner-base:go-ci image. Dependencies kept minimal
(chi router, yaml.v3) so CI's first run is fast and contained.