diff --git a/internal/config/types.go b/internal/config/types.go
index 7a3e923..3a08290 100644
--- a/internal/config/types.go
+++ b/internal/config/types.go
@@ -11,6 +11,8 @@ type ConfigInterface interface {
EnableRepair() bool
GetHost() string
GetPort() string
+ GetUsername() string
+ GetPassword() string
GetDirectories() []string
MeetsConditions(directory, torrentName string, torrentSize int64, torrentIDs, fileNames []string, fileSizes []int64) bool
GetOnLibraryUpdate() string
@@ -34,6 +36,8 @@ type ZurgConfig struct {
Host string `yaml:"host" json:"host"`
Port string `yaml:"port" json:"port"`
+ Username string `yaml:"username" json:"username"`
+ Password string `yaml:"password" json:"password"`
NumOfWorkers int `yaml:"concurrent_workers" json:"concurrent_workers"`
RefreshEverySeconds int `yaml:"check_for_changes_every_secs" json:"check_for_changes_every_secs"`
@@ -78,6 +82,14 @@ func (z *ZurgConfig) GetPort() string {
return z.Port
}
+func (z *ZurgConfig) GetUsername() string {
+ return z.Username
+}
+
+func (z *ZurgConfig) GetPassword() string {
+ return z.Password
+}
+
func (z *ZurgConfig) GetNumOfWorkers() int {
if z.NumOfWorkers == 0 {
return 50
diff --git a/internal/handlers/basicauth.go b/internal/handlers/basicauth.go
new file mode 100644
index 0000000..a6f0268
--- /dev/null
+++ b/internal/handlers/basicauth.go
@@ -0,0 +1,17 @@
+package handlers
+
+import "net/http"
+
+func (hs *Handlers) basicAuth(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ username, password, ok := r.BasicAuth()
+ if !ok || username != hs.cfg.GetUsername() || password != hs.cfg.GetPassword() {
+ w.Header().Set("WWW-Authenticate", `Basic realm="restricted"`)
+ w.WriteHeader(http.StatusUnauthorized)
+ w.Write([]byte(http.StatusText(http.StatusUnauthorized)))
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/internal/handlers/home.go b/internal/handlers/home.go
index 657d4b5..51ffc65 100644
--- a/internal/handlers/home.go
+++ b/internal/handlers/home.go
@@ -151,7 +151,7 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
| Premium |
- %d seconds |
+ %d days |
| Expiration |
@@ -267,28 +267,28 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
response.UserInfo.Points,
response.UserInfo.Locale,
response.UserInfo.Type,
- response.UserInfo.Premium,
- response.UserInfo.Expiration,
+ response.UserInfo.Premium/86400,
+ strings.Replace(response.UserInfo.Expiration, "Z", "+01:00", 1),
response.Config.Version,
strings.Replace(response.Config.Token, response.Config.Token[len(response.Config.Token)-48:], "*****", 1),
- response.Config.Host,
- response.Config.Port,
- response.Config.NumOfWorkers,
- response.Config.RefreshEverySeconds,
- response.Config.RetainRDTorrentName,
- response.Config.RetainFolderNameExtension,
- response.Config.CanRepair,
- response.Config.DeleteRarFiles,
- response.Config.RealDebridTimeout,
- response.Config.UseDownloadCache,
- response.Config.RateLimitSleepSeconds,
- response.Config.RetriesUntilFailed,
+ response.Config.GetHost(),
+ response.Config.GetPort(),
+ response.Config.GetNumOfWorkers(),
+ response.Config.GetRefreshEverySeconds(),
+ response.Config.EnableRetainRDTorrentName(),
+ response.Config.EnableRetainFolderNameExtension(),
+ response.Config.EnableRepair(),
+ response.Config.ShouldDeleteRarFiles(),
+ response.Config.GetRealDebridTimeout(),
+ response.Config.EnableDownloadCache(),
+ response.Config.GetRateLimitSleepSeconds(),
+ response.Config.GetRetriesUntilFailed(),
strings.Join(response.Config.PreferredHosts, ", "),
- response.Config.NetworkBufferSize,
- response.Config.ServeFromRclone,
- response.Config.VerifyDownloadLink,
- response.Config.ForceIPv6,
- response.Config.OnLibraryUpdate,
+ response.Config.GetNetworkBufferSize(),
+ response.Config.ShouldServeFromRclone(),
+ response.Config.ShouldVerifyDownloadLink(),
+ response.Config.ShouldForceIPv6(),
+ response.Config.GetOnLibraryUpdate(),
)
fmt.Fprint(resp, out)
diff --git a/internal/handlers/options.go b/internal/handlers/options.go
new file mode 100644
index 0000000..1f714de
--- /dev/null
+++ b/internal/handlers/options.go
@@ -0,0 +1,13 @@
+package handlers
+
+import "net/http"
+
+func (hs *Handlers) options(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "OPTIONS" {
+ w.WriteHeader(http.StatusOK)
+ return
+ }
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/internal/handlers/router.go b/internal/handlers/router.go
index 0d34c5c..799902b 100644
--- a/internal/handlers/router.go
+++ b/internal/handlers/router.go
@@ -39,7 +39,10 @@ func AttachHandlers(router *chi.Mux, downloader *universal.Downloader, torMgr *t
log: log,
}
- router.Use(optionsMiddleware)
+ if cfg.GetUsername() != "" {
+ router.Use(hs.basicAuth)
+ }
+ router.Use(hs.options)
router.Get("/", hs.handleHome)
// version
@@ -283,16 +286,6 @@ func (hs *Handlers) moveTorrentHandler(resp http.ResponseWriter, req *http.Reque
resp.WriteHeader(http.StatusNoContent)
}
-func optionsMiddleware(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Method == "OPTIONS" {
- w.WriteHeader(http.StatusOK)
- return
- }
- next.ServeHTTP(w, r)
- })
-}
-
// universal handlers
func (hs *Handlers) handleDownloadFile(resp http.ResponseWriter, req *http.Request) {
diff --git a/internal/torrent/repair.go b/internal/torrent/repair.go
index 3843b02..f462cda 100644
--- a/internal/torrent/repair.go
+++ b/internal/torrent/repair.go
@@ -133,11 +133,11 @@ func (t *TorrentManager) repair(torrent *Torrent) {
if t.reinsertTorrent(torrent, "") {
t.log.Infof("Successfully downloaded torrent %s to repair it", torrent.AccessKey)
return
- } else if !torrent.Unfixable {
- t.log.Warnf("Failed to repair by reinserting torrent %s, will only redownload broken files...", torrent.AccessKey)
- } else {
+ } else if torrent.Unfixable {
t.log.Warnf("Cannot repair torrent %s", torrent.AccessKey)
return
+ } else {
+ t.log.Warnf("Failed to repair by reinserting torrent %s, will only redownload broken files...", torrent.AccessKey)
}
} else {
t.log.Warnf("Torrent %s is not older than %d hours to be repaired by reinsertion, will only redownload broken files...", torrent.AccessKey, EXPIRED_LINK_TOLERANCE_HOURS)
@@ -210,9 +210,7 @@ func (t *TorrentManager) repair(torrent *Torrent) {
})
t.log.Debugf("During repair, zurg found %d broken files for torrent %s", len(brokenFiles), torrent.AccessKey)
- // todo: to verify removed logic when there's only 1 selected file selected and it's broken
-
- if len(brokenFiles) == 1 && torrent.SelectedFiles.Count() > 1 {
+ if len(brokenFiles) == 1 && torrent.SelectedFiles.Count() > 2 {
// if we download a single file, it will be named differently
// so we need to download 1 extra file to preserve the name
// this is only relevant if we enable retain_rd_torrent_name
@@ -227,7 +225,7 @@ func (t *TorrentManager) repair(torrent *Torrent) {
}
if len(brokenFiles) > 0 {
- t.log.Infof("Redownloading the %d broken files for torrent %s", len(brokenFiles), torrent.AccessKey)
+ t.log.Infof("Redownloading %dof%d files for torrent %s", len(brokenFiles), torrent.SelectedFiles.Count(), torrent.AccessKey)
brokenFileIDs := strings.Join(getFileIDs(brokenFiles), ",")
if t.reinsertTorrent(torrent, brokenFileIDs) {
t.log.Infof("Successfully downloaded torrent %s to repair it", torrent.AccessKey)
diff --git a/pkg/http/client.go b/pkg/http/client.go
index cafebe5..d4218b8 100644
--- a/pkg/http/client.go
+++ b/pkg/http/client.go
@@ -153,12 +153,13 @@ func (r *HTTPClient) Do(req *http.Request) (*http.Response, error) {
attempt := 0
for {
resp, err = r.client.Do(req)
- if resp != nil && resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
+ if resp != nil && (resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent) {
body, _ := io.ReadAll(resp.Body)
if body != nil {
var errResp ErrorResponse
jsonErr := json.Unmarshal(body, &errResp)
if jsonErr == nil {
+ errResp.Message += fmt.Sprintf(" (status code: %d)", resp.StatusCode)
err = &errResp
}
}