feat(releases): support magnet links (#730)

* feat(releases): support magnet links

* feat(feeds): support magnet links

* feat(actions): log messages

* fix: component warning

* fix: check hasprefix instead of hassuffix for magnet

* feat(release): resolve magnet uri from link

* fix(actions): deluge use magnet uri

* fix(macros): add `MagnetURI` var

* fix(actions): run magnet resolving before macros

* feat(feeds): set download type on creation
This commit is contained in:
ze0s 2023-02-28 22:16:10 +01:00 committed by GitHub
parent c6101cc765
commit ca196f0bf1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 770 additions and 260 deletions

View file

@ -13,19 +13,19 @@ import (
)
func (s *service) deluge(ctx context.Context, action *domain.Action, release domain.Release) ([]string, error) {
s.log.Debug().Msgf("action Deluge: %v", action.Name)
s.log.Debug().Msgf("action Deluge: %s", action.Name)
var err error
// get client for action
client, err := s.clientSvc.FindByID(ctx, action.ClientID)
if err != nil {
s.log.Error().Stack().Err(err).Msgf("error finding client: %v", action.ClientID)
s.log.Error().Stack().Err(err).Msgf("error finding client: %d", action.ClientID)
return nil, err
}
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)
}
var rejections []string
@ -54,7 +54,7 @@ func (s *service) delugeCheckRulesCanDownload(deluge delugeClient.DelugeClient,
// make sure it's not set to 0 by default
if client.Settings.Rules.MaxActiveDownloads > 0 {
// if max active downloads reached, check speed and if lower than threshold add anyways
// if max active downloads reached, check speed and if lower than threshold add anyway
if len(activeDownloads) >= client.Settings.Rules.MaxActiveDownloads {
s.log.Debug().Msg("max active downloads reached, skipping")
@ -101,7 +101,7 @@ func (s *service) delugeV1(ctx context.Context, client *domain.DownloadClient, a
// perform connection to Deluge server
err := deluge.Connect()
if err != nil {
return nil, errors.Wrap(err, "could not connect to client %v at %v", client.Name, client.Host)
return nil, errors.Wrap(err, "could not connect to client %s at %s", client.Name, client.Host)
}
defer deluge.Close()
@ -109,59 +109,92 @@ func (s *service) delugeV1(ctx context.Context, client *domain.DownloadClient, a
// perform connection to Deluge server
rejections, err := s.delugeCheckRulesCanDownload(deluge, client, action)
if err != nil {
s.log.Error().Err(err).Msgf("error checking client rules: %v", action.Name)
s.log.Error().Err(err).Msgf("error checking client rules: %s", action.Name)
return nil, err
}
if rejections != nil {
return rejections, nil
}
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
s.log.Error().Err(err).Msgf("could not download torrent file for release: %v", release.TorrentName)
return nil, err
}
}
t, err := os.ReadFile(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "could not read torrent file: %v", release.TorrentTmpFile)
}
// encode file to base64 before sending to deluge
encodedFile := base64.StdEncoding.EncodeToString(t)
if encodedFile == "" {
return nil, errors.Wrap(err, "could not encode torrent file: %v", release.TorrentTmpFile)
}
options, err := s.prepareDelugeOptions(action)
if err != nil {
return nil, errors.Wrap(err, "could not prepare options")
}
s.log.Trace().Msgf("action Deluge options: %+v", options)
torrentHash, err := deluge.AddTorrentFile(release.TorrentTmpFile, encodedFile, &options)
if err != nil {
return nil, errors.Wrap(err, "could not add torrent %v to client: %v", release.TorrentTmpFile, client.Name)
}
if action.Label != "" {
labelPluginActive, err := deluge.LabelPlugin()
if release.HasMagnetUri() {
options, err := s.prepareDelugeOptions(action)
if err != nil {
return nil, errors.Wrap(err, "could not load label plugin for client: %v", client.Name)
return nil, errors.Wrap(err, "could not prepare options")
}
if labelPluginActive != nil {
// TODO first check if label exists, if not, add it, otherwise set
err = labelPluginActive.SetTorrentLabel(torrentHash, action.Label)
s.log.Trace().Msgf("action Deluge options: %+v", options)
torrentHash, err := deluge.AddTorrentMagnet(release.MagnetURI, &options)
if err != nil {
return nil, errors.Wrap(err, "could not add torrent magnet %s to client: %s", release.TorrentURL, client.Name)
}
if action.Label != "" {
labelPluginActive, err := deluge.LabelPlugin()
if err != nil {
return nil, errors.Wrap(err, "could not set label: %v on client: %v", action.Label, client.Name)
return nil, errors.Wrap(err, "could not load label plugin for client: %s", client.Name)
}
if labelPluginActive != nil {
// TODO first check if label exists, if not, add it, otherwise set
err = labelPluginActive.SetTorrentLabel(torrentHash, action.Label)
if err != nil {
return nil, errors.Wrap(err, "could not set label: %s on client: %s", action.Label, client.Name)
}
}
}
}
s.log.Info().Msgf("torrent with hash %v successfully added to client: '%v'", torrentHash, client.Name)
s.log.Info().Msgf("torrent from magnet with hash %s successfully added to client: '%s'", torrentHash, client.Name)
return nil, nil
} else {
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
s.log.Error().Err(err).Msgf("could not download torrent file for release: %s", release.TorrentName)
return nil, err
}
}
t, err := os.ReadFile(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "could not read torrent file: %s", release.TorrentTmpFile)
}
// encode file to base64 before sending to deluge
encodedFile := base64.StdEncoding.EncodeToString(t)
if encodedFile == "" {
return nil, errors.Wrap(err, "could not encode torrent file: %s", release.TorrentTmpFile)
}
options, err := s.prepareDelugeOptions(action)
if err != nil {
return nil, errors.Wrap(err, "could not prepare options")
}
s.log.Trace().Msgf("action Deluge options: %+v", options)
torrentHash, err := deluge.AddTorrentFile(release.TorrentTmpFile, encodedFile, &options)
if err != nil {
return nil, errors.Wrap(err, "could not add torrent %v to client: %v", release.TorrentTmpFile, client.Name)
}
if action.Label != "" {
labelPluginActive, err := deluge.LabelPlugin()
if err != nil {
return nil, errors.Wrap(err, "could not load label plugin for client: %s", client.Name)
}
if labelPluginActive != nil {
// TODO first check if label exists, if not, add it, otherwise set
err = labelPluginActive.SetTorrentLabel(torrentHash, action.Label)
if err != nil {
return nil, errors.Wrap(err, "could not set label: %v on client: %s", action.Label, client.Name)
}
}
}
s.log.Info().Msgf("torrent with hash %s successfully added to client: '%s'", torrentHash, client.Name)
}
return nil, nil
}
@ -181,7 +214,7 @@ func (s *service) delugeV2(ctx context.Context, client *domain.DownloadClient, a
// perform connection to Deluge server
err := deluge.Connect()
if err != nil {
return nil, errors.Wrap(err, "could not connect to client %v at %v", client.Name, client.Host)
return nil, errors.Wrap(err, "could not connect to client %s at %s", client.Name, client.Host)
}
defer deluge.Close()
@ -189,60 +222,93 @@ func (s *service) delugeV2(ctx context.Context, client *domain.DownloadClient, a
// perform connection to Deluge server
rejections, err := s.delugeCheckRulesCanDownload(deluge, client, action)
if err != nil {
s.log.Error().Err(err).Msgf("error checking client rules: %v", action.Name)
s.log.Error().Err(err).Msgf("error checking client rules: %s", action.Name)
return nil, err
}
if rejections != nil {
return rejections, nil
}
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
s.log.Error().Err(err).Msgf("could not download torrent file for release: %v", release.TorrentName)
return nil, err
}
}
t, err := os.ReadFile(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "could not read torrent file: %v", release.TorrentTmpFile)
}
// encode file to base64 before sending to deluge
encodedFile := base64.StdEncoding.EncodeToString(t)
if encodedFile == "" {
return nil, errors.Wrap(err, "could not encode torrent file: %v", release.TorrentTmpFile)
}
// set options
options, err := s.prepareDelugeOptions(action)
if err != nil {
return nil, errors.Wrap(err, "could not prepare options")
}
s.log.Trace().Msgf("action Deluge options: %+v", options)
torrentHash, err := deluge.AddTorrentFile(release.TorrentTmpFile, encodedFile, &options)
if err != nil {
return nil, errors.Wrap(err, "could not add torrent %v to client: %v", release.TorrentTmpFile, client.Name)
}
if action.Label != "" {
labelPluginActive, err := deluge.LabelPlugin()
if release.HasMagnetUri() {
options, err := s.prepareDelugeOptions(action)
if err != nil {
return nil, errors.Wrap(err, "could not load label plugin for client: %v", client.Name)
return nil, errors.Wrap(err, "could not prepare options")
}
if labelPluginActive != nil {
// TODO first check if label exists, if not, add it, otherwise set
err = labelPluginActive.SetTorrentLabel(torrentHash, action.Label)
s.log.Trace().Msgf("action Deluge options: %+v", options)
torrentHash, err := deluge.AddTorrentMagnet(release.MagnetURI, &options)
if err != nil {
return nil, errors.Wrap(err, "could not add torrent magnet %s to client: %s", release.TorrentURL, client.Name)
}
if action.Label != "" {
labelPluginActive, err := deluge.LabelPlugin()
if err != nil {
return nil, errors.Wrap(err, "could not set label: %v on client: %v", action.Label, client.Name)
return nil, errors.Wrap(err, "could not load label plugin for client: %s", client.Name)
}
if labelPluginActive != nil {
// TODO first check if label exists, if not, add it, otherwise set
err = labelPluginActive.SetTorrentLabel(torrentHash, action.Label)
if err != nil {
return nil, errors.Wrap(err, "could not set label: %s on client: %s", action.Label, client.Name)
}
}
}
}
s.log.Info().Msgf("torrent with hash %v successfully added to client: '%v'", torrentHash, client.Name)
s.log.Info().Msgf("torrent with hash %s successfully added to client: '%s'", torrentHash, client.Name)
return nil, nil
} else {
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
s.log.Error().Err(err).Msgf("could not download torrent file for release: %s", release.TorrentName)
return nil, err
}
}
t, err := os.ReadFile(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "could not read torrent file: %s", release.TorrentTmpFile)
}
// encode file to base64 before sending to deluge
encodedFile := base64.StdEncoding.EncodeToString(t)
if encodedFile == "" {
return nil, errors.Wrap(err, "could not encode torrent file: %s", release.TorrentTmpFile)
}
// set options
options, err := s.prepareDelugeOptions(action)
if err != nil {
return nil, errors.Wrap(err, "could not prepare options")
}
s.log.Trace().Msgf("action Deluge options: %+v", options)
torrentHash, err := deluge.AddTorrentFile(release.TorrentTmpFile, encodedFile, &options)
if err != nil {
return nil, errors.Wrap(err, "could not add torrent %s to client: %s", release.TorrentTmpFile, client.Name)
}
if action.Label != "" {
labelPluginActive, err := deluge.LabelPlugin()
if err != nil {
return nil, errors.Wrap(err, "could not load label plugin for client: %s", client.Name)
}
if labelPluginActive != nil {
// TODO first check if label exists, if not, add it, otherwise set
err = labelPluginActive.SetTorrentLabel(torrentHash, action.Label)
if err != nil {
return nil, errors.Wrap(err, "could not set label: %s on client: %s", action.Label, client.Name)
}
}
}
s.log.Info().Msgf("torrent with hash %s successfully added to client: '%s'", torrentHash, client.Name)
}
return nil, nil
}

View file

@ -14,11 +14,11 @@ import (
)
func (s *service) execCmd(ctx context.Context, action *domain.Action, release domain.Release) error {
s.log.Debug().Msgf("action exec: %v release: %v", action.Name, release.TorrentName)
s.log.Debug().Msgf("action exec: %s release: %s", action.Name, release.TorrentName)
if release.TorrentTmpFile == "" && strings.Contains(action.ExecArgs, "TorrentPathName") {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
return errors.Wrap(err, "error downloading torrent file for release: %v", release.TorrentName)
return errors.Wrap(err, "error downloading torrent file for release: %s", release.TorrentName)
}
}
@ -26,7 +26,7 @@ func (s *service) execCmd(ctx context.Context, action *domain.Action, release do
if len(release.TorrentDataRawBytes) == 0 && release.TorrentTmpFile != "" {
t, err := os.ReadFile(release.TorrentTmpFile)
if err != nil {
return errors.Wrap(err, "could not read torrent file: %v", release.TorrentTmpFile)
return errors.Wrap(err, "could not read torrent file: %s", release.TorrentTmpFile)
}
release.TorrentDataRawBytes = t
@ -35,14 +35,14 @@ func (s *service) execCmd(ctx context.Context, action *domain.Action, release do
// check if program exists
cmd, err := exec.LookPath(action.ExecCmd)
if err != nil {
return errors.Wrap(err, "exec failed, could not find program: %v", action.ExecCmd)
return errors.Wrap(err, "exec failed, could not find program: %s", action.ExecCmd)
}
p := shellwords.NewParser()
p.ParseBacktick = true
args, err := p.Parse(action.ExecArgs)
if err != nil {
return errors.Wrap(err, "could not parse exec args: %v", action.ExecArgs)
return errors.Wrap(err, "could not parse exec args: %s", action.ExecArgs)
}
// we need to split on space into a string slice, so we can spread the args into exec
@ -56,14 +56,14 @@ func (s *service) execCmd(ctx context.Context, action *domain.Action, release do
output, err := command.CombinedOutput()
if err != nil {
// everything other than exit 0 is considered an error
return errors.Wrap(err, "error executing command: %v args: %v", cmd, args)
return errors.Wrap(err, "error executing command: %s args: %s", cmd, args)
}
s.log.Trace().Msgf("executed command: '%v'", string(output))
s.log.Trace().Msgf("executed command: '%s'", string(output))
duration := time.Since(start)
s.log.Info().Msgf("executed command: '%v', args: '%v' %v,%v, total time %v", cmd, args, release.TorrentName, release.Indexer, duration)
s.log.Info().Msgf("executed command: '%s', args: '%s' %s,%s, total time %v", cmd, args, release.TorrentName, release.Indexer, duration)
return nil
}

View file

@ -45,6 +45,7 @@ func (s *service) lidarr(ctx context.Context, action *domain.Action, release dom
r := lidarr.Release{
Title: release.TorrentName,
DownloadUrl: release.TorrentURL,
MagnetUrl: release.MagnetURI,
Size: int64(release.Size),
Indexer: release.Indexer,
DownloadProtocol: "torrent",

View file

@ -48,44 +48,69 @@ func (s *service) porla(ctx context.Context, action *domain.Action, release doma
return rejections, nil
}
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFile(); err != nil {
return nil, errors.Wrap(err, "error downloading torrent file for release: %s", release.TorrentName)
if release.HasMagnetUri() {
opts := &porla.TorrentsAddReq{
DownloadLimit: -1,
UploadLimit: -1,
SavePath: action.SavePath,
MagnetUri: release.MagnetURI,
}
}
file, err := os.Open(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "error opening file %s", release.TorrentTmpFile)
}
defer file.Close()
if action.LimitDownloadSpeed > 0 {
opts.DownloadLimit = action.LimitDownloadSpeed * 1000
}
reader := bufio.NewReader(file)
content, err := io.ReadAll(reader)
if err != nil {
return nil, errors.Wrap(err, "failed to read file: %s", release.TorrentTmpFile)
}
if action.LimitUploadSpeed > 0 {
opts.UploadLimit = action.LimitUploadSpeed * 1000
}
opts := &porla.TorrentsAddReq{
DownloadLimit: -1,
SavePath: action.SavePath,
Ti: base64.StdEncoding.EncodeToString(content),
UploadLimit: -1,
}
if err = prl.TorrentsAdd(ctx, opts); err != nil {
return nil, errors.Wrap(err, "could not add torrent from magnet %s to client: %s", release.MagnetURI, client.Name)
}
if action.LimitDownloadSpeed > 0 {
opts.DownloadLimit = action.LimitDownloadSpeed * 1000
}
s.log.Info().Msgf("torrent with hash %s successfully added to client: '%s'", release.TorrentHash, client.Name)
if action.LimitUploadSpeed > 0 {
opts.UploadLimit = action.LimitUploadSpeed * 1000
}
return nil, nil
} else {
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
return nil, errors.Wrap(err, "error downloading torrent file for release: %s", release.TorrentName)
}
}
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)
}
file, err := os.Open(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "error opening file %s", release.TorrentTmpFile)
}
defer file.Close()
s.log.Info().Msgf("torrent with hash %v successfully added to client: '%v'", release.TorrentHash, client.Name)
reader := bufio.NewReader(file)
content, err := io.ReadAll(reader)
if err != nil {
return nil, errors.Wrap(err, "failed to read file: %s", release.TorrentTmpFile)
}
opts := &porla.TorrentsAddReq{
DownloadLimit: -1,
SavePath: action.SavePath,
Ti: base64.StdEncoding.EncodeToString(content),
UploadLimit: -1,
}
if action.LimitDownloadSpeed > 0 {
opts.DownloadLimit = action.LimitDownloadSpeed * 1000
}
if action.LimitUploadSpeed > 0 {
opts.UploadLimit = action.LimitUploadSpeed * 1000
}
if err = prl.TorrentsAdd(ctx, opts); err != nil {
return nil, errors.Wrap(err, "could not add torrent %s to client: %s", release.TorrentTmpFile, client.Name)
}
s.log.Info().Msgf("torrent with hash %s successfully added to client: '%s'", release.TorrentHash, client.Name)
}
return nil, nil
}

View file

@ -11,48 +11,65 @@ import (
)
func (s *service) qbittorrent(ctx context.Context, action *domain.Action, release domain.Release) ([]string, error) {
s.log.Debug().Msgf("action qBittorrent: %v", action.Name)
s.log.Debug().Msgf("action qBittorrent: %s", action.Name)
c := s.clientSvc.GetCachedClient(ctx, action.ClientID)
rejections, err := s.qbittorrentCheckRulesCanDownload(ctx, action, c.Dc, c.Qbt)
if err != nil {
return nil, errors.Wrap(err, "error checking client rules: %v", action.Name)
return nil, errors.Wrap(err, "error checking client rules: %s", action.Name)
}
if len(rejections) > 0 {
return rejections, nil
}
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
return nil, errors.Wrap(err, "error downloading torrent file for release: %v", release.TorrentName)
if release.HasMagnetUri() {
options, err := s.prepareQbitOptions(action)
if err != nil {
return nil, errors.Wrap(err, "could not prepare options")
}
}
options, err := s.prepareQbitOptions(action)
if err != nil {
return nil, errors.Wrap(err, "could not prepare options")
}
s.log.Trace().Msgf("action qBittorrent options: %+v", options)
s.log.Trace().Msgf("action qBittorrent options: %+v", options)
if err = c.Qbt.AddTorrentFromFileCtx(ctx, release.TorrentTmpFile, options); err != nil {
return nil, errors.Wrap(err, "could not add torrent %v to client: %v", release.TorrentTmpFile, c.Dc.Name)
}
if !action.Paused && !action.ReAnnounceSkip && release.TorrentHash != "" {
opts := qbittorrent.ReannounceOptions{
Interval: int(action.ReAnnounceInterval),
MaxAttempts: int(action.ReAnnounceMaxAttempts),
DeleteOnFailure: action.ReAnnounceDelete,
if err = c.Qbt.AddTorrentFromUrlCtx(ctx, release.MagnetURI, options); err != nil {
return nil, errors.Wrap(err, "could not add torrent %s to client: %s", release.MagnetURI, c.Dc.Name)
}
if err := c.Qbt.ReannounceTorrentWithRetry(ctx, opts, release.TorrentHash); err != nil {
return nil, errors.Wrap(err, "could not reannounce torrent: %v", release.TorrentHash)
}
}
s.log.Info().Msgf("torrent with hash %v successfully added to client: '%v'", release.TorrentHash, c.Dc.Name)
s.log.Info().Msgf("torrent from magnet successfully added to client: '%s'", c.Dc.Name)
return nil, nil
} else {
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
return nil, errors.Wrap(err, "error downloading torrent file for release: %s", release.TorrentName)
}
}
options, err := s.prepareQbitOptions(action)
if err != nil {
return nil, errors.Wrap(err, "could not prepare options")
}
s.log.Trace().Msgf("action qBittorrent options: %+v", options)
if err = c.Qbt.AddTorrentFromFileCtx(ctx, release.TorrentTmpFile, options); err != nil {
return nil, errors.Wrap(err, "could not add torrent %s to client: %s", release.TorrentTmpFile, c.Dc.Name)
}
if !action.Paused && !action.ReAnnounceSkip && release.TorrentHash != "" {
opts := qbittorrent.ReannounceOptions{
Interval: int(action.ReAnnounceInterval),
MaxAttempts: int(action.ReAnnounceMaxAttempts),
DeleteOnFailure: action.ReAnnounceDelete,
}
if err := c.Qbt.ReannounceTorrentWithRetry(ctx, opts, release.TorrentHash); err != nil {
return nil, errors.Wrap(err, "could not reannounce torrent: %s", release.TorrentHash)
}
}
s.log.Info().Msgf("torrent with hash %s successfully added to client: '%s'", release.TorrentHash, c.Dc.Name)
}
return nil, nil
}

