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
}
// 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,
"monitorNewItems": "all",
"addOptions": map[string]any{
"monitor": monitor,
"addOptions": map[string]any{"searchForMissingAlbums": true},
"monitored": true,
"searchForMissingAlbums": true,
},
})
if err != nil {
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 {
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"])
}
}
+1 -1
View File
@@ -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