Lidarr: album adds that Lidarr accepts, artist monitoring that sticks #138

Merged
bvandeusen merged 2 commits from dev into main 2026-10-07 11:08:18 -04:00
3 changed files with 31 additions and 9 deletions
Showing only changes of commit bf6364b709 - Show all commits
+14 -3
View File
@@ -274,13 +274,20 @@ func (c *Client) LookupTrack(ctx context.Context, term string) ([]LookupResult,
return out, nil return out, nil
} }
// AddArtist posts to POST /api/v1/artist. MonitorAll=true sends monitor="all"; // AddArtist posts to POST /api/v1/artist. MonitorAll=true monitors every
// false sends "future". Returns nil on 2xx; typed error otherwise. // 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 // All four of artistName, foreignArtistId, qualityProfileId, and
// metadataProfileId are required by Lidarr. Omitting any one produces a // metadataProfileId are required by Lidarr. Omitting any one produces a
// 400 with field-level validation messages (e.g. "'Metadata Profile Id' // 400 with field-level validation messages (e.g. "'Metadata Profile Id'
// must be greater than '0'"). // 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 { func (c *Client) AddArtist(ctx context.Context, p AddArtistParams) error {
monitor := "future" monitor := "future"
if p.MonitorAll { if p.MonitorAll {
@@ -293,8 +300,12 @@ func (c *Client) AddArtist(ctx context.Context, p AddArtistParams) error {
"metadataProfileId": p.MetadataProfileID, "metadataProfileId": p.MetadataProfileID,
"rootFolderPath": p.RootFolderPath, "rootFolderPath": p.RootFolderPath,
"monitored": true, "monitored": true,
"monitorNewItems": "all",
"addOptions": map[string]any{
"monitor": monitor, "monitor": monitor,
"addOptions": map[string]any{"searchForMissingAlbums": true}, "monitored": true,
"searchForMissingAlbums": true,
},
}) })
if err != nil { if err != nil {
return fmt.Errorf("%w: marshal: %v", ErrInvalidPayload, err) return fmt.Errorf("%w: marshal: %v", ErrInvalidPayload, err)
+15 -4
View File
@@ -288,13 +288,24 @@ func TestAddArtist_PostsCorrectBody(t *testing.T) {
if decoded["monitored"] != true { if decoded["monitored"] != true {
t.Errorf("monitored = %v, want true", decoded["monitored"]) t.Errorf("monitored = %v, want true", decoded["monitored"])
} }
if decoded["monitor"] != "all" { if decoded["monitorNewItems"] != "all" {
t.Errorf("monitor = %v, want all (MonitorAll=true)", decoded["monitor"]) 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) opts, ok := decoded["addOptions"].(map[string]any)
if !ok { if !ok {
t.Fatalf("addOptions missing or wrong type: %T", decoded["addOptions"]) 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 { if opts["searchForMissingAlbums"] != true {
t.Errorf("addOptions.searchForMissingAlbums = %v, want true", opts["searchForMissingAlbums"]) t.Errorf("addOptions.searchForMissingAlbums = %v, want true", opts["searchForMissingAlbums"])
} }
@@ -309,8 +320,8 @@ func TestAddArtist_MonitorFuture(t *testing.T) {
defer srv.Close() defer srv.Close()
_ = c.AddArtist(context.Background(), AddArtistParams{MonitorAll: false}) _ = c.AddArtist(context.Background(), AddArtistParams{MonitorAll: false})
if decoded["monitor"] != "future" { if opts, _ := decoded["addOptions"].(map[string]any); opts["monitor"] != "future" {
t.Errorf("monitor = %v, want future (MonitorAll=false)", decoded["monitor"]) t.Errorf("addOptions.monitor = %v, want future (MonitorAll=false)", opts["monitor"])
} }
} }
+1 -1
View File
@@ -57,7 +57,7 @@ type AddArtistParams struct {
QualityProfileID int QualityProfileID int
MetadataProfileID int MetadataProfileID int
RootFolderPath string 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 // AddAlbumParams identify the album to add and carry the settings Lidarr