mirror of
https://github.com/idanoo/autobrr
synced 2025-07-22 16:29:12 +00:00
feat(clients): add support for qBittorrent 4.4.0+ (#558)
* refactor: move client to go-qbittorrent * refactor: move client to go-qbittorrent * feat(downloadclient): cache qbittorrent client * feat(downloadclient): update qbit * feat(downloadclient): client test and remove pkg qbit * feat(downloadclient): update pkg qbit * fix(release): method * feat(release): make GetCachedClient concurrent safe * feat(release): add additional tests for buildLegacyHost * feat(release): remove branching * chore: update pkg autobrr/go-qbittorrent to v.1.2.0
This commit is contained in:
parent
6ad4abe296
commit
29da2416ec
17 changed files with 379 additions and 1764 deletions
|
@ -7,21 +7,21 @@ import (
|
|||
"github.com/autobrr/autobrr/internal/domain"
|
||||
"github.com/autobrr/autobrr/pkg/errors"
|
||||
"github.com/autobrr/autobrr/pkg/lidarr"
|
||||
"github.com/autobrr/autobrr/pkg/qbittorrent"
|
||||
"github.com/autobrr/autobrr/pkg/radarr"
|
||||
"github.com/autobrr/autobrr/pkg/readarr"
|
||||
"github.com/autobrr/autobrr/pkg/sonarr"
|
||||
"github.com/autobrr/autobrr/pkg/whisparr"
|
||||
"github.com/autobrr/go-qbittorrent"
|
||||
|
||||
delugeClient "github.com/gdm85/go-libdeluge"
|
||||
"github.com/hekmon/transmissionrpc/v2"
|
||||
"github.com/mrobinsn/go-rtorrent/rtorrent"
|
||||
)
|
||||
|
||||
func (s *service) testConnection(client domain.DownloadClient) error {
|
||||
func (s *service) testConnection(ctx context.Context, client domain.DownloadClient) error {
|
||||
switch client.Type {
|
||||
case domain.DownloadClientTypeQbittorrent:
|
||||
return s.testQbittorrentConnection(client)
|
||||
return s.testQbittorrentConnection(ctx, client)
|
||||
|
||||
case domain.DownloadClientTypeDelugeV1, domain.DownloadClientTypeDelugeV2:
|
||||
return s.testDelugeConnection(client)
|
||||
|
@ -51,32 +51,28 @@ func (s *service) testConnection(client domain.DownloadClient) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *service) testQbittorrentConnection(client domain.DownloadClient) error {
|
||||
qbtSettings := qbittorrent.Settings{
|
||||
Hostname: client.Host,
|
||||
Port: uint(client.Port),
|
||||
func (s *service) testQbittorrentConnection(ctx context.Context, client domain.DownloadClient) error {
|
||||
qbtSettings := qbittorrent.Config{
|
||||
Host: client.BuildLegacyHost(),
|
||||
Username: client.Username,
|
||||
Password: client.Password,
|
||||
TLS: client.TLS,
|
||||
TLSSkipVerify: client.TLSSkipVerify,
|
||||
Log: s.subLogger,
|
||||
}
|
||||
|
||||
// only set basic auth if enabled
|
||||
if client.Settings.Basic.Auth {
|
||||
qbtSettings.BasicAuth = client.Settings.Basic.Auth
|
||||
qbtSettings.Basic.Username = client.Settings.Basic.Username
|
||||
qbtSettings.Basic.Password = client.Settings.Basic.Password
|
||||
qbtSettings.BasicUser = client.Settings.Basic.Username
|
||||
qbtSettings.BasicPass = client.Settings.Basic.Password
|
||||
}
|
||||
|
||||
qbt := qbittorrent.NewClient(qbtSettings)
|
||||
|
||||
if err := qbt.Login(); err != nil {
|
||||
if err := qbt.LoginCtx(ctx); err != nil {
|
||||
return errors.Wrap(err, "error logging into client: %v", client.Host)
|
||||
}
|
||||
|
||||
_, err := qbt.GetTorrents()
|
||||
if err != nil {
|
||||
if _, err := qbt.GetTorrentsCtx(ctx, qbittorrent.TorrentFilterOptions{Filter: qbittorrent.TorrentFilterAll}); err != nil {
|
||||
return errors.Wrap(err, "error getting torrents: %v", client.Host)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,12 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
"github.com/autobrr/autobrr/internal/logger"
|
||||
|
||||
"github.com/autobrr/go-qbittorrent"
|
||||
"github.com/dcarbone/zadapters/zstdlog"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
@ -18,19 +20,27 @@ type Service interface {
|
|||
Store(ctx context.Context, client domain.DownloadClient) (*domain.DownloadClient, error)
|
||||
Update(ctx context.Context, client domain.DownloadClient) (*domain.DownloadClient, error)
|
||||
Delete(ctx context.Context, clientID int) error
|
||||
Test(client domain.DownloadClient) error
|
||||
Test(ctx context.Context, client domain.DownloadClient) error
|
||||
|
||||
GetCachedClient(ctx context.Context, clientId int32) *domain.DownloadClientCached
|
||||
}
|
||||
|
||||
type service struct {
|
||||
log zerolog.Logger
|
||||
repo domain.DownloadClientRepo
|
||||
subLogger *log.Logger
|
||||
|
||||
qbitClients map[int32]*domain.DownloadClientCached
|
||||
m sync.RWMutex
|
||||
}
|
||||
|
||||
func NewService(log logger.Logger, repo domain.DownloadClientRepo) Service {
|
||||
s := &service{
|
||||
log: log.With().Str("module", "download_client").Logger(),
|
||||
repo: repo,
|
||||
|
||||
qbitClients: map[int32]*domain.DownloadClientCached{},
|
||||
m: sync.RWMutex{},
|
||||
}
|
||||
|
||||
s.subLogger = zstdlog.NewStdLoggerWithLevel(s.log.With().Logger(), zerolog.TraceLevel)
|
||||
|
@ -91,19 +101,29 @@ func (s *service) Update(ctx context.Context, client domain.DownloadClient) (*do
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if client.Type == domain.DownloadClientTypeQbittorrent {
|
||||
s.m.Lock()
|
||||
delete(s.qbitClients, int32(client.ID))
|
||||
s.m.Unlock()
|
||||
}
|
||||
|
||||
return c, err
|
||||
}
|
||||
|
||||
func (s *service) Delete(ctx context.Context, clientID int) error {
|
||||
err := s.repo.Delete(ctx, clientID)
|
||||
if err != nil {
|
||||
if err := s.repo.Delete(ctx, clientID); err != nil {
|
||||
s.log.Error().Err(err).Msgf("could not delete download client: %v", clientID)
|
||||
return err
|
||||
}
|
||||
|
||||
s.m.Lock()
|
||||
delete(s.qbitClients, int32(clientID))
|
||||
s.m.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) Test(client domain.DownloadClient) error {
|
||||
func (s *service) Test(ctx context.Context, client domain.DownloadClient) error {
|
||||
// basic validation of client
|
||||
if client.Host == "" {
|
||||
return errors.New("validation error: no host")
|
||||
|
@ -112,10 +132,61 @@ func (s *service) Test(client domain.DownloadClient) error {
|
|||
}
|
||||
|
||||
// test
|
||||
if err := s.testConnection(client); err != nil {
|
||||
if err := s.testConnection(ctx, client); err != nil {
|
||||
s.log.Error().Err(err).Msg("client connection test error")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) GetCachedClient(ctx context.Context, clientId int32) *domain.DownloadClientCached {
|
||||
|
||||
// check if client exists in cache
|
||||
s.m.RLock()
|
||||
cached, ok := s.qbitClients[clientId]
|
||||
s.m.RUnlock()
|
||||
|
||||
if ok {
|
||||
return cached
|
||||
}
|
||||
|
||||
// get client for action
|
||||
client, err := s.FindByID(ctx, clientId)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
qbtSettings := qbittorrent.Config{
|
||||
Host: client.BuildLegacyHost(),
|
||||
Username: client.Username,
|
||||
Password: client.Password,
|
||||
TLSSkipVerify: client.TLSSkipVerify,
|
||||
}
|
||||
|
||||
// setup sub logger adapter which is compatible with *log.Logger
|
||||
qbtSettings.Log = zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "qBittorrent").Str("client", client.Name).Logger(), zerolog.TraceLevel)
|
||||
|
||||
// only set basic auth if enabled
|
||||
if client.Settings.Basic.Auth {
|
||||
qbtSettings.BasicUser = client.Settings.Basic.Username
|
||||
qbtSettings.BasicPass = client.Settings.Basic.Password
|
||||
}
|
||||
|
||||
qc := &domain.DownloadClientCached{
|
||||
Dc: client,
|
||||
Qbt: qbittorrent.NewClient(qbtSettings),
|
||||
}
|
||||
|
||||
cached = qc
|
||||
|
||||
s.m.Lock()
|
||||
s.qbitClients[clientId] = cached
|
||||
s.m.Unlock()
|
||||
|
||||
return cached
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue