From eb7ca3374e899f779b03fac6696862788c3ffd89 Mon Sep 17 00:00:00 2001 From: Ludvig Lundgren Date: Sun, 1 May 2022 21:11:54 +0200 Subject: [PATCH] feat(actions): qbt set re-announce opts (#261) --- internal/action/qbittorrent.go | 27 ++++-- internal/database/action.go | 33 ++++++- internal/database/migrate.go | 130 +++++++++++++++++---------- internal/domain/action.go | 50 ++++++----- web/src/components/inputs/switch.tsx | 2 +- web/src/screens/filters/details.tsx | 72 +++++++++++---- web/src/types/Filter.d.ts | 4 + 7 files changed, 219 insertions(+), 99 deletions(-) diff --git a/internal/action/qbittorrent.go b/internal/action/qbittorrent.go index 3355731..9d1b6cb 100644 --- a/internal/action/qbittorrent.go +++ b/internal/action/qbittorrent.go @@ -71,8 +71,8 @@ func (s *service) qbittorrent(qbt *qbittorrent.Client, action domain.Action, rel return err } - if !action.Paused && release.TorrentHash != "" { - err = checkTrackerStatus(qbt, release.TorrentHash) + if !action.Paused && !action.ReAnnounceSkip && release.TorrentHash != "" { + err = s.checkTrackerStatus(qbt, action, release.TorrentHash) if err != nil { log.Error().Stack().Err(err).Msgf("could not reannounce torrent: %v", release.TorrentHash) return err @@ -156,14 +156,25 @@ func (s *service) qbittorrentCheckRulesCanDownload(action domain.Action) (bool, return true, qbt, nil } -func checkTrackerStatus(qb *qbittorrent.Client, hash string) error { +func (s *service) checkTrackerStatus(qb *qbittorrent.Client, action domain.Action, hash string) error { announceOK := false attempts := 0 // initial sleep to give tracker a head start - time.Sleep(6 * time.Second) + interval := ReannounceInterval + if action.ReAnnounceInterval == 0 { + time.Sleep(6 * time.Second) + } else { + interval = int(action.ReAnnounceInterval) + time.Sleep(time.Duration(interval) * time.Second) + } - for attempts < ReannounceMaxAttempts { + maxAttempts := ReannounceMaxAttempts + if action.ReAnnounceMaxAttempts > 0 { + maxAttempts = int(action.ReAnnounceMaxAttempts) + } + + for attempts < maxAttempts { log.Debug().Msgf("qBittorrent - run re-announce %v attempt: %v", hash, attempts) trackers, err := qb.GetTorrentTrackers(hash) @@ -193,16 +204,16 @@ func checkTrackerStatus(qb *qbittorrent.Client, hash string) error { } // add delay for next run - time.Sleep(ReannounceInterval * time.Millisecond) + time.Sleep(time.Duration(interval) * time.Second) attempts++ } // add extra delay before delete - // TODO add setting: delete on failure to reannounce time.Sleep(30 * time.Second) - if !announceOK { + // delete on failure to reannounce + if !announceOK && action.ReAnnounceDelete { log.Debug().Msgf("qBittorrent - re-announce for %v took too long, deleting torrent", hash) err := qb.DeleteTorrents([]string{hash}, false) diff --git a/internal/database/action.go b/internal/database/action.go index f013f98..6df7add 100644 --- a/internal/database/action.go +++ b/internal/database/action.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "encoding/json" + "github.com/autobrr/autobrr/internal/domain" sq "github.com/Masterminds/squirrel" @@ -67,6 +68,10 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int) ( "ignore_rules", "limit_download_speed", "limit_upload_speed", + "reannounce_skip", + "reannounce_delete", + "reannounce_interval", + "reannounce_max_attempts", "webhook_host", "webhook_type", "webhook_method", @@ -100,7 +105,7 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int) ( // filterID var paused, ignoreRules sql.NullBool - if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &limitDl, &limitUl, &webhookHost, &webhookType, &webhookMethod, &webhookData, &clientID); err != nil { + if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &limitDl, &limitUl, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &clientID); err != nil { log.Error().Stack().Err(err).Msg("action.findByFilterID: error scanning row") return nil, err } @@ -201,6 +206,10 @@ func (r *ActionRepo) List(ctx context.Context) ([]domain.Action, error) { "ignore_rules", "limit_download_speed", "limit_upload_speed", + "reannounce_skip", + "reannounce_delete", + "reannounce_interval", + "reannounce_max_attempts", "webhook_host", "webhook_type", "webhook_method", @@ -232,7 +241,7 @@ func (r *ActionRepo) List(ctx context.Context) ([]domain.Action, error) { var clientID sql.NullInt32 var paused, ignoreRules sql.NullBool - if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &limitDl, &limitUl, &webhookHost, &webhookType, &webhookMethod, &webhookData, &clientID); err != nil { + if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &limitDl, &limitUl, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &clientID); err != nil { log.Error().Stack().Err(err).Msg("action.list: error scanning row") return nil, err } @@ -343,6 +352,10 @@ func (r *ActionRepo) Store(ctx context.Context, action domain.Action) (*domain.A "ignore_rules", "limit_upload_speed", "limit_download_speed", + "reannounce_skip", + "reannounce_delete", + "reannounce_interval", + "reannounce_max_attempts", "webhook_host", "webhook_type", "webhook_method", @@ -365,6 +378,10 @@ func (r *ActionRepo) Store(ctx context.Context, action domain.Action) (*domain.A action.IgnoreRules, limitUL, limitDL, + action.ReAnnounceSkip, + action.ReAnnounceDelete, + action.ReAnnounceInterval, + action.ReAnnounceMaxAttempts, webhookHost, webhookType, webhookMethod, @@ -425,6 +442,10 @@ func (r *ActionRepo) Update(ctx context.Context, action domain.Action) (*domain. Set("ignore_rules", action.IgnoreRules). Set("limit_upload_speed", limitUL). Set("limit_download_speed", limitDL). + Set("reannounce_skip", action.ReAnnounceSkip). + Set("reannounce_delete", action.ReAnnounceDelete). + Set("reannounce_interval", action.ReAnnounceInterval). + Set("reannounce_max_attempts", action.ReAnnounceMaxAttempts). Set("webhook_host", webhookHost). Set("webhook_type", webhookType). Set("webhook_method", webhookMethod). @@ -507,6 +528,10 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, actions []*domain.A "ignore_rules", "limit_upload_speed", "limit_download_speed", + "reannounce_skip", + "reannounce_delete", + "reannounce_interval", + "reannounce_max_attempts", "webhook_host", "webhook_type", "webhook_method", @@ -529,6 +554,10 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, actions []*domain.A action.IgnoreRules, limitUL, limitDL, + action.ReAnnounceSkip, + action.ReAnnounceDelete, + action.ReAnnounceInterval, + action.ReAnnounceMaxAttempts, webhookHost, webhookType, webhookMethod, diff --git a/internal/database/migrate.go b/internal/database/migrate.go index f6456ea..3a81896 100644 --- a/internal/database/migrate.go +++ b/internal/database/migrate.go @@ -132,30 +132,34 @@ CREATE TABLE client CREATE TABLE action ( - id INTEGER PRIMARY KEY, - name TEXT, - type TEXT, - enabled BOOLEAN, - exec_cmd TEXT, - exec_args TEXT, - watch_folder TEXT, - category TEXT, - tags TEXT, - label TEXT, - save_path TEXT, - paused BOOLEAN, - ignore_rules BOOLEAN, - limit_upload_speed INT, - limit_download_speed INT, - webhook_host TEXT, - webhook_method TEXT, - webhook_type TEXT, - webhook_data TEXT, - webhook_headers TEXT [] DEFAULT '{}', - client_id INTEGER, - filter_id INTEGER, - FOREIGN KEY (filter_id) REFERENCES filter(id), - FOREIGN KEY (client_id) REFERENCES client(id) ON DELETE SET NULL + id INTEGER PRIMARY KEY, + name TEXT, + type TEXT, + enabled BOOLEAN, + exec_cmd TEXT, + exec_args TEXT, + watch_folder TEXT, + category TEXT, + tags TEXT, + label TEXT, + save_path TEXT, + paused BOOLEAN, + ignore_rules BOOLEAN, + limit_upload_speed INT, + limit_download_speed INT, + reannounce_skip BOOLEAN DEFAULT false, + reannounce_delete BOOLEAN DEFAULT false, + reannounce_interval INTEGER DEFAULT 7, + reannounce_max_attempts INTEGER DEFAULT 50, + webhook_host TEXT, + webhook_method TEXT, + webhook_type TEXT, + webhook_data TEXT, + webhook_headers TEXT[] DEFAULT '{}', + client_id INTEGER, + filter_id INTEGER, + FOREIGN KEY (filter_id) REFERENCES filter (id), + FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE SET NULL ); CREATE TABLE "release" @@ -648,6 +652,19 @@ ALTER TABLE release_action_status_dg_tmp ALTER TABLE release RENAME COLUMN "group"" TO "release_group"; `, + ` + ALTER TABLE "action" + ADD COLUMN reannounce_skip BOOLEAN DEFAULT false; + + ALTER TABLE "action" + ADD COLUMN reannounce_delete BOOLEAN DEFAULT false; + + ALTER TABLE "action" + ADD COLUMN reannounce_interval INTEGER DEFAULT 7; + + ALTER TABLE "action" + ADD COLUMN reannounce_max_attempts INTEGER DEFAULT 50; + `, } const postgresSchema = ` @@ -782,30 +799,34 @@ CREATE TABLE client CREATE TABLE action ( - id SERIAL PRIMARY KEY, - name TEXT, - type TEXT, - enabled BOOLEAN, - exec_cmd TEXT, - exec_args TEXT, - watch_folder TEXT, - category TEXT, - tags TEXT, - label TEXT, - save_path TEXT, - paused BOOLEAN, - ignore_rules BOOLEAN, - limit_upload_speed INT, - limit_download_speed INT, - webhook_host TEXT, - webhook_method TEXT, - webhook_type TEXT, - webhook_data TEXT, - webhook_headers TEXT [] DEFAULT '{}', - client_id INTEGER, - filter_id INTEGER, - FOREIGN KEY (filter_id) REFERENCES filter(id), - FOREIGN KEY (client_id) REFERENCES client(id) ON DELETE SET NULL + id SERIAL PRIMARY KEY, + name TEXT, + type TEXT, + enabled BOOLEAN, + exec_cmd TEXT, + exec_args TEXT, + watch_folder TEXT, + category TEXT, + tags TEXT, + label TEXT, + save_path TEXT, + paused BOOLEAN, + ignore_rules BOOLEAN, + limit_upload_speed INT, + limit_download_speed INT, + reannounce_skip BOOLEAN DEFAULT false, + reannounce_delete BOOLEAN DEFAULT false, + reannounce_interval INTEGER DEFAULT 7, + reannounce_max_attempts INTEGER DEFAULT 50, + webhook_host TEXT, + webhook_method TEXT, + webhook_type TEXT, + webhook_data TEXT, + webhook_headers TEXT[] DEFAULT '{}', + client_id INTEGER, + filter_id INTEGER, + FOREIGN KEY (filter_id) REFERENCES filter (id), + FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE SET NULL ); CREATE TABLE "release" @@ -1051,4 +1072,17 @@ var postgresMigrations = []string{ ALTER TABLE release ALTER COLUMN size TYPE BIGINT USING size::BIGINT; `, + ` + ALTER TABLE "action" + ADD COLUMN reannounce_skip BOOLEAN DEFAULT false; + + ALTER TABLE "action" + ADD COLUMN reannounce_delete BOOLEAN DEFAULT false; + + ALTER TABLE "action" + ADD COLUMN reannounce_interval INTEGER DEFAULT 7; + + ALTER TABLE "action" + ADD COLUMN reannounce_max_attempts INTEGER DEFAULT 50; + `, } diff --git a/internal/domain/action.go b/internal/domain/action.go index 0a3f9e1..0bec9ed 100644 --- a/internal/domain/action.go +++ b/internal/domain/action.go @@ -13,29 +13,33 @@ type ActionRepo interface { } type Action struct { - ID int `json:"id"` - Name string `json:"name"` - Type ActionType `json:"type"` - Enabled bool `json:"enabled"` - ExecCmd string `json:"exec_cmd,omitempty"` - ExecArgs string `json:"exec_args,omitempty"` - WatchFolder string `json:"watch_folder,omitempty"` - Category string `json:"category,omitempty"` - Tags string `json:"tags,omitempty"` - Label string `json:"label,omitempty"` - SavePath string `json:"save_path,omitempty"` - Paused bool `json:"paused,omitempty"` - IgnoreRules bool `json:"ignore_rules,omitempty"` - LimitUploadSpeed int64 `json:"limit_upload_speed,omitempty"` - LimitDownloadSpeed int64 `json:"limit_download_speed,omitempty"` - WebhookHost string `json:"webhook_host,omitempty"` - WebhookType string `json:"webhook_type,omitempty"` - WebhookMethod string `json:"webhook_method,omitempty"` - WebhookData string `json:"webhook_data,omitempty"` - WebhookHeaders []string `json:"webhook_headers,omitempty"` - FilterID int `json:"filter_id,omitempty"` - ClientID int32 `json:"client_id,omitempty"` - Client DownloadClient `json:"client,omitempty"` + ID int `json:"id"` + Name string `json:"name"` + Type ActionType `json:"type"` + Enabled bool `json:"enabled"` + ExecCmd string `json:"exec_cmd,omitempty"` + ExecArgs string `json:"exec_args,omitempty"` + WatchFolder string `json:"watch_folder,omitempty"` + Category string `json:"category,omitempty"` + Tags string `json:"tags,omitempty"` + Label string `json:"label,omitempty"` + SavePath string `json:"save_path,omitempty"` + Paused bool `json:"paused,omitempty"` + IgnoreRules bool `json:"ignore_rules,omitempty"` + LimitUploadSpeed int64 `json:"limit_upload_speed,omitempty"` + LimitDownloadSpeed int64 `json:"limit_download_speed,omitempty"` + ReAnnounceSkip bool `json:"reannounce_skip,omitempty"` + ReAnnounceDelete bool `json:"reannounce_delete,omitempty"` + ReAnnounceInterval int64 `json:"reannounce_interval,omitempty"` + ReAnnounceMaxAttempts int64 `json:"reannounce_max_attempts,omitempty"` + WebhookHost string `json:"webhook_host,omitempty"` + WebhookType string `json:"webhook_type,omitempty"` + WebhookMethod string `json:"webhook_method,omitempty"` + WebhookData string `json:"webhook_data,omitempty"` + WebhookHeaders []string `json:"webhook_headers,omitempty"` + FilterID int `json:"filter_id,omitempty"` + ClientID int32 `json:"client_id,omitempty"` + Client DownloadClient `json:"client,omitempty"` } type ActionType string diff --git a/web/src/components/inputs/switch.tsx b/web/src/components/inputs/switch.tsx index 0683a69..0be6fff 100644 --- a/web/src/components/inputs/switch.tsx +++ b/web/src/components/inputs/switch.tsx @@ -77,7 +77,7 @@ const SwitchGroup = ({ label, description }: SwitchGroupProps) => ( - + {label &&
diff --git a/web/src/screens/filters/details.tsx b/web/src/screens/filters/details.tsx index 4fc7889..9e8a73b 100644 --- a/web/src/screens/filters/details.tsx +++ b/web/src/screens/filters/details.tsx @@ -587,6 +587,10 @@ function FilterActions({ filter, values }: FilterActionsProps) { ignore_rules: false, limit_upload_speed: 0, limit_download_speed: 0, + reannounce_skip: false, + reannounce_delete: false, + reannounce_interval: 7, + reannounce_max_attempts: 25, filter_id: filter.id, webhook_host: "", webhook_type: "", @@ -730,25 +734,59 @@ function FilterActionsItem({ action, clients, idx, remove }: FilterActionsItemPr
-
- - -
- -
-
- +
+
+ +
-
+
+
+ + +
+ + + +
+
+ + +
+
+
+ + +
+
); case "DELUGE_V1": diff --git a/web/src/types/Filter.d.ts b/web/src/types/Filter.d.ts index 13fd574..f0acb22 100644 --- a/web/src/types/Filter.d.ts +++ b/web/src/types/Filter.d.ts @@ -68,6 +68,10 @@ interface Action { ignore_rules?: boolean; limit_upload_speed?: number; limit_download_speed?: number; + reannounce_skip: boolean; + reannounce_delete: boolean; + reannounce_interval: number; + reannounce_max_attempts: number; webhook_host: string, webhook_type: string; webhook_method: string;