mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 16:59:12 +00:00
feat(actions): qbit add options content layout and skip hash check (#393)
* feat(actions): qbit content layout and skip hash check * feat(actions): qbit options
This commit is contained in:
parent
db9d048f5d
commit
9508cbb46c
13 changed files with 394 additions and 50 deletions
|
@ -2,7 +2,6 @@ package action
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -102,10 +101,23 @@ func (s *service) qbittorrent(action domain.Action, release domain.Release) ([]s
|
|||
|
||||
func (s *service) prepareQbitOptions(action domain.Action, m domain.Macro) (map[string]string, error) {
|
||||
|
||||
options := map[string]string{}
|
||||
opts := &qbittorrent.TorrentAddOptions{}
|
||||
|
||||
if action.Paused {
|
||||
options["paused"] = "true"
|
||||
opts.Paused = BoolPointer(true)
|
||||
}
|
||||
if action.SkipHashCheck {
|
||||
opts.SkipHashCheck = BoolPointer(true)
|
||||
}
|
||||
if action.ContentLayout != "" {
|
||||
if action.ContentLayout == domain.ActionContentLayoutSubfolderCreate {
|
||||
layout := qbittorrent.ContentLayoutSubfolderCreate
|
||||
opts.ContentLayout = &layout
|
||||
} else if action.ContentLayout == domain.ActionContentLayoutSubfolderNone {
|
||||
layout := qbittorrent.ContentLayoutSubfolderNone
|
||||
opts.ContentLayout = &layout
|
||||
}
|
||||
// if ORIGINAL then leave empty
|
||||
}
|
||||
if action.SavePath != "" {
|
||||
// parse and replace values in argument string before continuing
|
||||
|
@ -114,8 +126,8 @@ func (s *service) prepareQbitOptions(action domain.Action, m domain.Macro) (map[
|
|||
return nil, errors.Wrap(err, "could not parse savepath macro: %v", action.SavePath)
|
||||
}
|
||||
|
||||
options["savepath"] = actionArgs
|
||||
options["autoTMM"] = "false"
|
||||
opts.SavePath = &actionArgs
|
||||
opts.AutoTMM = BoolPointer(false)
|
||||
}
|
||||
if action.Category != "" {
|
||||
// parse and replace values in argument string before continuing
|
||||
|
@ -124,7 +136,7 @@ func (s *service) prepareQbitOptions(action domain.Action, m domain.Macro) (map[
|
|||
return nil, errors.Wrap(err, "could not parse category macro: %v", action.Category)
|
||||
}
|
||||
|
||||
options["category"] = categoryArgs
|
||||
opts.Category = &categoryArgs
|
||||
}
|
||||
if action.Tags != "" {
|
||||
// parse and replace values in argument string before continuing
|
||||
|
@ -133,22 +145,26 @@ func (s *service) prepareQbitOptions(action domain.Action, m domain.Macro) (map[
|
|||
return nil, errors.Wrap(err, "could not parse tags macro: %v", action.Tags)
|
||||
}
|
||||
|
||||
options["tags"] = tagsArgs
|
||||
opts.Tags = &tagsArgs
|
||||
}
|
||||
if action.LimitUploadSpeed > 0 {
|
||||
options["upLimit"] = strconv.FormatInt(action.LimitUploadSpeed*1000, 10)
|
||||
opts.LimitUploadSpeed = &action.LimitUploadSpeed
|
||||
}
|
||||
if action.LimitDownloadSpeed > 0 {
|
||||
options["dlLimit"] = strconv.FormatInt(action.LimitDownloadSpeed*1000, 10)
|
||||
opts.LimitDownloadSpeed = &action.LimitDownloadSpeed
|
||||
}
|
||||
if action.LimitRatio > 0 {
|
||||
options["ratioLimit"] = strconv.FormatFloat(action.LimitRatio, 'r', 2, 64)
|
||||
opts.LimitRatio = &action.LimitRatio
|
||||
}
|
||||
if action.LimitSeedTime > 0 {
|
||||
options["seedingTimeLimit"] = strconv.FormatInt(action.LimitSeedTime, 10)
|
||||
opts.LimitSeedTime = &action.LimitSeedTime
|
||||
}
|
||||
|
||||
return options, nil
|
||||
return opts.Prepare(), nil
|
||||
}
|
||||
|
||||
func BoolPointer(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
func (s *service) qbittorrentCheckRulesCanDownload(action domain.Action, client *domain.DownloadClient, qbt *qbittorrent.Client) ([]string, error) {
|
||||
|
|
|
@ -70,6 +70,8 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int) (
|
|||
"save_path",
|
||||
"paused",
|
||||
"ignore_rules",
|
||||
"skip_hash_check",
|
||||
"content_layout",
|
||||
"limit_download_speed",
|
||||
"limit_upload_speed",
|
||||
"limit_ratio",
|
||||
|
@ -103,7 +105,7 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int) (
|
|||
for rows.Next() {
|
||||
var a domain.Action
|
||||
|
||||
var execCmd, execArgs, watchFolder, category, tags, label, savePath, webhookHost, webhookType, webhookMethod, webhookData sql.NullString
|
||||
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, webhookHost, webhookType, webhookMethod, webhookData sql.NullString
|
||||
var limitUl, limitDl, limitSeedTime sql.NullInt64
|
||||
var limitRatio sql.NullFloat64
|
||||
|
||||
|
@ -111,7 +113,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, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &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, &a.SkipHashCheck, &contentLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &clientID); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
|
@ -124,6 +126,7 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int) (
|
|||
a.SavePath = savePath.String
|
||||
a.Paused = paused.Bool
|
||||
a.IgnoreRules = ignoreRules.Bool
|
||||
a.ContentLayout = domain.ActionContentLayout(contentLayout.String)
|
||||
|
||||
a.LimitDownloadSpeed = limitDl.Int64
|
||||
a.LimitUploadSpeed = limitUl.Int64
|
||||
|
@ -324,6 +327,7 @@ func (r *ActionRepo) Store(ctx context.Context, action domain.Action) (*domain.A
|
|||
tags := toNullString(action.Tags)
|
||||
label := toNullString(action.Label)
|
||||
savePath := toNullString(action.SavePath)
|
||||
contentLayout := toNullString(string(action.ContentLayout))
|
||||
webhookHost := toNullString(action.WebhookHost)
|
||||
webhookData := toNullString(action.WebhookData)
|
||||
webhookType := toNullString(action.WebhookType)
|
||||
|
@ -351,6 +355,8 @@ func (r *ActionRepo) Store(ctx context.Context, action domain.Action) (*domain.A
|
|||
"save_path",
|
||||
"paused",
|
||||
"ignore_rules",
|
||||
"skip_hash_check",
|
||||
"content_layout",
|
||||
"limit_upload_speed",
|
||||
"limit_download_speed",
|
||||
"limit_ratio",
|
||||
|
@ -379,6 +385,8 @@ func (r *ActionRepo) Store(ctx context.Context, action domain.Action) (*domain.A
|
|||
savePath,
|
||||
action.Paused,
|
||||
action.IgnoreRules,
|
||||
action.SkipHashCheck,
|
||||
contentLayout,
|
||||
limitUL,
|
||||
limitDL,
|
||||
limitRatio,
|
||||
|
@ -418,6 +426,7 @@ func (r *ActionRepo) Update(ctx context.Context, action domain.Action) (*domain.
|
|||
tags := toNullString(action.Tags)
|
||||
label := toNullString(action.Label)
|
||||
savePath := toNullString(action.SavePath)
|
||||
contentLayout := toNullString(string(action.ContentLayout))
|
||||
webhookHost := toNullString(action.WebhookHost)
|
||||
webhookType := toNullString(action.WebhookType)
|
||||
webhookMethod := toNullString(action.WebhookMethod)
|
||||
|
@ -447,6 +456,8 @@ func (r *ActionRepo) Update(ctx context.Context, action domain.Action) (*domain.
|
|||
Set("save_path", savePath).
|
||||
Set("paused", action.Paused).
|
||||
Set("ignore_rules", action.IgnoreRules).
|
||||
Set("skip_hash_check", action.SkipHashCheck).
|
||||
Set("content_layout", contentLayout).
|
||||
Set("limit_upload_speed", limitUL).
|
||||
Set("limit_download_speed", limitDL).
|
||||
Set("limit_ratio", limitRatio).
|
||||
|
@ -507,6 +518,7 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, actions []*domain.A
|
|||
tags := toNullString(action.Tags)
|
||||
label := toNullString(action.Label)
|
||||
savePath := toNullString(action.SavePath)
|
||||
contentLayout := toNullString(string(action.ContentLayout))
|
||||
webhookHost := toNullString(action.WebhookHost)
|
||||
webhookType := toNullString(action.WebhookType)
|
||||
webhookMethod := toNullString(action.WebhookMethod)
|
||||
|
@ -533,6 +545,8 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, actions []*domain.A
|
|||
"save_path",
|
||||
"paused",
|
||||
"ignore_rules",
|
||||
"skip_hash_check",
|
||||
"content_layout",
|
||||
"limit_upload_speed",
|
||||
"limit_download_speed",
|
||||
"limit_ratio",
|
||||
|
@ -561,6 +575,8 @@ func (r *ActionRepo) StoreFilterActions(ctx context.Context, actions []*domain.A
|
|||
savePath,
|
||||
action.Paused,
|
||||
action.IgnoreRules,
|
||||
action.SkipHashCheck,
|
||||
contentLayout,
|
||||
limitUL,
|
||||
limitDL,
|
||||
limitRatio,
|
||||
|
|
|
@ -78,5 +78,7 @@ func (db *DB) migratePostgres() error {
|
|||
return errors.Wrap(err, "failed to bump schema version")
|
||||
}
|
||||
|
||||
db.log.Info().Msgf("Database schema upgraded to version: %v", len(postgresMigrations))
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
|
|
@ -158,6 +158,8 @@ CREATE TABLE action
|
|||
save_path TEXT,
|
||||
paused BOOLEAN,
|
||||
ignore_rules BOOLEAN,
|
||||
skip_hash_check BOOLEAN DEFAULT false,
|
||||
content_layout TEXT,
|
||||
limit_upload_speed INT,
|
||||
limit_download_speed INT,
|
||||
limit_ratio REAL,
|
||||
|
@ -527,4 +529,11 @@ CREATE INDEX indexer_identifier_index
|
|||
ALTER TABLE filter
|
||||
ADD COLUMN external_webhook_expect_status INTEGER;
|
||||
`,
|
||||
`
|
||||
ALTER TABLE action
|
||||
ADD COLUMN skip_hash_check BOOLEAN DEFAULT FALSE;
|
||||
|
||||
ALTER TABLE action
|
||||
ADD COLUMN content_layout TEXT;
|
||||
`,
|
||||
}
|
||||
|
|
|
@ -98,6 +98,8 @@ func (db *DB) migrateSQLite() error {
|
|||
return errors.Wrap(err, "failed to bump schema version")
|
||||
}
|
||||
|
||||
db.log.Info().Msgf("Database schema upgraded to version: %v", len(sqliteMigrations))
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
|
|
|
@ -158,6 +158,8 @@ CREATE TABLE action
|
|||
save_path TEXT,
|
||||
paused BOOLEAN,
|
||||
ignore_rules BOOLEAN,
|
||||
skip_hash_check BOOLEAN DEFAULT false,
|
||||
content_layout TEXT,
|
||||
limit_upload_speed INT,
|
||||
limit_download_speed INT,
|
||||
limit_ratio REAL,
|
||||
|
@ -847,4 +849,11 @@ CREATE INDEX indexer_identifier_index
|
|||
ALTER TABLE filter
|
||||
ADD COLUMN external_webhook_expect_status INTEGER;
|
||||
`,
|
||||
`
|
||||
ALTER TABLE action
|
||||
ADD COLUMN skip_hash_check BOOLEAN DEFAULT FALSE;
|
||||
|
||||
ALTER TABLE action
|
||||
ADD COLUMN content_layout TEXT;
|
||||
`,
|
||||
}
|
||||
|
|
|
@ -13,35 +13,37 @@ 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"`
|
||||
LimitRatio float64 `json:"limit_ratio,omitempty"`
|
||||
LimitSeedTime int64 `json:"limit_seed_time,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"`
|
||||
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"`
|
||||
SkipHashCheck bool `json:"skip_hash_check,omitempty"`
|
||||
ContentLayout ActionContentLayout `json:"content_layout,omitempty"`
|
||||
LimitUploadSpeed int64 `json:"limit_upload_speed,omitempty"`
|
||||
LimitDownloadSpeed int64 `json:"limit_download_speed,omitempty"`
|
||||
LimitRatio float64 `json:"limit_ratio,omitempty"`
|
||||
LimitSeedTime int64 `json:"limit_seed_time,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
|
||||
|
@ -60,3 +62,11 @@ const (
|
|||
ActionTypeLidarr ActionType = "LIDARR"
|
||||
ActionTypeWhisparr ActionType = "WHISPARR"
|
||||
)
|
||||
|
||||
type ActionContentLayout string
|
||||
|
||||
const (
|
||||
ActionContentLayoutOriginal ActionContentLayout = "ORIGINAL"
|
||||
ActionContentLayoutSubfolderNone ActionContentLayout = "SUBFOLDER_NONE"
|
||||
ActionContentLayoutSubfolderCreate ActionContentLayout = "SUBFOLDER_CREATE"
|
||||
)
|
||||
|
|
|
@ -83,14 +83,14 @@ func (s *service) FindByID(ctx context.Context, filterID int) (*domain.Filter, e
|
|||
// find actions and attach
|
||||
actions, err := s.actionRepo.FindByFilterID(ctx, filter.ID)
|
||||
if err != nil {
|
||||
s.log.Error().Msgf("could not find filter actions: %+v", &filter.ID)
|
||||
s.log.Error().Msgf("could not find filter actions for filter id: %v", filter.ID)
|
||||
}
|
||||
filter.Actions = actions
|
||||
|
||||
// find indexers and attach
|
||||
indexers, err := s.indexerSvc.FindByFilterID(ctx, filter.ID)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msgf("could not find indexers for filter: %+v", &filter.Name)
|
||||
s.log.Error().Err(err).Msgf("could not find indexers for filter: %v", filter.Name)
|
||||
return nil, err
|
||||
}
|
||||
filter.Indexers = indexers
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue