diff --git a/internal/action/lidarr.go b/internal/action/lidarr.go index c7ec076..d90c041 100644 --- a/internal/action/lidarr.go +++ b/internal/action/lidarr.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog/log" ) -func (s *service) lidarr(release domain.Release, action domain.Action) error { +func (s *service) lidarr(release domain.Release, action domain.Action) ([]string, error) { log.Trace().Msg("action LIDARR") // TODO validate data @@ -18,13 +18,13 @@ func (s *service) lidarr(release domain.Release, action domain.Action) error { // get client for action client, err := s.clientSvc.FindByID(context.TODO(), action.ClientID) if err != nil { - log.Error().Err(err).Msgf("error finding client: %v", action.ClientID) - return err + log.Error().Err(err).Msgf("lidarr: error finding client: %v", action.ClientID) + return nil, err } // return early if no client found if client == nil { - return err + return nil, err } // initial config @@ -52,23 +52,19 @@ func (s *service) lidarr(release domain.Release, action domain.Action) error { PublishDate: time.Now().Format(time.RFC3339), } - success, rejections, err := arr.Push(r) + rejections, err := arr.Push(r) if err != nil { log.Error().Stack().Err(err).Msgf("lidarr: failed to push release: %v", r) - return err + return nil, err } - if !success { + if rejections != nil { log.Debug().Msgf("lidarr: release push rejected: %v, indexer %v to %v reasons: '%v'", r.Title, r.Indexer, client.Host, rejections) - // save pushed release - s.bus.Publish("release:update-push-status-rejected", release.ID, rejections) - return nil + return rejections, nil } log.Debug().Msgf("lidarr: successfully pushed release: %v, indexer %v to %v", r.Title, r.Indexer, client.Host) - s.bus.Publish("release:update-push-status", release.ID, domain.ReleasePushStatusApproved) - - return nil + return nil, nil } diff --git a/internal/action/qbittorrent.go b/internal/action/qbittorrent.go index 87c0565..03aa2ac 100644 --- a/internal/action/qbittorrent.go +++ b/internal/action/qbittorrent.go @@ -10,8 +10,8 @@ import ( "github.com/rs/zerolog/log" ) -const REANNOUNCE_MAX_ATTEMPTS = 30 -const REANNOUNCE_INTERVAL = 7000 +const ReannounceMaxAttempts = 30 +const ReannounceInterval = 7000 func (s *service) qbittorrent(qbt *qbittorrent.Client, action domain.Action, hash string, torrentFile string) error { log.Debug().Msgf("action qBittorrent: %v", action.Name) @@ -134,7 +134,7 @@ func checkTrackerStatus(qb qbittorrent.Client, hash string) error { // initial sleep to give tracker a head start time.Sleep(2 * time.Second) - for attempts < REANNOUNCE_MAX_ATTEMPTS { + for attempts < ReannounceMaxAttempts { log.Debug().Msgf("qBittorrent - run re-announce %v attempt: %v", hash, attempts) trackers, err := qb.GetTorrentTrackers(hash) @@ -157,7 +157,7 @@ func checkTrackerStatus(qb qbittorrent.Client, hash string) error { attempts++ // add delay for next run - time.Sleep(REANNOUNCE_INTERVAL * time.Millisecond) + time.Sleep(ReannounceInterval * time.Millisecond) continue } else { diff --git a/internal/action/radarr.go b/internal/action/radarr.go index 43e0aae..7f3340d 100644 --- a/internal/action/radarr.go +++ b/internal/action/radarr.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog/log" ) -func (s *service) radarr(release domain.Release, action domain.Action) error { +func (s *service) radarr(release domain.Release, action domain.Action) ([]string, error) { log.Trace().Msg("action RADARR") // TODO validate data @@ -18,13 +18,13 @@ func (s *service) radarr(release domain.Release, action domain.Action) error { // get client for action client, err := s.clientSvc.FindByID(context.TODO(), action.ClientID) if err != nil { - log.Error().Err(err).Msgf("error finding client: %v", action.ClientID) - return err + log.Error().Err(err).Msgf("radarr: error finding client: %v", action.ClientID) + return nil, err } // return early if no client found if client == nil { - return err + return nil, err } // initial config @@ -52,23 +52,19 @@ func (s *service) radarr(release domain.Release, action domain.Action) error { PublishDate: time.Now().Format(time.RFC3339), } - success, rejections, err := arr.Push(r) + rejections, err := arr.Push(r) if err != nil { log.Error().Stack().Err(err).Msgf("radarr: failed to push release: %v", r) - return err + return nil, err } - if !success { + if rejections != nil { log.Debug().Msgf("radarr: release push rejected: %v, indexer %v to %v reasons: '%v'", r.Title, r.Indexer, client.Host, rejections) - // save pushed release - s.bus.Publish("release:update-push-status-rejected", release.ID, rejections) - return nil + return rejections, nil } log.Debug().Msgf("radarr: successfully pushed release: %v, indexer %v to %v", r.Title, r.Indexer, client.Host) - s.bus.Publish("release:update-push-status", release.ID, domain.ReleasePushStatusApproved) - - return nil + return nil, nil } diff --git a/internal/action/run.go b/internal/action/run.go index bb7d2e4..57caadd 100644 --- a/internal/action/run.go +++ b/internal/action/run.go @@ -13,169 +13,30 @@ import ( func (s *service) RunActions(actions []domain.Action, release domain.Release) error { - var err error - var tmpFile string - var hash string - for _, action := range actions { + // only run active actions if !action.Enabled { - // only run active actions continue } log.Debug().Msgf("process action: %v for '%v'", action.Name, release.TorrentName) - actionStatus := domain.ReleaseActionStatus{ - ReleaseID: release.ID, - Status: domain.ReleasePushStatusPending, - Action: action.Name, - Type: action.Type, - Rejections: []string{}, - Timestamp: time.Now(), - } - - s.bus.Publish("release:store-action-status", &actionStatus) - - switch action.Type { - case domain.ActionTypeTest: - s.test(action.Name) - - case domain.ActionTypeExec: - if release.TorrentTmpFile == "" { - t, err := release.DownloadTorrentFile(nil) - if err != nil { - log.Error().Stack().Err(err) - return err - } - - tmpFile = t.TmpFileName - } - - go func(release domain.Release, action domain.Action, tmpFile string) { - s.execCmd(release, action, tmpFile) - }(release, action, tmpFile) - - case domain.ActionTypeWatchFolder: - if release.TorrentTmpFile == "" { - t, err := release.DownloadTorrentFile(nil) - if err != nil { - log.Error().Stack().Err(err) - return err - } - - tmpFile = t.TmpFileName - } - s.watchFolder(action.WatchFolder, tmpFile) - - case domain.ActionTypeDelugeV1, domain.ActionTypeDelugeV2: - canDownload, err := s.delugeCheckRulesCanDownload(action) + go func(release domain.Release, action domain.Action) { + err := s.runAction(action, release) if err != nil { - log.Error().Stack().Err(err).Msgf("error checking client rules: %v", action.Name) - continue - } - if !canDownload { + log.Err(err).Stack().Msgf("process action failed: %v for '%v'", action.Name, release.TorrentName) + s.bus.Publish("release:store-action-status", &domain.ReleaseActionStatus{ - ID: actionStatus.ID, ReleaseID: release.ID, - Status: domain.ReleasePushStatusRejected, + Status: domain.ReleasePushStatusErr, Action: action.Name, Type: action.Type, - Rejections: []string{"deluge busy"}, + Rejections: []string{err.Error()}, + Timestamp: time.Now(), }) - continue + return } - if release.TorrentTmpFile == "" { - t, err := release.DownloadTorrentFile(nil) - if err != nil { - log.Error().Stack().Err(err) - return err - } - - tmpFile = t.TmpFileName - } - - go func(action domain.Action, tmpFile string) { - err = s.deluge(action, tmpFile) - if err != nil { - log.Error().Stack().Err(err).Msg("error sending torrent to Deluge") - } - }(action, tmpFile) - - case domain.ActionTypeQbittorrent: - canDownload, client, err := s.qbittorrentCheckRulesCanDownload(action) - if err != nil { - log.Error().Stack().Err(err).Msgf("error checking client rules: %v", action.Name) - continue - } - if !canDownload { - s.bus.Publish("release:store-action-status", &domain.ReleaseActionStatus{ - ID: actionStatus.ID, - ReleaseID: release.ID, - Status: domain.ReleasePushStatusRejected, - Action: action.Name, - Type: action.Type, - Rejections: []string{"qbittorrent busy"}, - }) - continue - } - - if release.TorrentTmpFile == "" { - t, err := release.DownloadTorrentFile(nil) - if err != nil { - log.Error().Stack().Err(err) - return err - } - - tmpFile = t.TmpFileName - hash = t.MetaInfo.HashInfoBytes().String() - } - - go func(action domain.Action, hash string, tmpFile string) { - err = s.qbittorrent(client, action, hash, tmpFile) - if err != nil { - log.Error().Stack().Err(err).Msg("error sending torrent to qBittorrent") - } - }(action, hash, tmpFile) - - case domain.ActionTypeRadarr: - go func(release domain.Release, action domain.Action) { - err = s.radarr(release, action) - if err != nil { - log.Error().Stack().Err(err).Msg("error sending torrent to radarr") - //continue - } - }(release, action) - - case domain.ActionTypeSonarr: - go func(release domain.Release, action domain.Action) { - err = s.sonarr(release, action) - if err != nil { - log.Error().Stack().Err(err).Msg("error sending torrent to sonarr") - //continue - } - }(release, action) - - case domain.ActionTypeLidarr: - go func(release domain.Release, action domain.Action) { - err = s.lidarr(release, action) - if err != nil { - log.Error().Stack().Err(err).Msg("error sending torrent to lidarr") - //continue - } - }(release, action) - - default: - log.Warn().Msgf("unsupported action: %v type: %v", action.Name, action.Type) - } - - s.bus.Publish("release:store-action-status", &domain.ReleaseActionStatus{ - ID: actionStatus.ID, - ReleaseID: release.ID, - Status: domain.ReleasePushStatusApproved, - Action: action.Name, - Type: action.Type, - Rejections: []string{}, - }) + }(release, action) } // safe to delete tmp file @@ -183,6 +44,144 @@ func (s *service) RunActions(actions []domain.Action, release domain.Release) er return nil } +func (s *service) runAction(action domain.Action, release domain.Release) error { + + var err error + var tmpFile string + var hash string + var rejections []string + + switch action.Type { + case domain.ActionTypeTest: + s.test(action.Name) + + case domain.ActionTypeExec: + if release.TorrentTmpFile == "" { + t, err := release.DownloadTorrentFile(nil) + if err != nil { + log.Error().Stack().Err(err) + return err + } + + tmpFile = t.TmpFileName + } + + s.execCmd(release, action, tmpFile) + + case domain.ActionTypeWatchFolder: + if release.TorrentTmpFile == "" { + t, err := release.DownloadTorrentFile(nil) + if err != nil { + log.Error().Stack().Err(err) + return err + } + + tmpFile = t.TmpFileName + } + s.watchFolder(action.WatchFolder, tmpFile) + + case domain.ActionTypeDelugeV1, domain.ActionTypeDelugeV2: + canDownload, err := s.delugeCheckRulesCanDownload(action) + if err != nil { + log.Error().Stack().Err(err).Msgf("error checking client rules: %v", action.Name) + return err + } + if !canDownload { + rejections = []string{"deluge busy"} + } + + if release.TorrentTmpFile == "" { + t, err := release.DownloadTorrentFile(nil) + if err != nil { + log.Error().Stack().Err(err) + return err + } + + tmpFile = t.TmpFileName + } + err = s.deluge(action, tmpFile) + if err != nil { + log.Error().Stack().Err(err).Msg("error sending torrent to Deluge") + return err + } + + case domain.ActionTypeQbittorrent: + canDownload, client, err := s.qbittorrentCheckRulesCanDownload(action) + if err != nil { + log.Error().Stack().Err(err).Msgf("error checking client rules: %v", action.Name) + return err + } + if !canDownload { + rejections = []string{"qBittorrent busy"} + } + + if release.TorrentTmpFile == "" { + t, err := release.DownloadTorrentFile(nil) + if err != nil { + log.Error().Stack().Err(err) + return err + } + + tmpFile = t.TmpFileName + hash = t.MetaInfo.HashInfoBytes().String() + } + err = s.qbittorrent(client, action, hash, tmpFile) + if err != nil { + log.Error().Stack().Err(err).Msg("error sending torrent to qBittorrent") + return err + } + + case domain.ActionTypeRadarr: + rejections, err = s.radarr(release, action) + if err != nil { + log.Error().Stack().Err(err).Msg("error sending torrent to radarr") + return err + } + + case domain.ActionTypeSonarr: + rejections, err = s.sonarr(release, action) + if err != nil { + log.Error().Stack().Err(err).Msg("error sending torrent to sonarr") + return err + } + + case domain.ActionTypeLidarr: + rejections, err = s.lidarr(release, action) + if err != nil { + log.Error().Stack().Err(err).Msg("error sending torrent to lidarr") + return err + } + + default: + log.Warn().Msgf("unsupported action: %v type: %v", action.Name, action.Type) + return nil + } + + if rejections != nil { + s.bus.Publish("release:push-rejected", &domain.ReleaseActionStatus{ + ReleaseID: release.ID, + Status: domain.ReleasePushStatusRejected, + Action: action.Name, + Type: action.Type, + Rejections: rejections, + Timestamp: time.Now(), + }) + + return nil + } + + s.bus.Publish("release:push-approved", &domain.ReleaseActionStatus{ + ReleaseID: release.ID, + Status: domain.ReleasePushStatusApproved, + Action: action.Name, + Type: action.Type, + Rejections: []string{}, + Timestamp: time.Now(), + }) + + return nil +} + func (s *service) CheckCanDownload(actions []domain.Action) bool { for _, action := range actions { if !action.Enabled { diff --git a/internal/action/sonarr.go b/internal/action/sonarr.go index 40bab6f..bcd4566 100644 --- a/internal/action/sonarr.go +++ b/internal/action/sonarr.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog/log" ) -func (s *service) sonarr(release domain.Release, action domain.Action) error { +func (s *service) sonarr(release domain.Release, action domain.Action) ([]string, error) { log.Trace().Msg("action SONARR") // TODO validate data @@ -18,13 +18,13 @@ func (s *service) sonarr(release domain.Release, action domain.Action) error { // get client for action client, err := s.clientSvc.FindByID(context.TODO(), action.ClientID) if err != nil { - log.Error().Err(err).Msgf("error finding client: %v", action.ClientID) - return err + log.Error().Err(err).Msgf("sonarr: error finding client: %v", action.ClientID) + return nil, err } // return early if no client found if client == nil { - return err + return nil, err } // initial config @@ -52,23 +52,19 @@ func (s *service) sonarr(release domain.Release, action domain.Action) error { PublishDate: time.Now().Format(time.RFC3339), } - success, rejections, err := arr.Push(r) + rejections, err := arr.Push(r) if err != nil { log.Error().Stack().Err(err).Msgf("sonarr: failed to push release: %v", r) - return err + return nil, err } - if !success { + if rejections != nil { log.Debug().Msgf("sonarr: release push rejected: %v, indexer %v to %v reasons: '%v'", r.Title, r.Indexer, client.Host, rejections) - // save pushed release - s.bus.Publish("release:update-push-status-rejected", release.ID, rejections) - return nil + return rejections, nil } log.Debug().Msgf("sonarr: successfully pushed release: %v, indexer %v to %v", r.Title, r.Indexer, client.Host) - s.bus.Publish("release:update-push-status", release.ID, domain.ReleasePushStatusApproved) - - return nil + return nil, nil } diff --git a/internal/domain/release.go b/internal/domain/release.go index 0d6c233..8a1bfb0 100644 --- a/internal/domain/release.go +++ b/internal/domain/release.go @@ -1457,7 +1457,7 @@ type ReleasePushStatus string const ( ReleasePushStatusApproved ReleasePushStatus = "PUSH_APPROVED" ReleasePushStatusRejected ReleasePushStatus = "PUSH_REJECTED" - ReleasePushStatusMixed ReleasePushStatus = "MIXED" // For multiple actions, one might go and the other not + ReleasePushStatusErr ReleasePushStatus = "PUSH_ERROR" ReleasePushStatusPending ReleasePushStatus = "PENDING" // Initial status ) diff --git a/internal/events/subscribers.go b/internal/events/subscribers.go index 8843dad..010beef 100644 --- a/internal/events/subscribers.go +++ b/internal/events/subscribers.go @@ -25,6 +25,8 @@ func NewSubscribers(eventbus EventBus.Bus, releaseSvc release.Service) Subscribe func (s Subscriber) Register() { s.eventbus.Subscribe("release:store-action-status", s.releaseActionStatus) + s.eventbus.Subscribe("release:push-rejected", s.releasePushRejected) + s.eventbus.Subscribe("release:push-approved", s.releasePushApproved) } func (s Subscriber) releaseActionStatus(actionStatus *domain.ReleaseActionStatus) { @@ -35,3 +37,21 @@ func (s Subscriber) releaseActionStatus(actionStatus *domain.ReleaseActionStatus log.Error().Err(err).Msgf("events: 'release:store-action-status' error") } } + +func (s Subscriber) releasePushRejected(actionStatus *domain.ReleaseActionStatus) { + log.Trace().Msgf("events: 'release:push-rejected' '%+v'", actionStatus) + + err := s.releaseSvc.StoreReleaseActionStatus(context.Background(), actionStatus) + if err != nil { + log.Error().Err(err).Msgf("events: 'release:push-rejected' error") + } +} + +func (s Subscriber) releasePushApproved(actionStatus *domain.ReleaseActionStatus) { + log.Trace().Msgf("events: 'release:push-approved' '%+v'", actionStatus) + + err := s.releaseSvc.StoreReleaseActionStatus(context.Background(), actionStatus) + if err != nil { + log.Error().Err(err).Msgf("events: 'release:push-approved' error") + } +} diff --git a/pkg/lidarr/lidarr.go b/pkg/lidarr/lidarr.go index 5f35872..fbbcdc8 100644 --- a/pkg/lidarr/lidarr.go +++ b/pkg/lidarr/lidarr.go @@ -22,7 +22,7 @@ type Config struct { type Client interface { Test() (*SystemStatusResponse, error) - Push(release Release) (bool, string, error) + Push(release Release) ([]string, error) } type client struct { @@ -93,15 +93,15 @@ func (c *client) Test() (*SystemStatusResponse, error) { return &response, nil } -func (c *client) Push(release Release) (bool, string, error) { +func (c *client) Push(release Release) ([]string, error) { res, err := c.post("release/push", release) if err != nil { log.Error().Stack().Err(err).Msg("lidarr client post error") - return false, "", err + return nil, err } if res == nil { - return false, "", nil + return nil, err } defer res.Body.Close() @@ -109,14 +109,14 @@ func (c *client) Push(release Release) (bool, string, error) { body, err := io.ReadAll(res.Body) if err != nil { log.Error().Stack().Err(err).Msg("lidarr client error reading body") - return false, "", err + return nil, err } pushResponse := PushResponse{} err = json.Unmarshal(body, &pushResponse) if err != nil { log.Error().Stack().Err(err).Msg("lidarr client error json unmarshal") - return false, "", err + return nil, err } log.Trace().Msgf("lidarr release/push response body: %+v", string(body)) @@ -126,8 +126,8 @@ func (c *client) Push(release Release) (bool, string, error) { rejections := strings.Join(pushResponse.Rejections, ", ") log.Trace().Msgf("lidarr push rejected: %s - reasons: %q", release.Title, rejections) - return false, rejections, nil + return pushResponse.Rejections, nil } - return true, "", nil + return nil, nil } diff --git a/pkg/lidarr/lidarr_test.go b/pkg/lidarr/lidarr_test.go index bd8ea5c..7d691e4 100644 --- a/pkg/lidarr/lidarr_test.go +++ b/pkg/lidarr/lidarr_test.go @@ -46,11 +46,12 @@ func Test_client_Push(t *testing.T) { release Release } tests := []struct { - name string - fields fields - args args - err error - wantErr bool + name string + fields fields + args args + err error + rejections []string + wantErr bool }{ { name: "push", @@ -72,8 +73,7 @@ func Test_client_Push(t *testing.T) { Protocol: "torrent", PublishDate: "2021-08-21T15:36:00Z", }}, - err: errors.New("lidarr push rejected Unknown Artist"), - wantErr: true, + rejections: []string{"Unknown Artist"}, }, { name: "push_error", @@ -95,15 +95,15 @@ func Test_client_Push(t *testing.T) { Protocol: "torrent", PublishDate: "2021-08-21T15:36:00Z", }}, - err: errors.New("lidarr push rejected Unknown Artist"), - wantErr: true, + rejections: []string{"Unknown Artist"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := New(tt.fields.config) - _, _, err := c.Push(tt.args.release) + rejections, err := c.Push(tt.args.release) + assert.Equal(t, tt.rejections, rejections) if tt.wantErr && assert.Error(t, err) { assert.Equal(t, tt.err, err) } diff --git a/pkg/radarr/radarr.go b/pkg/radarr/radarr.go index f5c5850..9513c04 100644 --- a/pkg/radarr/radarr.go +++ b/pkg/radarr/radarr.go @@ -22,7 +22,7 @@ type Config struct { type Client interface { Test() (*SystemStatusResponse, error) - Push(release Release) (bool, string, error) + Push(release Release) ([]string, error) } type client struct { @@ -92,15 +92,15 @@ func (c *client) Test() (*SystemStatusResponse, error) { return &response, nil } -func (c *client) Push(release Release) (bool, string, error) { +func (c *client) Push(release Release) ([]string, error) { res, err := c.post("release/push", release) if err != nil { log.Error().Stack().Err(err).Msg("radarr client post error") - return false, "", err + return nil, err } if res == nil { - return false, "", nil + return nil, nil } defer res.Body.Close() @@ -108,14 +108,14 @@ func (c *client) Push(release Release) (bool, string, error) { body, err := io.ReadAll(res.Body) if err != nil { log.Error().Stack().Err(err).Msg("radarr client error reading body") - return false, "", err + return nil, err } pushResponse := make([]PushResponse, 0) err = json.Unmarshal(body, &pushResponse) if err != nil { log.Error().Stack().Err(err).Msg("radarr client error json unmarshal") - return false, "", err + return nil, err } log.Trace().Msgf("radarr release/push response body: %+v", string(body)) @@ -125,9 +125,9 @@ func (c *client) Push(release Release) (bool, string, error) { rejections := strings.Join(pushResponse[0].Rejections, ", ") log.Trace().Msgf("radarr push rejected: %s - reasons: %q", release.Title, rejections) - return false, rejections, nil + return pushResponse[0].Rejections, nil } // success true - return true, "", nil + return nil, nil } diff --git a/pkg/radarr/radarr_test.go b/pkg/radarr/radarr_test.go index da9723e..2e9a751 100644 --- a/pkg/radarr/radarr_test.go +++ b/pkg/radarr/radarr_test.go @@ -47,11 +47,12 @@ func Test_client_Push(t *testing.T) { release Release } tests := []struct { - name string - fields fields - args args - err error - wantErr bool + name string + fields fields + args args + rejections []string + err error + wantErr bool }{ { name: "push", @@ -73,8 +74,9 @@ func Test_client_Push(t *testing.T) { Protocol: "torrent", PublishDate: "2021-08-21T15:36:00Z", }}, - err: errors.New("radarr push rejected Could not find Some Old Movie"), - wantErr: true, + rejections: []string{"Could not find Some Old Movie"}, + //err: errors.New("radarr push rejected Could not find Some Old Movie"), + //wantErr: true, }, { name: "push_error", @@ -96,15 +98,17 @@ func Test_client_Push(t *testing.T) { Protocol: "torrent", PublishDate: "2021-08-21T15:36:00Z", }}, - err: errors.New("radarr push rejected Could not find Some Old Movie"), - wantErr: true, + rejections: []string{"Could not find Some Old Movie"}, + //err: errors.New("radarr push rejected Could not find Some Old Movie"), + //wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := New(tt.fields.config) - _, _, err := c.Push(tt.args.release) + rejections, err := c.Push(tt.args.release) + assert.Equal(t, tt.rejections, rejections) if tt.wantErr && assert.Error(t, err) { assert.Equal(t, tt.err, err) } diff --git a/pkg/sonarr/sonarr.go b/pkg/sonarr/sonarr.go index b62a742..64ef891 100644 --- a/pkg/sonarr/sonarr.go +++ b/pkg/sonarr/sonarr.go @@ -22,7 +22,7 @@ type Config struct { type Client interface { Test() (*SystemStatusResponse, error) - Push(release Release) (bool, string, error) + Push(release Release) ([]string, error) } type client struct { @@ -93,15 +93,15 @@ func (c *client) Test() (*SystemStatusResponse, error) { return &response, nil } -func (c *client) Push(release Release) (bool, string, error) { +func (c *client) Push(release Release) ([]string, error) { res, err := c.post("release/push", release) if err != nil { log.Error().Stack().Err(err).Msg("sonarr client post error") - return false, "", err + return nil, err } if res == nil { - return false, "", nil + return nil, nil } defer res.Body.Close() @@ -109,14 +109,14 @@ func (c *client) Push(release Release) (bool, string, error) { body, err := io.ReadAll(res.Body) if err != nil { log.Error().Stack().Err(err).Msg("sonarr client error reading body") - return false, "", err + return nil, err } pushResponse := make([]PushResponse, 0) err = json.Unmarshal(body, &pushResponse) if err != nil { log.Error().Stack().Err(err).Msg("sonarr client error json unmarshal") - return false, "", err + return nil, err } log.Trace().Msgf("sonarr release/push response body: %+v", string(body)) @@ -126,9 +126,9 @@ func (c *client) Push(release Release) (bool, string, error) { rejections := strings.Join(pushResponse[0].Rejections, ", ") log.Trace().Msgf("sonarr push rejected: %s - reasons: %q", release.Title, rejections) - return false, rejections, nil + return pushResponse[0].Rejections, nil } // successful push - return true, "", nil + return nil, nil } diff --git a/pkg/sonarr/sonarr_test.go b/pkg/sonarr/sonarr_test.go index f2f43aa..4dfbf86 100644 --- a/pkg/sonarr/sonarr_test.go +++ b/pkg/sonarr/sonarr_test.go @@ -47,11 +47,12 @@ func Test_client_Push(t *testing.T) { release Release } tests := []struct { - name string - fields fields - args args - err error - wantErr bool + name string + fields fields + args args + rejections []string + err error + wantErr bool }{ { name: "push", @@ -73,8 +74,9 @@ func Test_client_Push(t *testing.T) { Protocol: "torrent", PublishDate: "2021-08-21T15:36:00Z", }}, - err: errors.New("sonarr push rejected Unknown Series"), - wantErr: true, + rejections: []string{"Unknown Series"}, + //err: errors.New("sonarr push rejected Unknown Series"), + //wantErr: true, }, { name: "push_error", @@ -96,15 +98,17 @@ func Test_client_Push(t *testing.T) { Protocol: "torrent", PublishDate: "2021-08-21T15:36:00Z", }}, - err: errors.New("sonarr push rejected Unknown Series"), - wantErr: true, + rejections: []string{"Unknown Series"}, + //err: errors.New("sonarr push rejected Unknown Series"), + //wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := New(tt.fields.config) - _, _, err := c.Push(tt.args.release) + rejections, err := c.Push(tt.args.release) + assert.Equal(t, tt.rejections, rejections) if tt.wantErr && assert.Error(t, err) { assert.Equal(t, tt.err, err) } diff --git a/web/src/screens/Releases.tsx b/web/src/screens/Releases.tsx index fd8d404..36dab44 100644 --- a/web/src/screens/Releases.tsx +++ b/web/src/screens/Releases.tsx @@ -1,4 +1,4 @@ -import { ExclamationCircleIcon } from "@heroicons/react/outline" +import { BanIcon, ExclamationCircleIcon } from "@heroicons/react/outline" import ClockIcon from "@heroicons/react/outline/ClockIcon" import { ChevronDoubleLeftIcon, ChevronLeftIcon, ChevronRightIcon, ChevronDoubleRightIcon, CheckIcon } from "@heroicons/react/solid" import { formatDistanceToNowStrict } from "date-fns" @@ -7,7 +7,7 @@ import { useQuery } from "react-query" import { useTable, useSortBy, usePagination } from "react-table" import APIClient from "../api/APIClient" import { EmptyListState } from "../components/emptystates" -import { classNames } from "../utils" +import { classNames, simplifyDate } from "../utils" export function Releases() { return ( @@ -127,19 +127,22 @@ interface ReleaseStatusCellProps { export function ReleaseStatusCell({ value, column, row }: ReleaseStatusCellProps) { const statusMap: any = { - "PUSH_REJECTED": + "PUSH_ERROR": , - "PUSH_APPROVED": + "PUSH_REJECTED": + , + "PUSH_APPROVED": , - "PENDING": + "PENDING": , } return (
- {value.map((v, idx) =>
{statusMap[v.status]}
)} + {value.map((v, idx) =>
{statusMap[v.status]}
)}
) } diff --git a/web/src/types/Release.d.ts b/web/src/types/Release.d.ts index 118f88e..8071952 100644 --- a/web/src/types/Release.d.ts +++ b/web/src/types/Release.d.ts @@ -18,7 +18,7 @@ interface ReleaseActionStatus { action: string; type: string; rejections: string[]; - timestamp: Date + timestamp: string } interface ReleaseFindResponse { diff --git a/web/src/utils/index.ts b/web/src/utils/index.ts index 8534593..809c9ec 100644 --- a/web/src/utils/index.ts +++ b/web/src/utils/index.ts @@ -1,3 +1,5 @@ +import { formatDistanceToNowStrict, formatISO9075 } from "date-fns"; + // sleep for x ms export function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); @@ -56,4 +58,23 @@ export function classNames(...classes: string[]) { } // column widths for inputs etc -export type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; \ No newline at end of file +export type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; + +// simplify date +export function simplifyDate(date: string) { + if (date !== "0001-01-01T00:00:00Z") { + return formatISO9075(new Date(date)) + } + return "n/a" +} + +// if empty date show as n/a +export function IsEmptyDate(date: string) { + if (date !== "0001-01-01T00:00:00Z") { + return formatDistanceToNowStrict( + new Date(date), + { addSuffix: true } + ) + } + return "n/a" +} \ No newline at end of file