diff --git a/internal/config/types.go b/internal/config/types.go
index 59a9875..23c0cbd 100644
--- a/internal/config/types.go
+++ b/internal/config/types.go
@@ -29,20 +29,22 @@ type ConfigInterface interface {
EnableDownloadMount() bool
GetRateLimitSleepSecs() int
ShouldDeleteRarFiles() bool
+ GetDownloadsEveryMins() int
}
type ZurgConfig struct {
Version string `yaml:"zurg" json:"-"`
Token string `yaml:"token" json:"-"`
- Host string `yaml:"host" json:"host"`
- Port string `yaml:"port" json:"port"`
- Username string `yaml:"username" json:"username"`
- Password string `yaml:"password" json:"password"`
- Proxy string `yaml:"proxy" json:"proxy"`
- NumOfWorkers int `yaml:"concurrent_workers" json:"concurrent_workers"`
- RefreshEverySecs int `yaml:"check_for_changes_every_secs" json:"check_for_changes_every_secs"`
- RepairEveryMins int `yaml:"repair_every_mins" json:"repair_every_mins"`
+ Host string `yaml:"host" json:"host"`
+ Port string `yaml:"port" json:"port"`
+ Username string `yaml:"username" json:"username"`
+ Password string `yaml:"password" json:"password"`
+ Proxy string `yaml:"proxy" json:"proxy"`
+ NumOfWorkers int `yaml:"concurrent_workers" json:"concurrent_workers"`
+ RefreshEverySecs int `yaml:"check_for_changes_every_secs" json:"check_for_changes_every_secs"`
+ RepairEveryMins int `yaml:"repair_every_mins" json:"repair_every_mins"`
+ DownloadsEveryMins int `yaml:"downloads_every_mins" json:"downloads_every_mins"`
IgnoreRenames bool `yaml:"ignore_renames" json:"ignore_renames"`
RetainRDTorrentName bool `yaml:"retain_rd_torrent_name" json:"retain_rd_torrent_name"`
@@ -119,6 +121,13 @@ func (z *ZurgConfig) GetRepairEveryMins() int {
return z.RepairEveryMins
}
+func (z *ZurgConfig) GetDownloadsEveryMins() int {
+ if z.DownloadsEveryMins == 0 {
+ return 60
+ }
+ return z.DownloadsEveryMins
+}
+
func (z *ZurgConfig) EnableRepair() bool {
return z.CanRepair
}
diff --git a/internal/handlers/home.go b/internal/handlers/home.go
index 6d58c2e..3535297 100644
--- a/internal/handlers/home.go
+++ b/internal/handlers/home.go
@@ -68,185 +68,204 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
}
out := `
-
- | zurg |
-
-
- | Version |
- %s |
-
-
- | Built At |
- %s |
-
-
- | Git Commit |
- %s |
-
-
- | HTML |
- %s |
-
-
- | DAV |
- %s |
-
-
- | Infuse |
- %s |
-
-
- | Logs |
- %s |
-
-
- | Memory Allocation |
- %d MB |
-
-
- | Total Memory Allocated |
- %d MB |
-
-
- | System Memory |
- %d MB |
-
-
- | Number of GC Cycles |
- %d |
-
-
- | Process ID |
- %d |
-
-
- | Sponsor Zurg |
- Patreon |
+
+ | zurg |
+
+
+ | Version |
+ %s |
+
+
+ | Built At |
+ %s |
+
+
+ | Git Commit |
+ %s |
+
+
+ | HTML |
+ %s |
+
+
+ | DAV |
+ %s |
+
+
+ | Infuse |
+ %s |
+
+
+ | Logs |
+ %s |
+
+
+ | Memory Allocation |
+ %d MB |
+
+
+ | Total Memory Allocated |
+ %d MB |
+
+
+ | System Memory |
+ %d MB |
+
+
+ | Number of GC Cycles |
+ %d |
+
+
+ | Process ID |
+ %d |
+
+
+ | Sponsor Zurg |
+ Patreon |
%s |
-
-
- | Github |
+
+
+ | Github |
%s |
-
-
- | Paypal |
+
+
+ | Paypal |
%s |
-
-
- | User Info |
- Username |
+
+
+ | User Info |
+ Username |
%s |
-
-
- | Points |
+
+
+ | Points |
%d |
-
-
- | Locale |
+
+
+ | Locale |
%s |
-
-
- | Type |
+
+
+ | Type |
%s |
-
-
- | Premium |
+
+
+ | Premium |
%d days |
-
-
- | Expiration |
+
+
+ | Expiration |
%s |
-
-
- | Config |
- Version |
- %s |
-
-
- | Token |
+
+
+ | Config |
+ Version |
%s |
-
-
- | Host |
+
+
+ | Token |
%s |
-
-
- | Port |
+
+
+ | Host |
%s |
-
-
- | Workers |
+
+
+ | Port |
+ %s |
+
+
+ | Workers |
%d running / %d free / %d total |
-
-
- | Refresh Every... |
+
+
+ | Refresh Every... |
%d secs |
-
-
- | Retain RD Torrent Name |
+
+
+ | Retain RD Torrent Name |
%t |
-
-
- | Retain Folder Name Extension |
+
+
+ | Retain Folder Name Extension |
%t |
-
-
- | Can Repair |
+
+
+ | Can Repair |
%t |
-
-
- | Delete Rar Files |
+
+
+ | Repair Every... |
+ %d mins |
+
+
+ | Delete Rar Files |
%t |
-
-
- | API Timeout |
+
+
+ | API Timeout |
%d secs |
-
-
- | Download Timeout |
+
+
+ | Download Timeout |
%d secs |
-
-
- | Use Download Mount |
+
+
+ | Use Download Mount |
%t |
-
-
- | Rate Limit Sleep for... |
+
+
+ | Refresh Download Mount Every... |
+ %d mins |
+
+
+ | Rate Limit Sleep for... |
%d secs |
-
-
- | Retries Until Failed |
+
+
+ | Retries Until Failed |
%d |
-
-
- | Network Buffer Size |
+
+
+ | Network Buffer Size |
%d bytes |
-
-
- | Serve From Rclone |
+
+
+ | Serve From Rclone |
%t |
-
-
- | Verify Download Link |
+
+
+ | Verify Download Link |
%t |
-
-
- | Force IPv6 |
+
+
+ | Force IPv6 |
%t |
-
-
- | On Library Update |
+
+
+ | On Library Update |
%s |
-
-
- | Utilities |
+
+
+ | Utilities |
-
-
-
-
- |
-
+
+
+
+
+
+
+
`
out = fmt.Sprintf(out,
@@ -289,10 +308,12 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
response.Config.EnableRetainRDTorrentName(),
response.Config.EnableRetainFolderNameExtension(),
response.Config.EnableRepair(),
+ response.Config.GetRepairEveryMins(),
response.Config.ShouldDeleteRarFiles(),
response.Config.GetApiTimeoutSecs(),
response.Config.GetDownloadTimeoutSecs(),
response.Config.EnableDownloadMount(),
+ response.Config.GetDownloadsEveryMins(),
response.Config.GetRateLimitSleepSecs(),
response.Config.GetRetriesUntilFailed(),
response.Config.GetNetworkBufferSize(),
@@ -306,24 +327,34 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
}
func (zr *Handlers) handleRebootWorkerPool(resp http.ResponseWriter, req *http.Request) {
- // zr.workerPool.Release()
+ resp.Header().Set("Refresh", "2; url=/")
+ zr.workerPool.Release()
zr.workerPool.Reboot()
zr.log.Infof("Rebooted worker pool")
- fmt.Fprint(resp, "Rebooted worker pool, please close this window")
+ fmt.Fprint(resp, "Rebooting worker pool...")
}
-func (zr *Handlers) handleRebootRefreshPool(resp http.ResponseWriter, req *http.Request) {
+func (zr *Handlers) handleRebootRefreshWorker(resp http.ResponseWriter, req *http.Request) {
+ resp.Header().Set("Refresh", "2; url=/")
zr.torMgr.RefreshKillSwitch <- struct{}{}
zr.torMgr.StartRefreshJob()
zr.log.Infof("Rebooted refresh worker")
- fmt.Fprint(resp, "Rebooted refresh worker, please close this window")
+ fmt.Fprint(resp, "Rebooting refresh worker...")
}
-func (zr *Handlers) handleRebootRepairPool(resp http.ResponseWriter, req *http.Request) {
+func (zr *Handlers) handleRebootRepairWorker(resp http.ResponseWriter, req *http.Request) {
+ resp.Header().Set("Refresh", "2; url=/")
zr.torMgr.RepairKillSwitch <- struct{}{}
zr.torMgr.StartRepairJob()
zr.log.Infof("Rebooted repair worker")
- fmt.Fprint(resp, "Rebooted repair worker, please close this window")
+ fmt.Fprint(resp, "Rebooting repair worker...")
+}
+
+func (zr *Handlers) handleRemountDownloads(resp http.ResponseWriter, req *http.Request) {
+ resp.Header().Set("Refresh", "2; url=/")
+ zr.torMgr.RemountTrigger <- struct{}{}
+ zr.log.Infof("Triggered remount of downloads")
+ fmt.Fprint(resp, "Remounting downloads...")
}
func bToMb(b uint64) uint64 {
diff --git a/internal/handlers/router.go b/internal/handlers/router.go
index 8668100..a4162a5 100644
--- a/internal/handlers/router.go
+++ b/internal/handlers/router.go
@@ -20,14 +20,12 @@ import (
)
type Handlers struct {
- downloader *universal.Downloader
- torMgr *torrent.TorrentManager
- cfg config.ConfigInterface
- api *realdebrid.RealDebrid
- workerPool *ants.Pool
- refreshPool *ants.Pool
- repairPool *ants.Pool
- log *logutil.Logger
+ downloader *universal.Downloader
+ torMgr *torrent.TorrentManager
+ cfg config.ConfigInterface
+ api *realdebrid.RealDebrid
+ workerPool *ants.Pool
+ log *logutil.Logger
}
func init() {
@@ -52,9 +50,10 @@ func AttachHandlers(router *chi.Mux, downloader *universal.Downloader, torMgr *t
router.Use(hs.options)
router.Get("/", hs.handleHome)
- router.Get("/reboot/worker", hs.handleRebootWorkerPool)
- router.Get("/reboot/refresh", hs.handleRebootRefreshPool)
- router.Get("/reboot/repair", hs.handleRebootRepairPool)
+ router.Post("/reboot/worker", hs.handleRebootWorkerPool)
+ router.Post("/reboot/refresh", hs.handleRebootRefreshWorker)
+ router.Post("/reboot/repair", hs.handleRebootRepairWorker)
+ router.Post("/remount/downloads", hs.handleRemountDownloads)
// version
router.Get(fmt.Sprintf("/{mountType}/%s", version.FILE), hs.handleVersionFile)
router.Head(fmt.Sprintf("/{mountType}/%s", version.FILE), hs.handleCheckVersionFile)
diff --git a/internal/torrent/manager.go b/internal/torrent/manager.go
index 30cd5f6..84f301c 100644
--- a/internal/torrent/manager.go
+++ b/internal/torrent/manager.go
@@ -6,6 +6,7 @@ import (
"path/filepath"
"strings"
"sync"
+ "time"
"github.com/debridmediamanager/zurg/internal/config"
"github.com/debridmediamanager/zurg/pkg/logutil"
@@ -33,6 +34,7 @@ type TorrentManager struct {
workerPool *ants.Pool
RefreshKillSwitch chan struct{}
RepairKillSwitch chan struct{}
+ RemountTrigger chan struct{}
repairTrigger chan *Torrent
repairSet mapset.Set[*Torrent]
repairRunning bool
@@ -49,9 +51,9 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, w
Api: api,
DirectoryMap: cmap.New[cmap.ConcurrentMap[string, *Torrent]](),
DownloadCache: cmap.New[*realdebrid.Download](),
- DownloadMap: cmap.New[*realdebrid.Download](),
RefreshKillSwitch: make(chan struct{}, 1),
RepairKillSwitch: make(chan struct{}, 1),
+ RemountTrigger: make(chan struct{}, 1),
allAccessKeys: mapset.NewSet[string](),
latestState: &LibraryState{},
requiredVersion: "0.9.3-hotfix.4",
@@ -60,11 +62,12 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, w
}
t.fixers = t.readFixersFromFile()
t.initializeDirectories()
- t.mountDownloads()
t.refreshTorrents()
t.setNewLatestState(t.getCurrentState())
t.StartRefreshJob()
t.StartRepairJob()
+ t.mountDownloads()
+ t.StartDownloadsJob()
return t
}
@@ -85,7 +88,9 @@ func (t *TorrentManager) UnrestrictUntilOk(link string) *realdebrid.Download {
}
if ret != nil && ret.Link != "" && ret.Filename != "" {
t.DownloadCache.Set(ret.Link, ret)
- t.DownloadMap.Set(ret.Filename, ret)
+ if t.Config.EnableDownloadMount() {
+ t.DownloadMap.Set(ret.Filename, ret)
+ }
}
return ret
}
@@ -179,6 +184,7 @@ func (t *TorrentManager) mountDownloads() {
if !t.Config.EnableDownloadMount() {
return
}
+ t.DownloadMap = cmap.New[*realdebrid.Download]()
_ = t.workerPool.Submit(func() {
page := 1
offset := 0
@@ -196,7 +202,23 @@ func (t *TorrentManager) mountDownloads() {
break
}
}
- t.log.Infof("Compiled into %d downloads", t.DownloadCache.Count())
+ t.log.Infof("Compiled into %d downloads", t.DownloadMap.Count())
+ })
+}
+
+func (t *TorrentManager) StartDownloadsJob() {
+ _ = t.workerPool.Submit(func() {
+ remountTicker := time.NewTicker(time.Duration(t.Config.GetDownloadsEveryMins()) * time.Minute)
+ defer remountTicker.Stop()
+
+ for {
+ select {
+ case <-remountTicker.C:
+ t.mountDownloads()
+ case <-t.RemountTrigger:
+ t.mountDownloads()
+ }
+ }
})
}
diff --git a/internal/universal/downloader.go b/internal/universal/downloader.go
index 2e0dc9a..ff0548a 100644
--- a/internal/universal/downloader.go
+++ b/internal/universal/downloader.go
@@ -104,19 +104,6 @@ func (dl *Downloader) DownloadLink(fileName, link string, resp http.ResponseWrit
http.Error(resp, "File is not available", http.StatusInternalServerError)
return
} else {
- lFilename := strings.ToLower(fileName)
- unrestrictFilename := strings.ToLower(strings.TrimPrefix(unrestrict.Filename, "/"))
- if strings.Contains(lFilename, unrestrictFilename) {
- // this is possible if there's only 1 streamable file in the torrent
- // and then suddenly it's a rar file
- actualExt := filepath.Ext(unrestrictFilename)
- expectedExt := filepath.Ext(lFilename)
- if actualExt != expectedExt && unrestrict.Streamable != 1 {
- log.Warnf("File was changed and is not streamable: %s and %s (link=%s)", fileName, unrestrict.Filename, unrestrict.Link)
- } else {
- log.Warnf("Filename mismatch: %s and %s", fileName, unrestrict.Filename)
- }
- }
if cfg.ShouldServeFromRclone() {
if cfg.ShouldVerifyDownloadLink() {
if !dl.client.CanFetchFirstByte(unrestrict.Download) {