View file

@ -44,6 +44,7 @@ func (s *service) radarr(ctx context.Context, action *domain.Action, release dom
r := radarr.Release{
Title: release.TorrentName,
DownloadUrl: release.TorrentURL,
MagnetUrl: release.MagnetURI,
Size: int64(release.Size),
Indexer: release.Indexer,
DownloadProtocol: "torrent",

View file

@ -44,6 +44,7 @@ func (s *service) readarr(ctx context.Context, action *domain.Action, release do
r := readarr.Release{
Title: release.TorrentName,
DownloadUrl: release.TorrentURL,
MagnetUrl: release.MagnetURI,
Size: int64(release.Size),
Indexer: release.Indexer,
DownloadProtocol: "torrent",

View file

@ -2,66 +2,93 @@ package action
import (
"context"
"os"
"github.com/autobrr/autobrr/internal/domain"
"github.com/autobrr/autobrr/pkg/errors"
"os"
"github.com/mrobinsn/go-rtorrent/rtorrent"
)
func (s *service) rtorrent(ctx context.Context, action *domain.Action, release domain.Release) ([]string, error) {
s.log.Debug().Msgf("action rTorrent: %v", action.Name)
s.log.Debug().Msgf("action rTorrent: %s", action.Name)
var err error
// get client for action
client, err := s.clientSvc.FindByID(ctx, action.ClientID)
if err != nil {
s.log.Error().Stack().Err(err).Msgf("error finding client: %v", action.ClientID)
s.log.Error().Stack().Err(err).Msgf("error finding client: %d", action.ClientID)
return nil, err
}
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)
}
var rejections []string
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
s.log.Error().Err(err).Msgf("could not download torrent file for release: %v", release.TorrentName)
return nil, err
}
}
// create client
rt := rtorrent.New(client.Host, true)
tmpFile, err := os.ReadFile(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "could not read torrent file: %v", release.TorrentTmpFile)
}
if release.HasMagnetUri() {
var args []*rtorrent.FieldValue
var args []*rtorrent.FieldValue
if action.Label != "" {
args = append(args, &rtorrent.FieldValue{
Field: rtorrent.DLabel,
Value: action.Label,
})
}
if action.SavePath != "" {
args = append(args, &rtorrent.FieldValue{
Field: rtorrent.DDirectory,
Value: action.SavePath,
})
}
if action.Label != "" {
args = append(args, &rtorrent.FieldValue{
Field: rtorrent.DLabel,
Value: action.Label,
})
}
if action.SavePath != "" {
args = append(args, &rtorrent.FieldValue{
Field: rtorrent.DDirectory,
Value: action.SavePath,
})
}
if err := rt.Add(release.MagnetURI, args...); err != nil {
return nil, errors.Wrap(err, "could not add torrent from magnet: %s", release.MagnetURI)
}
if err := rt.AddTorrent(tmpFile, args...); err != nil {
return nil, errors.Wrap(err, "could not add torrent file: %v", release.TorrentTmpFile)
}
s.log.Info().Msgf("torrent from magnet successfully added to client: '%s'", client.Name)
s.log.Info().Msgf("torrent with hash %v successfully added to client: '%v'", "", client.Name)
return nil, nil
} else {
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
s.log.Error().Err(err).Msgf("could not download torrent file for release: %s", release.TorrentName)
return nil, err
}
}
tmpFile, err := os.ReadFile(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "could not read torrent file: %s", release.TorrentTmpFile)
}
var args []*rtorrent.FieldValue
if action.Label != "" {
args = append(args, &rtorrent.FieldValue{
Field: rtorrent.DLabel,
Value: action.Label,
})
}
if action.SavePath != "" {
args = append(args, &rtorrent.FieldValue{
Field: rtorrent.DDirectory,
Value: action.SavePath,
})
}
if err := rt.AddTorrent(tmpFile, args...); err != nil {
return nil, errors.Wrap(err, "could not add torrent file: %s", release.TorrentTmpFile)
}
s.log.Info().Msgf("torrent successfully added to client: '%s'", client.Name)
}
return rejections, nil
}

