fix(player+lidarr): mini-player sync race; durable approve + dedup

player: setQueueFromTracks fast-starts a single source at player-index 0
while the full queue is broadcast, so the transient currentIndexStream→0
emission clobbered the correct mediaItem with queue.value[0] (the first
track). Mini bar / playlist marker pinned to the wrong track until a
later index event (~the "passive ~30s recovery"). Track a logical-index
base so the player→queue mapping stays correct during the fill window;
also fixes the latent forward-fill auto-advance off-by-base.

lidarr #50: approving no longer fails when Lidarr is down. Approve
records the decision durably first, then best-effort adds; the
reconciler idempotently (re)sends unconfirmed adds every tick until they
stick (new additive lidarr_add_confirmed_at; AddArtist/AddAlbum map
Lidarr's "already exists" 400 → ErrAlreadyExists). No failed-state or
expiry by design — Lidarr keeps trying, operator monitors.

lidarr #51: Create() is now idempotent — a non-terminal request for the
same MBID returns the existing row instead of inserting a duplicate.

Rewrites the obsolete LidarrUnreachable_503 test to assert the durable-
approve contract; threads a client factory into NewReconciler.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 12:24:20 -04:00
parent b76ea66165
commit e68e1b10a6
13 changed files with 382 additions and 100 deletions
+26
View File
@@ -93,3 +93,29 @@ SELECT EXISTS (
OR (kind = 'album' AND lidarr_album_mbid = @mbid)
OR (kind = 'track' AND lidarr_track_mbid = @mbid))
);
-- name: GetNonTerminalRequestForMBID :one
-- Returns the oldest non-terminal (pending/approved/completed) request
-- whose own-kind MBID matches @mbid, or pgx.ErrNoRows when none. Create()
-- uses this to dedupe: a re-request for something already in flight
-- returns the existing row instead of inserting a duplicate. MBIDs are
-- globally unique per entity type, so the cross-column OR cannot
-- false-match (an artist MBID never collides with an album/track MBID).
SELECT * FROM lidarr_requests
WHERE status IN ('pending', 'approved', 'completed')
AND ((kind = 'artist' AND lidarr_artist_mbid = @mbid)
OR (kind = 'album' AND lidarr_album_mbid = @mbid)
OR (kind = 'track' AND lidarr_track_mbid = @mbid))
ORDER BY requested_at ASC
LIMIT 1;
-- name: MarkLidarrRequestAddConfirmed :exec
-- Idempotently records that Lidarr accepted the add for an approved
-- request. The lidarr_add_confirmed_at IS NULL guard makes a redundant
-- reconciler retry a no-op rather than bumping updated_at every tick.
UPDATE lidarr_requests
SET lidarr_add_confirmed_at = now(),
updated_at = now()
WHERE id = $1
AND status = 'approved'
AND lidarr_add_confirmed_at IS NULL;