mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 16:59:12 +00:00
feat(download-clients): porla implement rules (#711)
* feat(downloadclients): Porla implement rules * feat(downloadclients): Porla add basic auth support * feat(porla): use new token for auth * feat(porla): update check can download rules
This commit is contained in:
parent
209e23de4f
commit
d100703784
9 changed files with 248 additions and 59 deletions
|
@ -4,54 +4,66 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/autobrr/autobrr/internal/domain"
|
"github.com/autobrr/autobrr/internal/domain"
|
||||||
"github.com/autobrr/autobrr/pkg/errors"
|
"github.com/autobrr/autobrr/pkg/errors"
|
||||||
"github.com/autobrr/autobrr/pkg/porla"
|
"github.com/autobrr/autobrr/pkg/porla"
|
||||||
|
|
||||||
"github.com/dcarbone/zadapters/zstdlog"
|
"github.com/dcarbone/zadapters/zstdlog"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *service) porla(action domain.Action, release domain.Release) ([]string, error) {
|
func (s *service) porla(ctx context.Context, action *domain.Action, release domain.Release) ([]string, error) {
|
||||||
s.log.Debug().Msgf("action Porla: %v", action.Name)
|
s.log.Debug().Msgf("action Porla: %s", action.Name)
|
||||||
|
|
||||||
client, err := s.clientSvc.FindByID(context.TODO(), action.ClientID)
|
client, err := s.clientSvc.FindByID(ctx, action.ClientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error finding client: %v", action.ClientID)
|
return nil, errors.Wrap(err, "error finding client: %d", action.ClientID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if client == nil {
|
if client == nil {
|
||||||
return nil, errors.New("could not find client by id: %v", action.ClientID)
|
return nil, errors.New("could not find client by id: %d", action.ClientID)
|
||||||
}
|
}
|
||||||
|
|
||||||
porlaSettings := porla.Settings{
|
porlaSettings := porla.Config{
|
||||||
Hostname: client.Host,
|
Hostname: client.Host,
|
||||||
AuthToken: client.Settings.APIKey,
|
AuthToken: client.Settings.APIKey,
|
||||||
|
TLSSkipVerify: client.TLSSkipVerify,
|
||||||
|
BasicUser: client.Settings.Basic.Username,
|
||||||
|
BasicPass: client.Settings.Basic.Password,
|
||||||
}
|
}
|
||||||
|
|
||||||
porlaSettings.Log = zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "Porla").Str("client", client.Name).Logger(), zerolog.TraceLevel)
|
porlaSettings.Log = zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "Porla").Str("client", client.Name).Logger(), zerolog.TraceLevel)
|
||||||
|
|
||||||
prl := porla.NewClient(porlaSettings)
|
prl := porla.NewClient(porlaSettings)
|
||||||
|
|
||||||
|
rejections, err := s.porlaCheckRulesCanDownload(ctx, action, client, prl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error checking Porla client rules: %s", action.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rejections) > 0 {
|
||||||
|
return rejections, nil
|
||||||
|
}
|
||||||
|
|
||||||
if release.TorrentTmpFile == "" {
|
if release.TorrentTmpFile == "" {
|
||||||
if err := release.DownloadTorrentFile(); err != nil {
|
if err := release.DownloadTorrentFile(); err != nil {
|
||||||
return nil, errors.Wrap(err, "error downloading torrent file for release: %v", release.TorrentName)
|
return nil, errors.Wrap(err, "error downloading torrent file for release: %s", release.TorrentName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.Open(release.TorrentTmpFile)
|
file, err := os.Open(release.TorrentTmpFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error opening file %v", release.TorrentTmpFile)
|
return nil, errors.Wrap(err, "error opening file %s", release.TorrentTmpFile)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
reader := bufio.NewReader(file)
|
reader := bufio.NewReader(file)
|
||||||
content, err := ioutil.ReadAll(reader)
|
content, err := io.ReadAll(reader)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to read file: %v", release.TorrentTmpFile)
|
return nil, errors.Wrap(err, "failed to read file: %s", release.TorrentTmpFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := &porla.TorrentsAddReq{
|
opts := &porla.TorrentsAddReq{
|
||||||
|
@ -69,7 +81,7 @@ func (s *service) porla(action domain.Action, release domain.Release) ([]string,
|
||||||
opts.UploadLimit = action.LimitUploadSpeed * 1000
|
opts.UploadLimit = action.LimitUploadSpeed * 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = prl.TorrentsAdd(opts); err != nil {
|
if err = prl.TorrentsAdd(ctx, opts); err != nil {
|
||||||
return nil, errors.Wrap(err, "could not add torrent %v to client: %v", release.TorrentTmpFile, client.Name)
|
return nil, errors.Wrap(err, "could not add torrent %v to client: %v", release.TorrentTmpFile, client.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,3 +89,27 @@ func (s *service) porla(action domain.Action, release domain.Release) ([]string,
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) porlaCheckRulesCanDownload(ctx context.Context, action *domain.Action, client *domain.DownloadClient, prla *porla.Client) ([]string, error) {
|
||||||
|
s.log.Trace().Msgf("action Porla: %s check rules", action.Name)
|
||||||
|
|
||||||
|
// check for active downloads and other rules
|
||||||
|
if client.Settings.Rules.Enabled && !action.IgnoreRules {
|
||||||
|
torrents, err := prla.TorrentsList(ctx, &porla.TorrentsListFilters{Query: "is:downloading and not is:paused"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not fetch active downloads")
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.Settings.Rules.MaxActiveDownloads > 0 {
|
||||||
|
if len(torrents.Torrents) >= client.Settings.Rules.MaxActiveDownloads {
|
||||||
|
rejection := "max active downloads reached, skipping"
|
||||||
|
|
||||||
|
s.log.Debug().Msg(rejection)
|
||||||
|
|
||||||
|
return []string{rejection}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (s *service) RunAction(ctx context.Context, action *domain.Action, release
|
||||||
rejections, err = s.transmission(ctx, action, release)
|
rejections, err = s.transmission(ctx, action, release)
|
||||||
|
|
||||||
case domain.ActionTypePorla:
|
case domain.ActionTypePorla:
|
||||||
rejections, err = s.porla(*action, release)
|
rejections, err = s.porla(ctx, action, release)
|
||||||
|
|
||||||
case domain.ActionTypeRadarr:
|
case domain.ActionTypeRadarr:
|
||||||
rejections, err = s.radarr(ctx, action, release)
|
rejections, err = s.radarr(ctx, action, release)
|
||||||
|
|
|
@ -264,7 +264,7 @@ func (s *service) testReadarrConnection(ctx context.Context, client domain.Downl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) testPorlaConnection(client domain.DownloadClient) error {
|
func (s *service) testPorlaConnection(client domain.DownloadClient) error {
|
||||||
p := porla.NewClient(porla.Settings{
|
p := porla.NewClient(porla.Config{
|
||||||
Hostname: client.Host,
|
Hostname: client.Host,
|
||||||
AuthToken: client.Settings.APIKey,
|
AuthToken: client.Settings.APIKey,
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@ package jsonrpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
Call(method string, params ...interface{}) (*RPCResponse, error)
|
Call(method string, params ...interface{}) (*RPCResponse, error)
|
||||||
|
CallCtx(ctx context.Context, method string, params ...interface{}) (*RPCResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RPCRequest struct {
|
type RPCRequest struct {
|
||||||
|
@ -61,11 +63,23 @@ type rpcClient struct {
|
||||||
endpoint string
|
endpoint string
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
headers map[string]string
|
headers map[string]string
|
||||||
|
|
||||||
|
// HTTP Basic auth username
|
||||||
|
basicUser string
|
||||||
|
|
||||||
|
// HTTP Basic auth password
|
||||||
|
basicPass string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientOpts struct {
|
type ClientOpts struct {
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
Headers map[string]string
|
Headers map[string]string
|
||||||
|
|
||||||
|
// HTTP Basic auth username
|
||||||
|
BasicUser string
|
||||||
|
|
||||||
|
// HTTP Basic auth password
|
||||||
|
BasicPass string
|
||||||
}
|
}
|
||||||
|
|
||||||
type RPCResponses []*RPCResponse
|
type RPCResponses []*RPCResponse
|
||||||
|
@ -95,6 +109,9 @@ func NewClientWithOpts(endpoint string, opts *ClientOpts) Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.basicUser = opts.BasicUser
|
||||||
|
c.basicPass = opts.BasicPass
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,22 +123,38 @@ func (c *rpcClient) Call(method string, params ...interface{}) (*RPCResponse, er
|
||||||
Params: Params(params...),
|
Params: Params(params...),
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.doCall(request)
|
return c.doCall(context.TODO(), request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rpcClient) newRequest(req interface{}) (*http.Request, error) {
|
func (c *rpcClient) CallCtx(ctx context.Context, method string, params ...interface{}) (*RPCResponse, error) {
|
||||||
|
request := RPCRequest{
|
||||||
|
ID: 1,
|
||||||
|
JsonRPC: "2.0",
|
||||||
|
Method: method,
|
||||||
|
Params: Params(params...),
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.doCall(ctx, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rpcClient) newRequest(ctx context.Context, req interface{}) (*http.Request, error) {
|
||||||
body, err := json.Marshal(req)
|
body, err := json.Marshal(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not marshal request")
|
return nil, errors.Wrap(err, "could not marshal request")
|
||||||
}
|
}
|
||||||
|
|
||||||
request, err := http.NewRequest("POST", c.endpoint, bytes.NewReader(body))
|
request, err := http.NewRequestWithContext(ctx, "POST", c.endpoint, bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error creating request")
|
return nil, errors.Wrap(err, "error creating request")
|
||||||
}
|
}
|
||||||
request.Header.Set("Content-Type", "application/json")
|
request.Header.Set("Content-Type", "application/json")
|
||||||
request.Header.Set("Accept", "application/json")
|
request.Header.Set("Accept", "application/json")
|
||||||
|
|
||||||
|
// set basic auth
|
||||||
|
if c.basicUser != "" && c.basicPass != "" {
|
||||||
|
request.SetBasicAuth(c.basicUser, c.basicPass)
|
||||||
|
}
|
||||||
|
|
||||||
for k, v := range c.headers {
|
for k, v := range c.headers {
|
||||||
request.Header.Set(k, v)
|
request.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
|
@ -129,9 +162,9 @@ func (c *rpcClient) newRequest(req interface{}) (*http.Request, error) {
|
||||||
return request, nil
|
return request, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rpcClient) doCall(request RPCRequest) (*RPCResponse, error) {
|
func (c *rpcClient) doCall(ctx context.Context, request RPCRequest) (*RPCResponse, error) {
|
||||||
|
|
||||||
httpRequest, err := c.newRequest(request)
|
httpRequest, err := c.newRequest(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not create rpc http request")
|
return nil, errors.Wrap(err, "could not create rpc http request")
|
||||||
}
|
}
|
||||||
|
@ -225,8 +258,7 @@ func (r *RPCResponse) GetObject(toType interface{}) error {
|
||||||
return errors.Wrap(err, "could not marshal object")
|
return errors.Wrap(err, "could not marshal object")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(js, toType)
|
if err = json.Unmarshal(js, toType); err != nil {
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "could not unmarshal object")
|
return errors.Wrap(err, "could not unmarshal object")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,85 @@
|
||||||
package porla
|
package porla
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/autobrr/autobrr/pkg/jsonrpc"
|
"github.com/autobrr/autobrr/pkg/jsonrpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultTimeout = 60 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
Name string
|
Name string
|
||||||
Hostname string
|
Hostname string
|
||||||
|
cfg Config
|
||||||
rpcClient jsonrpc.Client
|
rpcClient jsonrpc.Client
|
||||||
|
http *http.Client
|
||||||
|
timeout time.Duration
|
||||||
|
|
||||||
|
log *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type Settings struct {
|
type Config struct {
|
||||||
Hostname string
|
Hostname string
|
||||||
AuthToken string
|
AuthToken string
|
||||||
Log *log.Logger
|
|
||||||
|
// TLS skip cert validation
|
||||||
|
TLSSkipVerify bool
|
||||||
|
|
||||||
|
// HTTP Basic auth username
|
||||||
|
BasicUser string
|
||||||
|
|
||||||
|
// HTTP Basic auth password
|
||||||
|
BasicPass string
|
||||||
|
|
||||||
|
Timeout int
|
||||||
|
Log *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(settings Settings) *Client {
|
func NewClient(cfg Config) *Client {
|
||||||
c := &Client{
|
c := &Client{
|
||||||
rpcClient: jsonrpc.NewClientWithOpts(settings.Hostname+"/api/v1/jsonrpc", &jsonrpc.ClientOpts{
|
cfg: cfg,
|
||||||
Headers: map[string]string{
|
log: log.New(io.Discard, "", log.LstdFlags),
|
||||||
"Authorization": "Bearer " + settings.AuthToken,
|
timeout: DefaultTimeout,
|
||||||
},
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// override logger if we pass one
|
||||||
|
if cfg.Log != nil {
|
||||||
|
c.log = cfg.Log
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Timeout > 0 {
|
||||||
|
c.timeout = time.Duration(cfg.Timeout) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
c.http = &http.Client{
|
||||||
|
Timeout: c.timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
customTransport := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
if cfg.TLSSkipVerify {
|
||||||
|
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Timeout: c.timeout,
|
||||||
|
Transport: customTransport,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.rpcClient = jsonrpc.NewClientWithOpts(cfg.Hostname+"/api/v1/jsonrpc", &jsonrpc.ClientOpts{
|
||||||
|
Headers: map[string]string{
|
||||||
|
"X-Porla-Token": cfg.AuthToken,
|
||||||
|
},
|
||||||
|
HTTPClient: httpClient,
|
||||||
|
BasicUser: cfg.BasicUser,
|
||||||
|
BasicPass: cfg.BasicPass,
|
||||||
|
})
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,3 +18,36 @@ type TorrentsAddReq struct {
|
||||||
|
|
||||||
type TorrentsAddRes struct {
|
type TorrentsAddRes struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TorrentsListReq struct {
|
||||||
|
Filters *TorrentsListFilters `json:"filters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TorrentsListFilters struct {
|
||||||
|
Query string `json:"query"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TorrentsListRes struct {
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
TorrentsTotal int `json:"torrents_total"`
|
||||||
|
Torrents []Torrent `json:"torrents"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Torrent struct {
|
||||||
|
DownloadRate int `json:"download_rate"`
|
||||||
|
UploadRate int `json:"upload_rate"`
|
||||||
|
Flags int `json:"flags"`
|
||||||
|
InfoHash []string `json:"info_hash"`
|
||||||
|
ListPeers int `json:"list_peers"`
|
||||||
|
ListSeeds int `json:"list_seeds"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
NumPeers int `json:"num_peers"`
|
||||||
|
NumSeeds int `json:"num_seeds"`
|
||||||
|
Progress float64 `json:"progress"`
|
||||||
|
QueuePosition int `json:"queue_position"`
|
||||||
|
SavePath string `json:"save_path"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
TotalDone int `json:"total_done"`
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package porla
|
package porla
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
func (c *Client) Version() (*SysVersionsPorla, error) {
|
func (c *Client) Version() (*SysVersionsPorla, error) {
|
||||||
response, err := c.rpcClient.Call("sys.versions")
|
response, err := c.rpcClient.Call("sys.versions")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -12,18 +13,15 @@ func (c *Client) Version() (*SysVersionsPorla, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var versions *SysVersions
|
var versions *SysVersions
|
||||||
err = response.GetObject(&versions)
|
if err = response.GetObject(&versions); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &versions.Porla, nil
|
return &versions.Porla, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) TorrentsAdd(req *TorrentsAddReq) error {
|
func (c *Client) TorrentsAdd(ctx context.Context, req *TorrentsAddReq) error {
|
||||||
response, err := c.rpcClient.Call("torrents.add", req)
|
response, err := c.rpcClient.CallCtx(ctx, "torrents.add", req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -33,11 +31,27 @@ func (c *Client) TorrentsAdd(req *TorrentsAddReq) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var res *TorrentsAddRes
|
var res *TorrentsAddRes
|
||||||
err = response.GetObject(&res)
|
if err = response.GetObject(&res); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) TorrentsList(ctx context.Context, filters *TorrentsListFilters) (*TorrentsListRes, error) {
|
||||||
|
response, err := c.rpcClient.CallCtx(ctx, "torrents.list", TorrentsListReq{Filters: filters})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Error != nil {
|
||||||
|
return nil, response.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
var res *TorrentsListRes
|
||||||
|
if err = response.GetObject(&res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
|
@ -162,7 +162,7 @@ function FormFieldsQbit() {
|
||||||
|
|
||||||
function FormFieldsPorla() {
|
function FormFieldsPorla() {
|
||||||
const {
|
const {
|
||||||
values: {}
|
values: { tls, settings }
|
||||||
} = useFormikContext<InitialValues>();
|
} = useFormikContext<InitialValues>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -173,7 +173,25 @@ function FormFieldsPorla() {
|
||||||
help="Eg. http(s)://client.domain.ltd, http(s)://domain.ltd/porla, http://domain.ltd:port"
|
help="Eg. http(s)://client.domain.ltd, http(s)://domain.ltd/porla, http://domain.ltd:port"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SwitchGroupWide name="tls" label="TLS" />
|
||||||
|
|
||||||
<PasswordFieldWide name="settings.apikey" label="Auth token" />
|
<PasswordFieldWide name="settings.apikey" label="Auth token" />
|
||||||
|
|
||||||
|
{tls && (
|
||||||
|
<SwitchGroupWide
|
||||||
|
name="tls_skip_verify"
|
||||||
|
label="Skip TLS verification (insecure)"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<SwitchGroupWide name="settings.basic.auth" label="Basic auth" />
|
||||||
|
|
||||||
|
{settings.basic?.auth === true && (
|
||||||
|
<>
|
||||||
|
<TextFieldWide name="settings.basic.username" label="Username" />
|
||||||
|
<PasswordFieldWide name="settings.basic.password" label="Password" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -264,7 +282,7 @@ function FormFieldsRulesBasic() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormFieldsRules() {
|
function FormFieldsRulesQbit() {
|
||||||
const {
|
const {
|
||||||
values: { settings }
|
values: { settings }
|
||||||
} = useFormikContext<InitialValues>();
|
} = useFormikContext<InitialValues>();
|
||||||
|
@ -325,7 +343,8 @@ function FormFieldsRules() {
|
||||||
export const rulesComponentMap: componentMapType = {
|
export const rulesComponentMap: componentMapType = {
|
||||||
DELUGE_V1: <FormFieldsRulesBasic/>,
|
DELUGE_V1: <FormFieldsRulesBasic/>,
|
||||||
DELUGE_V2: <FormFieldsRulesBasic/>,
|
DELUGE_V2: <FormFieldsRulesBasic/>,
|
||||||
QBITTORRENT: <FormFieldsRules/>
|
QBITTORRENT: <FormFieldsRulesQbit/>,
|
||||||
|
PORLA: <FormFieldsRulesBasic/>
|
||||||
};
|
};
|
||||||
|
|
||||||
interface formButtonsProps {
|
interface formButtonsProps {
|
||||||
|
|
|
@ -398,20 +398,6 @@ const TypeForm = ({ action, idx, clients }: TypeFormProps) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case "RADARR":
|
|
||||||
case "SONARR":
|
|
||||||
case "LIDARR":
|
|
||||||
case "WHISPARR":
|
|
||||||
case "READARR":
|
|
||||||
return (
|
|
||||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
|
||||||
<DownloadClientSelect
|
|
||||||
name={`actions.${idx}.client_id`}
|
|
||||||
action={action}
|
|
||||||
clients={clients}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "PORLA":
|
case "PORLA":
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
@ -448,6 +434,20 @@ const TypeForm = ({ action, idx, clients }: TypeFormProps) => {
|
||||||
</CollapsableSection>
|
</CollapsableSection>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
case "RADARR":
|
||||||
|
case "SONARR":
|
||||||
|
case "LIDARR":
|
||||||
|
case "WHISPARR":
|
||||||
|
case "READARR":
|
||||||
|
return (
|
||||||
|
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||||
|
<DownloadClientSelect
|
||||||
|
name={`actions.${idx}.client_id`}
|
||||||
|
action={action}
|
||||||
|
clients={clients}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue