- Update Readme

- Add funding.yml
- Add Arr Queue cleanner worker
- Rewrote worker
This commit is contained in:
Mukhtar Akere
2025-02-19 23:52:53 +01:00
parent 9a7bff04ef
commit 108da305b3
21 changed files with 340 additions and 69 deletions
+13 -12
View File
@@ -25,19 +25,20 @@ var (
)
type Arr struct {
Name string `json:"name"`
Host string `json:"host"`
Token string `json:"token"`
Type Type `json:"type"`
verifiedDirs sync.Map // map[string]struct{} -> dir -> struct{}
Name string `json:"name"`
Host string `json:"host"`
Token string `json:"token"`
Type Type `json:"type"`
Cleanup bool `json:"cleanup"`
}
func NewArr(name, host, token string, arrType Type) *Arr {
func New(name, host, token string, cleanup bool) *Arr {
return &Arr{
Name: name,
Host: host,
Token: token,
Type: arrType,
Name: name,
Host: host,
Token: token,
Type: InferType(host, name),
Cleanup: cleanup,
}
}
@@ -71,7 +72,7 @@ type Storage struct {
mu sync.RWMutex
}
func inferType(host, name string) Type {
func InferType(host, name string) Type {
switch {
case strings.Contains(host, "sonarr") || strings.Contains(name, "sonarr"):
return Sonarr
@@ -90,7 +91,7 @@ func NewStorage() *Storage {
arrs := make(map[string]*Arr)
for _, a := range config.GetConfig().Arrs {
name := a.Name
arrs[name] = NewArr(name, a.Host, a.Token, inferType(a.Host, name))
arrs[name] = New(name, a.Host, a.Token, a.Cleanup)
}
return &Storage{
Arrs: arrs,
+16 -9
View File
@@ -112,14 +112,8 @@ func GetMovies(a *Arr, tvId string) ([]Content, error) {
return contents, nil
}
func (a *Arr) SearchMissing(files []ContentFile) error {
func (a *Arr) search(ids []int) error {
var payload interface{}
ids := make([]int, 0)
for _, f := range files {
ids = append(ids, f.Id)
}
switch a.Type {
case Sonarr:
payload = struct {
@@ -143,14 +137,27 @@ func (a *Arr) SearchMissing(files []ContentFile) error {
resp, err := a.Request(http.MethodPost, "api/v3/command", payload)
if err != nil {
return fmt.Errorf("failed to search missing: %v", err)
return fmt.Errorf("failed to automatic search: %v", err)
}
if statusOk := strconv.Itoa(resp.StatusCode)[0] == '2'; !statusOk {
return fmt.Errorf("failed to search missing. Status Code: %s", resp.Status)
return fmt.Errorf("failed to automatic search. Status Code: %s", resp.Status)
}
return nil
}
func (a *Arr) SearchMissing(files []ContentFile) error {
ids := make([]int, 0)
for _, f := range files {
ids = append(ids, f.Id)
}
if len(ids) == 0 {
return nil
}
return a.search(ids)
}
func (a *Arr) DeleteFiles(files []ContentFile) error {
ids := make([]int, 0)
for _, f := range files {
+128 -1
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"net/http"
gourl "net/url"
"strings"
)
type HistorySchema struct {
@@ -18,6 +19,37 @@ type HistorySchema struct {
} `json:"records"`
}
type QueueResponseScheme struct {
Page int `json:"page"`
PageSize int `json:"pageSize"`
SortKey string `json:"sortKey"`
SortDirection string `json:"sortDirection"`
TotalRecords int `json:"totalRecords"`
Records []QueueSchema `json:"records"`
}
type QueueSchema struct {
SeriesId int `json:"seriesId"`
EpisodeId int `json:"episodeId"`
SeasonNumber int `json:"seasonNumber"`
Title string `json:"title"`
Status string `json:"status"`
TrackedDownloadStatus string `json:"trackedDownloadStatus"`
TrackedDownloadState string `json:"trackedDownloadState"`
StatusMessages []struct {
Title string `json:"title"`
Messages []string `json:"messages"`
} `json:"statusMessages"`
DownloadId string `json:"downloadId"`
Protocol string `json:"protocol"`
DownloadClient string `json:"downloadClient"`
DownloadClientHasPostImportCategory bool `json:"downloadClientHasPostImportCategory"`
Indexer string `json:"indexer"`
OutputPath string `json:"outputPath"`
EpisodeHasFile bool `json:"episodeHasFile"`
Id int `json:"id"`
}
func (a *Arr) GetHistory(downloadId, eventType string) *HistorySchema {
query := gourl.Values{}
if downloadId != "" {
@@ -25,7 +57,7 @@ func (a *Arr) GetHistory(downloadId, eventType string) *HistorySchema {
}
query.Add("eventType", eventType)
query.Add("pageSize", "100")
url := "history" + "?" + query.Encode()
url := "api/v3/history" + "?" + query.Encode()
resp, err := a.Request(http.MethodGet, url, nil)
if err != nil {
return nil
@@ -39,3 +71,98 @@ func (a *Arr) GetHistory(downloadId, eventType string) *HistorySchema {
return data
}
func (a *Arr) GetQueue() []QueueSchema {
query := gourl.Values{}
query.Add("page", "1")
query.Add("pageSize", "200")
results := make([]QueueSchema, 0)
for {
url := "api/v3/queue" + "?" + query.Encode()
resp, err := a.Request(http.MethodGet, url, nil)
if err != nil {
break
}
defer resp.Body.Close()
var data QueueResponseScheme
if err = json.NewDecoder(resp.Body).Decode(&data); err != nil {
break
}
if len(results) < data.TotalRecords {
results = append(results, data.Records...)
query.Set("page", string(rune(data.Page+1)))
} else {
break
}
}
return results
}
func (a *Arr) CleanupQueue() error {
queue := a.GetQueue()
type messedUp struct {
id int
episodeId int
seasonNum int
}
cleanups := make(map[int][]messedUp)
for _, q := range queue {
isMessedUp := false
if q.Protocol == "torrent" && q.Status == "completed" && q.TrackedDownloadStatus == "warning" && q.TrackedDownloadState == "importPending" {
messages := q.StatusMessages
if len(messages) > 0 {
for _, m := range messages {
if strings.Contains(strings.Join(m.Messages, " "), "No files found are eligible for import in") {
isMessedUp = true
break
}
}
}
}
if isMessedUp {
cleanups[q.SeriesId] = append(cleanups[q.SeriesId], messedUp{
id: q.Id,
episodeId: q.EpisodeId,
seasonNum: q.SeasonNumber,
})
}
}
if len(cleanups) == 0 {
return nil
}
queueIds := make([]int, 0)
episodesIds := make([]int, 0)
for _, c := range cleanups {
// Delete the messed up episodes from queue
for _, m := range c {
queueIds = append(queueIds, m.id)
episodesIds = append(episodesIds, m.episodeId)
}
}
// Delete the messed up episodes from queue
payload := struct {
Ids []int `json:"ids"`
}{
Ids: queueIds,
}
// Blocklist that hash(it's typically not complete, then research the episode)
query := gourl.Values{}
query.Add("removeFromClient", "true")
query.Add("blocklist", "true")
query.Add("skipRedownload", "false")
query.Add("changeCategory", "false")
url := "api/v3/queue/bulk" + "?" + query.Encode()
_, err := a.Request(http.MethodDelete, url, payload)
if err != nil {
return err
}
return nil
}
+1 -1
View File
@@ -22,7 +22,7 @@ func (a *Arr) Refresh() error {
return fmt.Errorf("failed to refresh monitored downloads for %s", cmp.Or(a.Name, a.Host))
}
func (a *Arr) MarkAsFailed(infoHash string) error {
func (a *Arr) Blacklist(infoHash string) error {
downloadId := strings.ToUpper(infoHash)
history := a.GetHistory(downloadId, "grabbed")
if history == nil {