Feature/torbox provider improvements (#100)
- Add Torbox WebDAV implementation - Fix Issues with sample and extension checks
This commit is contained in:
@@ -24,7 +24,7 @@ func (c *Config) IsAllowedFile(filename string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultExtensions() []string {
|
func getDefaultExtensions() []string {
|
||||||
videoExts := strings.Split("webm,m4v,3gp,nsv,ty,strm,rm,rmvb,m3u,ifo,mov,qt,divx,xvid,bivx,nrg,pva,wmv,asf,asx,ogm,ogv,m2v,avi,bin,dat,dvr-ms,mpg,mpeg,mp4,avc,vp3,svq3,nuv,viv,dv,fli,flv,wpl,img,iso,vob,mkv,mk3d,ts,wtv,m2ts'", ",")
|
videoExts := strings.Split("webm,m4v,3gp,nsv,ty,strm,rm,rmvb,m3u,ifo,mov,qt,divx,xvid,bivx,nrg,pva,wmv,asf,asx,ogm,ogv,m2v,avi,bin,dat,dvr-ms,mpg,mpeg,mp4,avc,vp3,svq3,nuv,viv,dv,fli,flv,wpl,img,iso,vob,mkv,mk3d,ts,wtv,m2ts", ",")
|
||||||
musicExts := strings.Split("MP3,WAV,FLAC,OGG,WMA,AIFF,ALAC,M4A,APE,AC3,DTS,M4P,MID,MIDI,MKA,MP2,MPA,RA,VOC,WV,AMR", ",")
|
musicExts := strings.Split("MP3,WAV,FLAC,OGG,WMA,AIFF,ALAC,M4A,APE,AC3,DTS,M4P,MID,MIDI,MKA,MP2,MPA,RA,VOC,WV,AMR", ",")
|
||||||
|
|
||||||
// Combine both slices
|
// Combine both slices
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ func IsMediaFile(path string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsSampleFile(path string) bool {
|
func IsSampleFile(path string) bool {
|
||||||
if strings.HasSuffix(strings.ToLower(path), "sample.mkv") {
|
filename := filepath.Base(path)
|
||||||
|
if strings.HasSuffix(strings.ToLower(filename), "sample.mkv") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return RegexMatch(sampleRegex, path)
|
return RegexMatch(sampleRegex, path)
|
||||||
|
|||||||
@@ -4,13 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"github.com/sirrobot01/decypharr/internal/config"
|
|
||||||
"github.com/sirrobot01/decypharr/internal/logger"
|
|
||||||
"github.com/sirrobot01/decypharr/internal/request"
|
|
||||||
"github.com/sirrobot01/decypharr/internal/utils"
|
|
||||||
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
|
||||||
"github.com/sirrobot01/decypharr/pkg/version"
|
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
gourl "net/url"
|
gourl "net/url"
|
||||||
@@ -21,6 +14,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/sirrobot01/decypharr/internal/config"
|
||||||
|
"github.com/sirrobot01/decypharr/internal/logger"
|
||||||
|
"github.com/sirrobot01/decypharr/internal/request"
|
||||||
|
"github.com/sirrobot01/decypharr/internal/utils"
|
||||||
|
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
||||||
|
"github.com/sirrobot01/decypharr/pkg/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Torbox struct {
|
type Torbox struct {
|
||||||
@@ -168,7 +169,7 @@ func (tb *Torbox) SubmitMagnet(torrent *types.Torrent) (*types.Torrent, error) {
|
|||||||
return torrent, nil
|
return torrent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTorboxStatus(status string, finished bool) string {
|
func (tb *Torbox) getTorboxStatus(status string, finished bool) string {
|
||||||
if finished {
|
if finished {
|
||||||
return "downloaded"
|
return "downloaded"
|
||||||
}
|
}
|
||||||
@@ -176,12 +177,16 @@ func getTorboxStatus(status string, finished bool) string {
|
|||||||
"checkingResumeData", "metaDL", "pausedUP", "queuedUP", "checkingUP",
|
"checkingResumeData", "metaDL", "pausedUP", "queuedUP", "checkingUP",
|
||||||
"forcedUP", "allocating", "downloading", "metaDL", "pausedDL",
|
"forcedUP", "allocating", "downloading", "metaDL", "pausedDL",
|
||||||
"queuedDL", "checkingDL", "forcedDL", "checkingResumeData", "moving"}
|
"queuedDL", "checkingDL", "forcedDL", "checkingResumeData", "moving"}
|
||||||
|
|
||||||
|
var determinedStatus string
|
||||||
switch {
|
switch {
|
||||||
case utils.Contains(downloading, status):
|
case utils.Contains(downloading, status):
|
||||||
return "downloading"
|
determinedStatus = "downloading"
|
||||||
default:
|
default:
|
||||||
return "error"
|
determinedStatus = "error"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return determinedStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *Torbox) GetTorrent(torrentId string) (*types.Torrent, error) {
|
func (tb *Torbox) GetTorrent(torrentId string) (*types.Torrent, error) {
|
||||||
@@ -206,7 +211,7 @@ func (tb *Torbox) GetTorrent(torrentId string) (*types.Torrent, error) {
|
|||||||
Bytes: data.Size,
|
Bytes: data.Size,
|
||||||
Folder: data.Name,
|
Folder: data.Name,
|
||||||
Progress: data.Progress * 100,
|
Progress: data.Progress * 100,
|
||||||
Status: getTorboxStatus(data.DownloadState, data.DownloadFinished),
|
Status: tb.getTorboxStatus(data.DownloadState, data.DownloadFinished),
|
||||||
Speed: data.DownloadSpeed,
|
Speed: data.DownloadSpeed,
|
||||||
Seeders: data.Seeds,
|
Seeders: data.Seeds,
|
||||||
Filename: data.Name,
|
Filename: data.Name,
|
||||||
@@ -217,19 +222,33 @@ func (tb *Torbox) GetTorrent(torrentId string) (*types.Torrent, error) {
|
|||||||
Added: data.CreatedAt.Format(time.RFC3339),
|
Added: data.CreatedAt.Format(time.RFC3339),
|
||||||
}
|
}
|
||||||
cfg := config.Get()
|
cfg := config.Get()
|
||||||
|
|
||||||
|
totalFiles := 0
|
||||||
|
skippedSamples := 0
|
||||||
|
skippedFileType := 0
|
||||||
|
skippedSize := 0
|
||||||
|
validFiles := 0
|
||||||
|
filesWithLinks := 0
|
||||||
|
|
||||||
for _, f := range data.Files {
|
for _, f := range data.Files {
|
||||||
|
totalFiles++
|
||||||
fileName := filepath.Base(f.Name)
|
fileName := filepath.Base(f.Name)
|
||||||
|
|
||||||
if !tb.addSamples && utils.IsSampleFile(f.AbsolutePath) {
|
if !tb.addSamples && utils.IsSampleFile(f.AbsolutePath) {
|
||||||
// Skip sample files
|
skippedSamples++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !cfg.IsAllowedFile(fileName) {
|
if !cfg.IsAllowedFile(fileName) {
|
||||||
|
skippedFileType++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cfg.IsSizeAllowed(f.Size) {
|
if !cfg.IsSizeAllowed(f.Size) {
|
||||||
|
skippedSize++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validFiles++
|
||||||
file := types.File{
|
file := types.File{
|
||||||
TorrentId: t.Id,
|
TorrentId: t.Id,
|
||||||
Id: strconv.Itoa(f.Id),
|
Id: strconv.Itoa(f.Id),
|
||||||
@@ -237,8 +256,26 @@ func (tb *Torbox) GetTorrent(torrentId string) (*types.Torrent, error) {
|
|||||||
Size: f.Size,
|
Size: f.Size,
|
||||||
Path: f.Name,
|
Path: f.Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For downloaded torrents, set a placeholder link to indicate file is available
|
||||||
|
if data.DownloadFinished {
|
||||||
|
file.Link = fmt.Sprintf("torbox://%s/%d", t.Id, f.Id)
|
||||||
|
filesWithLinks++
|
||||||
|
}
|
||||||
|
|
||||||
t.Files[fileName] = file
|
t.Files[fileName] = file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log summary only if there are issues or for debugging
|
||||||
|
tb.logger.Debug().
|
||||||
|
Str("torrent_id", t.Id).
|
||||||
|
Str("torrent_name", t.Name).
|
||||||
|
Bool("download_finished", data.DownloadFinished).
|
||||||
|
Str("status", t.Status).
|
||||||
|
Int("total_files", totalFiles).
|
||||||
|
Int("valid_files", validFiles).
|
||||||
|
Int("final_file_count", len(t.Files)).
|
||||||
|
Msg("Torrent file processing completed")
|
||||||
var cleanPath string
|
var cleanPath string
|
||||||
if len(t.Files) > 0 {
|
if len(t.Files) > 0 {
|
||||||
cleanPath = path.Clean(data.Files[0].Name)
|
cleanPath = path.Clean(data.Files[0].Name)
|
||||||
@@ -266,24 +303,33 @@ func (tb *Torbox) UpdateTorrent(t *types.Torrent) error {
|
|||||||
}
|
}
|
||||||
data := res.Data
|
data := res.Data
|
||||||
name := data.Name
|
name := data.Name
|
||||||
|
|
||||||
t.Name = name
|
t.Name = name
|
||||||
t.Bytes = data.Size
|
t.Bytes = data.Size
|
||||||
t.Folder = name
|
t.Folder = name
|
||||||
t.Progress = data.Progress * 100
|
t.Progress = data.Progress * 100
|
||||||
t.Status = getTorboxStatus(data.DownloadState, data.DownloadFinished)
|
t.Status = tb.getTorboxStatus(data.DownloadState, data.DownloadFinished)
|
||||||
t.Speed = data.DownloadSpeed
|
t.Speed = data.DownloadSpeed
|
||||||
t.Seeders = data.Seeds
|
t.Seeders = data.Seeds
|
||||||
t.Filename = name
|
t.Filename = name
|
||||||
t.OriginalFilename = name
|
t.OriginalFilename = name
|
||||||
t.MountPath = tb.MountPath
|
t.MountPath = tb.MountPath
|
||||||
t.Debrid = tb.name
|
t.Debrid = tb.name
|
||||||
|
|
||||||
|
// Clear existing files map to rebuild it
|
||||||
|
t.Files = make(map[string]types.File)
|
||||||
|
|
||||||
cfg := config.Get()
|
cfg := config.Get()
|
||||||
|
validFiles := 0
|
||||||
|
filesWithLinks := 0
|
||||||
|
|
||||||
for _, f := range data.Files {
|
for _, f := range data.Files {
|
||||||
fileName := filepath.Base(f.Name)
|
fileName := filepath.Base(f.Name)
|
||||||
|
|
||||||
if !tb.addSamples && utils.IsSampleFile(f.AbsolutePath) {
|
if !tb.addSamples && utils.IsSampleFile(f.AbsolutePath) {
|
||||||
// Skip sample files
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cfg.IsAllowedFile(fileName) {
|
if !cfg.IsAllowedFile(fileName) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -291,6 +337,8 @@ func (tb *Torbox) UpdateTorrent(t *types.Torrent) error {
|
|||||||
if !cfg.IsSizeAllowed(f.Size) {
|
if !cfg.IsSizeAllowed(f.Size) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validFiles++
|
||||||
file := types.File{
|
file := types.File{
|
||||||
TorrentId: t.Id,
|
TorrentId: t.Id,
|
||||||
Id: strconv.Itoa(f.Id),
|
Id: strconv.Itoa(f.Id),
|
||||||
@@ -298,8 +346,16 @@ func (tb *Torbox) UpdateTorrent(t *types.Torrent) error {
|
|||||||
Size: f.Size,
|
Size: f.Size,
|
||||||
Path: fileName,
|
Path: fileName,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For downloaded torrents, set a placeholder link to indicate file is available
|
||||||
|
if data.DownloadFinished {
|
||||||
|
file.Link = fmt.Sprintf("torbox://%s/%s", t.Id, strconv.Itoa(f.Id))
|
||||||
|
filesWithLinks++
|
||||||
|
}
|
||||||
|
|
||||||
t.Files[fileName] = file
|
t.Files[fileName] = file
|
||||||
}
|
}
|
||||||
|
|
||||||
var cleanPath string
|
var cleanPath string
|
||||||
if len(t.Files) > 0 {
|
if len(t.Files) > 0 {
|
||||||
cleanPath = path.Clean(data.Files[0].Name)
|
cleanPath = path.Clean(data.Files[0].Name)
|
||||||
@@ -409,30 +465,58 @@ func (tb *Torbox) GetDownloadLink(t *types.Torrent, file *types.File) (*types.Do
|
|||||||
query.Add("token", tb.APIKey)
|
query.Add("token", tb.APIKey)
|
||||||
query.Add("file_id", file.Id)
|
query.Add("file_id", file.Id)
|
||||||
url += "?" + query.Encode()
|
url += "?" + query.Encode()
|
||||||
|
|
||||||
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.Error().
|
||||||
|
Err(err).
|
||||||
|
Str("torrent_id", t.Id).
|
||||||
|
Str("file_id", file.Id).
|
||||||
|
Msg("Failed to make request to Torbox API")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var data DownloadLinksResponse
|
var data DownloadLinksResponse
|
||||||
if err = json.Unmarshal(resp, &data); err != nil {
|
if err = json.Unmarshal(resp, &data); err != nil {
|
||||||
|
tb.logger.Error().
|
||||||
|
Err(err).
|
||||||
|
Str("torrent_id", t.Id).
|
||||||
|
Str("file_id", file.Id).
|
||||||
|
Msg("Failed to unmarshal Torbox API response")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.Data == nil {
|
if data.Data == nil {
|
||||||
|
tb.logger.Error().
|
||||||
|
Str("torrent_id", t.Id).
|
||||||
|
Str("file_id", file.Id).
|
||||||
|
Bool("success", data.Success).
|
||||||
|
Interface("error", data.Error).
|
||||||
|
Str("detail", data.Detail).
|
||||||
|
Msg("Torbox API returned no data")
|
||||||
return nil, fmt.Errorf("error getting download links")
|
return nil, fmt.Errorf("error getting download links")
|
||||||
}
|
}
|
||||||
|
|
||||||
link := *data.Data
|
link := *data.Data
|
||||||
if link == "" {
|
if link == "" {
|
||||||
|
tb.logger.Error().
|
||||||
|
Str("torrent_id", t.Id).
|
||||||
|
Str("file_id", file.Id).
|
||||||
|
Msg("Torbox API returned empty download link")
|
||||||
return nil, fmt.Errorf("error getting download links")
|
return nil, fmt.Errorf("error getting download links")
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
return &types.DownloadLink{
|
downloadLink := &types.DownloadLink{
|
||||||
Link: file.Link,
|
Link: file.Link,
|
||||||
DownloadLink: link,
|
DownloadLink: link,
|
||||||
Id: file.Id,
|
Id: file.Id,
|
||||||
Generated: now,
|
Generated: now,
|
||||||
ExpiresAt: now.Add(tb.autoExpiresLinksAfter),
|
ExpiresAt: now.Add(tb.autoExpiresLinksAfter),
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
return downloadLink, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *Torbox) GetDownloadingStatus() []string {
|
func (tb *Torbox) GetDownloadingStatus() []string {
|
||||||
@@ -440,7 +524,87 @@ func (tb *Torbox) GetDownloadingStatus() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tb *Torbox) GetTorrents() ([]*types.Torrent, error) {
|
func (tb *Torbox) GetTorrents() ([]*types.Torrent, error) {
|
||||||
return nil, nil
|
url := fmt.Sprintf("%s/api/torrents/mylist", tb.Host)
|
||||||
|
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||||
|
resp, err := tb.client.MakeRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res TorrentsListResponse
|
||||||
|
err = json.Unmarshal(resp, &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !res.Success || res.Data == nil {
|
||||||
|
return nil, fmt.Errorf("torbox API error: %v", res.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
torrents := make([]*types.Torrent, 0, len(*res.Data))
|
||||||
|
cfg := config.Get()
|
||||||
|
|
||||||
|
for _, data := range *res.Data {
|
||||||
|
t := &types.Torrent{
|
||||||
|
Id: strconv.Itoa(data.Id),
|
||||||
|
Name: data.Name,
|
||||||
|
Bytes: data.Size,
|
||||||
|
Folder: data.Name,
|
||||||
|
Progress: data.Progress * 100,
|
||||||
|
Status: tb.getTorboxStatus(data.DownloadState, data.DownloadFinished),
|
||||||
|
Speed: data.DownloadSpeed,
|
||||||
|
Seeders: data.Seeds,
|
||||||
|
Filename: data.Name,
|
||||||
|
OriginalFilename: data.Name,
|
||||||
|
MountPath: tb.MountPath,
|
||||||
|
Debrid: tb.name,
|
||||||
|
Files: make(map[string]types.File),
|
||||||
|
Added: data.CreatedAt.Format(time.RFC3339),
|
||||||
|
InfoHash: data.Hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process files
|
||||||
|
for _, f := range data.Files {
|
||||||
|
fileName := filepath.Base(f.Name)
|
||||||
|
if !tb.addSamples && utils.IsSampleFile(f.AbsolutePath) {
|
||||||
|
// Skip sample files
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !cfg.IsAllowedFile(fileName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !cfg.IsSizeAllowed(f.Size) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
file := types.File{
|
||||||
|
TorrentId: t.Id,
|
||||||
|
Id: strconv.Itoa(f.Id),
|
||||||
|
Name: fileName,
|
||||||
|
Size: f.Size,
|
||||||
|
Path: f.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// For downloaded torrents, set a placeholder link to indicate file is available
|
||||||
|
if data.DownloadFinished {
|
||||||
|
file.Link = fmt.Sprintf("torbox://%s/%d", t.Id, f.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Files[fileName] = file
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set original filename based on first file or torrent name
|
||||||
|
var cleanPath string
|
||||||
|
if len(t.Files) > 0 {
|
||||||
|
cleanPath = path.Clean(data.Files[0].Name)
|
||||||
|
} else {
|
||||||
|
cleanPath = path.Clean(data.Name)
|
||||||
|
}
|
||||||
|
t.OriginalFilename = strings.Split(cleanPath, "/")[0]
|
||||||
|
|
||||||
|
torrents = append(torrents, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return torrents, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *Torbox) GetDownloadUncached() bool {
|
func (tb *Torbox) GetDownloadUncached() bool {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ type torboxInfo struct {
|
|||||||
} `json:"files"`
|
} `json:"files"`
|
||||||
DownloadPath string `json:"download_path"`
|
DownloadPath string `json:"download_path"`
|
||||||
InactiveCheck int `json:"inactive_check"`
|
InactiveCheck int `json:"inactive_check"`
|
||||||
Availability int `json:"availability"`
|
Availability float64 `json:"availability"`
|
||||||
DownloadFinished bool `json:"download_finished"`
|
DownloadFinished bool `json:"download_finished"`
|
||||||
Tracker interface{} `json:"tracker"`
|
Tracker interface{} `json:"tracker"`
|
||||||
TotalUploaded int `json:"total_uploaded"`
|
TotalUploaded int `json:"total_uploaded"`
|
||||||
@@ -73,3 +73,5 @@ type torboxInfo struct {
|
|||||||
type InfoResponse APIResponse[torboxInfo]
|
type InfoResponse APIResponse[torboxInfo]
|
||||||
|
|
||||||
type DownloadLinksResponse APIResponse[string]
|
type DownloadLinksResponse APIResponse[string]
|
||||||
|
|
||||||
|
type TorrentsListResponse APIResponse[[]torboxInfo]
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -17,13 +16,16 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
||||||
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
_ "time/tzdata"
|
||||||
|
|
||||||
"github.com/go-co-op/gocron/v2"
|
"github.com/go-co-op/gocron/v2"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/sirrobot01/decypharr/internal/config"
|
"github.com/sirrobot01/decypharr/internal/config"
|
||||||
"github.com/sirrobot01/decypharr/internal/logger"
|
"github.com/sirrobot01/decypharr/internal/logger"
|
||||||
"github.com/sirrobot01/decypharr/internal/utils"
|
"github.com/sirrobot01/decypharr/internal/utils"
|
||||||
_ "time/tzdata"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebDavFolderNaming string
|
type WebDavFolderNaming string
|
||||||
@@ -682,8 +684,13 @@ func (c *Cache) ProcessTorrent(t *types.Torrent) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isComplete(t.Files) {
|
if !isComplete(t.Files) {
|
||||||
c.logger.Debug().Msgf("Torrent %s is still not complete. Triggering a reinsert(disabled)", t.Id)
|
c.logger.Debug().
|
||||||
|
Str("torrent_id", t.Id).
|
||||||
|
Str("torrent_name", t.Name).
|
||||||
|
Int("total_files", len(t.Files)).
|
||||||
|
Msg("Torrent still not complete after refresh")
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
addedOn, err := time.Parse(time.RFC3339, t.Added)
|
addedOn, err := time.Parse(time.RFC3339, t.Added)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
addedOn = time.Now()
|
addedOn = time.Now()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package store
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/sirrobot01/decypharr/internal/utils"
|
"github.com/sirrobot01/decypharr/internal/utils"
|
||||||
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
||||||
)
|
)
|
||||||
@@ -102,9 +103,16 @@ func (c *Cache) fetchDownloadLink(torrentName, filename, fileLink string) (*type
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Trace().Msgf("Getting download link for %s(%s)", filename, file.Link)
|
c.logger.Trace().Msgf("Getting download link for %s(%s)", filename, file.Link)
|
||||||
|
|
||||||
downloadLink, err := c.client.GetDownloadLink(ct.Torrent, &file)
|
downloadLink, err := c.client.GetDownloadLink(ct.Torrent, &file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
if errors.Is(err, utils.HosterUnavailableError) {
|
if errors.Is(err, utils.HosterUnavailableError) {
|
||||||
|
c.logger.Trace().
|
||||||
|
Str("filename", filename).
|
||||||
|
Str("torrent_id", ct.Id).
|
||||||
|
Msg("Hoster unavailable, attempting to reinsert torrent")
|
||||||
|
|
||||||
newCt, err := c.reInsertTorrent(ct)
|
newCt, err := c.reInsertTorrent(ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to reinsert torrent: %w", err)
|
return nil, fmt.Errorf("failed to reinsert torrent: %w", err)
|
||||||
@@ -117,12 +125,11 @@ func (c *Cache) fetchDownloadLink(torrentName, filename, fileLink string) (*type
|
|||||||
// Retry getting the download link
|
// Retry getting the download link
|
||||||
downloadLink, err = c.client.GetDownloadLink(ct.Torrent, &file)
|
downloadLink, err = c.client.GetDownloadLink(ct.Torrent, &file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("retry failed to get download link: %w", err)
|
||||||
}
|
}
|
||||||
if downloadLink == nil {
|
if downloadLink == nil {
|
||||||
return nil, fmt.Errorf("download link is empty for")
|
return nil, fmt.Errorf("download link is empty after retry")
|
||||||
}
|
}
|
||||||
return nil, nil
|
|
||||||
} else if errors.Is(err, utils.TrafficExceededError) {
|
} else if errors.Is(err, utils.TrafficExceededError) {
|
||||||
// This is likely a fair usage limit error
|
// This is likely a fair usage limit error
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
||||||
|
|
||||||
"github.com/cavaliergopher/grab/v3"
|
"github.com/cavaliergopher/grab/v3"
|
||||||
"github.com/sirrobot01/decypharr/internal/utils"
|
"github.com/sirrobot01/decypharr/internal/utils"
|
||||||
)
|
)
|
||||||
@@ -212,7 +213,7 @@ func (s *Store) processSymlink(torrent *Torrent, debridTorrent *types.Torrent) (
|
|||||||
if _, err := os.Stat(fullFilePath); !os.IsNotExist(err) {
|
if _, err := os.Stat(fullFilePath); !os.IsNotExist(err) {
|
||||||
fileSymlinkPath := filepath.Join(torrentSymlinkPath, file.Name)
|
fileSymlinkPath := filepath.Join(torrentSymlinkPath, file.Name)
|
||||||
if err := os.Symlink(fullFilePath, fileSymlinkPath); err != nil && !os.IsExist(err) {
|
if err := os.Symlink(fullFilePath, fileSymlinkPath); err != nil && !os.IsExist(err) {
|
||||||
s.logger.Debug().Msgf("Failed to create symlink: %s: %v", fileSymlinkPath, err)
|
s.logger.Warn().Msgf("Failed to create symlink: %s: %v", fileSymlinkPath, err)
|
||||||
} else {
|
} else {
|
||||||
filePaths = append(filePaths, fileSymlinkPath)
|
filePaths = append(filePaths, fileSymlinkPath)
|
||||||
delete(pending, path)
|
delete(pending, path)
|
||||||
|
|||||||
@@ -96,9 +96,7 @@ func (s *Store) trackAvailableSlots(ctx context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, slots := range availableSlots {
|
for _, slots := range availableSlots {
|
||||||
|
|
||||||
s.logger.Debug().Msgf("Available slots for %s: %d", name, slots)
|
|
||||||
// If slots are available, process the next import request from the queue
|
// If slots are available, process the next import request from the queue
|
||||||
for slots > 0 {
|
for slots > 0 {
|
||||||
select {
|
select {
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sirrobot01/decypharr/internal/request"
|
|
||||||
"github.com/sirrobot01/decypharr/internal/utils"
|
|
||||||
debridTypes "github.com/sirrobot01/decypharr/pkg/debrid"
|
|
||||||
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirrobot01/decypharr/internal/request"
|
||||||
|
"github.com/sirrobot01/decypharr/internal/utils"
|
||||||
|
debridTypes "github.com/sirrobot01/decypharr/pkg/debrid"
|
||||||
|
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Store) AddTorrent(ctx context.Context, importReq *ImportRequest) error {
|
func (s *Store) AddTorrent(ctx context.Context, importReq *ImportRequest) error {
|
||||||
@@ -60,10 +61,16 @@ func (s *Store) processFiles(torrent *Torrent, debridTorrent *types.Torrent, imp
|
|||||||
_arr := importReq.Arr
|
_arr := importReq.Arr
|
||||||
backoff := time.NewTimer(s.refreshInterval)
|
backoff := time.NewTimer(s.refreshInterval)
|
||||||
defer backoff.Stop()
|
defer backoff.Stop()
|
||||||
|
|
||||||
for debridTorrent.Status != "downloaded" {
|
for debridTorrent.Status != "downloaded" {
|
||||||
s.logger.Debug().Msgf("%s <- (%s) Download Progress: %.2f%%", debridTorrent.Debrid, debridTorrent.Name, debridTorrent.Progress)
|
|
||||||
dbT, err := client.CheckStatus(debridTorrent)
|
dbT, err := client.CheckStatus(debridTorrent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
s.logger.Error().
|
||||||
|
Str("torrent_id", debridTorrent.Id).
|
||||||
|
Str("torrent_name", debridTorrent.Name).
|
||||||
|
Err(err).
|
||||||
|
Msg("Error checking torrent status")
|
||||||
if dbT != nil && dbT.Id != "" {
|
if dbT != nil && dbT.Id != "" {
|
||||||
// Delete the torrent if it was not downloaded
|
// Delete the torrent if it was not downloaded
|
||||||
go func() {
|
go func() {
|
||||||
@@ -83,15 +90,16 @@ func (s *Store) processFiles(torrent *Torrent, debridTorrent *types.Torrent, imp
|
|||||||
torrent = s.partialTorrentUpdate(torrent, debridTorrent)
|
torrent = s.partialTorrentUpdate(torrent, debridTorrent)
|
||||||
|
|
||||||
// Exit the loop for downloading statuses to prevent memory buildup
|
// Exit the loop for downloading statuses to prevent memory buildup
|
||||||
if debridTorrent.Status == "downloaded" || !utils.Contains(downloadingStatuses, debridTorrent.Status) {
|
exitCondition1 := debridTorrent.Status == "downloaded"
|
||||||
|
exitCondition2 := !utils.Contains(downloadingStatuses, debridTorrent.Status)
|
||||||
|
|
||||||
|
if exitCondition1 || exitCondition2 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
select {
|
<-backoff.C
|
||||||
case <-backoff.C:
|
// Increase interval gradually, cap at max
|
||||||
// Increase interval gradually, cap at max
|
nextInterval := min(s.refreshInterval*2, 30*time.Second)
|
||||||
nextInterval := min(s.refreshInterval*2, 30*time.Second)
|
backoff.Reset(nextInterval)
|
||||||
backoff.Reset(nextInterval)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var torrentSymlinkPath string
|
var torrentSymlinkPath string
|
||||||
var err error
|
var err error
|
||||||
@@ -109,7 +117,6 @@ func (s *Store) processFiles(torrent *Torrent, debridTorrent *types.Torrent, imp
|
|||||||
}()
|
}()
|
||||||
s.logger.Error().Err(err).Msgf("Error occured while processing torrent %s", debridTorrent.Name)
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuccess := func(torrentSymlinkPath string) {
|
onSuccess := func(torrentSymlinkPath string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user