diff --git a/pkg/arr/content.go b/pkg/arr/content.go index 9f2e192..74c42ca 100644 --- a/pkg/arr/content.go +++ b/pkg/arr/content.go @@ -94,7 +94,8 @@ func (a *Arr) GetMedia(mediaId string) ([]Content, error) { files = append(files, ContentFile{ FileId: file.Id, Path: file.Path, - Id: eId, + Id: d.Id, + EpisodeId: eId, SeasonNumber: file.SeasonNumber, }) } @@ -179,7 +180,7 @@ func (a *Arr) searchSonarr(files []ContentFile) error { errs <- fmt.Errorf("failed to automatic search: %v", err) return } - if statusOk := strconv.Itoa(resp.StatusCode)[0] == '2'; !statusOk { + if resp.StatusCode >= 300 || resp.StatusCode < 200 { errs <- fmt.Errorf("failed to automatic search. Status Code: %s", resp.Status) return } diff --git a/pkg/arr/types.go b/pkg/arr/types.go index a951f3c..1b01f51 100644 --- a/pkg/arr/types.go +++ b/pkg/arr/types.go @@ -17,6 +17,7 @@ type ContentFile struct { Name string `json:"name"` Path string `json:"path"` Id int `json:"id"` + EpisodeId int `json:"showId"` FileId int `json:"fileId"` TargetPath string `json:"targetPath"` IsSymlink bool `json:"isSymlink"` diff --git a/pkg/qbit/http.go b/pkg/qbit/http.go index bd57c36..d40769a 100644 --- a/pkg/qbit/http.go +++ b/pkg/qbit/http.go @@ -152,7 +152,7 @@ func (q *QBit) handleTorrentsInfo(w http.ResponseWriter, r *http.Request) { category := ctx.Value("category").(string) filter := strings.Trim(r.URL.Query().Get("filter"), "") hashes, _ := ctx.Value("hashes").([]string) - torrents := q.Storage.GetAll(category, filter, hashes) + torrents := q.Storage.GetAllSorted(category, filter, hashes, "added_on", false) request.JSONResponse(w, torrents, http.StatusOK) } diff --git a/pkg/repair/repair.go b/pkg/repair/repair.go index dfe7bd0..725a076 100644 --- a/pkg/repair/repair.go +++ b/pkg/repair/repair.go @@ -74,7 +74,7 @@ const ( type Job struct { ID string `json:"id"` - Arrs []*arr.Arr `json:"arrs"` + Arrs []string `json:"arrs"` MediaIDs []string `json:"media_ids"` StartedAt time.Time `json:"created_at"` BrokenItems map[string][]arr.ContentFile `json:"broken_items"` @@ -96,44 +96,34 @@ func (j *Job) discordContext() string { **Started At**: %s **Completed At**: %s ` - arrs := make([]string, 0) - for _, a := range j.Arrs { - arrs = append(arrs, a.Name) - } dateFmt := "2006-01-02 15:04:05" - return fmt.Sprintf(format, j.ID, strings.Join(arrs, ","), strings.Join(j.MediaIDs, ", "), j.Status, j.StartedAt.Format(dateFmt), j.CompletedAt.Format(dateFmt)) + return fmt.Sprintf(format, j.ID, strings.Join(j.Arrs, ","), strings.Join(j.MediaIDs, ", "), j.Status, j.StartedAt.Format(dateFmt), j.CompletedAt.Format(dateFmt)) } -func (r *Repair) getArrs(arrNames []string) []*arr.Arr { - checkSkip := true // This is useful when user triggers repair with specific arrs - arrs := make([]*arr.Arr, 0) +func (r *Repair) getArrs(arrNames []string) []string { + arrs := make([]string, 0) if len(arrNames) == 0 { // No specific arrs, get all // Also check if any arrs are set to skip repair - arrs = r.arrs.GetAll() + _arrs := r.arrs.GetAll() + for _, a := range _arrs { + if a.SkipRepair { + continue + } + arrs = append(arrs, a.Name) + } } else { - checkSkip = false for _, name := range arrNames { a := r.arrs.Get(name) if a == nil || a.Host == "" || a.Token == "" { continue } - arrs = append(arrs, a) + arrs = append(arrs, a.Name) } } - if !checkSkip { - return arrs - } - filtered := make([]*arr.Arr, 0) - for _, a := range arrs { - if a.SkipRepair { - continue - } - filtered = append(filtered, a) - } - return filtered + return arrs } func jobKey(arrNames []string, mediaIDs []string) string { @@ -221,7 +211,7 @@ func (r *Repair) repair(job *Job) error { if len(job.MediaIDs) == 0 { items, err = r.repairArr(job, a, "") if err != nil { - r.logger.Error().Err(err).Msgf("Error repairing %s", a.Name) + r.logger.Error().Err(err).Msgf("Error repairing %s", a) return err } } else { @@ -235,7 +225,7 @@ func (r *Repair) repair(job *Job) error { someItems, err := r.repairArr(job, a, id) if err != nil { - r.logger.Error().Err(err).Msgf("Error repairing %s with ID %s", a.Name, id) + r.logger.Error().Err(err).Msgf("Error repairing %s with ID %s", a, id) return err } items = append(items, someItems...) @@ -245,7 +235,7 @@ func (r *Repair) repair(job *Job) error { // Safely append the found items to the shared slice if len(items) > 0 { mu.Lock() - brokenItems[a.Name] = items + brokenItems[a] = items mu.Unlock() } @@ -341,8 +331,9 @@ func (r *Repair) Start(ctx context.Context) error { } } -func (r *Repair) repairArr(j *Job, a *arr.Arr, tmdbId string) ([]arr.ContentFile, error) { +func (r *Repair) repairArr(j *Job, _arr string, tmdbId string) ([]arr.ContentFile, error) { brokenItems := make([]arr.ContentFile, 0) + a := r.arrs.Get(_arr) r.logger.Info().Msgf("Starting repair for %s", a.Name) media, err := a.GetMedia(tmdbId) @@ -665,7 +656,9 @@ func (r *Repair) loadFromFile() { jobs := make(map[string]*Job) err = json.Unmarshal(data, &jobs) if err != nil { - r.logger.Debug().Err(err).Msg("Failed to unmarshal jobs") + r.logger.Trace().Err(err).Msg("Failed to unmarshal jobs; resetting") + r.Jobs = make(map[string]*Job) + return } r.Jobs = jobs } diff --git a/pkg/web/web/repair.html b/pkg/web/web/repair.html index e8bf78f..f99a45d 100644 --- a/pkg/web/web/repair.html +++ b/pkg/web/web/repair.html @@ -286,7 +286,7 @@ ${job.id.substring(0, 8)} - ${job.arrs.map(a => a.name).join(', ')} + ${job.arrs.join(', ')} ${formattedDate} ${status} ${totalItems} @@ -447,7 +447,10 @@ async function processJob(jobId) { try { const response = await fetch(`/internal/repair/jobs/${jobId}/process`, { - method: 'POST' + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, }); if (!response.ok) throw new Error(await response.text()); @@ -496,7 +499,7 @@ document.getElementById('modalJobStatus').innerHTML = `${status}`; // Set other job details - document.getElementById('modalJobArrs').textContent = job.arrs.map(a => a.name).join(', '); + document.getElementById('modalJobArrs').textContent = job.arrs.join(', '); document.getElementById('modalJobMediaIds').textContent = job.media_ids && job.media_ids.length > 0 ? job.media_ids.join(', ') : 'All'; document.getElementById('modalJobAutoProcess').textContent = job.auto_process ? 'Yes' : 'No';