feat(actions): qBittorrent add priority handling (#1315)

* feat(qbittorrent): add priority handling

* fix: check if torrent queueing is enabled

* fix: only check for torrent queueing if priority is set

* fix: improve tooltip message

* feat: enable torrent queueing if disabled

* change to SetPreferencesQueueingEnabled

* feat(actions): rename field

* chore: bump pkg go-qbittorrent to v1.8.0

* chore(deps): update go-qbittorrent to v1.8.1

* chore(deps): go mod tidy

---------

Co-authored-by: ze0s <ze0s@riseup.net>
This commit is contained in:
soup 2023-12-25 22:39:31 +01:00 committed by GitHub
parent 0ab404f81a
commit c6122dbc41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 115 additions and 30 deletions

View file

@ -65,6 +65,45 @@ func (s *service) qbittorrent(ctx context.Context, action *domain.Action, releas
return nil, errors.Wrap(err, "could not add torrent %s to client: %s", release.TorrentTmpFile, c.Dc.Name)
}
if release.TorrentHash != "" {
// check if torrent queueing is enabled if priority is set
switch action.PriorityLayout {
case domain.PriorityLayoutMax, domain.PriorityLayoutMin:
prefs, err := c.Qbt.GetAppPreferencesCtx(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not get application preferences from client: '%s'", c.Dc.Name)
}
// enable queueing if it's disabled
if !prefs.QueueingEnabled {
if err := c.Qbt.SetPreferencesQueueingEnabled(true); err != nil {
return nil, errors.Wrap(err, "could not enable torrent queueing")
}
s.log.Trace().Msgf("torrent queueing was disabled, now enabled in client: '%s'", c.Dc.Name)
}
// set priority if queueing is enabled
if action.PriorityLayout == domain.PriorityLayoutMax {
if err := c.Qbt.SetMaxPriorityCtx(ctx, []string{release.TorrentHash}); err != nil {
return nil, errors.Wrap(err, "could not set torrent %s to max priority", release.TorrentHash)
}
s.log.Debug().Msgf("torrent with hash %s set to max priority in client: '%s'", release.TorrentHash, c.Dc.Name)
} else { // domain.PriorityLayoutMin
if err := c.Qbt.SetMinPriorityCtx(ctx, []string{release.TorrentHash}); err != nil {
return nil, errors.Wrap(err, "could not set torrent %s to min priority", release.TorrentHash)
}
s.log.Debug().Msgf("torrent with hash %s set to min priority in client: '%s'", release.TorrentHash, c.Dc.Name)
}
case domain.PriorityLayoutDefault:
// do nothing as it's disabled or unset
default:
s.log.Warn().Msgf("unknown priority setting: '%v', no priority changes made", action.PriorityLayout)
}
} else {
// add anyway if no hash
s.log.Trace().Msg("no torrent hash provided, skipping priority setting")
}
if !action.Paused && !action.ReAnnounceSkip && release.TorrentHash != "" {
opts := qbittorrent.ReannounceOptions{
Interval: int(action.ReAnnounceInterval),

View file

@ -77,6 +77,7 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int, a
"ignore_rules",
"skip_hash_check",
"content_layout",
"priority",
"limit_download_speed",
"limit_upload_speed",
"limit_ratio",
@ -115,14 +116,14 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int, a
for rows.Next() {
var a domain.Action
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, webhookHost, webhookType, webhookMethod, webhookData sql.NullString
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, priorityLayout, webhookHost, webhookType, webhookMethod, webhookData sql.NullString
var limitUl, limitDl, limitSeedTime sql.NullInt64
var limitRatio sql.NullFloat64
var externalClientID, 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, &a.SkipHashCheck, &contentLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &clientID); err != nil {
if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &a.SkipHashCheck, &contentLayout, &priorityLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &clientID); err != nil {
return nil, errors.Wrap(err, "error scanning row")
}
@ -136,6 +137,7 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int, a
a.Paused = paused.Bool
a.IgnoreRules = ignoreRules.Bool
a.ContentLayout = domain.ActionContentLayout(contentLayout.String)
a.PriorityLayout = domain.PriorityLayout(priorityLayout.String)
a.LimitDownloadSpeed = limitDl.Int64
a.LimitUploadSpeed = limitUl.Int64
@ -227,6 +229,7 @@ func (r *ActionRepo) List(ctx context.Context) ([]domain.Action, error) {
"ignore_rules",
"skip_hash_check",
"content_layout",
"priority",
"limit_download_speed",
"limit_upload_speed",
"limit_ratio",
@ -260,13 +263,13 @@ func (r *ActionRepo) List(ctx context.Context) ([]domain.Action, error) {
for rows.Next() {
var a domain.Action
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, webhookHost, webhookType, webhookMethod, webhookData sql.NullString
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, priorityLayout, webhookHost, webhookType, webhookMethod, webhookData sql.NullString
var limitUl, limitDl, limitSeedTime sql.NullInt64
var limitRatio sql.NullFloat64
var externalClientID, 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, &a.SkipHashCheck, &contentLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &clientID); err != nil {
if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &a.SkipHashCheck, &contentLayout, &priorityLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &clientID); err != nil {
return nil, errors.Wrap(err, "error scanning row")
}
@ -277,6 +280,7 @@ func (r *ActionRepo) List(ctx context.Context) ([]domain.Action, error) {
a.Paused = paused.Bool
a.IgnoreRules = ignoreRules.Bool
a.ContentLayout = domain.ActionContentLayout(contentLayout.String)
a.PriorityLayout = domain.PriorityLayout(priorityLayout.String)
a.LimitDownloadSpeed = limitDl.Int64
a.LimitUploadSpeed = limitUl.Int64
@ -319,6 +323,7 @@ func (r *ActionRepo) Get(ctx context.Context, req *domain.GetActionRequest) (*do
"ignore_rules",
"skip_hash_check",
"content_layout",
"priority",
"limit_download_speed",
"limit_upload_speed",
"limit_ratio",
@ -354,13 +359,13 @@ func (r *ActionRepo) Get(ctx context.Context, req *domain.GetActionRequest) (*do
var a domain.Action
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, webhookHost, webhookType, webhookMethod, webhookData sql.NullString
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, priorityLayout, webhookHost, webhookType, webhookMethod, webhookData sql.NullString
var limitUl, limitDl, limitSeedTime sql.NullInt64
var limitRatio sql.NullFloat64
var externalClientID, clientID, filterID sql.NullInt32
var paused, ignoreRules sql.NullBool
if err := row.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &a.SkipHashCheck, &contentLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &clientID, &filterID); err != nil {
if err := row.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &a.SkipHashCheck, &contentLayout, &priorityLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &clientID, &filterID); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, domain.ErrRecordNotFound
}
@ -378,6 +383,7 @@ func (r *ActionRepo) Get(ctx context.Context, req *domain.GetActionRequest) (*do
a.Paused = paused.Bool
a.IgnoreRules = ignoreRules.Bool
a.ContentLayout = domain.ActionContentLayout(contentLayout.String)
a.PriorityLayout = domain.PriorityLayout(priorityLayout.String)
a.LimitDownloadSpeed = limitDl.Int64
a.LimitUploadSpeed = limitUl.Int64
@ -454,6 +460,7 @@ func (r *ActionRepo) Store(ctx context.Context, action domain.Action) (*domain.A
"ignore_rules",
"skip_hash_check",
"content_layout",
"priority",
"limit_upload_speed",
"limit_download_speed",
"limit_ratio",
@ -485,6 +492,7 @@ func (r *ActionRepo) Store(ctx context.Context, action domain.Action) (*domain.A
action.IgnoreRules,
action.SkipHashCheck,
toNullString(string(action.ContentLayout)),
toNullString(string(action.PriorityLayout)),
toNullInt64(action.LimitUploadSpeed),
toNullInt64(action.LimitDownloadSpeed),
toNullFloat64(action.LimitRatio),
@ -534,6 +542,7 @@ func (r *ActionRepo) Update(ctx context.Context, action domain.Action) (*domain.
Set("ignore_rules", action.IgnoreRules).
Set("skip_hash_check", action.SkipHashCheck).
Set("content_layout", toNullString(string(action.ContentLayout))).
Set("priority", toNullString(string(action.PriorityLayout))).
Set("limit_upload_speed", toNullInt64(action.LimitUploadSpeed)).
Set("limit_download_speed", toNullInt64(action.LimitDownloadSpeed)).
Set("limit_ratio", toNullFloat64(action.LimitRatio)).
@ -593,6 +602,7 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, filterID int64, act
Set("ignore_rules", action.IgnoreRules).
Set("skip_hash_check", action.SkipHashCheck).
Set("content_layout", toNullString(string(action.ContentLayout))).
Set("priority", toNullString(string(action.PriorityLayout))).
Set("limit_upload_speed", toNullInt64(action.LimitUploadSpeed)).
Set("limit_download_speed", toNullInt64(action.LimitDownloadSpeed)).
Set("limit_ratio", toNullFloat64(action.LimitRatio)).
@ -639,6 +649,7 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, filterID int64, act
"ignore_rules",
"skip_hash_check",
"content_layout",
"priority",
"limit_upload_speed",
"limit_download_speed",
"limit_ratio",
@ -670,6 +681,7 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, filterID int64, act
action.IgnoreRules,
action.SkipHashCheck,
toNullString(string(action.ContentLayout)),
toNullString(string(action.PriorityLayout)),
toNullInt64(action.LimitUploadSpeed),
toNullInt64(action.LimitDownloadSpeed),
toNullFloat64(action.LimitRatio),

View file

@ -199,6 +199,7 @@ CREATE TABLE action
limit_download_speed INT,
limit_ratio REAL,
limit_seed_time INT,
priority TEXT,
reannounce_skip BOOLEAN DEFAULT false,
reannounce_delete BOOLEAN DEFAULT false,
reannounce_interval INTEGER DEFAULT 7,
@ -507,20 +508,20 @@ var postgresMigrations = []string{
`
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;
`,
`
ALTER TABLE "action"
ADD COLUMN limit_ratio REAL DEFAULT 0;
ALTER TABLE "action"
ADD COLUMN limit_seed_time INTEGER DEFAULT 0;
`,
@ -685,7 +686,7 @@ FROM filter f WHERE f.name = release_action_status.filter);
`,
`ALTER TABLE "release"
ADD COLUMN info_url TEXT;
ALTER TABLE "release"
ADD COLUMN download_url TEXT;
`,
@ -728,7 +729,7 @@ ALTER TABLE irc_network
ADD COLUMN bouncer_addr TEXT;`,
`ALTER TABLE release_action_status
DROP CONSTRAINT IF EXISTS release_action_status_action_id_fkey;
ALTER TABLE release_action_status
DROP CONSTRAINT IF EXISTS release_action_status_action_id_fk;
@ -834,5 +835,8 @@ ALTER TABLE filter_external
`,
`ALTER TABLE feed
ALTER COLUMN max_age SET DEFAULT 0;
`,
`ALTER TABLE action
ADD COLUMN priority TEXT;
`,
}

View file

@ -199,6 +199,7 @@ CREATE TABLE action
limit_download_speed INT,
limit_ratio REAL,
limit_seed_time INT,
priority TEXT,
reannounce_skip BOOLEAN DEFAULT false,
reannounce_delete BOOLEAN DEFAULT false,
reannounce_interval INTEGER DEFAULT 7,
@ -763,20 +764,20 @@ ALTER TABLE release_action_status_dg_tmp
`
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;
`,
`
ALTER TABLE "action"
ADD COLUMN limit_ratio REAL DEFAULT 0;
ALTER TABLE "action"
ADD COLUMN limit_seed_time INTEGER DEFAULT 0;
`,
@ -986,7 +987,7 @@ CREATE TABLE irc_network_dg_tmp
unique (server, port, nick)
);
INSERT INTO irc_network_dg_tmp(id, enabled, name, server, port, tls, pass, nick, auth_mechanism, auth_account, auth_password, invite_command,
INSERT INTO irc_network_dg_tmp(id, enabled, name, server, port, tls, pass, nick, auth_mechanism, auth_account, auth_password, invite_command,
connected, connected_since, created_at, updated_at)
SELECT id,
enabled,
@ -1079,7 +1080,7 @@ FROM filter f WHERE f.name = release_action_status.filter);
`,
`ALTER TABLE "release"
ADD COLUMN info_url TEXT;
ALTER TABLE "release"
ADD COLUMN download_url TEXT;
`,
@ -1088,7 +1089,7 @@ ADD COLUMN download_url TEXT;
ALTER TABLE filter
ADD COLUMN except_tags_match_logic TEXT;
UPDATE filter
SET tags_match_logic = 'ANY'
WHERE tags IS NOT NULL;
@ -1475,5 +1476,8 @@ DROP TABLE feed;
ALTER TABLE feed_dg_tmp
RENAME TO feed;
`,
`ALTER TABLE action
ADD COLUMN priority TEXT;
`,
}

View file

@ -42,6 +42,7 @@ type Action struct {
LimitDownloadSpeed int64 `json:"limit_download_speed,omitempty"`
LimitRatio float64 `json:"limit_ratio,omitempty"`
LimitSeedTime int64 `json:"limit_seed_time,omitempty"`
PriorityLayout PriorityLayout `json:"priority,omitempty"`
ReAnnounceSkip bool `json:"reannounce_skip,omitempty"`
ReAnnounceDelete bool `json:"reannounce_delete,omitempty"`
ReAnnounceInterval int64 `json:"reannounce_interval,omitempty"`
@ -128,6 +129,14 @@ const (
ActionContentLayoutSubfolderCreate ActionContentLayout = "SUBFOLDER_CREATE"
)
type PriorityLayout string
const (
PriorityLayoutMax PriorityLayout = "MAX"
PriorityLayoutMin PriorityLayout = "MIN"
PriorityLayoutDefault PriorityLayout = ""
)
type GetActionRequest struct {
Id int
}