Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lidarr sync. #665

Merged
merged 4 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ require (
golift.io/nzbget v0.1.5
golift.io/qbit v0.0.0-20240203190039-8f98b32d1e66
golift.io/rotatorr v0.0.0-20230911015553-cd2abbd726c7
golift.io/starr v1.0.1-0.20231103013021-4a3f9d0d4fb4
golift.io/starr v1.0.1-0.20240208165736-094d58d6eb5f
golift.io/version v0.0.2
golift.io/xtractr v0.2.2
modernc.org/sqlite v1.28.0
Expand Down Expand Up @@ -104,7 +104,7 @@ require (
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/tools v0.17.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
Expand Down
13 changes: 4 additions & 9 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down Expand Up @@ -342,8 +340,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -387,7 +385,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
Expand Down Expand Up @@ -449,8 +446,6 @@ golift.io/datacounter v1.0.4 h1:b2gLQCs8WYRtKjOMG0qM82rF1o0+oKUXB9QH7kjmxZ4=
golift.io/datacounter v1.0.4/go.mod h1:79Yf1ucynYvZzVS/hpfrAFt6y/w82FMlOJgh+MBaZvs=
golift.io/deluge v0.10.1 h1:wu1GzXsDYzWGnRl4mNEd2IeY0O7+jhYJ4IKBPfDEanM=
golift.io/deluge v0.10.1/go.mod h1:i6h0V+nRzG4XymHQ5kC4d4Z6JZw2M83gMqcZhWgiD1k=
golift.io/mulery v0.0.6-0.20240206194956-b0ec70c7ca65 h1:twHM68+hx9AIfMIYMokBNVKEOhM9gdRUPuq/v7Rb+3g=
golift.io/mulery v0.0.6-0.20240206194956-b0ec70c7ca65/go.mod h1:xIzxZSHGqWtqPsOd9SDhffsSne1sAHQy+njKRehO5qE=
golift.io/mulery v0.0.6-0.20240206225728-baab5ff49ba4 h1:dkrhAMbY8er/B05utjNH3mO6JzvTd+YlDXZ9nUHaj4U=
golift.io/mulery v0.0.6-0.20240206225728-baab5ff49ba4/go.mod h1:xIzxZSHGqWtqPsOd9SDhffsSne1sAHQy+njKRehO5qE=
golift.io/mulery v0.0.6-0.20240209224629-534e48717ccd h1:vTXqt5CYVUuUIbYRC6R2k3YqZHzfJPqZdOoU2zMOc+U=
Expand All @@ -461,8 +456,8 @@ golift.io/qbit v0.0.0-20240203190039-8f98b32d1e66 h1:ipkWG/7cwFrExfQuSuiFAmQjq+j
golift.io/qbit v0.0.0-20240203190039-8f98b32d1e66/go.mod h1:xdt9wiIDIfIyj6jM93JjAd9Lp5TN9G350E4il0uJyEM=
golift.io/rotatorr v0.0.0-20230911015553-cd2abbd726c7 h1:8reg8mRdLxCz168FaGPf/kVxmDRDc92/Dhub54trdOc=
golift.io/rotatorr v0.0.0-20230911015553-cd2abbd726c7/go.mod h1:59bC4ue06MetIY4iiHu3PCqVbzW0leGoCONZhH8dPZ8=
golift.io/starr v1.0.1-0.20231103013021-4a3f9d0d4fb4 h1:IzZwvMMbHIwJ+JRZ5m9y1J+xAGccYr4fBd7IVYYpTx8=
golift.io/starr v1.0.1-0.20231103013021-4a3f9d0d4fb4/go.mod h1:IlyGyRgyXg7o1zZ5KU7ReboL8JvHVbAY/Ri4hoxI3ZY=
golift.io/starr v1.0.1-0.20240208165736-094d58d6eb5f h1:GaQqE60s2xIWSKBqkiBhf/w2WlD42zTjv1hJuTXO9IY=
golift.io/starr v1.0.1-0.20240208165736-094d58d6eb5f/go.mod h1:RniEbCXsqNH8j9pyKC1BepJ2Fx71Khk225MRuszVJgc=
golift.io/version v0.0.2 h1:i0gXRuSDHKs4O0sVDUg4+vNIuOxYoXhaxspftu2FRTE=
golift.io/version v0.0.2/go.mod h1:76aHNz8/Pm7CbuxIsDi97jABL5Zui3f2uZxDm4vB6hU=
golift.io/xtractr v0.2.2 h1:MvujxeuX629d1rQs2VJbbcvYMvMmN5SzIkEflU5ryOc=
Expand Down
232 changes: 226 additions & 6 deletions pkg/apps/lidarr.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@ func (a *Apps) lidarrHandlers() {
a.HandleAPIpath(starr.Lidarr, "/check/{mbid:[-a-z0-9]+}", lidarrCheckAlbum, "GET")
a.HandleAPIpath(starr.Lidarr, "/get/{albumid:[0-9]+}", lidarrGetAlbum, "GET")
a.HandleAPIpath(starr.Lidarr, "/metadataProfiles", lidarrMetadata, "GET")
a.HandleAPIpath(starr.Lidarr, "/qualityDefinitions", lidarrQualityDefs, "GET")
a.HandleAPIpath(starr.Lidarr, "/naming", lidarrGetNaming, "GET")
a.HandleAPIpath(starr.Lidarr, "/naming", lidarrUpdateNaming, "PUT")
a.HandleAPIpath(starr.Lidarr, "/customformats", lidarrGetCustomFormats, "GET")
a.HandleAPIpath(starr.Lidarr, "/customformats", lidarrAddCustomFormat, "POST")
a.HandleAPIpath(starr.Lidarr, "/customformats", lidarrUpdateCustomFormat, "PUT")
a.HandleAPIpath(starr.Lidarr, "/customformats/{cfid:[0-9]+}", lidarrUpdateCustomFormat, "PUT")
a.HandleAPIpath(starr.Lidarr, "/customformats/{cfid:[0-9]+}", lidarrDeleteCustomFormat, "DELETE")
a.HandleAPIpath(starr.Lidarr, "/customformats/all", lidarrDeleteAllCustomFormats, "DELETE")
a.HandleAPIpath(starr.Lidarr, "/qualitydefinition", lidarrUpdateQualityDefinition, "PUT")
a.HandleAPIpath(starr.Lidarr, "/qualityDefinitions", lidarrGetQualityDefinitions, "GET")
a.HandleAPIpath(starr.Lidarr, "/qualityProfiles", lidarrQualityProfiles, "GET")
a.HandleAPIpath(starr.Lidarr, "/qualityProfile", lidarrGetQualityProfile, "GET")
a.HandleAPIpath(starr.Lidarr, "/qualityProfile", lidarrAddQualityProfile, "POST")
Expand Down Expand Up @@ -178,7 +187,7 @@ func lidarrData(album *lidarr.Album) map[string]interface{} {
// @Tags Lidarr
// @Produce json
// @Param instance path int64 true "instance ID"
// @Param mbid path int64 true "movie brains ID"
// @Param mbid path int64 true "music brains ID"
// @Success 200 {object} apps.Respond.apiResponse{message=string} "not found"
// @Failure 409 {object} apps.Respond.apiResponse{message=string} "already exists"
// @Failure 503 {object} apps.Respond.apiResponse{message=string} "instance error"
Expand Down Expand Up @@ -270,6 +279,217 @@ func lidarrMetadata(req *http.Request) (int, interface{}) {
return http.StatusOK, p
}

// @Description Returns Lidarr track naming conventions.
// @Summary Retrieve Lidarr Track Naming
// @Tags Lidarr
// @Produce json
// @Param instance path int64 true "instance ID"
// @Success 200 {object} apps.Respond.apiResponse{message=lidarr.Naming} "naming conventions"
// @Failure 500 {object} apps.Respond.apiResponse{message=string} "instance error"
// @Failure 404 {object} string "bad token or api key"
// @Router /api/lidarr/{instance}/naming [get]
// @Security ApiKeyAuth
func lidarrGetNaming(req *http.Request) (int, interface{}) {
naming, err := getLidarr(req).GetNamingContext(req.Context())
if err != nil {
return apiError(http.StatusInternalServerError, "getting naming", err)
}

return http.StatusOK, naming
}

// @Description Updates the Lidarr track naming conventions.
// @Summary Update Lidarr Track Naming
// @Tags Lidarr
// @Produce json
// @Accept json
// @Param PUT body lidarr.Naming true "naming conventions"
// @Success 200 {object} apps.Respond.apiResponse{message=int64} "naming ID"
// @Failure 400 {object} apps.Respond.apiResponse{message=string} "bad json input"
// @Failure 500 {object} apps.Respond.apiResponse{message=string} "instance error"
// @Failure 404 {object} string "bad token or api key"
// @Router /api/lidarr/{instance}/naming [put]
// @Security ApiKeyAuth
func lidarrUpdateNaming(req *http.Request) (int, interface{}) {
var naming lidarr.Naming

err := json.NewDecoder(req.Body).Decode(&naming)
if err != nil {
return apiError(http.StatusBadRequest, "decoding payload", err)
}

output, err := getLidarr(req).UpdateNamingContext(req.Context(), &naming)
if err != nil {
return apiError(http.StatusServiceUnavailable, "updating naming", err)
}

return http.StatusOK, output.ID
}

// @Description Creates a new Custom Format in Lidarr.
// @Summary Create Lidarr Custom Format
// @Tags Lidarr
// @Produce json
// @Accept json
// @Param instance path int64 true "instance ID"
// @Param POST body lidarr.CustomFormatInput true "New Custom Format content"
// @Success 200 {object} apps.Respond.apiResponse{message=lidarr.CustomFormatOutput} "custom format"
// @Failure 400 {object} apps.Respond.apiResponse{message=string} "invalid json provided"
// @Failure 500 {object} apps.Respond.apiResponse{message=string} "instance error"
// @Failure 404 {object} string "bad token or api key"
// @Router /api/lidarr/{instance}/customformats [post]
// @Security ApiKeyAuth
func lidarrAddCustomFormat(req *http.Request) (int, interface{}) {
var cusform lidarr.CustomFormatInput

err := json.NewDecoder(req.Body).Decode(&cusform)
if err != nil {
return apiError(http.StatusBadRequest, "decoding payload", err)
}

resp, err := getLidarr(req).AddCustomFormatContext(req.Context(), &cusform)
if err != nil {
return apiError(http.StatusInternalServerError, "adding custom format", err)
}

return http.StatusOK, resp
}

// @Description Returns all Custom Formats Data from Lidarr.
// @Summary Get Lidarr Custom Formats Data
// @Tags Lidarr
// @Produce json
// @Param instance path int64 true "instance ID"
// @Success 200 {object} apps.Respond.apiResponse{message=[]lidarr.CustomFormatOutput} "custom formats"
// @Failure 500 {object} apps.Respond.apiResponse{message=string} "instance error"
// @Failure 404 {object} string "bad token or api key"
// @Router /api/lidarr/{instance}/customformats [get]
// @Security ApiKeyAuth
func lidarrGetCustomFormats(req *http.Request) (int, interface{}) {
cusform, err := getLidarr(req).GetCustomFormatsContext(req.Context())
if err != nil {
return apiError(http.StatusInternalServerError, "getting custom formats", err)
}

return http.StatusOK, cusform
}

// @Description Updates a Custom Format in Lidarr.
// @Summary Update Lidarr Custom Format
// @Tags Lidarr
// @Produce json
// @Accept json
// @Param instance path int64 true "instance ID"
// @Param PUT body lidarr.CustomFormatInput true "Updated Custom Format content"
// @Success 200 {object} apps.Respond.apiResponse{message=lidarr.CustomFormatOutput} "custom format"
// @Failure 400 {object} apps.Respond.apiResponse{message=string} "invalid json provided"
// @Failure 500 {object} apps.Respond.apiResponse{message=string} "instance error"
// @Failure 404 {object} string "bad token or api key"
// @Router /api/lidarr/{instance}/customformats/{formatID} [put]
// @Security ApiKeyAuth
func lidarrUpdateCustomFormat(req *http.Request) (int, interface{}) {
var cusform lidarr.CustomFormatInput
if err := json.NewDecoder(req.Body).Decode(&cusform); err != nil {
return apiError(http.StatusBadRequest, "decoding payload", err)
}

output, err := getLidarr(req).UpdateCustomFormatContext(req.Context(), &cusform)
if err != nil {
return apiError(http.StatusInternalServerError, "updating custom format", err)
}

return http.StatusOK, output
}

// @Description Delete a Custom Format from Lidarr.
// @Summary Delete Lidarr Custom Format
// @Tags Lidarr
// @Produce json
// @Param instance path int64 true "instance ID"
// @Param formatID path int64 true "Custom Format ID"
// @Success 200 {object} apps.Respond.apiResponse{message=string} "ok"
// @Failure 500 {object} apps.Respond.apiResponse{message=string} "instance error"
// @Failure 404 {object} string "bad token or api key"
// @Router /api/lidarr/{instance}/customformats/{formatID} [delete]
// @Security ApiKeyAuth
func lidarrDeleteCustomFormat(req *http.Request) (int, interface{}) {
cfID, _ := strconv.ParseInt(mux.Vars(req)["cfid"], mnd.Base10, mnd.Bits64)

err := getLidarr(req).DeleteCustomFormatContext(req.Context(), cfID)
if err != nil {
return apiError(http.StatusInternalServerError, "deleting custom format", err)
}

return http.StatusOK, "OK"
}

// @Description Delete all Custom Formats from Lidarr.
// @Summary Delete all Lidarr Custom Formats
// @Tags Lidarr
// @Produce json
// @Param instance path int64 true "instance ID"
// @Success 200 {object} apps.Respond.apiResponse{message=apps.deleteResponse} "item delete counters"
// @Failure 500 {object} apps.Respond.apiResponse{message=string} "instance error"
// @Failure 404 {object} string "bad token or api key"
// @Router /api/lidarr/{instance}/customformats/all [delete]
// @Security ApiKeyAuth
func lidarrDeleteAllCustomFormats(req *http.Request) (int, interface{}) {
formats, err := getLidarr(req).GetCustomFormatsContext(req.Context())
if err != nil {
return apiError(http.StatusInternalServerError, "getting custom formats", err)
}

var (
deleted int
errs []string
)

for _, format := range formats {
err := getLidarr(req).DeleteCustomFormatContext(req.Context(), format.ID)
if err != nil {
errs = append(errs, err.Error())
continue
}

deleted++
}

return http.StatusOK, &deleteResponse{
Found: len(formats),
Deleted: deleted,
Errors: errs,
}
}

// @Description Updates all Quality Definitions in Lidarr.
// @Summary Update Lidarr Quality Definitions
// @Tags Lidarr
// @Produce json
// @Accept json
// @Param instance path int64 true "instance ID"
// @Param PUT body []lidarr.QualityDefinition true "Updated quality definitions"
// @Success 200 {object} apps.Respond.apiResponse{message=[]lidarr.QualityDefinition} "quality definitions return"
// @Failure 400 {object} apps.Respond.apiResponse{message=string} "invalid json provided"
// @Failure 500 {object} apps.Respond.apiResponse{message=string} "instance error"
// @Failure 404 {object} string "bad token or api key"
// @Router /api/lidarr/{instance}/qualitydefinition [put]
// @Security ApiKeyAuth
//
//nolint:lll
func lidarrUpdateQualityDefinition(req *http.Request) (int, interface{}) {
var input []*lidarr.QualityDefinition
if err := json.NewDecoder(req.Body).Decode(&input); err != nil {
return apiError(http.StatusBadRequest, "decoding payload", err)
}

output, err := getLidarr(req).UpdateQualityDefinitionsContext(req.Context(), input)
if err != nil {
return apiError(http.StatusInternalServerError, "updating quality definition", err)
}

return http.StatusOK, output
}

// @Description Fetches all Quality Definitions from Lidarr.
// @Summary Get Lidarr Quality Definitions
// @Tags Lidarr
Expand All @@ -280,9 +500,9 @@ func lidarrMetadata(req *http.Request) (int, interface{}) {
// @Failure 404 {object} string "bad token or api key"
// @Router /api/lidarr/{instance}/qualityDefinitions [get]
// @Security ApiKeyAuth
func lidarrQualityDefs(req *http.Request) (int, interface{}) {
func lidarrGetQualityDefinitions(req *http.Request) (int, interface{}) {
// Get the profiles from lidarr.
definitions, err := getLidarr(req).GetQualityDefinitionContext(req.Context())
definitions, err := getLidarr(req).GetQualityDefinitionsContext(req.Context())
if err != nil {
return apiError(http.StatusInternalServerError, "getting profiles", err)
}
Expand Down Expand Up @@ -364,7 +584,7 @@ func lidarrAddQualityProfile(req *http.Request) (int, interface{}) {
return apiError(http.StatusBadRequest, "decoding payload", err)
}

// Get the profiles from radarr.
// Get the profiles from lidarr.
id, err := getLidarr(req).AddQualityProfileContext(req.Context(), &profile)
if err != nil {
return apiError(http.StatusInternalServerError, "adding profile", err)
Expand Down Expand Up @@ -402,7 +622,7 @@ func lidarrUpdateQualityProfile(req *http.Request) (int, interface{}) {
return http.StatusUnprocessableEntity, ErrNonZeroID
}

// Get the profiles from radarr.
// Get the profiles from lidarr.
_, err = getLidarr(req).UpdateQualityProfileContext(req.Context(), &profile)
if err != nil {
return apiError(http.StatusInternalServerError, "updating profile", err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/apps/radarr.go
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,7 @@ func radarrGetQualityDefinitions(req *http.Request) (int, interface{}) {
// @Produce json
// @Accept json
// @Param instance path int64 true "instance ID"
// @Param PUT body []radarr.QualityDefinition true "Updated Import Listcontent"
// @Param PUT body []radarr.QualityDefinition true "Updated quality definitions"
// @Success 200 {object} apps.Respond.apiResponse{message=[]radarr.QualityDefinition} "quality definitions return"
// @Failure 400 {object} apps.Respond.apiResponse{message=string} "invalid json provided"
// @Failure 500 {object} apps.Respond.apiResponse{message=string} "instance error"
Expand Down
Loading
Loading