From dadba3d1ae622f263f1b9429cdd3f93ec2bfd545 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Thu, 8 Feb 2024 00:08:28 -0800 Subject: [PATCH 1/3] add lidarr naming and formats --- .github/workflows/codetests.yml | 6 +- lidarr/customformat.go | 145 ++++++++++++++++++++++++++++++++ lidarr/naming.go | 67 +++++++++++++++ 3 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 lidarr/customformat.go create mode 100644 lidarr/naming.go diff --git a/.github/workflows/codetests.yml b/.github/workflows/codetests.yml index 7a2304d..7d04344 100644 --- a/.github/workflows/codetests.yml +++ b/.github/workflows/codetests.yml @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '1.21' + go-version: stable - name: go-test run: go test -race -v -covermode=atomic ./... # Runs golangci-lint on macos against freebsd and macos. @@ -44,7 +44,7 @@ jobs: steps: - uses: actions/setup-go@v5 with: - go-version: '1.21' + go-version: stable - uses: actions/checkout@v4 - name: golangci-lint uses: golangci/golangci-lint-action@v3 @@ -62,7 +62,7 @@ jobs: steps: - uses: actions/setup-go@v5 with: - go-version: '1.21' + go-version: stable - uses: actions/checkout@v4 - name: golangci-lint uses: golangci/golangci-lint-action@v3 diff --git a/lidarr/customformat.go b/lidarr/customformat.go new file mode 100644 index 0000000..fbb3eda --- /dev/null +++ b/lidarr/customformat.go @@ -0,0 +1,145 @@ +package lidarr + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "path" + + "golift.io/starr" +) + +const bpCustomFormat = APIver + "/customFormat" + +// CustomFormatInput is the input for a new or updated CustomFormat. +type CustomFormatInput struct { + ID int64 `json:"id,omitempty"` + Name string `json:"name"` + IncludeCFWhenRenaming bool `json:"includeCustomFormatWhenRenaming"` + Specifications []*CustomFormatInputSpec `json:"specifications"` +} + +// CustomFormatInputSpec is part of a CustomFormatInput. +type CustomFormatInputSpec struct { + Name string `json:"name"` + Implementation string `json:"implementation"` + Negate bool `json:"negate"` + Required bool `json:"required"` + Fields []*starr.FieldInput `json:"fields"` +} + +// CustomFormatOutput is the output from the CustomFormat methods. +type CustomFormatOutput struct { + ID int64 `json:"id"` + Name string `json:"name"` + IncludeCFWhenRenaming bool `json:"includeCustomFormatWhenRenaming"` + Specifications []*CustomFormatOutputSpec `json:"specifications"` +} + +// CustomFormatOutputSpec is part of a CustomFormatOutput. +type CustomFormatOutputSpec struct { + ID int64 `json:"id"` + Name string `json:"name"` + Implementation string `json:"implementation"` + ImplementationName string `json:"implementationName"` + InfoLink string `json:"infoLink"` + Negate bool `json:"negate"` + Required bool `json:"required"` + Fields []*starr.FieldOutput `json:"fields"` +} + +// GetCustomFormats returns all configured Custom Formats. +func (l *Lidarr) GetCustomFormats() ([]*CustomFormatOutput, error) { + return l.GetCustomFormatsContext(context.Background()) +} + +// GetCustomFormatsContext returns all configured Custom Formats. +func (l *Lidarr) GetCustomFormatsContext(ctx context.Context) ([]*CustomFormatOutput, error) { + var output []*CustomFormatOutput + + req := starr.Request{URI: bpCustomFormat} + if err := l.GetInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Get(%s): %w", &req, err) + } + + return output, nil +} + +// GetCustomFormat returns a single custom format. +func (l *Lidarr) GetCustomFormat(customformatID int64) (*CustomFormatOutput, error) { + return l.GetCustomFormatContext(context.Background(), customformatID) +} + +// GetCustomFormatContext returns a single custom format. +func (l *Lidarr) GetCustomFormatContext(ctx context.Context, customformatID int64) (*CustomFormatOutput, error) { + var output CustomFormatOutput + + req := starr.Request{URI: path.Join(bpCustomFormat, fmt.Sprint(customformatID))} + if err := l.GetInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Get(%s): %w", &req, err) + } + + return &output, nil +} + +// AddCustomFormat creates a new custom format and returns the response (with ID). +func (l *Lidarr) AddCustomFormat(format *CustomFormatInput) (*CustomFormatOutput, error) { + return l.AddCustomFormatContext(context.Background(), format) +} + +// AddCustomFormatContext creates a new custom format and returns the response (with ID). +func (l *Lidarr) AddCustomFormatContext(ctx context.Context, format *CustomFormatInput) (*CustomFormatOutput, error) { + var output CustomFormatOutput + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(format); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpCustomFormat, err) + } + + req := starr.Request{URI: bpCustomFormat, Body: &body} + if err := l.PostInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Post(%s): %w", &req, err) + } + + return &output, nil +} + +// UpdateCustomFormat updates an existing custom format and returns the response. +func (l *Lidarr) UpdateCustomFormat(cf *CustomFormatInput) (*CustomFormatOutput, error) { + return l.UpdateCustomFormatContext(context.Background(), cf) +} + +// UpdateCustomFormatContext updates an existing custom format and returns the response. +func (l *Lidarr) UpdateCustomFormatContext(ctx context.Context, + format *CustomFormatInput, +) (*CustomFormatOutput, error) { + var output CustomFormatOutput + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(format); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpCustomFormat, err) + } + + req := starr.Request{URI: path.Join(bpCustomFormat, fmt.Sprint(format.ID)), Body: &body} + if err := l.PutInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Put(%s): %w", &req, err) + } + + return &output, nil +} + +// DeleteCustomFormat deletes a custom format. +func (l *Lidarr) DeleteCustomFormat(cfID int64) error { + return l.DeleteCustomFormatContext(context.Background(), cfID) +} + +// DeleteCustomFormatContext deletes a custom format. +func (l *Lidarr) DeleteCustomFormatContext(ctx context.Context, cfID int64) error { + req := starr.Request{URI: path.Join(bpCustomFormat, fmt.Sprint(cfID))} + if err := l.DeleteAny(ctx, req); err != nil { + return fmt.Errorf("api.Delete(%s): %w", &req, err) + } + + return nil +} diff --git a/lidarr/naming.go b/lidarr/naming.go new file mode 100644 index 0000000..30405d1 --- /dev/null +++ b/lidarr/naming.go @@ -0,0 +1,67 @@ +package lidarr + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + + "golift.io/starr" +) + +// Define Base Path for Naming calls. +const bpNaming = APIver + "/config/naming" + +// Naming represents the config/naming endpoint in Lidarr. +type Naming struct { + RenameTracks bool `json:"renameTracks"` + ReplaceIllegalCharacters bool `json:"replaceIllegalCharacters"` + IncludeArtistName bool `json:"includeArtistName"` + IncludeAlbumTitle bool `json:"includeAlbumTitle"` + IncludeQuality bool `json:"includeQuality"` + ReplaceSpaces bool `json:"replaceSpaces"` + ColonReplacementFormat int `json:"colonReplacementFormat"` + ID int64 `json:"id"` + StandardTrackFormat string `json:"standardTrackFormat"` + MultiDiscTrackFormat string `json:"multiDiscTrackFormat"` + ArtistFolderFormat string `json:"artistFolderFormat"` +} + +// GetNaming returns the file naming rules. +func (l *Lidarr) GetNaming() (*Naming, error) { + return l.GetNamingContext(context.Background()) +} + +// GetNamingContext returns the file naming rules. +func (l *Lidarr) GetNamingContext(ctx context.Context) (*Naming, error) { + var output Naming + + req := starr.Request{URI: bpNaming} + if err := l.GetInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Get(%s): %w", &req, err) + } + + return &output, nil +} + +// UpdateNaming updates the file naming rules. +func (l *Lidarr) UpdateNaming(naming *Naming) (*Naming, error) { + return l.UpdateNamingContext(context.Background(), naming) +} + +// UpdateNamingContext updates the file naming rules. +func (l *Lidarr) UpdateNamingContext(ctx context.Context, naming *Naming) (*Naming, error) { + var output Naming + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(naming); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpNaming, err) + } + + req := starr.Request{URI: bpNaming, Body: &body} + if err := l.PutInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Put(%s): %w", &req, err) + } + + return &output, nil +} From 094d58d6eb5fd74c91d31b73f754b5d46909a895 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Thu, 8 Feb 2024 08:57:36 -0800 Subject: [PATCH 2/3] update quality defs too --- lidarr/customformat.go | 6 +-- lidarr/qualitydefinition.go | 93 ++++++++++++++++++++++++++++++++----- radarr/qualitydefinition.go | 2 +- shared.go | 6 +++ 4 files changed, 92 insertions(+), 15 deletions(-) diff --git a/lidarr/customformat.go b/lidarr/customformat.go index fbb3eda..96101f9 100644 --- a/lidarr/customformat.go +++ b/lidarr/customformat.go @@ -75,7 +75,7 @@ func (l *Lidarr) GetCustomFormat(customformatID int64) (*CustomFormatOutput, err func (l *Lidarr) GetCustomFormatContext(ctx context.Context, customformatID int64) (*CustomFormatOutput, error) { var output CustomFormatOutput - req := starr.Request{URI: path.Join(bpCustomFormat, fmt.Sprint(customformatID))} + req := starr.Request{URI: path.Join(bpCustomFormat, starr.Itoa(customformatID))} if err := l.GetInto(ctx, req, &output); err != nil { return nil, fmt.Errorf("api.Get(%s): %w", &req, err) } @@ -121,7 +121,7 @@ func (l *Lidarr) UpdateCustomFormatContext(ctx context.Context, return nil, fmt.Errorf("json.Marshal(%s): %w", bpCustomFormat, err) } - req := starr.Request{URI: path.Join(bpCustomFormat, fmt.Sprint(format.ID)), Body: &body} + req := starr.Request{URI: path.Join(bpCustomFormat, starr.Itoa(format.ID)), Body: &body} if err := l.PutInto(ctx, req, &output); err != nil { return nil, fmt.Errorf("api.Put(%s): %w", &req, err) } @@ -136,7 +136,7 @@ func (l *Lidarr) DeleteCustomFormat(cfID int64) error { // DeleteCustomFormatContext deletes a custom format. func (l *Lidarr) DeleteCustomFormatContext(ctx context.Context, cfID int64) error { - req := starr.Request{URI: path.Join(bpCustomFormat, fmt.Sprint(cfID))} + req := starr.Request{URI: path.Join(bpCustomFormat, starr.Itoa(cfID))} if err := l.DeleteAny(ctx, req); err != nil { return fmt.Errorf("api.Delete(%s): %w", &req, err) } diff --git a/lidarr/qualitydefinition.go b/lidarr/qualitydefinition.go index c8b2b99..5b14d32 100644 --- a/lidarr/qualitydefinition.go +++ b/lidarr/qualitydefinition.go @@ -1,8 +1,11 @@ package lidarr import ( + "bytes" "context" + "encoding/json" "fmt" + "path" "golift.io/starr" ) @@ -11,21 +14,22 @@ const bpQualityDefinition = APIver + "/qualitydefinition" // QualityDefinition is the /api/v1/qualitydefinition endpoint. type QualityDefinition struct { - ID int64 `json:"id"` - Quality *starr.Value `json:"quality"` - Title string `json:"title"` - Weight int64 `json:"weight"` - MinSize float64 `json:"minSize"` - MaxSize float64 `json:"maxSize,omitempty"` + ID int64 `json:"id"` + Quality *starr.Value `json:"quality"` + Title string `json:"title"` + Weight int64 `json:"weight"` + MinSize float64 `json:"minSize"` + MaxSize float64 `json:"maxSize,omitempty"` + PreferredSize float64 `json:"preferredSize"` } -// GetQualityDefinition returns the Quality Definitions. -func (l *Lidarr) GetQualityDefinition() ([]*QualityDefinition, error) { - return l.GetQualityDefinitionContext(context.Background()) +// GetQualityDefinitions returns all configured quality definitions. +func (l *Lidarr) GetQualityDefinitions() ([]*QualityDefinition, error) { + return l.GetQualityDefinitionsContext(context.Background()) } -// GetQualityDefinitionContext returns the Quality Definitions. -func (l *Lidarr) GetQualityDefinitionContext(ctx context.Context) ([]*QualityDefinition, error) { +// GetQualityDefinitionsContext returns all configured quality definitions. +func (l *Lidarr) GetQualityDefinitionsContext(ctx context.Context) ([]*QualityDefinition, error) { var output []*QualityDefinition req := starr.Request{URI: bpQualityDefinition} @@ -35,3 +39,70 @@ func (l *Lidarr) GetQualityDefinitionContext(ctx context.Context) ([]*QualityDef return output, nil } + +// GetQualityDefinition returns a single quality definition. +func (l *Lidarr) GetQualityDefinition(qualityDefinitionID int64) (*QualityDefinition, error) { + return l.GetQualityDefinitionContext(context.Background(), qualityDefinitionID) +} + +// GetQualityDefinitionContext returns a single quality definition. +func (l *Lidarr) GetQualityDefinitionContext(ctx context.Context, qdID int64) (*QualityDefinition, error) { + var output QualityDefinition + + req := starr.Request{URI: path.Join(bpQualityDefinition, starr.Itoa(qdID))} + if err := l.GetInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Get(%s): %w", &req, err) + } + + return &output, nil +} + +// UpdateQualityDefinition updates a quality definition. +func (l *Lidarr) UpdateQualityDefinition(definition *QualityDefinition) (*QualityDefinition, error) { + return l.UpdateQualityDefinitionContext(context.Background(), definition) +} + +// UpdateQualityDefinitionContext updates a quality definition. +func (l *Lidarr) UpdateQualityDefinitionContext( + ctx context.Context, + definition *QualityDefinition, +) (*QualityDefinition, error) { + var output QualityDefinition + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(definition); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpQualityDefinition, err) + } + + req := starr.Request{URI: path.Join(bpQualityDefinition, starr.Itoa(definition.ID)), Body: &body} + if err := l.PutInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Put(%s): %w", &req, err) + } + + return &output, nil +} + +// UpdateQualityDefinitions updates all quality definitions. +func (l *Lidarr) UpdateQualityDefinitions(definition []*QualityDefinition) ([]*QualityDefinition, error) { + return l.UpdateQualityDefinitionsContext(context.Background(), definition) +} + +// UpdateQualityDefinitionsContext updates all quality definitions. +func (l *Lidarr) UpdateQualityDefinitionsContext( + ctx context.Context, + definition []*QualityDefinition, +) ([]*QualityDefinition, error) { + var output []*QualityDefinition + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(definition); err != nil { + return nil, fmt.Errorf("json.Marshal(%s): %w", bpQualityDefinition, err) + } + + req := starr.Request{URI: path.Join(bpQualityDefinition, "update"), Body: &body} + if err := l.PutInto(ctx, req, &output); err != nil { + return nil, fmt.Errorf("api.Put(%s): %w", &req, err) + } + + return output, nil +} diff --git a/radarr/qualitydefinition.go b/radarr/qualitydefinition.go index 9f2c434..9339fb4 100644 --- a/radarr/qualitydefinition.go +++ b/radarr/qualitydefinition.go @@ -24,7 +24,7 @@ type QualityDefinition struct { // Define Base Path for Quality Definition calls. const bpQualityDefinition = APIver + "/qualityDefinition" -// GetQualityDefinitions returns all configured quality definitions. +// GetQualityDefinitions returns all configured qualityQ definitions. func (r *Radarr) GetQualityDefinitions() ([]*QualityDefinition, error) { return r.GetQualityDefinitionsContext(context.Background()) } diff --git a/shared.go b/shared.go index 3fd432e..cbfb640 100644 --- a/shared.go +++ b/shared.go @@ -308,3 +308,9 @@ type TimeSpan struct { TotalMinutes int64 `json:"totalMinutes"` TotalSeconds int64 `json:"totalSeconds"` } + +// Itoa converts an int64 to a string. +func Itoa(val int64) string { + const base10 = 10 + return strconv.FormatInt(val, base10) +} From 6b23e4f4c8ba92e7a21dfdff4f5dd1d7e61fbd22 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Thu, 8 Feb 2024 09:06:03 -0800 Subject: [PATCH 3/3] fix typo --- radarr/qualitydefinition.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radarr/qualitydefinition.go b/radarr/qualitydefinition.go index 9339fb4..9f2c434 100644 --- a/radarr/qualitydefinition.go +++ b/radarr/qualitydefinition.go @@ -24,7 +24,7 @@ type QualityDefinition struct { // Define Base Path for Quality Definition calls. const bpQualityDefinition = APIver + "/qualityDefinition" -// GetQualityDefinitions returns all configured qualityQ definitions. +// GetQualityDefinitions returns all configured quality definitions. func (r *Radarr) GetQualityDefinitions() ([]*QualityDefinition, error) { return r.GetQualityDefinitionsContext(context.Background()) }