mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 08:49:13 +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
|
@ -2,6 +2,7 @@ package jsonrpc
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -13,6 +14,7 @@ import (
|
|||
|
||||
type Client interface {
|
||||
Call(method string, params ...interface{}) (*RPCResponse, error)
|
||||
CallCtx(ctx context.Context, method string, params ...interface{}) (*RPCResponse, error)
|
||||
}
|
||||
|
||||
type RPCRequest struct {
|
||||
|
@ -61,11 +63,23 @@ type rpcClient struct {
|
|||
endpoint string
|
||||
httpClient *http.Client
|
||||
headers map[string]string
|
||||
|
||||
// HTTP Basic auth username
|
||||
basicUser string
|
||||
|
||||
// HTTP Basic auth password
|
||||
basicPass string
|
||||
}
|
||||
|
||||
type ClientOpts struct {
|
||||
HTTPClient *http.Client
|
||||
Headers map[string]string
|
||||
|
||||
// HTTP Basic auth username
|
||||
BasicUser string
|
||||
|
||||
// HTTP Basic auth password
|
||||
BasicPass string
|
||||
}
|
||||
|
||||
type RPCResponses []*RPCResponse
|
||||
|
@ -95,6 +109,9 @@ func NewClientWithOpts(endpoint string, opts *ClientOpts) Client {
|
|||
}
|
||||
}
|
||||
|
||||
c.basicUser = opts.BasicUser
|
||||
c.basicPass = opts.BasicPass
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
|
@ -106,22 +123,38 @@ func (c *rpcClient) Call(method string, params ...interface{}) (*RPCResponse, er
|
|||
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)
|
||||
if err != nil {
|
||||
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 {
|
||||
return nil, errors.Wrap(err, "error creating request")
|
||||
}
|
||||
request.Header.Set("Content-Type", "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 {
|
||||
request.Header.Set(k, v)
|
||||
}
|
||||
|
@ -129,9 +162,9 @@ func (c *rpcClient) newRequest(req interface{}) (*http.Request, error) {
|
|||
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 {
|
||||
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")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(js, toType)
|
||||
if err != nil {
|
||||
if err = json.Unmarshal(js, toType); err != nil {
|
||||
return errors.Wrap(err, "could not unmarshal object")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,30 +1,85 @@
|
|||
package porla
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/pkg/jsonrpc"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultTimeout = 60 * time.Second
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Name string
|
||||
Hostname string
|
||||
cfg Config
|
||||
rpcClient jsonrpc.Client
|
||||
http *http.Client
|
||||
timeout time.Duration
|
||||
|
||||
log *log.Logger
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
type Config struct {
|
||||
Hostname 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{
|
||||
rpcClient: jsonrpc.NewClientWithOpts(settings.Hostname+"/api/v1/jsonrpc", &jsonrpc.ClientOpts{
|
||||
Headers: map[string]string{
|
||||
"Authorization": "Bearer " + settings.AuthToken,
|
||||
},
|
||||
}),
|
||||
cfg: cfg,
|
||||
log: log.New(io.Discard, "", log.LstdFlags),
|
||||
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
|
||||
}
|
||||
|
|
|
@ -18,3 +18,36 @@ type TorrentsAddReq 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
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Client) Version() (*SysVersionsPorla, error) {
|
||||
response, err := c.rpcClient.Call("sys.versions")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -12,18 +13,15 @@ func (c *Client) Version() (*SysVersionsPorla, error) {
|
|||
}
|
||||
|
||||
var versions *SysVersions
|
||||
err = response.GetObject(&versions)
|
||||
|
||||
if err != nil {
|
||||
if err = response.GetObject(&versions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &versions.Porla, nil
|
||||
}
|
||||
|
||||
func (c *Client) TorrentsAdd(req *TorrentsAddReq) error {
|
||||
response, err := c.rpcClient.Call("torrents.add", req)
|
||||
|
||||
func (c *Client) TorrentsAdd(ctx context.Context, req *TorrentsAddReq) error {
|
||||
response, err := c.rpcClient.CallCtx(ctx, "torrents.add", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -33,11 +31,27 @@ func (c *Client) TorrentsAdd(req *TorrentsAddReq) error {
|
|||
}
|
||||
|
||||
var res *TorrentsAddRes
|
||||
err = response.GetObject(&res)
|
||||
|
||||
if err != nil {
|
||||
if err = response.GetObject(&res); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue