diff --git a/interfaces/Config/templates/config_server.tmpl b/interfaces/Config/templates/config_server.tmpl
index feddc6e..f786407 100644
--- a/interfaces/Config/templates/config_server.tmpl
+++ b/interfaces/Config/templates/config_server.tmpl
@@ -104,6 +104,11 @@
$T('srv-explain-send_group')
+
+
+ $T('explain-required')
+
+
+
+ />
+ $T('explain-required')
+
+
/>
$T('explain-optional')
diff --git a/sabnzbd/config.py b/sabnzbd/config.py
index bc49b7c..ddcc0cf 100644
--- a/sabnzbd/config.py
+++ b/sabnzbd/config.py
@@ -409,6 +409,7 @@ class ConfigServer:
self.ssl_verify = OptionNumber(name, "ssl_verify", 2, add=False)
self.ssl_ciphers = OptionStr(name, "ssl_ciphers", add=False)
self.enable = OptionBool(name, "enable", True, add=False)
+ self.required = OptionBool(name, "required", False, add=False)
self.optional = OptionBool(name, "optional", False, add=False)
self.retention = OptionNumber(name, "retention", 0, add=False)
self.expire_date = OptionStr(name, "expire_date", add=False)
@@ -442,6 +443,7 @@ class ConfigServer:
"ssl_ciphers",
"send_group",
"enable",
+ "required",
"optional",
"retention",
"expire_date",
@@ -476,6 +478,7 @@ class ConfigServer:
output_dict["ssl_verify"] = self.ssl_verify()
output_dict["ssl_ciphers"] = self.ssl_ciphers()
output_dict["enable"] = self.enable()
+ output_dict["required"] = self.required()
output_dict["optional"] = self.optional()
output_dict["retention"] = self.retention()
output_dict["expire_date"] = self.expire_date()
diff --git a/sabnzbd/downloader.py b/sabnzbd/downloader.py
index 74dfcff..e9ddf81 100644
--- a/sabnzbd/downloader.py
+++ b/sabnzbd/downloader.py
@@ -73,6 +73,7 @@ class Server:
"ssl",
"ssl_verify",
"ssl_ciphers",
+ "required",
"optional",
"retention",
"send_group",
@@ -109,6 +110,7 @@ class Server:
send_group,
username=None,
password=None,
+ required=False,
optional=False,
retention=0,
):
@@ -125,6 +127,7 @@ class Server:
self.ssl: bool = ssl
self.ssl_verify: int = ssl_verify
self.ssl_ciphers: str = ssl_ciphers
+ self.required: bool = required
self.optional: bool = optional
self.retention: int = retention
self.send_group: bool = send_group
@@ -312,6 +315,7 @@ class Downloader(Thread):
ssl_ciphers = srv.ssl_ciphers()
username = srv.username()
password = srv.password()
+ required = srv.required()
optional = srv.optional()
retention = int(srv.retention() * 24 * 3600) # days ==> seconds
send_group = srv.send_group()
@@ -344,6 +348,7 @@ class Downloader(Thread):
send_group,
username,
password,
+ required,
optional,
retention,
)
@@ -473,12 +478,16 @@ class Downloader(Thread):
if server.errormsg != errormsg:
server.errormsg = errormsg
logging.warning(errormsg)
- logging.warning(T("Server %s will be ignored for %s minutes"), server.host, _PENALTY_TIMEOUT)
+ if not server.required:
+ logging.warning(T("Server %s will be ignored for %s minutes"), server.host, _PENALTY_TIMEOUT)
# Not fully the same as the code below for optional servers
server.bad_cons = 0
- server.deactivate()
- self.plan_server(server, _PENALTY_TIMEOUT)
+ if server.required:
+ sabnzbd.Scheduler.plan_required_server_resume()
+ else:
+ server.deactivate()
+ self.plan_server(server, _PENALTY_TIMEOUT)
# Optional and active server had too many problems.
# Disable it now and send a re-enable plan to the scheduler
@@ -835,12 +844,17 @@ class Downloader(Thread):
penalty = _PENALTY_UNKNOWN
block = True
if block or (penalty and server.optional):
+ retry_article = False
if server.active:
- server.deactivate()
- if penalty and (block or server.optional):
- self.plan_server(server, penalty)
- # Note that the article is discard for this server
- self.__reset_nw(nw, retry_article=False, send_quit=True)
+ if server.required:
+ sabnzbd.Scheduler.plan_required_server_resume()
+ retry_article = True
+ else:
+ server.deactivate()
+ if penalty and (block or server.optional):
+ self.plan_server(server, penalty)
+ # Note that the article is discard for this server if the server is not required
+ self.__reset_nw(nw, retry_article=retry_article, send_quit=True)
continue
except:
logging.error(
diff --git a/sabnzbd/interface.py b/sabnzbd/interface.py
index 099937f..7d54b43 100644
--- a/sabnzbd/interface.py
+++ b/sabnzbd/interface.py
@@ -1234,7 +1234,7 @@ def handle_server(kwargs, root=None, new_svr=False):
if new_svr:
server = unique_svr_name(server)
- for kw in ("ssl", "send_group", "enable", "optional"):
+ for kw in ("ssl", "send_group", "enable", "required", "optional"):
if kw not in kwargs.keys():
kwargs[kw] = None
if svr and not new_svr:
diff --git a/sabnzbd/scheduler.py b/sabnzbd/scheduler.py
index 9b5cefd..055ed7a 100644
--- a/sabnzbd/scheduler.py
+++ b/sabnzbd/scheduler.py
@@ -391,6 +391,11 @@ class Scheduler:
self.__check_diskspace, "check_diskspace", 5 * 60, 9 * 60, "threaded", args=[full_dir, required_space]
)
+ def plan_required_server_resume(self, interval: int = 5):
+ """Create task for resuming downloading"""
+ if not sabnzbd.Downloader.paused:
+ self.plan_resume(interval)
+
def cancel_resume_task(self):
"""Cancel the current auto resume task"""
if self.resume_task:
diff --git a/sabnzbd/skintext.py b/sabnzbd/skintext.py
index 3f7c640..5d329a7 100644
--- a/sabnzbd/skintext.py
+++ b/sabnzbd/skintext.py
@@ -644,6 +644,10 @@ SKIN_TEXT = {
"ssl_verify-strict": TT("Strict"),
"srv-priority": TT("Priority"), #: Server priority
"explain-svrprio": TT("0 is highest priority, 100 is the lowest priority"), #: Explain server priority
+ "srv-required": TT("Required"), #: Server required tickbox
+ "explain-required": TT(
+ "In case of connection failures, the download queue will be paused for a few minutes instead of skipping this server"
+ ), #: Explain server required tickbox
"srv-optional": TT("Optional"), #: Server optional tickbox
"explain-optional": TT(
"For unreliable servers, will be ignored longer in case of failures"