diff --git a/internal/action/qbittorrent.go b/internal/action/qbittorrent.go index 667019e..0d1aa0a 100644 --- a/internal/action/qbittorrent.go +++ b/internal/action/qbittorrent.go @@ -104,6 +104,8 @@ func (s *service) prepareQbitOptions(action *domain.Action) (map[string]string, func (s *service) qbittorrentCheckRulesCanDownload(ctx context.Context, action *domain.Action, client *domain.DownloadClient, qbt *qbittorrent.Client) ([]string, error) { s.log.Trace().Msgf("action qBittorrent: %v check rules", action.Name) + checked := false + // check for active downloads and other rules if client.Settings.Rules.Enabled && !action.IgnoreRules { activeDownloads, err := qbt.GetTorrentsActiveDownloadsCtx(ctx) @@ -117,33 +119,16 @@ func (s *service) qbittorrentCheckRulesCanDownload(ctx context.Context, action * // if max active downloads reached, check speed and if lower than threshold add anyway if len(activeDownloads) >= client.Settings.Rules.MaxActiveDownloads { if client.Settings.Rules.IgnoreSlowTorrents { - // check speeds of downloads - info, err := qbt.GetTransferInfoCtx(ctx) - if err != nil { - return nil, errors.Wrap(err, "could not get transfer info") + if client.Settings.Rules.IgnoreSlowTorrentsCondition == domain.IgnoreSlowTorrentsModeMaxReached { + rejections, err := s.qbittorrentCheckIgnoreSlow(ctx, client, qbt) + if err != nil { + return rejections, err + } + + s.log.Debug().Msg("active downloads are slower than set limit, lets add it") + + checked = true } - - // if current transfer speed is more than threshold return out and skip - // DlInfoSpeed is in bytes so lets convert to KB to match DownloadSpeedThreshold - if info.DlInfoSpeed/1024 >= client.Settings.Rules.DownloadSpeedThreshold { - rejection := fmt.Sprintf("max active downloads reached and total download speed above threshold: %d, skipping", client.Settings.Rules.DownloadSpeedThreshold) - - s.log.Debug().Msg(rejection) - - return []string{rejection}, nil - } - - // if current transfer speed is more than threshold return out and skip - // UpInfoSpeed is in bytes so lets convert to KB to match UploadSpeedThreshold - if info.UpInfoSpeed/1024 >= client.Settings.Rules.UploadSpeedThreshold { - rejection := fmt.Sprintf("max active downloads reached and total upload speed above threshold: %d, skipping", client.Settings.Rules.UploadSpeedThreshold) - - s.log.Debug().Msg(rejection) - - return []string{rejection}, nil - } - - s.log.Debug().Msg("active downloads are slower than set limit, lets add it") } else { rejection := "max active downloads reached, skipping" @@ -153,7 +138,56 @@ func (s *service) qbittorrentCheckRulesCanDownload(ctx context.Context, action * } } } + + if !checked && client.Settings.Rules.IgnoreSlowTorrentsCondition == domain.IgnoreSlowTorrentsModeAlways { + rejections, err := s.qbittorrentCheckIgnoreSlow(ctx, client, qbt) + if err != nil { + return rejections, err + } + + if len(rejections) > 0 { + return rejections, nil + } + } } return nil, nil } + +func (s *service) qbittorrentCheckIgnoreSlow(ctx context.Context, client *domain.DownloadClient, qbt *qbittorrent.Client) ([]string, error) { + // get transfer info + info, err := qbt.GetTransferInfoCtx(ctx) + if err != nil { + return nil, errors.Wrap(err, "could not get transfer info") + } + + s.log.Debug().Msgf("checking client ignore slow torrent rules: %+v", info) + + if client.Settings.Rules.DownloadSpeedThreshold > 0 { + // if current transfer speed is more than threshold return out and skip + // DlInfoSpeed is in bytes so lets convert to KB to match DownloadSpeedThreshold + if info.DlInfoSpeed/1024 >= client.Settings.Rules.DownloadSpeedThreshold { + rejection := fmt.Sprintf("max active downloads reached and total download speed (%d) above threshold: (%d), skipping", info.DlInfoSpeed/1024, client.Settings.Rules.DownloadSpeedThreshold) + + s.log.Debug().Msg(rejection) + + return []string{rejection}, nil + } + } + + if client.Settings.Rules.UploadSpeedThreshold > 0 { + // if current transfer speed is more than threshold return out and skip + // UpInfoSpeed is in bytes so lets convert to KB to match UploadSpeedThreshold + if info.UpInfoSpeed/1024 >= client.Settings.Rules.UploadSpeedThreshold { + rejection := fmt.Sprintf("max active downloads reached and total upload speed (%d) above threshold: (%d), skipping", info.UpInfoSpeed/1024, client.Settings.Rules.UploadSpeedThreshold) + + s.log.Debug().Msg(rejection) + + return []string{rejection}, nil + } + } + + s.log.Debug().Msg("active downloads are slower than set limit, lets add it") + + return nil, nil +} diff --git a/internal/domain/client.go b/internal/domain/client.go index 60f809f..cf8b973 100644 --- a/internal/domain/client.go +++ b/internal/domain/client.go @@ -44,11 +44,12 @@ type DownloadClientSettings struct { } type DownloadClientRules struct { - Enabled bool `json:"enabled"` - MaxActiveDownloads int `json:"max_active_downloads"` - IgnoreSlowTorrents bool `json:"ignore_slow_torrents"` - DownloadSpeedThreshold int64 `json:"download_speed_threshold"` - UploadSpeedThreshold int64 `json:"upload_speed_threshold"` + Enabled bool `json:"enabled"` + MaxActiveDownloads int `json:"max_active_downloads"` + IgnoreSlowTorrents bool `json:"ignore_slow_torrents"` + IgnoreSlowTorrentsCondition IgnoreSlowTorrentsCondition `json:"ignore_slow_torrents_condition,omitempty"` + DownloadSpeedThreshold int64 `json:"download_speed_threshold"` + UploadSpeedThreshold int64 `json:"upload_speed_threshold"` } type BasicAuth struct { @@ -57,6 +58,13 @@ type BasicAuth struct { Password string `json:"password,omitempty"` } +type IgnoreSlowTorrentsCondition string + +const ( + IgnoreSlowTorrentsModeAlways IgnoreSlowTorrentsCondition = "ALWAYS" + IgnoreSlowTorrentsModeMaxReached IgnoreSlowTorrentsCondition = "MAX_DOWNLOADS_REACHED" +) + type DownloadClientType string const ( diff --git a/web/src/components/inputs/input_wide.tsx b/web/src/components/inputs/input_wide.tsx index 2d3254b..ee6b07a 100644 --- a/web/src/components/inputs/input_wide.tsx +++ b/web/src/components/inputs/input_wide.tsx @@ -5,6 +5,8 @@ import { useToggle } from "../../hooks/hooks"; import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid"; import { Switch } from "@headlessui/react"; import { ErrorField } from "./common"; +import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select"; +import { SelectFieldProps } from "./select"; interface TextFieldWideProps { name: string; @@ -303,3 +305,100 @@ export const SwitchGroupWideRed = ({ ); +const Input = (props: InputProps) => { + return ( + + ); +}; + +const Control = (props: ControlProps) => { + return ( + + ); +}; + +const Menu = (props: MenuProps) => { + return ( + + ); +}; + +const Option = (props: OptionProps) => { + return ( + + ); +}; + +export const SelectFieldWide = ({ + name, + label, + optionDefaultText, + options +}: SelectFieldProps) => ( +
+
+ +
+
+ + {({ + field, + form: { setFieldValue } + }: FieldProps) => ( +