- Delete empty files selected torrents
- Add more info to UI - Add a global file download limit for local downloads
This commit is contained in:
@@ -39,6 +39,7 @@ type QBitTorrent struct {
|
|||||||
Categories []string `json:"categories,omitempty"`
|
Categories []string `json:"categories,omitempty"`
|
||||||
RefreshInterval int `json:"refresh_interval,omitempty"`
|
RefreshInterval int `json:"refresh_interval,omitempty"`
|
||||||
SkipPreCache bool `json:"skip_pre_cache,omitempty"`
|
SkipPreCache bool `json:"skip_pre_cache,omitempty"`
|
||||||
|
MaxDownloads int `json:"max_downloads,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Arr struct {
|
type Arr struct {
|
||||||
|
|||||||
@@ -232,14 +232,12 @@ func (ad *AllDebrid) CheckStatus(torrent *types.Torrent, isSymlink bool) (*types
|
|||||||
break
|
break
|
||||||
} else if slices.Contains(ad.GetDownloadingStatus(), status) {
|
} else if slices.Contains(ad.GetDownloadingStatus(), status) {
|
||||||
if !torrent.DownloadUncached {
|
if !torrent.DownloadUncached {
|
||||||
_ = ad.DeleteTorrent(torrent.Id)
|
|
||||||
return torrent, fmt.Errorf("torrent: %s not cached", torrent.Name)
|
return torrent, fmt.Errorf("torrent: %s not cached", torrent.Name)
|
||||||
}
|
}
|
||||||
// Break out of the loop if the torrent is downloading.
|
// 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
|
// This is necessary to prevent infinite loop since we moved to sync downloading and async processing
|
||||||
return torrent, nil
|
return torrent, nil
|
||||||
} else {
|
} else {
|
||||||
_ = ad.DeleteTorrent(torrent.Id)
|
|
||||||
return torrent, fmt.Errorf("torrent: %s has error", torrent.Name)
|
return torrent, fmt.Errorf("torrent: %s has error", torrent.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,12 @@ func ProcessTorrent(d *Engine, magnet *utils.Magnet, a *arr.Arr, isSymlink, over
|
|||||||
}
|
}
|
||||||
logger.Info().Msgf("Torrent: %s(id=%s) submitted to %s", dbt.Name, dbt.Id, db.GetName())
|
logger.Info().Msgf("Torrent: %s(id=%s) submitted to %s", dbt.Name, dbt.Id, db.GetName())
|
||||||
d.LastUsed = index
|
d.LastUsed = index
|
||||||
return db.CheckStatus(dbt, isSymlink)
|
torrent, err := db.CheckStatus(dbt, isSymlink)
|
||||||
|
if err != nil && torrent != nil && torrent.Id != "" {
|
||||||
|
// Delete the torrent if it was not downloaded
|
||||||
|
_ = db.DeleteTorrent(torrent.Id)
|
||||||
|
}
|
||||||
|
return torrent, err
|
||||||
}
|
}
|
||||||
err := fmt.Errorf("failed to process torrent")
|
err := fmt.Errorf("failed to process torrent")
|
||||||
for _, e := range errs {
|
for _, e := range errs {
|
||||||
|
|||||||
@@ -167,14 +167,14 @@ func (c *Cache) reInsertTorrent(ct *CachedTorrent) (*CachedTorrent, error) {
|
|||||||
}
|
}
|
||||||
torrent.DownloadUncached = false // Set to false, avoid re-downloading
|
torrent.DownloadUncached = false // Set to false, avoid re-downloading
|
||||||
torrent, err = c.client.CheckStatus(torrent, true)
|
torrent, err = c.client.CheckStatus(torrent, true)
|
||||||
if err != nil && torrent != nil {
|
if err != nil {
|
||||||
|
if torrent != nil && torrent.Id != "" {
|
||||||
|
// Delete the torrent if it was not downloaded
|
||||||
|
_ = c.client.DeleteTorrent(torrent.Id)
|
||||||
|
}
|
||||||
c.failedToReinsert.Store(oldID, struct{}{})
|
c.failedToReinsert.Store(oldID, struct{}{})
|
||||||
return ct, fmt.Errorf("failed to check status: %w", err)
|
return ct, fmt.Errorf("failed to check status: %w", err)
|
||||||
}
|
}
|
||||||
if torrent == nil {
|
|
||||||
c.failedToReinsert.Store(oldID, struct{}{})
|
|
||||||
return ct, fmt.Errorf("failed to check status: empty torrent")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the torrent in the cache
|
// Update the torrent in the cache
|
||||||
addedOn, err := time.Parse(time.RFC3339, torrent.Added)
|
addedOn, err := time.Parse(time.RFC3339, torrent.Added)
|
||||||
|
|||||||
@@ -227,14 +227,12 @@ func (dl *DebridLink) CheckStatus(torrent *types.Torrent, isSymlink bool) (*type
|
|||||||
break
|
break
|
||||||
} else if slices.Contains(dl.GetDownloadingStatus(), status) {
|
} else if slices.Contains(dl.GetDownloadingStatus(), status) {
|
||||||
if !torrent.DownloadUncached {
|
if !torrent.DownloadUncached {
|
||||||
_ = dl.DeleteTorrent(torrent.Id)
|
|
||||||
return torrent, fmt.Errorf("torrent: %s not cached", torrent.Name)
|
return torrent, fmt.Errorf("torrent: %s not cached", torrent.Name)
|
||||||
}
|
}
|
||||||
// Break out of the loop if the torrent is downloading.
|
// 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
|
// This is necessary to prevent infinite loop since we moved to sync downloading and async processing
|
||||||
return torrent, nil
|
return torrent, nil
|
||||||
} else {
|
} else {
|
||||||
_ = dl.DeleteTorrent(torrent.Id)
|
|
||||||
return torrent, fmt.Errorf("torrent: %s has error", torrent.Name)
|
return torrent, fmt.Errorf("torrent: %s has error", torrent.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -354,12 +354,10 @@ func (r *RealDebrid) CheckStatus(t *types.Torrent, isSymlink bool) (*types.Torre
|
|||||||
break
|
break
|
||||||
} else if slices.Contains(r.GetDownloadingStatus(), status) {
|
} else if slices.Contains(r.GetDownloadingStatus(), status) {
|
||||||
if !t.DownloadUncached {
|
if !t.DownloadUncached {
|
||||||
_ = r.DeleteTorrent(t.Id)
|
|
||||||
return t, fmt.Errorf("torrent: %s not cached", t.Name)
|
return t, fmt.Errorf("torrent: %s not cached", t.Name)
|
||||||
}
|
}
|
||||||
return t, nil
|
return t, nil
|
||||||
} else {
|
} else {
|
||||||
_ = r.DeleteTorrent(t.Id)
|
|
||||||
return t, fmt.Errorf("torrent: %s has error: %s", t.Name, status)
|
return t, fmt.Errorf("torrent: %s has error: %s", t.Name, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,7 +480,7 @@ func (r *RealDebrid) _getDownloadLink(file *types.File) (*types.DownloadLink, er
|
|||||||
}
|
}
|
||||||
var data UnrestrictResponse
|
var data UnrestrictResponse
|
||||||
if err = json.Unmarshal(b, &data); err != nil {
|
if err = json.Unmarshal(b, &data); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("realdebrid API error: Error unmarshalling response: %w", err)
|
||||||
}
|
}
|
||||||
if data.Download == "" {
|
if data.Download == "" {
|
||||||
return nil, fmt.Errorf("realdebrid API error: download link not found")
|
return nil, fmt.Errorf("realdebrid API error: download link not found")
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import (
|
|||||||
"github.com/sirrobot01/decypharr/internal/request"
|
"github.com/sirrobot01/decypharr/internal/request"
|
||||||
"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"
|
||||||
|
"github.com/sirrobot01/decypharr/pkg/version"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
gourl "net/url"
|
gourl "net/url"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -41,6 +43,7 @@ func New(dc config.Debrid) *Torbox {
|
|||||||
|
|
||||||
headers := map[string]string{
|
headers := map[string]string{
|
||||||
"Authorization": fmt.Sprintf("Bearer %s", dc.APIKey),
|
"Authorization": fmt.Sprintf("Bearer %s", dc.APIKey),
|
||||||
|
"User-Agent": fmt.Sprintf("Decypharr/%s (%s; %s)", version.GetInfo(), runtime.GOOS, runtime.GOARCH),
|
||||||
}
|
}
|
||||||
_log := logger.New(dc.Name)
|
_log := logger.New(dc.Name)
|
||||||
client := request.New(
|
client := request.New(
|
||||||
@@ -259,14 +262,12 @@ func (tb *Torbox) CheckStatus(torrent *types.Torrent, isSymlink bool) (*types.To
|
|||||||
break
|
break
|
||||||
} else if slices.Contains(tb.GetDownloadingStatus(), status) {
|
} else if slices.Contains(tb.GetDownloadingStatus(), status) {
|
||||||
if !torrent.DownloadUncached {
|
if !torrent.DownloadUncached {
|
||||||
_ = tb.DeleteTorrent(torrent.Id)
|
|
||||||
return torrent, fmt.Errorf("torrent: %s not cached", torrent.Name)
|
return torrent, fmt.Errorf("torrent: %s not cached", torrent.Name)
|
||||||
}
|
}
|
||||||
// Break out of the loop if the torrent is downloading.
|
// 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
|
// This is necessary to prevent infinite loop since we moved to sync downloading and async processing
|
||||||
return torrent, nil
|
return torrent, nil
|
||||||
} else {
|
} else {
|
||||||
_ = tb.DeleteTorrent(torrent.Id)
|
|
||||||
return torrent, fmt.Errorf("torrent: %s has error", torrent.Name)
|
return torrent, fmt.Errorf("torrent: %s has error", torrent.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func (q *QBit) ProcessManualFile(torrent *Torrent) (string, error) {
|
|||||||
func (q *QBit) downloadFiles(torrent *Torrent, parent string) {
|
func (q *QBit) downloadFiles(torrent *Torrent, parent string) {
|
||||||
debridTorrent := torrent.DebridTorrent
|
debridTorrent := torrent.DebridTorrent
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
semaphore := make(chan struct{}, 5)
|
|
||||||
totalSize := int64(0)
|
totalSize := int64(0)
|
||||||
for _, file := range debridTorrent.Files {
|
for _, file := range debridTorrent.Files {
|
||||||
totalSize += file.Size
|
totalSize += file.Size
|
||||||
@@ -92,8 +92,8 @@ func (q *QBit) downloadFiles(torrent *Torrent, parent string) {
|
|||||||
q.UpdateTorrentMin(torrent, debridTorrent)
|
q.UpdateTorrentMin(torrent, debridTorrent)
|
||||||
}
|
}
|
||||||
client := &grab.Client{
|
client := &grab.Client{
|
||||||
UserAgent: "qBitTorrent",
|
UserAgent: "Decypharr[QBitTorrent]",
|
||||||
HTTPClient: request.New(request.WithTimeout(0)),
|
HTTPClient: request.New(request.WithTimeout(60 * time.Second)),
|
||||||
}
|
}
|
||||||
for _, file := range debridTorrent.Files {
|
for _, file := range debridTorrent.Files {
|
||||||
if file.DownloadLink == nil {
|
if file.DownloadLink == nil {
|
||||||
@@ -101,10 +101,10 @@ func (q *QBit) downloadFiles(torrent *Torrent, parent string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
semaphore <- struct{}{}
|
q.downloadSemaphore <- struct{}{}
|
||||||
go func(file debrid.File) {
|
go func(file debrid.File) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer func() { <-semaphore }()
|
defer func() { <-q.downloadSemaphore }()
|
||||||
filename := file.Link
|
filename := file.Link
|
||||||
|
|
||||||
err := Download(
|
err := Download(
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ type QBit struct {
|
|||||||
Tags []string
|
Tags []string
|
||||||
RefreshInterval int
|
RefreshInterval int
|
||||||
SkipPreCache bool
|
SkipPreCache bool
|
||||||
|
|
||||||
|
downloadSemaphore chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *QBit {
|
func New() *QBit {
|
||||||
@@ -28,14 +30,15 @@ func New() *QBit {
|
|||||||
port := cmp.Or(_cfg.Port, os.Getenv("QBIT_PORT"), "8282")
|
port := cmp.Or(_cfg.Port, os.Getenv("QBIT_PORT"), "8282")
|
||||||
refreshInterval := cmp.Or(cfg.RefreshInterval, 10)
|
refreshInterval := cmp.Or(cfg.RefreshInterval, 10)
|
||||||
return &QBit{
|
return &QBit{
|
||||||
Username: cfg.Username,
|
Username: cfg.Username,
|
||||||
Password: cfg.Password,
|
Password: cfg.Password,
|
||||||
Port: port,
|
Port: port,
|
||||||
DownloadFolder: cfg.DownloadFolder,
|
DownloadFolder: cfg.DownloadFolder,
|
||||||
Categories: cfg.Categories,
|
Categories: cfg.Categories,
|
||||||
Storage: NewTorrentStorage(filepath.Join(_cfg.Path, "torrents.json")),
|
Storage: NewTorrentStorage(filepath.Join(_cfg.Path, "torrents.json")),
|
||||||
logger: logger.New("qbit"),
|
logger: logger.New("qbit"),
|
||||||
RefreshInterval: refreshInterval,
|
RefreshInterval: refreshInterval,
|
||||||
SkipPreCache: cfg.SkipPreCache,
|
SkipPreCache: cfg.SkipPreCache,
|
||||||
|
downloadSemaphore: make(chan struct{}, cmp.Or(cfg.MaxDownloads, 5)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ func (q *QBit) ProcessFiles(torrent *Torrent, debridTorrent *debrid.Torrent, arr
|
|||||||
q.logger.Debug().Msgf("%s <- (%s) Download Progress: %.2f%%", debridTorrent.Debrid, debridTorrent.Name, debridTorrent.Progress)
|
q.logger.Debug().Msgf("%s <- (%s) Download Progress: %.2f%%", debridTorrent.Debrid, debridTorrent.Name, debridTorrent.Progress)
|
||||||
dbT, err := client.CheckStatus(debridTorrent, isSymlink)
|
dbT, err := client.CheckStatus(debridTorrent, isSymlink)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if dbT != nil && dbT.Id != "" {
|
||||||
|
// Delete the torrent if it was not downloaded
|
||||||
|
_ = client.DeleteTorrent(dbT.Id)
|
||||||
|
}
|
||||||
q.logger.Error().Msgf("Error checking status: %v", err)
|
q.logger.Error().Msgf("Error checking status: %v", err)
|
||||||
q.MarkAsFailed(torrent)
|
q.MarkAsFailed(torrent)
|
||||||
if err := arr.Refresh(); err != nil {
|
if err := arr.Refresh(); err != nil {
|
||||||
|
|||||||
@@ -193,18 +193,25 @@
|
|||||||
<div class="setup-step d-none" id="step3">
|
<div class="setup-step d-none" id="step3">
|
||||||
<div class="section mb-5">
|
<div class="section mb-5">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-4 mb-3">
|
||||||
<label class="form-label" for="qbit.download_folder">Symlink/Download Folder</label>
|
<label class="form-label" for="qbit.download_folder">Symlink/Download Folder</label>
|
||||||
<input type="text" class="form-control" name="qbit.download_folder" id="qbit.download_folder">
|
<input type="text" class="form-control" name="qbit.download_folder" id="qbit.download_folder">
|
||||||
|
<small class="form-text text-muted">Folder where the downloaded files will be stored</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label class="form-label" for="qbit.refresh_interval">Refresh Interval (seconds)</label>
|
<label class="form-label" for="qbit.refresh_interval">Refresh Interval (seconds)</label>
|
||||||
<input type="number" class="form-control" name="qbit.refresh_interval" id="qbit.refresh_interval">
|
<input type="number" class="form-control" name="qbit.refresh_interval" id="qbit.refresh_interval">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-5 mb-3">
|
||||||
|
<label class="form-label" for="qbit.max_downloads">Maximum Downloads Limit</label>
|
||||||
|
<input type="number" class="form-control" name="qbit.max_downloads" id="qbit.max_downloads">
|
||||||
|
<small class="form-text text-muted">Maximum number of simultaneous local downloads across all torrents</small>
|
||||||
|
</div>
|
||||||
<div class="col mb-3">
|
<div class="col mb-3">
|
||||||
<div class="form-check me-3 d-inline-block">
|
<div class="form-check me-3 d-inline-block">
|
||||||
<input type="checkbox" class="form-check-input" name="qbit.skip_pre_cache" id="qbit.skip_pre_cache">
|
<input type="checkbox" class="form-check-input" name="qbit.skip_pre_cache" id="qbit.skip_pre_cache">
|
||||||
<label class="form-check-label" for="qbit.skip_pre_cache">Disable Pre-Cache On Download (unchecking this caches a tiny part of your file to speed up import)</label>
|
<label class="form-check-label" for="qbit.skip_pre_cache">Disable Pre-Cache On Download</label>
|
||||||
|
<small class="form-text text-muted">Unchecking this caches a tiny part of your file to speed up import</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -248,39 +255,45 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="repairCol" class="d-none">
|
<div id="repairCol" class="d-none">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-4 mb-3">
|
||||||
<label class="form-label" for="repair.interval">Interval</label>
|
<label class="form-label" for="repair.interval">Interval</label>
|
||||||
<input type="text" class="form-control" name="repair.interval" id="repair.interval" placeholder="e.g., 24h">
|
<input type="text" class="form-control" name="repair.interval" id="repair.interval" placeholder="e.g., 24h">
|
||||||
|
<small class="form-text text-muted">Interval for the repair process(e.g., 24h, 1d, 03:00)</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 mb-3">
|
<div class="col-md-5 mb-3">
|
||||||
<label class="form-label" for="repair.zurg_url">Zurg URL</label>
|
<label class="form-label" for="repair.zurg_url">Zurg URL</label>
|
||||||
<input type="text" class="form-control" name="repair.zurg_url" id="repair.zurg_url" placeholder="http://zurg:9999">
|
<input type="text" class="form-control" name="repair.zurg_url" id="repair.zurg_url" placeholder="http://zurg:9999">
|
||||||
|
<small class="form-text text-muted">Speeds up the repair process by using Zurg</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-2 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input type="checkbox" class="form-check-input" name="repair.use_webdav" id="repair.use_webdav">
|
<input type="checkbox" class="form-check-input" name="repair.use_webdav" id="repair.use_webdav">
|
||||||
<label class="form-check-label" for="repair.use_webdav">Use Webdav</label>
|
<label class="form-check-label" for="repair.use_webdav">Use Webdav</label>
|
||||||
</div>
|
</div>
|
||||||
|
<small class="form-text text-muted">Use Internal Webdav for repair(make sure webdav is enabled in the debrid section</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input type="checkbox" class="form-check-input" name="repair.run_on_start" id="repair.run_on_start">
|
<input type="checkbox" class="form-check-input" name="repair.run_on_start" id="repair.run_on_start">
|
||||||
<label class="form-check-label" for="repair.run_on_start">Run on Start</label>
|
<label class="form-check-label" for="repair.run_on_start">Run on Start</label>
|
||||||
</div>
|
</div>
|
||||||
|
<small class="form-text text-muted">Run repair on startup</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input type="checkbox" class="form-check-input" name="repair.reinsert" id="repair.reinsert">
|
<input type="checkbox" class="form-check-input" name="repair.reinsert" id="repair.reinsert">
|
||||||
<label class="form-check-label" for="repair.reinsert">Re-Insert Nerfed Release</label>
|
<label class="form-check-label" for="repair.reinsert">Re-Insert Nerfed Release</label>
|
||||||
</div>
|
</div>
|
||||||
|
<small class="form-text text-muted">Tries to autofix broken releases by re-inserting the torrent</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input type="checkbox" class="form-check-input" name="repair.auto_process" id="repair.auto_process">
|
<input type="checkbox" class="form-check-input" name="repair.auto_process" id="repair.auto_process">
|
||||||
<label class="form-check-label" for="repair.auto_process">Auto Process(Scheduled jobs will be auto-processed)</label>
|
<label class="form-check-label" for="repair.auto_process">Auto Process</label>
|
||||||
</div>
|
</div>
|
||||||
|
<small class="form-text text-muted">Automatically process the repair job(delete broken symlinks and searches the arr again)</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -321,28 +334,40 @@
|
|||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].api_key" >API Key</label>
|
<label class="form-label" for="debrid[${index}].api_key" >API Key</label>
|
||||||
<input type="password" class="form-control" name="debrid[${index}].api_key" id="debrid[${index}].api_key" required>
|
<input type="password" class="form-control" name="debrid[${index}].api_key" id="debrid[${index}].api_key" required>
|
||||||
|
<small class="form-text text-muted">API Key for the debrid service</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].folder" >Mount Folder</label>
|
<label class="form-label" for="debrid[${index}].folder">Mount/Rclone Folder</label>
|
||||||
<input type="text" class="form-control" name="debrid[${index}].folder" id="debrid[${index}].folder" placeholder="e.g. /mnt/remote/realdebrid" required>
|
<input type="text" class="form-control" name="debrid[${index}].folder" id="debrid[${index}].folder" placeholder="e.g. /mnt/remote/realdebrid" required>
|
||||||
|
<small class="form-text text-muted">Path to where you've mounted the debrid files. Usually your rclone path</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].rate_limit" >Rate Limit</label>
|
<label class="form-label" for="debrid[${index}].rate_limit" >Rate Limit</label>
|
||||||
<input type="text" class="form-control" name="debrid[${index}].rate_limit" id="debrid[${index}].rate_limit" placeholder="e.g., 200/minute" value="250/minute">
|
<input type="text" class="form-control" name="debrid[${index}].rate_limit" id="debrid[${index}].rate_limit" placeholder="e.g., 200/minute" value="250/minute">
|
||||||
|
<small class="form-text text-muted">Rate limit for the debrid service. Confirm your debrid service rate limit</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
</div>
|
||||||
<div class="form-check me-3 d-inline-block">
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="form-check me-3">
|
||||||
<input type="checkbox" class="form-check-input" name="debrid[${index}].download_uncached" id="debrid[${index}].download_uncached">
|
<input type="checkbox" class="form-check-input" name="debrid[${index}].download_uncached" id="debrid[${index}].download_uncached">
|
||||||
<label class="form-check-label" for="debrid[${index}].download_uncached" >Download Uncached</label>
|
<label class="form-check-label" for="debrid[${index}].download_uncached">Download Uncached</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check me-3 d-inline-block">
|
<small class="form-text text-muted">Download uncached files from the debrid service</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="form-check me-3">
|
||||||
<input type="checkbox" class="form-check-input" name="debrid[${index}].check_cached" id="debrid[${index}].check_cached">
|
<input type="checkbox" class="form-check-input" name="debrid[${index}].check_cached" id="debrid[${index}].check_cached">
|
||||||
<label class="form-check-label" for="debrid[${index}].check_cached" >Check Cached</label>
|
<label class="form-check-label" for="debrid[${index}].check_cached" disabled>Check Cached</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check d-inline-block">
|
<small class="form-text text-muted">Check if the file is cached before downloading(Disabled)</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="form-check me-3">
|
||||||
<input type="checkbox" class="form-check-input useWebdav" name="debrid[${index}].use_webdav" id="debrid[${index}].use_webdav">
|
<input type="checkbox" class="form-check-input useWebdav" name="debrid[${index}].use_webdav" id="debrid[${index}].use_webdav">
|
||||||
<label class="form-check-label" for="debrid[${index}].use_webdav" >Use WebDav</label>
|
<label class="form-check-label" for="debrid[${index}].use_webdav">Enable WebDav Server</label>
|
||||||
</div>
|
</div>
|
||||||
|
<small class="form-text text-muted">Create an internal webdav for this debrid</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-3 webdav d-none">
|
<div class="row mt-3 webdav d-none">
|
||||||
@@ -350,14 +375,17 @@
|
|||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].torrents_refresh_interval">Torrents Refresh Interval</label>
|
<label class="form-label" for="debrid[${index}].torrents_refresh_interval">Torrents Refresh Interval</label>
|
||||||
<input type="text" class="form-control webdav-field" name="debrid[${index}].torrents_refresh_interval" id="debrid[${index}].torrents_refresh_interval" placeholder="15s" value="15s">
|
<input type="text" class="form-control webdav-field" name="debrid[${index}].torrents_refresh_interval" id="debrid[${index}].torrents_refresh_interval" placeholder="15s" value="15s">
|
||||||
|
<small class="form-text text-muted">How often to refresh the torrents list from debrid(instant when using webdav)</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].download_links_refresh_interval">Download Links Refresh Interval</label>
|
<label class="form-label" for="debrid[${index}].download_links_refresh_interval">Download Links Refresh Interval</label>
|
||||||
<input type="text" class="form-control webdav-field" name="debrid[${index}].download_links_refresh_interval" id="debrid[${index}].download_links_refresh_interval" placeholder="40m" value="40m">
|
<input type="text" class="form-control webdav-field" name="debrid[${index}].download_links_refresh_interval" id="debrid[${index}].download_links_refresh_interval" placeholder="40m" value="40m">
|
||||||
|
<small class="form-text text-muted">How often to refresh the download links list from debrid</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].auto_expire_links_after">Expire Links After</label>
|
<label class="form-label" for="debrid[${index}].auto_expire_links_after">Expire Links After</label>
|
||||||
<input type="text" class="form-control webdav-field" name="debrid[${index}].auto_expire_links_after" id="debrid[${index}].auto_expire_links_after" placeholder="3d" value="3d">
|
<input type="text" class="form-control webdav-field" name="debrid[${index}].auto_expire_links_after" id="debrid[${index}].auto_expire_links_after" placeholder="3d" value="3d">
|
||||||
|
<small class="form-text text-muted">How long to keep the links in the webdav before expiring</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].folder_naming">Folder Naming Structure</label>
|
<label class="form-label" for="debrid[${index}].folder_naming">Folder Naming Structure</label>
|
||||||
@@ -369,22 +397,27 @@
|
|||||||
<option value="id">Use ID</option>
|
<option value="id">Use ID</option>
|
||||||
<option value="infohash">Use Infohash</option>
|
<option value="infohash">Use Infohash</option>
|
||||||
</select>
|
</select>
|
||||||
|
<small class="form-text text-muted">How to name each torrent directory in the webdav</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].workers">Number of Workers</label>
|
<label class="form-label" for="debrid[${index}].workers">Number of Workers</label>
|
||||||
<input type="text" class="form-control webdav-field" name="debrid[${index}].workers" id="debrid[${index}].workers" placeholder="e.g., 50" value="50">
|
<input type="text" class="form-control webdav-field" name="debrid[${index}].workers" id="debrid[${index}].workers" placeholder="e.g., 50" value="50">
|
||||||
|
<small class="form-text text-muted">Number of workers to use for the webdav server(when refreshing)</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].rc_url">Rclone RC URL</label>
|
<label class="form-label" for="debrid[${index}].rc_url">Rclone RC URL</label>
|
||||||
<input type="text" class="form-control webdav-field" name="debrid[${index}].rc_url" id="debrid[${index}].rc_url" placeholder="e.g., http://localhost:9990">
|
<input type="text" class="form-control webdav-field" name="debrid[${index}].rc_url" id="debrid[${index}].rc_url" placeholder="e.g., http://localhost:9990">
|
||||||
|
<small class="form-text text-muted">Rclone RC URL for the webdav server(speeds up import significantly)</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].rc_user">Rclone RC User</label>
|
<label class="form-label" for="debrid[${index}].rc_user">Rclone RC User</label>
|
||||||
<input type="text" class="form-control webdav-field" name="debrid[${index}].rc_user" id="debrid[${index}].rc_user">
|
<input type="text" class="form-control webdav-field" name="debrid[${index}].rc_user" id="debrid[${index}].rc_user">
|
||||||
|
<small class="form-text text-muted">Rclone RC User for the webdav server</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3">
|
||||||
<label class="form-label" for="debrid[${index}].rc_pass">Rclone RC Password</label>
|
<label class="form-label" for="debrid[${index}].rc_pass">Rclone RC Password</label>
|
||||||
<input type="password" class="form-control webdav-field" name="debrid[${index}].rc_pass" id="debrid[${index}].rc_pass">
|
<input type="password" class="form-control webdav-field" name="debrid[${index}].rc_pass" id="debrid[${index}].rc_pass">
|
||||||
|
<small class="form-text text-muted">Rclone RC Password for the webdav server</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -736,6 +769,7 @@
|
|||||||
qbittorrent: {
|
qbittorrent: {
|
||||||
download_folder: document.querySelector('[name="qbit.download_folder"]').value,
|
download_folder: document.querySelector('[name="qbit.download_folder"]').value,
|
||||||
refresh_interval: parseInt(document.querySelector('[name="qbit.refresh_interval"]').value || '0', 10),
|
refresh_interval: parseInt(document.querySelector('[name="qbit.refresh_interval"]').value || '0', 10),
|
||||||
|
max_downloads: parseInt(document.querySelector('[name="qbit.max_downloads"]').value || '0', 5),
|
||||||
skip_pre_cache: document.querySelector('[name="qbit.skip_pre_cache"]').checked
|
skip_pre_cache: document.querySelector('[name="qbit.skip_pre_cache"]').checked
|
||||||
},
|
},
|
||||||
arrs: [],
|
arrs: [],
|
||||||
|
|||||||
Reference in New Issue
Block a user