feat(actions): qbt set re-announce opts (#261)

This commit is contained in:
Ludvig Lundgren 2022-05-01 21:11:54 +02:00 committed by GitHub
parent 4d419abb72
commit eb7ca3374e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 219 additions and 99 deletions

View file

@ -71,8 +71,8 @@ func (s *service) qbittorrent(qbt *qbittorrent.Client, action domain.Action, rel
return err return err
} }
if !action.Paused && release.TorrentHash != "" { if !action.Paused && !action.ReAnnounceSkip && release.TorrentHash != "" {
err = checkTrackerStatus(qbt, release.TorrentHash) err = s.checkTrackerStatus(qbt, action, release.TorrentHash)
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("could not reannounce torrent: %v", release.TorrentHash) log.Error().Stack().Err(err).Msgf("could not reannounce torrent: %v", release.TorrentHash)
return err return err
@ -156,14 +156,25 @@ func (s *service) qbittorrentCheckRulesCanDownload(action domain.Action) (bool,
return true, qbt, nil 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 announceOK := false
attempts := 0 attempts := 0
// initial sleep to give tracker a head start // initial sleep to give tracker a head start
interval := ReannounceInterval
if action.ReAnnounceInterval == 0 {
time.Sleep(6 * time.Second) 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) log.Debug().Msgf("qBittorrent - run re-announce %v attempt: %v", hash, attempts)
trackers, err := qb.GetTorrentTrackers(hash) trackers, err := qb.GetTorrentTrackers(hash)
@ -193,16 +204,16 @@ func checkTrackerStatus(qb *qbittorrent.Client, hash string) error {
} }
// add delay for next run // add delay for next run
time.Sleep(ReannounceInterval * time.Millisecond) time.Sleep(time.Duration(interval) * time.Second)
attempts++ attempts++
} }
// add extra delay before delete // add extra delay before delete
// TODO add setting: delete on failure to reannounce
time.Sleep(30 * time.Second) 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) log.Debug().Msgf("qBittorrent - re-announce for %v took too long, deleting torrent", hash)
err := qb.DeleteTorrents([]string{hash}, false) err := qb.DeleteTorrents([]string{hash}, false)

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"github.com/autobrr/autobrr/internal/domain" "github.com/autobrr/autobrr/internal/domain"
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
@ -67,6 +68,10 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int) (
"ignore_rules", "ignore_rules",
"limit_download_speed", "limit_download_speed",
"limit_upload_speed", "limit_upload_speed",
"reannounce_skip",
"reannounce_delete",
"reannounce_interval",
"reannounce_max_attempts",
"webhook_host", "webhook_host",
"webhook_type", "webhook_type",
"webhook_method", "webhook_method",
@ -100,7 +105,7 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int) (
// filterID // filterID
var paused, ignoreRules sql.NullBool 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") log.Error().Stack().Err(err).Msg("action.findByFilterID: error scanning row")
return nil, err return nil, err
} }
@ -201,6 +206,10 @@ func (r *ActionRepo) List(ctx context.Context) ([]domain.Action, error) {
"ignore_rules", "ignore_rules",
"limit_download_speed", "limit_download_speed",
"limit_upload_speed", "limit_upload_speed",
"reannounce_skip",
"reannounce_delete",
"reannounce_interval",
"reannounce_max_attempts",
"webhook_host", "webhook_host",
"webhook_type", "webhook_type",
"webhook_method", "webhook_method",
@ -232,7 +241,7 @@ func (r *ActionRepo) List(ctx context.Context) ([]domain.Action, error) {
var clientID sql.NullInt32 var clientID sql.NullInt32
var paused, ignoreRules sql.NullBool 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") log.Error().Stack().Err(err).Msg("action.list: error scanning row")
return nil, err return nil, err
} }
@ -343,6 +352,10 @@ func (r *ActionRepo) Store(ctx context.Context, action domain.Action) (*domain.A
"ignore_rules", "ignore_rules",
"limit_upload_speed", "limit_upload_speed",
"limit_download_speed", "limit_download_speed",
"reannounce_skip",
"reannounce_delete",
"reannounce_interval",
"reannounce_max_attempts",
"webhook_host", "webhook_host",
"webhook_type", "webhook_type",
"webhook_method", "webhook_method",
@ -365,6 +378,10 @@ func (r *ActionRepo) Store(ctx context.Context, action domain.Action) (*domain.A
action.IgnoreRules, action.IgnoreRules,
limitUL, limitUL,
limitDL, limitDL,
action.ReAnnounceSkip,
action.ReAnnounceDelete,
action.ReAnnounceInterval,
action.ReAnnounceMaxAttempts,
webhookHost, webhookHost,
webhookType, webhookType,
webhookMethod, webhookMethod,
@ -425,6 +442,10 @@ func (r *ActionRepo) Update(ctx context.Context, action domain.Action) (*domain.
Set("ignore_rules", action.IgnoreRules). Set("ignore_rules", action.IgnoreRules).
Set("limit_upload_speed", limitUL). Set("limit_upload_speed", limitUL).
Set("limit_download_speed", limitDL). 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_host", webhookHost).
Set("webhook_type", webhookType). Set("webhook_type", webhookType).
Set("webhook_method", webhookMethod). Set("webhook_method", webhookMethod).
@ -507,6 +528,10 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, actions []*domain.A
"ignore_rules", "ignore_rules",
"limit_upload_speed", "limit_upload_speed",
"limit_download_speed", "limit_download_speed",
"reannounce_skip",
"reannounce_delete",
"reannounce_interval",
"reannounce_max_attempts",
"webhook_host", "webhook_host",
"webhook_type", "webhook_type",
"webhook_method", "webhook_method",
@ -529,6 +554,10 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, actions []*domain.A
action.IgnoreRules, action.IgnoreRules,
limitUL, limitUL,
limitDL, limitDL,
action.ReAnnounceSkip,
action.ReAnnounceDelete,
action.ReAnnounceInterval,
action.ReAnnounceMaxAttempts,
webhookHost, webhookHost,
webhookType, webhookType,
webhookMethod, webhookMethod,

View file

@ -147,6 +147,10 @@ CREATE TABLE action
ignore_rules BOOLEAN, ignore_rules BOOLEAN,
limit_upload_speed INT, limit_upload_speed INT,
limit_download_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_host TEXT,
webhook_method TEXT, webhook_method TEXT,
webhook_type TEXT, webhook_type TEXT,
@ -648,6 +652,19 @@ ALTER TABLE release_action_status_dg_tmp
ALTER TABLE release ALTER TABLE release
RENAME COLUMN "group"" TO "release_group"; 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 = ` const postgresSchema = `
@ -797,6 +814,10 @@ CREATE TABLE action
ignore_rules BOOLEAN, ignore_rules BOOLEAN,
limit_upload_speed INT, limit_upload_speed INT,
limit_download_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_host TEXT,
webhook_method TEXT, webhook_method TEXT,
webhook_type TEXT, webhook_type TEXT,
@ -1051,4 +1072,17 @@ var postgresMigrations = []string{
ALTER TABLE release ALTER TABLE release
ALTER COLUMN size TYPE BIGINT USING size::BIGINT; 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;
`,
} }

View file

@ -28,6 +28,10 @@ type Action struct {
IgnoreRules bool `json:"ignore_rules,omitempty"` IgnoreRules bool `json:"ignore_rules,omitempty"`
LimitUploadSpeed int64 `json:"limit_upload_speed,omitempty"` LimitUploadSpeed int64 `json:"limit_upload_speed,omitempty"`
LimitDownloadSpeed int64 `json:"limit_download_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"` WebhookHost string `json:"webhook_host,omitempty"`
WebhookType string `json:"webhook_type,omitempty"` WebhookType string `json:"webhook_type,omitempty"`
WebhookMethod string `json:"webhook_method,omitempty"` WebhookMethod string `json:"webhook_method,omitempty"`

View file

@ -77,7 +77,7 @@ const SwitchGroup = ({
label, label,
description description
}: SwitchGroupProps) => ( }: SwitchGroupProps) => (
<HeadlessSwitch.Group as="li" className="py-4 flex items-center justify-between"> <HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between">
{label && <div className="flex flex-col"> {label && <div className="flex flex-col">
<HeadlessSwitch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-gray-100" <HeadlessSwitch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-gray-100"
passive> passive>

View file

@ -587,6 +587,10 @@ function FilterActions({ filter, values }: FilterActionsProps) {
ignore_rules: false, ignore_rules: false,
limit_upload_speed: 0, limit_upload_speed: 0,
limit_download_speed: 0, limit_download_speed: 0,
reannounce_skip: false,
reannounce_delete: false,
reannounce_interval: 7,
reannounce_max_attempts: 25,
filter_id: filter.id, filter_id: filter.id,
webhook_host: "", webhook_host: "",
webhook_type: "", webhook_type: "",
@ -730,6 +734,8 @@ function FilterActionsItem({ action, clients, idx, remove }: FilterActionsItemPr
<TextField name={`actions.${idx}.tags`} label="Tags" columns={6} placeholder="eg. tag1,tag2" /> <TextField name={`actions.${idx}.tags`} label="Tags" columns={6} placeholder="eg. tag1,tag2" />
</div> </div>
<CollapsableSection title="Rules" subtitle="client options">
<div className="col-span-12">
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<NumberField <NumberField
name={`actions.${idx}.limit_download_speed`} name={`actions.${idx}.limit_download_speed`}
@ -740,15 +746,47 @@ function FilterActionsItem({ action, clients, idx, remove }: FilterActionsItemPr
label="Limit upload speed (KB/s)" label="Limit upload speed (KB/s)"
/> />
</div> </div>
</div>
<div className="mt-6 grid grid-cols-12 gap-6">
<div className="col-span-6"> <div className="col-span-6">
<SwitchGroup <SwitchGroup
name={`actions.${idx}.paused`} name={`actions.${idx}.paused`}
label="Add paused" label="Add paused"
description="Add torrent as paused"
/>
<SwitchGroup
name={`actions.${idx}.ignore_rules`}
label="Ignore client rules"
description="Download if max active reached"
/>
</div>
</CollapsableSection>
<CollapsableSection title="Advanced" subtitle="Advanced options">
<div className="col-span-12">
<div className="mt-6 grid grid-cols-12 gap-6">
<NumberField
name={`actions.${idx}.reannounce_interval`}
label="Reannounce interval. Run every X seconds"
/>
<NumberField
name={`actions.${idx}.reannounce_max_attempts`}
label="Run reannounce Y times"
/> />
</div> </div>
</div> </div>
<div className="col-span-6">
<SwitchGroup
name={`actions.${idx}.reannounce_skip`}
label="Skip reannounce"
description="If reannounce is not needed, skip"
/>
<SwitchGroup
name={`actions.${idx}.reannounce_delete`}
label="Delete stalled"
description="Delete stalled torrents after X attempts"
/>
</div>
</CollapsableSection>
</div> </div>
); );
case "DELUGE_V1": case "DELUGE_V1":

View file

@ -68,6 +68,10 @@ interface Action {
ignore_rules?: boolean; ignore_rules?: boolean;
limit_upload_speed?: number; limit_upload_speed?: number;
limit_download_speed?: number; limit_download_speed?: number;
reannounce_skip: boolean;
reannounce_delete: boolean;
reannounce_interval: number;
reannounce_max_attempts: number;
webhook_host: string, webhook_host: string,
webhook_type: string; webhook_type: string;
webhook_method: string; webhook_method: string;