e68e1b10a6
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>
34 lines
1.5 KiB
Go
34 lines
1.5 KiB
Go
package lidarr
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
|
|
)
|
|
|
|
// Sentinel errors. Callers branch on these via errors.Is, not on
|
|
// HTTP status codes — the client maps codes to errors.
|
|
var (
|
|
ErrUnreachable = errors.New("lidarr: unreachable")
|
|
ErrAuthFailed = errors.New("lidarr: auth failed") // 401 / 403
|
|
ErrLookupFailed = errors.New("lidarr: lookup failed") // 4xx other than 401/403
|
|
ErrServerError = errors.New("lidarr: server error") // 5xx
|
|
ErrInvalidPayload = errors.New("lidarr: invalid payload")
|
|
// ErrAlreadyExists is returned by AddArtist/AddAlbum when Lidarr
|
|
// rejects the add with a 400 because the artist/album is already in
|
|
// its library. This is the desired end state, not a failure — the
|
|
// idempotent retry path (reconciler re-sending an add after a partial
|
|
// success) treats it as confirmed rather than spinning forever.
|
|
ErrAlreadyExists = errors.New("lidarr: already exists")
|
|
// ErrNotFound is returned by LookupArtistByMBID and LookupAlbumByMBID
|
|
// when Lidarr returns 200 with an empty array — i.e., the MBID isn't in
|
|
// Lidarr's monitored set. Distinguished from network/auth errors so admin
|
|
// handlers can surface it as `lidarr_album_lookup_failed` (502) instead
|
|
// of `lidarr_unreachable` (503).
|
|
//
|
|
// Aliased to apierror.ErrNotFound so handler error mapping (errors.Is
|
|
// against the shared sentinel) and existing per-package callsites both
|
|
// resolve to the same pointer.
|
|
ErrNotFound = apierror.ErrNotFound
|
|
)
|