From c6122dbc41a9473c3a90f2f7f98ed98a6a47b6bf Mon Sep 17 00:00:00 2001 From: soup Date: Mon, 25 Dec 2023 22:39:31 +0100 Subject: [PATCH] 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 --- go.mod | 4 +- go.sum | 10 +---- internal/action/qbittorrent.go | 39 +++++++++++++++++++ internal/database/action.go | 24 +++++++++--- internal/database/postgres_migrate.go | 16 +++++--- internal/database/sqlite_migrate.go | 18 +++++---- internal/domain/action.go | 9 +++++ web/src/domain/constants.ts | 6 +++ web/src/screens/filters/sections/Actions.tsx | 1 + .../action_components/ActionQBittorrent.tsx | 15 ++++++- web/src/types/Filter.d.ts | 3 ++ 11 files changed, 115 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 7d44ab8..4d6b4ef 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/anacrolix/torrent v1.53.1 github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef github.com/autobrr/go-deluge v1.1.0 - github.com/autobrr/go-qbittorrent v1.7.2-0.20231029234932-67580aa0e42a + github.com/autobrr/go-qbittorrent v1.8.1 github.com/autobrr/go-rtorrent v1.10.0 github.com/avast/retry-go v3.0.0+incompatible github.com/avast/retry-go/v4 v4.5.1 @@ -18,6 +18,7 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/ergochat/irc-go v0.4.0 github.com/fsnotify/fsnotify v1.7.0 + github.com/go-andiamo/splitter v1.2.5 github.com/go-chi/chi/v5 v5.0.10 github.com/go-chi/render v1.0.3 github.com/gorilla/sessions v1.2.2 @@ -61,7 +62,6 @@ require ( github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/gdm85/go-rencode v0.1.8 // indirect - github.com/go-andiamo/splitter v1.2.5 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gosimple/unidecode v1.0.1 // indirect diff --git a/go.sum b/go.sum index 4df6841..adf48bc 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,8 @@ github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII= github.com/autobrr/go-deluge v1.1.0 h1:wT+FUxjNrYnUhOcZmZSIApCz4tT2n0FzXVfuvOBtcIM= github.com/autobrr/go-deluge v1.1.0/go.mod h1:ndiXT1eHWv/ATNk9TpE8GHIs8OSSUnsImt4Syk+y5LM= -github.com/autobrr/go-qbittorrent v1.7.2-0.20231029234932-67580aa0e42a h1:oMPFpu0QXbkpeJu1LhcO2+pu5pff2cQLWQx/Fv5z8AM= -github.com/autobrr/go-qbittorrent v1.7.2-0.20231029234932-67580aa0e42a/go.mod h1:z88B3+O/1/3doQABErvIOOxE4hjpmIpulu6XzDG/q78= +github.com/autobrr/go-qbittorrent v1.8.1 h1:QQxuEaCKThTmV0LhU6tHZQvYv+eCrKmiCRY0G1RiJG4= +github.com/autobrr/go-qbittorrent v1.8.1/go.mod h1:z88B3+O/1/3doQABErvIOOxE4hjpmIpulu6XzDG/q78= github.com/autobrr/go-rtorrent v1.10.0 h1:SCs7Rdi1BZ3MxNoVIdWK0qTUHQyhSj9rEU8KUTRi4Ug= github.com/autobrr/go-rtorrent v1.10.0/go.mod h1:1CyQ2tcLOGP+p9drOqFiVPb/+QvfExMPCHnEGQd0BmM= github.com/autobrr/sse/v2 v2.0.0-20230520125637-530e06346d7d h1:9EGCYgeugAVWLBAtjHC7AFnXSwUdYfCB98WaOgdDREE= @@ -475,8 +475,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -504,8 +502,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -702,8 +698,6 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/action/qbittorrent.go b/internal/action/qbittorrent.go index 86fef8d..4809636 100644 --- a/internal/action/qbittorrent.go +++ b/internal/action/qbittorrent.go @@ -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), diff --git a/internal/database/action.go b/internal/database/action.go index e5dc256..5ab1a71 100644 --- a/internal/database/action.go +++ b/internal/database/action.go @@ -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), diff --git a/internal/database/postgres_migrate.go b/internal/database/postgres_migrate.go index a506508..1521755 100644 --- a/internal/database/postgres_migrate.go +++ b/internal/database/postgres_migrate.go @@ -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; `, } diff --git a/internal/database/sqlite_migrate.go b/internal/database/sqlite_migrate.go index 9c7cfe3..5f11f1b 100644 --- a/internal/database/sqlite_migrate.go +++ b/internal/database/sqlite_migrate.go @@ -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; `, } diff --git a/internal/domain/action.go b/internal/domain/action.go index 720e24e..c3e759c 100644 --- a/internal/domain/action.go +++ b/internal/domain/action.go @@ -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 } diff --git a/web/src/domain/constants.ts b/web/src/domain/constants.ts index 3ef8d9f..b6dbaa4 100644 --- a/web/src/domain/constants.ts +++ b/web/src/domain/constants.ts @@ -349,6 +349,12 @@ export const ActionContentLayoutOptions: SelectGenericOption[] = [ + { label: "Top of queue", description: "Top of queue", value: "MAX" }, + { label: "Bottom of queue", description: "Bottom of queue", value: "MIN" }, + { label: "Disabled", description: "Disabled", value: "" } +]; + export const ActionRtorrentRenameOptions: SelectGenericOption[] = [ { label: "No", description: "No", value: "ORIGINAL" }, { label: "Yes", description: "Yes", value: "SUBFOLDER_NONE" } diff --git a/web/src/screens/filters/sections/Actions.tsx b/web/src/screens/filters/sections/Actions.tsx index eda6143..b312861 100644 --- a/web/src/screens/filters/sections/Actions.tsx +++ b/web/src/screens/filters/sections/Actions.tsx @@ -54,6 +54,7 @@ export function Actions({ filter, values }: FilterActionsProps) { ignore_rules: false, skip_hash_check: false, content_layout: "" || undefined, + priority: "" || undefined, limit_upload_speed: 0, limit_download_speed: 0, limit_ratio: 0, diff --git a/web/src/screens/filters/sections/action_components/ActionQBittorrent.tsx b/web/src/screens/filters/sections/action_components/ActionQBittorrent.tsx index d629c2c..5ae3069 100644 --- a/web/src/screens/filters/sections/action_components/ActionQBittorrent.tsx +++ b/web/src/screens/filters/sections/action_components/ActionQBittorrent.tsx @@ -1,7 +1,7 @@ import { Link } from "react-router-dom"; import { DocsLink } from "@components/ExternalLink"; -import { ActionContentLayoutOptions } from "@domain/constants"; +import { ActionContentLayoutOptions, ActionPriorityOptions } from "@domain/constants"; import * as Input from "@components/inputs"; import { CollapsibleSection } from "../_components"; @@ -105,6 +105,19 @@ export const QBittorrent = ({ idx, action, clients }: ClientActionProps) => ( description="Add torrent and skip hash check" /> + + +

Torrent Queueing will be enabled for you if it is disabled. Ensure you set your preferred limits for it in your client.

+ + } + /> +