final prep for 1.0.3
This commit is contained in:
20
README.md
20
README.md
@@ -1,21 +1,21 @@
|
|||||||
# DecyphArr
|
# Decypharr
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**DecyphArr** is an implementation of QbitTorrent with **Multiple Debrid service support**, written in Go.
|
**Decypharr** is an implementation of QbitTorrent with **Multiple Debrid service support**, written in Go.
|
||||||
|
|
||||||
## What is DecyphArr?
|
## What is Decypharr?
|
||||||
|
|
||||||
DecyphArr combines the power of QBittorrent with popular Debrid services to enhance your media management. It provides a familiar interface for Sonarr, Radarr, and other \*Arr applications while leveraging the capabilities of Debrid providers.
|
Decypharr combines the power of QBittorrent with popular Debrid services to enhance your media management. It provides a familiar interface for Sonarr, Radarr, and other \*Arr applications.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- 🔄 Mock Qbittorent API that supports the Arrs (Sonarr, Radarr, Lidarr etc)
|
- Mock Qbittorent API that supports the Arrs (Sonarr, Radarr, Lidarr etc)
|
||||||
- 🖥️ Full-fledged UI for managing torrents
|
- Full-fledged UI for managing torrents
|
||||||
- 🛡️ Proxy support for filtering out un-cached Debrid torrents
|
- Proxy support for filtering out un-cached Debrid torrents
|
||||||
- 🔌 Multiple Debrid providers support
|
- Multiple Debrid providers support
|
||||||
- 📁 WebDAV server support for each debrid provider
|
- WebDAV server support for each debrid provider
|
||||||
- 🔧 Repair Worker for missing files
|
- Repair Worker for missing files
|
||||||
|
|
||||||
## Supported Debrid Providers
|
## Supported Debrid Providers
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ func main() {
|
|||||||
var (
|
var (
|
||||||
configPath string
|
configPath string
|
||||||
isBasicCheck bool
|
isBasicCheck bool
|
||||||
|
debug bool
|
||||||
)
|
)
|
||||||
flag.StringVar(&configPath, "config", "/data", "path to the data folder")
|
flag.StringVar(&configPath, "config", "/data", "path to the data folder")
|
||||||
flag.BoolVar(&isBasicCheck, "basic", false, "perform basic health check without WebDAV")
|
flag.BoolVar(&isBasicCheck, "basic", false, "perform basic health check without WebDAV")
|
||||||
|
flag.BoolVar(&debug, "debug", false, "enable debug mode for detailed output")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
config.SetConfigPath(configPath)
|
config.SetConfigPath(configPath)
|
||||||
cfg := config.Get()
|
cfg := config.Get()
|
||||||
@@ -67,16 +69,17 @@ func main() {
|
|||||||
status.WebUI = true
|
status.WebUI = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check WebDAV if enabled
|
if isBasicCheck {
|
||||||
if !isBasicCheck && webdavPath != "" {
|
status.WebDAVService = checkBaseWebdav(ctx, baseUrl, port)
|
||||||
if checkWebDAV(ctx, baseUrl, port, webdavPath) {
|
} else {
|
||||||
|
// If not a basic check, check WebDAV with debrid path
|
||||||
|
if webdavPath != "" {
|
||||||
|
status.WebDAVService = checkDebridWebDAV(ctx, baseUrl, port, webdavPath)
|
||||||
|
} else {
|
||||||
|
// If no WebDAV path is set, consider it healthy
|
||||||
status.WebDAVService = true
|
status.WebDAVService = true
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// If WebDAV is not enabled, consider it healthy
|
|
||||||
status.WebDAVService = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine overall status
|
// Determine overall status
|
||||||
// Consider the application healthy if core services are running
|
// Consider the application healthy if core services are running
|
||||||
status.OverallStatus = status.QbitAPI && status.WebUI
|
status.OverallStatus = status.QbitAPI && status.WebUI
|
||||||
@@ -85,7 +88,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Optional: output health status as JSON for logging
|
// Optional: output health status as JSON for logging
|
||||||
if os.Getenv("DEBUG") == "true" {
|
if debug {
|
||||||
statusJSON, _ := json.MarshalIndent(status, "", " ")
|
statusJSON, _ := json.MarshalIndent(status, "", " ")
|
||||||
fmt.Println(string(statusJSON))
|
fmt.Println(string(statusJSON))
|
||||||
}
|
}
|
||||||
@@ -136,7 +139,24 @@ func checkWebUI(ctx context.Context, baseUrl, port string) bool {
|
|||||||
return resp.StatusCode == http.StatusOK
|
return resp.StatusCode == http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkWebDAV(ctx context.Context, baseUrl, port, path string) bool {
|
func checkBaseWebdav(ctx context.Context, baseUrl, port string) bool {
|
||||||
|
url := fmt.Sprintf("http://localhost:%s%swebdav/", port, baseUrl)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "PROPFIND", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return resp.StatusCode == http.StatusMultiStatus ||
|
||||||
|
resp.StatusCode == http.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDebridWebDAV(ctx context.Context, baseUrl, port, path string) bool {
|
||||||
url := fmt.Sprintf("http://localhost:%s%swebdav/%s", port, baseUrl, path)
|
url := fmt.Sprintf("http://localhost:%s%swebdav/%s", port, baseUrl, path)
|
||||||
req, err := http.NewRequestWithContext(ctx, "PROPFIND", url, nil)
|
req, err := http.NewRequestWithContext(ctx, "PROPFIND", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -150,7 +170,6 @@ func checkWebDAV(ctx context.Context, baseUrl, port, path string) bool {
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
return resp.StatusCode == http.StatusMultiStatus ||
|
return resp.StatusCode == http.StatusMultiStatus ||
|
||||||
resp.StatusCode == http.StatusOK ||
|
resp.StatusCode == http.StatusOK
|
||||||
resp.StatusCode == http.StatusServiceUnavailable // It's still indexing
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ func (ad *AllDebrid) GetTorrent(torrentId string) (*types.Torrent, error) {
|
|||||||
var res TorrentInfoResponse
|
var res TorrentInfoResponse
|
||||||
err = json.Unmarshal(resp, &res)
|
err = json.Unmarshal(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ad.logger.Info().Msgf("Error unmarshalling torrent info: %s", err)
|
ad.logger.Error().Err(err).Msgf("Error unmarshalling torrent info")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data := res.Data.Magnets
|
data := res.Data.Magnets
|
||||||
@@ -232,7 +232,7 @@ func (ad *AllDebrid) UpdateTorrent(t *types.Torrent) error {
|
|||||||
var res TorrentInfoResponse
|
var res TorrentInfoResponse
|
||||||
err = json.Unmarshal(resp, &res)
|
err = json.Unmarshal(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ad.logger.Info().Msgf("Error unmarshalling torrent info: %s", err)
|
ad.logger.Error().Err(err).Msgf("Error unmarshalling torrent info")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data := res.Data.Magnets
|
data := res.Data.Magnets
|
||||||
@@ -393,7 +393,7 @@ func (ad *AllDebrid) GetTorrents() ([]*types.Torrent, error) {
|
|||||||
var res TorrentsListResponse
|
var res TorrentsListResponse
|
||||||
err = json.Unmarshal(resp, &res)
|
err = json.Unmarshal(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ad.logger.Info().Msgf("Error unmarshalling torrent info: %s", err)
|
ad.logger.Error().Err(err).Msgf("Error unmarshalling torrent info")
|
||||||
return torrents, err
|
return torrents, err
|
||||||
}
|
}
|
||||||
for _, magnet := range res.Data.Magnets {
|
for _, magnet := range res.Data.Magnets {
|
||||||
|
|||||||
@@ -110,13 +110,13 @@ func (dl *DebridLink) IsAvailable(hashes []string) map[string]bool {
|
|||||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||||
resp, err := dl.client.MakeRequest(req)
|
resp, err := dl.client.MakeRequest(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dl.logger.Info().Msgf("Error checking availability: %v", err)
|
dl.logger.Error().Err(err).Msgf("Error checking availability")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
var data AvailableResponse
|
var data AvailableResponse
|
||||||
err = json.Unmarshal(resp, &data)
|
err = json.Unmarshal(resp, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dl.logger.Info().Msgf("Error marshalling availability: %v", err)
|
dl.logger.Error().Err(err).Msgf("Error marshalling availability")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if data.Value == nil {
|
if data.Value == nil {
|
||||||
@@ -406,7 +406,7 @@ func (dl *DebridLink) getTorrents(page, perPage int) ([]*types.Torrent, error) {
|
|||||||
var res torrentInfo
|
var res torrentInfo
|
||||||
err = json.Unmarshal(resp, &res)
|
err = json.Unmarshal(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dl.logger.Info().Msgf("Error unmarshalling torrent info: %s", err)
|
dl.logger.Error().Err(err).Msgf("Error unmarshalling torrent info")
|
||||||
return torrents, err
|
return torrents, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func New(dc config.Debrid) (*RealDebrid, error) {
|
|||||||
request.WithHeaders(headers),
|
request.WithHeaders(headers),
|
||||||
request.WithRateLimiter(rl),
|
request.WithRateLimiter(rl),
|
||||||
request.WithLogger(_log),
|
request.WithLogger(_log),
|
||||||
request.WithMaxRetries(5),
|
request.WithMaxRetries(10),
|
||||||
request.WithRetryableStatus(429, 502),
|
request.WithRetryableStatus(429, 502),
|
||||||
request.WithProxy(dc.Proxy),
|
request.WithProxy(dc.Proxy),
|
||||||
),
|
),
|
||||||
@@ -302,13 +302,13 @@ func (r *RealDebrid) IsAvailable(hashes []string) map[string]bool {
|
|||||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||||
resp, err := r.client.MakeRequest(req)
|
resp, err := r.client.MakeRequest(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Info().Msgf("Error checking availability: %v", err)
|
r.logger.Error().Err(err).Msgf("Error checking availability")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
var data AvailabilityResponse
|
var data AvailabilityResponse
|
||||||
err = json.Unmarshal(resp, &data)
|
err = json.Unmarshal(resp, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Info().Msgf("Error marshalling availability: %v", err)
|
r.logger.Error().Err(err).Msgf("Error marshalling availability")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
for _, h := range hashes[i:end] {
|
for _, h := range hashes[i:end] {
|
||||||
|
|||||||
@@ -117,13 +117,13 @@ func (tb *Torbox) IsAvailable(hashes []string) map[string]bool {
|
|||||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||||
resp, err := tb.client.MakeRequest(req)
|
resp, err := tb.client.MakeRequest(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tb.logger.Info().Msgf("Error checking availability: %v", err)
|
tb.logger.Error().Err(err).Msgf("Error checking availability")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
var res AvailableResponse
|
var res AvailableResponse
|
||||||
err = json.Unmarshal(resp, &res)
|
err = json.Unmarshal(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tb.logger.Info().Msgf("Error marshalling availability: %v", err)
|
tb.logger.Error().Err(err).Msgf("Error marshalling availability")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if res.Data == nil {
|
if res.Data == nil {
|
||||||
|
|||||||
@@ -231,6 +231,8 @@ func (c *Cache) Start(ctx context.Context) error {
|
|||||||
if err := c.Sync(ctx); err != nil {
|
if err := c.Sync(ctx); err != nil {
|
||||||
return fmt.Errorf("failed to sync cache: %w", err)
|
return fmt.Errorf("failed to sync cache: %w", err)
|
||||||
}
|
}
|
||||||
|
// Fire the ready channel
|
||||||
|
close(c.ready)
|
||||||
|
|
||||||
// initial download links
|
// initial download links
|
||||||
go c.refreshDownloadLinks(ctx)
|
go c.refreshDownloadLinks(ctx)
|
||||||
@@ -242,8 +244,6 @@ func (c *Cache) Start(ctx context.Context) error {
|
|||||||
c.repairChan = make(chan RepairRequest, 100)
|
c.repairChan = make(chan RepairRequest, 100)
|
||||||
go c.repairWorker(ctx)
|
go c.repairWorker(ctx)
|
||||||
|
|
||||||
// Fire the ready channel
|
|
||||||
close(c.ready)
|
|
||||||
cfg := config.Get()
|
cfg := config.Get()
|
||||||
name := c.client.GetName()
|
name := c.client.GetName()
|
||||||
addr := cfg.BindAddress + ":" + cfg.Port + cfg.URLBase + "webdav/" + name + "/"
|
addr := cfg.BindAddress + ":" + cfg.Port + cfg.URLBase + "webdav/" + name + "/"
|
||||||
|
|||||||
@@ -261,7 +261,4 @@ func (c *Cache) refreshDownloadLinks(ctx context.Context) {
|
|||||||
c.downloadLinks.Delete(k)
|
c.downloadLinks.Delete(k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Trace().Msgf("Refreshed %d download links", len(downloadLinks))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func (q *QBit) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := _arr.Validate(); err != nil {
|
if err := _arr.Validate(); err != nil {
|
||||||
q.logger.Info().Msgf("Error validating arr: %v", err)
|
q.logger.Error().Err(err).Msgf("Error validating arr")
|
||||||
}
|
}
|
||||||
_, _ = w.Write([]byte("Ok."))
|
_, _ = w.Write([]byte("Ok."))
|
||||||
}
|
}
|
||||||
@@ -73,13 +73,13 @@ func (q *QBit) handleTorrentsAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
contentType := r.Header.Get("Content-Type")
|
contentType := r.Header.Get("Content-Type")
|
||||||
if strings.Contains(contentType, "multipart/form-data") {
|
if strings.Contains(contentType, "multipart/form-data") {
|
||||||
if err := r.ParseMultipartForm(32 << 20); err != nil {
|
if err := r.ParseMultipartForm(32 << 20); err != nil {
|
||||||
q.logger.Info().Msgf("Error parsing multipart form: %v", err)
|
q.logger.Error().Err(err).Msgf("Error parsing multipart form")
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if strings.Contains(contentType, "application/x-www-form-urlencoded") {
|
} else if strings.Contains(contentType, "application/x-www-form-urlencoded") {
|
||||||
if err := r.ParseForm(); err != nil {
|
if err := r.ParseForm(); err != nil {
|
||||||
q.logger.Info().Msgf("Error parsing form: %v", err)
|
q.logger.Error().Err(err).Msgf("Error parsing form")
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ func (q *QBit) handleTorrentsAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
for _, url := range urlList {
|
for _, url := range urlList {
|
||||||
if err := q.addMagnet(ctx, url, _arr, debridName, isSymlink); err != nil {
|
if err := q.addMagnet(ctx, url, _arr, debridName, isSymlink); err != nil {
|
||||||
q.logger.Info().Msgf("Error adding magnet: %v", err)
|
q.logger.Error().Err(err).Msgf("Error adding magnet")
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ func (q *QBit) handleTorrentsAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
if files := r.MultipartForm.File["torrents"]; len(files) > 0 {
|
if files := r.MultipartForm.File["torrents"]; len(files) > 0 {
|
||||||
for _, fileHeader := range files {
|
for _, fileHeader := range files {
|
||||||
if err := q.addTorrent(ctx, fileHeader, _arr, debridName, isSymlink); err != nil {
|
if err := q.addTorrent(ctx, fileHeader, _arr, debridName, isSymlink); err != nil {
|
||||||
q.logger.Info().Msgf("Error adding torrent: %v", err)
|
q.logger.Error().Err(err).Msgf("Error adding torrent")
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ func (r *Repair) repairArr(job *Job, _arr string, tmdbId string) ([]arr.ContentF
|
|||||||
}
|
}
|
||||||
// Check first media to confirm mounts are accessible
|
// Check first media to confirm mounts are accessible
|
||||||
if !r.isMediaAccessible(media) {
|
if !r.isMediaAccessible(media) {
|
||||||
r.logger.Info().Msgf("Skipping repair. Parent directory not accessible for. Check your mounts")
|
r.logger.Info().Msgf("Skipping repair. Parent directory not accessible. Check your mounts")
|
||||||
return brokenItems, nil
|
return brokenItems, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,19 +520,18 @@ func (r *Repair) isMediaAccessible(media []arr.Content) bool {
|
|||||||
firstFile := files[0]
|
firstFile := files[0]
|
||||||
symlinkPath := getSymlinkTarget(firstFile.Path)
|
symlinkPath := getSymlinkTarget(firstFile.Path)
|
||||||
|
|
||||||
|
if symlinkPath == "" {
|
||||||
|
r.logger.Debug().Msgf("No symlink target found for %s", firstFile.Path)
|
||||||
|
return false
|
||||||
|
}
|
||||||
r.logger.Debug().Msgf("Checking symlink parent directory for %s", symlinkPath)
|
r.logger.Debug().Msgf("Checking symlink parent directory for %s", symlinkPath)
|
||||||
parentSymlink := ""
|
|
||||||
|
|
||||||
if symlinkPath != "" {
|
parentSymlink := filepath.Dir(filepath.Dir(symlinkPath)) // /mnt/zurg/torrents/movie/movie.mkv -> /mnt/zurg/torrents
|
||||||
parentSymlink = filepath.Dir(filepath.Dir(symlinkPath)) // /mnt/zurg/torrents/movie/movie.mkv -> /mnt/zurg/torrents
|
if _, err := os.Stat(parentSymlink); os.IsNotExist(err) {
|
||||||
|
r.logger.Debug().Msgf("Cannot access parent directory %s for %s", parentSymlink, firstFile.Path)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
if parentSymlink != "" {
|
return true
|
||||||
if _, err := os.Stat(parentSymlink); os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repair) getBrokenFiles(job *Job, media arr.Content) []arr.ContentFile {
|
func (r *Repair) getBrokenFiles(job *Job, media arr.Content) []arr.ContentFile {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func (s *Server) Start(ctx context.Context) error {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
s.logger.Info().Msgf("Error starting server: %v", err)
|
s.logger.Error().Err(err).Msgf("Error starting server")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func GetStore() *Store {
|
|||||||
arr: arrs,
|
arr: arrs,
|
||||||
debrid: deb,
|
debrid: deb,
|
||||||
torrents: newTorrentStorage(cfg.TorrentsFile()),
|
torrents: newTorrentStorage(cfg.TorrentsFile()),
|
||||||
logger: logger.New("store"),
|
logger: logger.Default(), // Use default logger [decypharr]
|
||||||
refreshInterval: time.Duration(cmp.Or(qbitCfg.RefreshInterval, 10)) * time.Minute,
|
refreshInterval: time.Duration(cmp.Or(qbitCfg.RefreshInterval, 10)) * time.Minute,
|
||||||
skipPreCache: qbitCfg.SkipPreCache,
|
skipPreCache: qbitCfg.SkipPreCache,
|
||||||
downloadSemaphore: make(chan struct{}, cmp.Or(qbitCfg.MaxDownloads, 5)),
|
downloadSemaphore: make(chan struct{}, cmp.Or(qbitCfg.MaxDownloads, 5)),
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func (s *Store) processFiles(torrent *Torrent, debridTorrent *types.Torrent, imp
|
|||||||
go func() {
|
go func() {
|
||||||
_ = client.DeleteTorrent(debridTorrent.Id)
|
_ = client.DeleteTorrent(debridTorrent.Id)
|
||||||
}()
|
}()
|
||||||
s.logger.Info().Msgf("Error: %v", err)
|
s.logger.Error().Err(err).Msgf("Error occured while processing torrent %s", debridTorrent.Name)
|
||||||
importReq.markAsFailed(err, torrent, debridTorrent)
|
importReq.markAsFailed(err, torrent, debridTorrent)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user