From bf6364b709d5e1518a468790baf9a558700398bc Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 7 Oct 2026 10:58:55 -0400 Subject: [PATCH] fix(lidarr): send the artist add's monitor choice in addOptions (#5239) ArtistResource has no top-level `monitor`. Lidarr reads the choice from AddOptions (AddArtistOptions, a MonitoringOptions), so the "all"/"future" we sent there was dropped on deserialisation. AddOptions.Monitor stayed Unknown, and AlbumMonitoredService.SetAlbumMonitoredStatus returns early on Unknown. The request's monitoring was never applied. Send monitor and monitored inside addOptions with searchForMissingAlbums, the shape Lidarr's getNewArtist.js posts, and set monitorNewItems "all" explicitly: both choices mean new releases are watched. Co-Authored-By: Claude Opus 5.5 --- internal/lidarr/client.go | 19 +++++++++++++++---- internal/lidarr/client_test.go | 19 +++++++++++++++---- internal/lidarr/types.go | 2 +- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/internal/lidarr/client.go b/internal/lidarr/client.go index 3c6b7d4b..9d90cc51 100644 --- a/internal/lidarr/client.go +++ b/internal/lidarr/client.go @@ -274,13 +274,20 @@ func (c *Client) LookupTrack(ctx context.Context, term string) ([]LookupResult, return out, nil } -// AddArtist posts to POST /api/v1/artist. MonitorAll=true sends monitor="all"; -// false sends "future". Returns nil on 2xx; typed error otherwise. +// AddArtist posts to POST /api/v1/artist. MonitorAll=true monitors every +// album ("all"); false monitors only releases from now on ("future"). New +// releases are monitored either way. Returns nil on 2xx; typed error +// otherwise. // // All four of artistName, foreignArtistId, qualityProfileId, and // metadataProfileId are required by Lidarr. Omitting any one produces a // 400 with field-level validation messages (e.g. "'Metadata Profile Id' // must be greater than '0'"). +// +// The monitor choice belongs in addOptions (AddArtistOptions, a +// MonitoringOptions). ArtistResource has no top-level `monitor`, so a +// choice sent there is dropped, AddOptions.Monitor stays Unknown and +// Lidarr skips applying it (#5239). func (c *Client) AddArtist(ctx context.Context, p AddArtistParams) error { monitor := "future" if p.MonitorAll { @@ -293,8 +300,12 @@ func (c *Client) AddArtist(ctx context.Context, p AddArtistParams) error { "metadataProfileId": p.MetadataProfileID, "rootFolderPath": p.RootFolderPath, "monitored": true, - "monitor": monitor, - "addOptions": map[string]any{"searchForMissingAlbums": true}, + "monitorNewItems": "all", + "addOptions": map[string]any{ + "monitor": monitor, + "monitored": true, + "searchForMissingAlbums": true, + }, }) if err != nil { return fmt.Errorf("%w: marshal: %v", ErrInvalidPayload, err) diff --git a/internal/lidarr/client_test.go b/internal/lidarr/client_test.go index c797af00..43e7b0af 100644 --- a/internal/lidarr/client_test.go +++ b/internal/lidarr/client_test.go @@ -288,13 +288,24 @@ func TestAddArtist_PostsCorrectBody(t *testing.T) { if decoded["monitored"] != true { t.Errorf("monitored = %v, want true", decoded["monitored"]) } - if decoded["monitor"] != "all" { - t.Errorf("monitor = %v, want all (MonitorAll=true)", decoded["monitor"]) + if decoded["monitorNewItems"] != "all" { + t.Errorf("monitorNewItems = %v, want all", decoded["monitorNewItems"]) + } + // #5239: ArtistResource has no top-level monitor; Lidarr reads the + // choice from addOptions and silently drops it anywhere else. + if _, set := decoded["monitor"]; set { + t.Errorf("monitor sent at top level (%v), where Lidarr ignores it", decoded["monitor"]) } opts, ok := decoded["addOptions"].(map[string]any) if !ok { t.Fatalf("addOptions missing or wrong type: %T", decoded["addOptions"]) } + if opts["monitor"] != "all" { + t.Errorf("addOptions.monitor = %v, want all (MonitorAll=true)", opts["monitor"]) + } + if opts["monitored"] != true { + t.Errorf("addOptions.monitored = %v, want true", opts["monitored"]) + } if opts["searchForMissingAlbums"] != true { t.Errorf("addOptions.searchForMissingAlbums = %v, want true", opts["searchForMissingAlbums"]) } @@ -309,8 +320,8 @@ func TestAddArtist_MonitorFuture(t *testing.T) { defer srv.Close() _ = c.AddArtist(context.Background(), AddArtistParams{MonitorAll: false}) - if decoded["monitor"] != "future" { - t.Errorf("monitor = %v, want future (MonitorAll=false)", decoded["monitor"]) + if opts, _ := decoded["addOptions"].(map[string]any); opts["monitor"] != "future" { + t.Errorf("addOptions.monitor = %v, want future (MonitorAll=false)", opts["monitor"]) } } diff --git a/internal/lidarr/types.go b/internal/lidarr/types.go index 7a5858f7..3fd9db36 100644 --- a/internal/lidarr/types.go +++ b/internal/lidarr/types.go @@ -57,7 +57,7 @@ type AddArtistParams struct { QualityProfileID int MetadataProfileID int RootFolderPath string - MonitorAll bool // true => monitor="all"; false => "future" + MonitorAll bool // true => addOptions.monitor="all"; false => "future" } // AddAlbumParams identify the album to add and carry the settings Lidarr