diff --git a/pkg/debrid/debrid_link.go b/pkg/debrid/debrid_link.go index 240cf49..39eceda 100644 --- a/pkg/debrid/debrid_link.go +++ b/pkg/debrid/debrid_link.go @@ -197,9 +197,7 @@ func (r *DebridLink) CheckStatus(torrent *Torrent, isSymlink bool) (*Torrent, er return torrent, err } status := torrent.Status - if status == "error" || status == "dead" || status == "magnet_error" { - return torrent, fmt.Errorf("torrent: %s has error", torrent.Name) - } else if status == "downloaded" { + if status == "downloaded" { r.logger.Info().Msgf("Torrent: %s downloaded", torrent.Name) if !isSymlink { err = r.GetDownloadLinks(torrent) @@ -216,6 +214,8 @@ func (r *DebridLink) CheckStatus(torrent *Torrent, isSymlink bool) (*Torrent, er // Break out of the loop if the torrent is downloading. // This is necessary to prevent infinite loop since we moved to sync downloading and async processing break + } else { + return torrent, fmt.Errorf("torrent: %s has error", torrent.Name) } } diff --git a/pkg/debrid/realdebrid.go b/pkg/debrid/realdebrid.go index 4e1b192..2ecdabc 100644 --- a/pkg/debrid/realdebrid.go +++ b/pkg/debrid/realdebrid.go @@ -183,9 +183,7 @@ func (r *RealDebrid) CheckStatus(torrent *Torrent, isSymlink bool) (*Torrent, er torrent.Status = status torrent.Debrid = r downloadingStatus := []string{"downloading", "magnet_conversion", "queued", "compressing", "uploading"} - if status == "error" || status == "dead" || status == "magnet_error" { - return torrent, fmt.Errorf("torrent: %s has error: %s", torrent.Name, status) - } else if status == "waiting_files_selection" { + if status == "waiting_files_selection" { files := GetTorrentFiles(data) torrent.Files = files if len(files) == 0 { @@ -222,6 +220,8 @@ func (r *RealDebrid) CheckStatus(torrent *Torrent, isSymlink bool) (*Torrent, er // Break out of the loop if the torrent is downloading. // This is necessary to prevent infinite loop since we moved to sync downloading and async processing break + } else { + return torrent, fmt.Errorf("torrent: %s has error: %s", torrent.Name, status) } } diff --git a/pkg/debrid/torbox.go b/pkg/debrid/torbox.go index 7cc7d0a..cdf8243 100644 --- a/pkg/debrid/torbox.go +++ b/pkg/debrid/torbox.go @@ -206,9 +206,7 @@ func (r *Torbox) CheckStatus(torrent *Torrent, isSymlink bool) (*Torrent, error) return tb, err } status := torrent.Status - if status == "error" || status == "dead" || status == "magnet_error" { - return torrent, fmt.Errorf("torrent: %s has error", torrent.Name) - } else if status == "downloaded" { + if status == "downloaded" { r.logger.Info().Msgf("Torrent: %s downloaded", torrent.Name) if !isSymlink { err = r.GetDownloadLinks(torrent) @@ -225,6 +223,8 @@ func (r *Torbox) CheckStatus(torrent *Torrent, isSymlink bool) (*Torrent, error) // Break out of the loop if the torrent is downloading. // This is necessary to prevent infinite loop since we moved to sync downloading and async processing break + } else { + return torrent, fmt.Errorf("torrent: %s has error", torrent.Name) } } diff --git a/pkg/debrid/torrent.go b/pkg/debrid/torrent.go index cf08b47..6b98061 100644 --- a/pkg/debrid/torrent.go +++ b/pkg/debrid/torrent.go @@ -6,6 +6,7 @@ import ( "github.com/sirrobot01/debrid-blackhole/pkg/arr" "os" "path/filepath" + "sync" ) type Arr struct { @@ -45,8 +46,10 @@ type Torrent struct { Links []string `json:"links"` DownloadLinks []TorrentDownloadLinks `json:"download_links"` - Debrid Service - Arr *arr.Arr + Debrid Service `json:"-"` + Arr *arr.Arr `json:"arr"` + Mu sync.Mutex `json:"-"` + SizeDownloaded int64 `json:"-"` // This is used for local download } type TorrentDownloadLinks struct { diff --git a/pkg/downloaders/grab.go b/pkg/downloaders/grab.go index d186f9c..1af7ef3 100644 --- a/pkg/downloaders/grab.go +++ b/pkg/downloaders/grab.go @@ -2,7 +2,6 @@ package downloaders import ( "crypto/tls" - "fmt" "github.com/cavaliergopher/grab/v3" "net/http" "time" @@ -21,35 +20,37 @@ func GetGrabClient() *grab.Client { } } -func NormalGrab(client *grab.Client, url, filename string) error { +func NormalGrab(client *grab.Client, url, filename string, progressCallback func(int64)) error { req, err := grab.NewRequest(filename, url) if err != nil { return err } resp := client.Do(req) - if err := resp.Err(); err != nil { - return err - } - t := time.NewTicker(2 * time.Second) + t := time.NewTicker(time.Second) defer t.Stop() + + var lastReported int64 Loop: for { select { case <-t.C: - fmt.Printf(" %s: transferred %d / %d bytes (%.2f%%)", - resp.Filename, - resp.BytesComplete(), - resp.Size(), - 100*resp.Progress()) - + current := resp.BytesComplete() + if current != lastReported { + if progressCallback != nil { + progressCallback(current - lastReported) + } + lastReported = current + } case <-resp.Done: - // download is complete break Loop } } - if err := resp.Err(); err != nil { - return err + + // Report final bytes + if progressCallback != nil { + progressCallback(resp.BytesComplete() - lastReported) } - return nil + + return resp.Err() } diff --git a/pkg/qbit/server/templates/download.html b/pkg/qbit/server/templates/download.html index 4a4a179..bdc844b 100644 --- a/pkg/qbit/server/templates/download.html +++ b/pkg/qbit/server/templates/download.html @@ -96,8 +96,6 @@ } else { alert(`Successfully added ${result.results.length} torrents!`); } - - document.getElementById('magnetURI').value = ''; } catch (error) { alert(`Error adding downloads: ${error.message}`); } finally { diff --git a/pkg/qbit/shared/downloader.go b/pkg/qbit/shared/downloader.go index 386494e..243a848 100644 --- a/pkg/qbit/shared/downloader.go +++ b/pkg/qbit/shared/downloader.go @@ -11,7 +11,8 @@ import ( "time" ) -func (q *QBit) processManualFiles(debridTorrent *debrid.Torrent) (string, error) { +func (q *QBit) ProcessManualFile(torrent *Torrent) (string, error) { + debridTorrent := torrent.DebridTorrent q.logger.Info().Msgf("Downloading %d files...", len(debridTorrent.DownloadLinks)) torrentPath := common.RemoveExtension(debridTorrent.OriginalFilename) parent := common.RemoveInvalidChars(filepath.Join(q.DownloadFolder, debridTorrent.Arr.Name, torrentPath)) @@ -20,14 +21,38 @@ func (q *QBit) processManualFiles(debridTorrent *debrid.Torrent) (string, error) // add previous error to the error and return return "", fmt.Errorf("failed to create directory: %s: %v", parent, err) } - q.downloadFiles(debridTorrent, parent) + q.downloadFiles(torrent, parent) return torrentPath, nil } -func (q *QBit) downloadFiles(debridTorrent *debrid.Torrent, parent string) { +func (q *QBit) downloadFiles(torrent *Torrent, parent string) { + debridTorrent := torrent.DebridTorrent var wg sync.WaitGroup semaphore := make(chan struct{}, 5) - client := downloaders.GetHTTPClient() + totalSize := int64(0) + for _, file := range debridTorrent.Files { + totalSize += file.Size + } + debridTorrent.Mu.Lock() + debridTorrent.SizeDownloaded = 0 // Reset downloaded bytes + debridTorrent.Progress = 0 // Reset progress + debridTorrent.Mu.Unlock() + client := downloaders.GetGrabClient() + progressCallback := func(downloaded int64) { + debridTorrent.Mu.Lock() + defer debridTorrent.Mu.Unlock() + torrent.Mu.Lock() + defer torrent.Mu.Unlock() + + // Update total downloaded bytes + debridTorrent.SizeDownloaded += downloaded + + // Calculate overall progress + if totalSize > 0 { + debridTorrent.Progress = float64(debridTorrent.SizeDownloaded) / float64(totalSize) * 100 + } + q.UpdateTorrentMin(torrent, debridTorrent) + } for _, link := range debridTorrent.DownloadLinks { if link.DownloadLink == "" { q.logger.Info().Msgf("No download link found for %s", link.Filename) @@ -38,11 +63,19 @@ func (q *QBit) downloadFiles(debridTorrent *debrid.Torrent, parent string) { go func(link debrid.TorrentDownloadLinks) { defer wg.Done() defer func() { <-semaphore }() - err := downloaders.NormalHTTP(client, link.DownloadLink, filepath.Join(parent, link.Filename)) + filename := link.Filename + + err := downloaders.NormalGrab( + client, + link.DownloadLink, + filepath.Join(parent, filename), + progressCallback, + ) + if err != nil { - q.logger.Info().Msgf("Error downloading %s: %v", link.DownloadLink, err) + q.logger.Error().Msgf("Failed to download %s: %v", filename, err) } else { - q.logger.Info().Msgf("Downloaded %s successfully", link.DownloadLink) + q.logger.Info().Msgf("Downloaded %s", filename) } }(link) } @@ -50,7 +83,8 @@ func (q *QBit) downloadFiles(debridTorrent *debrid.Torrent, parent string) { q.logger.Info().Msgf("Downloaded all files for %s", debridTorrent.Name) } -func (q *QBit) ProcessSymlink(debridTorrent *debrid.Torrent) (string, error) { +func (q *QBit) ProcessSymlink(torrent *Torrent) (string, error) { + debridTorrent := torrent.DebridTorrent var wg sync.WaitGroup files := debridTorrent.Files ready := make(chan debrid.TorrentFile, len(files)) diff --git a/pkg/qbit/shared/structs.go b/pkg/qbit/shared/structs.go index 46246b6..929e7d1 100644 --- a/pkg/qbit/shared/structs.go +++ b/pkg/qbit/shared/structs.go @@ -1,6 +1,9 @@ package shared -import "github.com/sirrobot01/debrid-blackhole/pkg/debrid" +import ( + "github.com/sirrobot01/debrid-blackhole/pkg/debrid" + "sync" +) type BuildInfo struct { Libtorrent string `json:"libtorrent"` @@ -219,6 +222,8 @@ type Torrent struct { UploadedSession int64 `json:"uploaded_session,omitempty"` Upspeed int `json:"upspeed,omitempty"` Source string `json:"source,omitempty"` + + Mu sync.Mutex `json:"-"` } func (t *Torrent) IsReady() bool { diff --git a/pkg/qbit/shared/torrent.go b/pkg/qbit/shared/torrent.go index 3544abe..253a1f7 100644 --- a/pkg/qbit/shared/torrent.go +++ b/pkg/qbit/shared/torrent.go @@ -112,9 +112,9 @@ func (q *QBit) ProcessFiles(torrent *Torrent, debridTorrent *debrid.Torrent, arr ) debridTorrent.Arr = arr if isSymlink { - torrentPath, err = q.ProcessSymlink(debridTorrent) + torrentPath, err = q.ProcessSymlink(torrent) } else { - torrentPath, err = q.processManualFiles(debridTorrent) + torrentPath, err = q.ProcessManualFile(torrent) } if err != nil { q.MarkAsFailed(torrent)