View file

@ -4,6 +4,7 @@ import (
"bytes"
"context"
"crypto/tls"
"fmt"
"io"
"net/http"
"os"
@ -24,12 +25,18 @@ func (s *service) RunAction(ctx context.Context, action *domain.Action, release
defer func() {
if r := recover(); r != nil {
s.log.Error().Msgf("recovering from panic in run action %v error: %v", action.Name, r)
err = errors.New("panic in action: %v", action.Name)
s.log.Error().Msgf("recovering from panic in run action %s error: %v", action.Name, r)
err = errors.New("panic in action: %s", action.Name)
return
}
}()
// if set, try to resolve MagnetURI before parsing macros
// to allow webhook and exec to get the magnet_uri
if err := release.ResolveMagnetUri(ctx); err != nil {
return nil, err
}
// parse all macros in one go
if err := action.ParseMacros(release); err != nil {
return nil, err
@ -147,6 +154,10 @@ func (s *service) test(name string) {
}
func (s *service) watchFolder(ctx context.Context, action *domain.Action, release domain.Release) error {
if release.HasMagnetUri() {
return fmt.Errorf("action watch folder does not support magnet links: %s", release.TorrentName)
}
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
return errors.Wrap(err, "watch folder: could not download torrent file for release: %v", release.TorrentName)

View file

@ -44,6 +44,7 @@ func (s *service) sonarr(ctx context.Context, action *domain.Action, release dom
r := sonarr.Release{
Title: release.TorrentName,
DownloadUrl: release.TorrentURL,
MagnetUrl: release.MagnetURI,
Size: int64(release.Size),
Indexer: release.Indexer,
DownloadProtocol: "torrent",

View file

@ -10,60 +10,83 @@ import (
)
func (s *service) transmission(ctx context.Context, action *domain.Action, release domain.Release) ([]string, error) {
s.log.Debug().Msgf("action Transmission: %v", action.Name)
s.log.Debug().Msgf("action Transmission: %s", action.Name)
var err error
// get client for action
client, err := s.clientSvc.FindByID(ctx, action.ClientID)
if err != nil {
s.log.Error().Stack().Err(err).Msgf("error finding client: %v", action.ClientID)
s.log.Error().Stack().Err(err).Msgf("error finding client: %d", action.ClientID)
return nil, err
}
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)
}
var rejections []string
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
s.log.Error().Err(err).Msgf("could not download torrent file for release: %v", release.TorrentName)
return nil, err
}
}
tbt, err := transmissionrpc.New(client.Host, client.Username, client.Password, &transmissionrpc.AdvancedConfig{
HTTPS: client.TLS,
Port: uint16(client.Port),
})
if err != nil {
return nil, errors.Wrap(err, "error logging into client: %v", client.Host)
return nil, errors.Wrap(err, "error logging into client: %s", client.Host)
}
b64, err := transmissionrpc.File2Base64(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "cant encode file %v into base64", release.TorrentTmpFile)
}
if release.HasMagnetUri() {
payload := transmissionrpc.TorrentAddPayload{
Filename: &release.MagnetURI,
}
if action.SavePath != "" {
payload.DownloadDir = &action.SavePath
}
if action.Paused {
payload.Paused = &action.Paused
}
payload := transmissionrpc.TorrentAddPayload{
MetaInfo: &b64,
}
if action.SavePath != "" {
payload.DownloadDir = &action.SavePath
}
if action.Paused {
payload.Paused = &action.Paused
}
// Prepare and send payload
torrent, err := tbt.TorrentAdd(ctx, payload)
if err != nil {
return nil, errors.Wrap(err, "could not add torrent from magnet %s to client: %s", release.MagnetURI, client.Host)
}
// Prepare and send payload
torrent, err := tbt.TorrentAdd(ctx, payload)
if err != nil {
return nil, errors.Wrap(err, "could not add torrent %v to client: %v", release.TorrentTmpFile, client.Host)
}
s.log.Info().Msgf("torrent from magnet with hash %v successfully added to client: '%s'", torrent.HashString, client.Name)
s.log.Info().Msgf("torrent with hash %v successfully added to client: '%v'", torrent.HashString, client.Name)
return nil, nil
} else {
if release.TorrentTmpFile == "" {
if err := release.DownloadTorrentFileCtx(ctx); err != nil {
s.log.Error().Err(err).Msgf("could not download torrent file for release: %s", release.TorrentName)
return nil, err
}
}
b64, err := transmissionrpc.File2Base64(release.TorrentTmpFile)
if err != nil {
return nil, errors.Wrap(err, "cant encode file %s into base64", release.TorrentTmpFile)
}
payload := transmissionrpc.TorrentAddPayload{
MetaInfo: &b64,
}
if action.SavePath != "" {
payload.DownloadDir = &action.SavePath
}
if action.Paused {
payload.Paused = &action.Paused
}
// Prepare and send payload
torrent, err := tbt.TorrentAdd(ctx, payload)
if err != nil {
return nil, errors.Wrap(err, "could not add torrent %v to client: %v", release.TorrentTmpFile, client.Host)
}
s.log.Info().Msgf("torrent with hash %v successfully added to client: '%s'", torrent.HashString, client.Name)
}
return rejections, nil
}

View file

@ -44,6 +44,7 @@ func (s *service) whisparr(ctx context.Context, action *domain.Action, release d
r := whisparr.Release{
Title: release.TorrentName,
DownloadUrl: release.TorrentURL,
MagnetUrl: release.MagnetURI,
Size: int64(release.Size),
Indexer: release.Indexer,
DownloadProtocol: "torrent",