Compare commits

...

17 Commits

Author SHA1 Message Date
Safihre
dbff203c62 Update text files for 4.2.3RC2 2024-02-26 22:30:38 +01:00
Safihre
f45eb891cd Correct translatable texts about connection threads 2024-02-26 22:23:51 +01:00
Safihre
77b58240cf Reduce Server test timeout to 10s
Unless otherwise specified
Related to #2802
2024-02-26 22:23:51 +01:00
Safihre
97ae1ff10e Refactor part of database.py
Use autocommit and skip unnecessary checks on every connect
2024-02-26 22:23:51 +01:00
Safihre
8734a4f24b Update text files for 4.2.3RC1 2024-02-19 22:19:43 +01:00
Shane Mc Cormack
480fce55a8 Handle NNTP error code 451 (#2808)
* Handle error code 451.

This is used by some servers to show that an article was intentionally removed.
Fix #2807

* Add a warning when an unknown status code is given for an article.

* Make warning message translatable.
2024-02-19 22:06:29 +01:00
thezoggy
d4136fadd2 Add missing tooltips (#2800)
Co-authored-by: Safihre <safihre@sabnzbd.org>
2024-02-18 13:42:48 +01:00
Safihre
308bc375bd Update log message about version check 2024-02-18 13:42:31 +01:00
Safihre
3bbcf6a41e Update standby command on macOS 2024-02-18 13:41:33 +01:00
Safihre
3d5d10a4c1 Remove parsing of Group command code
Since we never request it.
2024-02-18 13:41:05 +01:00
Safihre
0e979c14f0 Remove Send Group option
Closes #2715
2024-02-18 13:38:31 +01:00
Safihre
70f49114ac Wrong archive password is used for Retry
Closes #2790
2024-02-18 13:38:13 +01:00
Safihre
699d75bb9f Update text files for 4.2.2 2024-01-30 21:46:56 +01:00
Safihre
95822704c8 Update black formatting 2024-01-30 21:36:34 +01:00
Safihre
76e5f69e67 Only attempt Windows Toasts on Windows 10 and above 2024-01-29 16:34:03 +01:00
Safihre
abd31d0249 Update text files for 4.2.2RC2 2024-01-24 13:28:19 +01:00
Safihre
9ae7ee6e2d Add logging which notification will be sent 2024-01-22 15:15:44 +01:00
23 changed files with 125 additions and 142 deletions

View File

@@ -81,7 +81,7 @@ jobs:
# We need the official Python, because the GA ones only support newer macOS versions
# The deployment target is picked up by the Python build tools automatically
# If updated, make sure to also set LSMinimumSystemVersion in SABnzbd.spec
PYTHON_VERSION: "3.12.1"
PYTHON_VERSION: "3.12.2"
MACOSX_DEPLOYMENT_TARGET: "10.9"
# We need to force compile for universal2 support
CFLAGS: -arch x86_64 -arch arm64

View File

@@ -1,14 +1,27 @@
Release Notes - SABnzbd 4.2.2 Release Candidate 1
Release Notes - SABnzbd 4.2.3 Release Candidate 2
=========================================================
This is the second bug-fix release of SABnzbd 4.2.0.
This is the third bug-fix release of SABnzbd 4.2.0.
## Bug-fixes and changes since 4.2.2:
* **Bug-fixes:**
* Handle new status code for missing articles, which could result in timeouts.
* Retry of failed job would not use the password provided.
* Optimize database handling in order to prevent locking errors.
* macOS: System standby after finishing the queue would not always work.
* **Changes:**
* Remove `Send Group` option for Servers.
## Bug-fixes and changes since 4.2.1:
* **Bug-fixes:**
* RSS readout could result in a crash if duplicate detection is enabled.
* RSS readout could result in a crash if Duplicate Detection was enabled.
* Passwords were not always correctly parsed.
* Warnings could show even if `helpful_warnings` was disabled.
* Duplicate Detection would trigger again on URLs if they were resumed.
* Windows: Fatal crash could occur if ran as Service or on older Windows versions.
* **Changes:**
* Parsing of filenames from the NZB was extended to allow more exotic formatting.

View File

@@ -106,11 +106,6 @@
<span class="desc">$T('explain-ssl_ciphers') <br>$T('readwiki')
<a href="https://sabnzbd.org/wiki/advanced/ssl-ciphers" target="_blank">https://sabnzbd.org/wiki/advanced/ssl-ciphers</a></span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="send_group">$T('srv-send_group')</label>
<input type="checkbox" name="send_group" id="send_group" value="1" />
<span class="desc">$T('srv-explain-send_group')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="required">$T('srv-required')</label>
<input type="checkbox" name="required" id="required" value="1" />
@@ -248,11 +243,6 @@
<input type="checkbox" name="optional" id="optional$cur" value="1" <!--#if int($server['optional']) != 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-optional')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="send_group$cur">$T('srv-send_group')</label>
<input type="checkbox" name="send_group" id="send_group$cur" value="1" <!--#if int($server['send_group']) != 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('srv-explain-send_group')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="expire_date$cur">$T('srv-expire_date')</label>
<input type="date" name="expire_date" id="expire_date$cur" value="$server['expire_date']" />

View File

@@ -140,7 +140,7 @@
</label>
</div>
<a href="#" class="hover-button" data-bind="visible: history.isMultiEditing(), click: history.doMultiDelete">
<a href="#" class="hover-button" title="$T('nzo-delete')" data-bind="visible: history.isMultiEditing(), click: history.doMultiDelete">
<span class="glyphicon glyphicon-trash"></span>
</a>
<a href="#modal-purge-history" class="hover-button" title="$T('purgeHist')" data-bind="visible: !history.isMultiEditing()" data-toggle="modal" data-tooltip="true" data-placement="left">

View File

@@ -174,7 +174,7 @@
<label for="multiedit-checkall-queue">
<input type="checkbox" name="multieditCheckAll" id="multiedit-checkall-queue" title="$T('Glitter-checkAll')" data-bind="click: queue.checkAllJobs" data-tooltip="true" data-placement="top" />
</label>
<a href="#" class="hover-button" data-bind="click: queue.doMultiDelete">
<a href="#" class="hover-button" title="$T('removeNZB-Files')" data-bind="click: queue.doMultiDelete">
<span class="glyphicon glyphicon-trash"></span>
</a>
</div>

View File

@@ -30,7 +30,8 @@
<url type="faq">https://sabnzbd.org/wiki/faq</url>
<url type="contact">https://sabnzbd.org/live-chat.html</url>
<releases>
<release version="4.2.2" date="2024-02-01" type="stable"/>
<release version="4.2.3" date="2024-03-10" type="stable"/>
<release version="4.2.2" date="2024-01-31" type="stable"/>
<release version="4.2.1" date="2024-01-05" type="stable"/>
<release version="4.2.0" date="2024-01-03" type="stable"/>
<release version="4.1.0" date="2023-09-26" type="stable"/>

View File

@@ -447,7 +447,6 @@ class ConfigServer:
self.expire_date = OptionStr(name, "expire_date", add=False)
self.quota = OptionStr(name, "quota", add=False)
self.usage_at_start = OptionNumber(name, "usage_at_start", add=False)
self.send_group = OptionBool(name, "send_group", False, add=False)
self.priority = OptionNumber(name, "priority", 0, 0, 99, add=False)
self.notes = OptionStr(name, "notes", add=False)
@@ -473,7 +472,6 @@ class ConfigServer:
"ssl",
"ssl_verify",
"ssl_ciphers",
"send_group",
"enable",
"required",
"optional",
@@ -516,7 +514,6 @@ class ConfigServer:
output_dict["expire_date"] = self.expire_date()
output_dict["quota"] = self.quota()
output_dict["usage_at_start"] = self.usage_at_start()
output_dict["send_group"] = self.send_group()
output_dict["priority"] = self.priority()
output_dict["notes"] = self.notes()
return output_dict

View File

@@ -75,6 +75,7 @@ DEF_LOG_CHERRY = "cherrypy.log"
DEF_ARTICLE_CACHE_DEFAULT = "500M"
DEF_ARTICLE_CACHE_MAX = "1G"
DEF_TIMEOUT = 60
DEF_TEST_TIMEOUT = 10
DEF_SCANRATE = 5
DEF_HTTPS_CERT_FILE = "server.cert"
DEF_HTTPS_KEY_FILE = "server.key"

View File

@@ -38,7 +38,7 @@ from sabnzbd.encoding import ubtou, utob
from sabnzbd.misc import int_conv, caller_name, opts_to_pp, to_units
from sabnzbd.filesystem import remove_file, clip_path
DB_LOCK = threading.RLock()
DB_LOCK = threading.Lock()
class HistoryDB:
@@ -50,66 +50,69 @@ class HistoryDB:
# These class attributes will be accessed directly because
# they need to be shared by all instances
db_path = None # Will contain full path to history database
done_cleaning = False # Ensure we only do one Vacuum per session
db_path = None # Full path to history database
startup_done = False
@synchronized(DB_LOCK)
def __init__(self):
"""Determine database path and create connection"""
self.connection: Optional[Connection] = None
self.cursor: Optional[Cursor] = None
if not HistoryDB.db_path:
HistoryDB.db_path = os.path.join(sabnzbd.cfg.admin_dir.get_path(), DB_HISTORY_NAME)
self.connect()
def connect(self):
"""Create a connection to the database"""
create_table = not os.path.exists(HistoryDB.db_path)
if not HistoryDB.db_path:
HistoryDB.db_path = os.path.join(sabnzbd.cfg.admin_dir.get_path(), DB_HISTORY_NAME)
create_table = not HistoryDB.startup_done and not os.path.exists(HistoryDB.db_path)
self.connection = sqlite3.connect(HistoryDB.db_path)
self.connection.isolation_level = None # autocommit attribute only introduced in Python 3.12
self.connection.row_factory = sqlite3.Row
self.cursor = self.connection.cursor()
if create_table:
self.create_history_db()
elif not HistoryDB.done_cleaning:
# Run VACUUM on sqlite
# Perform initialization only once
if not HistoryDB.startup_done:
if create_table:
self.create_history_db()
# When an object (table, index, or trigger) is dropped from the database, it leaves behind empty space
# http://www.sqlite.org/lang_vacuum.html
HistoryDB.done_cleaning = True
self.execute("VACUUM")
self.execute("PRAGMA user_version;")
try:
version = self.cursor.fetchone()["user_version"]
except (IndexError, TypeError):
version = 0
# See if we need to perform any updates
self.execute("PRAGMA user_version;")
try:
version = self.cursor.fetchone()["user_version"]
except (IndexError, TypeError):
version = 0
# Add any new columns added since last DB version
# Use "and" to stop when database has been reset due to corruption
if version < 1:
_ = (
self.execute("PRAGMA user_version = 1;", save=True)
and self.execute("ALTER TABLE history ADD COLUMN series TEXT;", save=True)
and self.execute("ALTER TABLE history ADD COLUMN md5sum TEXT;", save=True)
)
if version < 2:
_ = self.execute("PRAGMA user_version = 2;", save=True) and self.execute(
"ALTER TABLE history ADD COLUMN password TEXT;", save=True
)
if version < 3:
# Transfer data to new column (requires WHERE-hack), original column should be removed later
_ = (
self.execute("PRAGMA user_version = 3;", save=True)
and self.execute("ALTER TABLE history ADD COLUMN duplicate_key TEXT;", save=True)
and self.execute("UPDATE history SET duplicate_key = series WHERE 1 = 1;", save=True)
)
# Add any new columns added since last DB version
# Use "and" to stop when database has been reset due to corruption
if version < 1:
_ = (
self.execute("PRAGMA user_version = 1;")
and self.execute("ALTER TABLE history ADD COLUMN series TEXT;")
and self.execute("ALTER TABLE history ADD COLUMN md5sum TEXT;")
)
if version < 2:
_ = self.execute("PRAGMA user_version = 2;") and self.execute(
"ALTER TABLE history ADD COLUMN password TEXT;"
)
if version < 3:
# Transfer data to new column (requires WHERE-hack), original column should be removed later
_ = (
self.execute("PRAGMA user_version = 3;")
and self.execute("ALTER TABLE history ADD COLUMN duplicate_key TEXT;")
and self.execute("UPDATE history SET duplicate_key = series WHERE 1 = 1;")
)
HistoryDB.startup_done = True
def execute(self, command: str, args: Sequence = (), save: bool = False) -> bool:
def execute(self, command: str, args: Sequence = ()) -> bool:
"""Wrapper for executing SQL commands"""
for tries in (4, 3, 2, 1, 0):
try:
self.cursor.execute(command, args)
if save:
self.connection.commit()
return True
except:
error = str(sys.exc_info()[1])
@@ -129,6 +132,7 @@ class HistoryDB:
remove_file(HistoryDB.db_path)
except:
pass
HistoryDB.startup_done = False
self.connect()
# Return False in case of "duplicate column" error
# because the column addition in connect() must be terminated
@@ -141,6 +145,7 @@ class HistoryDB:
try:
self.connection.rollback()
except:
# Can fail in case of automatic rollback
logging.debug("Rollback Failed:", exc_info=True)
return False
@@ -178,10 +183,9 @@ class HistoryDB:
"password" TEXT,
"duplicate_key" TEXT
)
""",
save=True,
"""
)
self.execute("PRAGMA user_version = 3;", save=True)
self.execute("PRAGMA user_version = 3;")
def close(self):
"""Close database connection"""
@@ -196,9 +200,7 @@ class HistoryDB:
"""Remove all completed jobs from the database, optional with `search` pattern"""
search = convert_search(search)
logging.info("Removing all completed jobs from history")
return self.execute(
"""DELETE FROM history WHERE name LIKE ? AND status = ?""", (search, Status.COMPLETED), save=True
)
return self.execute("""DELETE FROM history WHERE name LIKE ? AND status = ?""", (search, Status.COMPLETED))
def get_failed_paths(self, search=None):
"""Return list of all storage paths of failed jobs (may contain non-existing or empty paths)"""
@@ -215,9 +217,7 @@ class HistoryDB:
"""Remove all failed jobs from the database, optional with `search` pattern"""
search = convert_search(search)
logging.info("Removing all failed jobs from history")
return self.execute(
"""DELETE FROM history WHERE name LIKE ? AND status = ?""", (search, Status.FAILED), save=True
)
return self.execute("""DELETE FROM history WHERE name LIKE ? AND status = ?""", (search, Status.FAILED))
def remove_history(self, jobs=None):
"""Remove all jobs in the list `jobs`, empty list will remove all completed jobs"""
@@ -228,7 +228,7 @@ class HistoryDB:
jobs = [jobs]
for job in jobs:
self.execute("""DELETE FROM history WHERE nzo_id = ?""", (job,), save=True)
self.execute("""DELETE FROM history WHERE nzo_id = ?""", (job,))
logging.info("[%s] Removing job %s from history", caller_name(), job)
def auto_history_purge(self):
@@ -247,9 +247,7 @@ class HistoryDB:
if days_to_keep > 0:
logging.info("Removing completed jobs older than %s days from history", days_to_keep)
return self.execute(
"""DELETE FROM history WHERE status = ? AND completed < ?""",
(Status.COMPLETED, seconds_to_keep),
save=True,
"""DELETE FROM history WHERE status = ? AND completed < ?""", (Status.COMPLETED, seconds_to_keep)
)
else:
# How many to keep?
@@ -261,7 +259,6 @@ class HistoryDB:
SELECT id FROM history WHERE status = ? ORDER BY completed DESC LIMIT ?
)""",
(Status.COMPLETED, Status.COMPLETED, to_keep),
save=True,
)
def add_history_db(self, nzo, storage: str, postproc_time: int, script_output: str, script_line: str):
@@ -274,7 +271,6 @@ class HistoryDB:
downloaded, fail_message, url_info, bytes, duplicate_key, md5sum, password)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
t,
save=True,
)
logging.info("Added job %s to history", nzo.final_name)

View File

@@ -80,7 +80,6 @@ class Server:
"required",
"optional",
"retention",
"send_group",
"username",
"password",
"busy_threads",
@@ -111,7 +110,6 @@ class Server:
use_ssl,
ssl_verify,
ssl_ciphers,
send_group,
username=None,
password=None,
required=False,
@@ -134,15 +132,6 @@ class Server:
self.required: bool = required
self.optional: bool = optional
self.retention: int = retention
self.send_group: bool = send_group
# TODO: Remove after next release
if send_group:
helpful_warning(
"You have 'Send Group' enabled for %s. Could you let us know why? https://github.com/sabnzbd/sabnzbd/discussions/2715",
self.displayname,
)
self.username: Optional[str] = username
self.password: Optional[str] = password
@@ -331,7 +320,6 @@ class Downloader(Thread):
required = srv.required()
optional = srv.optional()
retention = int(srv.retention() * 24 * 3600) # days ==> seconds
send_group = srv.send_group()
create = True
if oldserver:
@@ -358,7 +346,6 @@ class Downloader(Thread):
ssl,
ssl_verify,
ssl_ciphers,
send_group,
username,
password,
required,
@@ -754,13 +741,7 @@ class Downloader(Thread):
done = True
logging.debug("Article <%s> is present", article.article)
elif nw.status_code == 211:
logging.debug("group command ok -> %s", nw.nntp_msg)
nw.group = nw.article.nzf.nzo.group
nw.reset_data_buffer()
self.__request_article(nw)
elif nw.status_code in (411, 423, 430):
elif nw.status_code in (411, 423, 430, 451):
done = True
logging.debug(
"Thread %s@%s: Article %s missing (error=%s)",
@@ -783,6 +764,18 @@ class Downloader(Thread):
nw.reset_data_buffer()
self.__request_article(nw)
else:
logging.warning(
T("%s@%s: Received unknown status code %s for article %s: %s"),
nw.thrdnum,
nw.server.host,
nw.status_code,
article.article,
nw.nntp_msg.splitlines()[0],
)
done = True
nw.reset_data_buffer()
if done:
# Successful data, clear "bad" counter
server.bad_cons = 0
@@ -975,16 +968,9 @@ class Downloader(Thread):
def __request_article(self, nw: NewsWrapper):
try:
nzo = nw.article.nzf.nzo
if nw.server.send_group and nzo.group != nw.group:
group = nzo.group
if sabnzbd.LOG_ALL:
logging.debug("Thread %s@%s: GROUP <%s>", nw.thrdnum, nw.server.host, group)
nw.send_group(group)
else:
if sabnzbd.LOG_ALL:
logging.debug("Thread %s@%s: BODY %s", nw.thrdnum, nw.server.host, nw.article.article)
nw.body()
if sabnzbd.LOG_ALL:
logging.debug("Thread %s@%s: BODY %s", nw.thrdnum, nw.server.host, nw.article.article)
nw.body()
# Mark as ready to be read
self.add_socket(nw.nntp.fileno, nw)
except socket.error as err:

View File

@@ -50,7 +50,6 @@ from sabnzbd.misc import (
is_lan_addr,
is_local_addr,
is_loopback_addr,
helpful_warning,
recursive_html_escape,
is_none,
get_cpu_name,
@@ -81,7 +80,7 @@ from sabnzbd.constants import (
GUESSIT_SORT_TYPES,
VALID_NZB_FILES,
VALID_ARCHIVES,
DEF_TIMEOUT,
DEF_TEST_TIMEOUT,
)
from sabnzbd.lang import list_languages
from sabnzbd.api import (
@@ -1144,7 +1143,7 @@ def handle_server(kwargs, root=None, new_svr=False):
kwargs["connections"] = "1"
if kwargs.get("enable") == "1":
if not happyeyeballs(host, int_conv(port), int_conv(kwargs.get("timeout"), default=DEF_TIMEOUT)):
if not happyeyeballs(host, int_conv(port), int_conv(kwargs.get("timeout"), default=DEF_TEST_TIMEOUT)):
return badParameterResponse(T('Server address "%s:%s" is not valid.') % (host, port), ajax)
# Default server name is just the host name
@@ -1162,7 +1161,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", "required", "optional"):
for kw in ("ssl", "enable", "required", "optional"):
if kw not in kwargs.keys():
kwargs[kw] = None
if svr and not new_svr:

View File

@@ -482,7 +482,7 @@ def check_latest_version():
# Fetch version info
data = get_from_url("https://sabnzbd.org/latest.txt")
if not data:
logging.info("Cannot retrieve version information from GitHub.com")
logging.info("Cannot retrieve version information from sabnzbd.org")
logging.debug("Traceback: ", exc_info=True)
return

View File

@@ -177,13 +177,6 @@ class NewsWrapper:
self.nntp.sock.sendall(command)
self.reset_data_buffer()
def send_group(self, group: str):
"""Send the NNTP GROUP command"""
self.timeout = time.time() + self.server.timeout
command = utob("GROUP %s\r\n" % group)
self.nntp.sock.sendall(command)
self.reset_data_buffer()
def recv_chunk(self) -> Tuple[int, bool]:
"""Receive data, return #bytes, done, skip"""
# Resize the buffer in the extremely unlikely case that it got full
@@ -390,7 +383,7 @@ class NNTP:
# Ignore if the socket was already closed, resulting in errors
if not self.closed:
msg = "Failed to connect: %s %s@%s:%s (%s)" % (
msg = T("Failed to connect: %s %s@%s:%s (%s)") % (
str(error),
self.nw.thrdnum,
self.nw.server.host,

View File

@@ -23,6 +23,7 @@ sabnzbd.notifier - Send notifications to any notification services
import os.path
import logging
import platform
import urllib.request
import urllib.parse
import http.client
@@ -34,7 +35,7 @@ import sabnzbd
import sabnzbd.cfg
from sabnzbd.encoding import utob
from sabnzbd.filesystem import make_script_path
from sabnzbd.misc import build_and_run_command
from sabnzbd.misc import build_and_run_command, int_conv
from sabnzbd.newsunpack import create_env
if sabnzbd.WIN32:
@@ -42,11 +43,15 @@ if sabnzbd.WIN32:
from win32comext.shell import shell
from windows_toasts import InteractableWindowsToaster, Toast, ToastActivatedEventArgs, ToastButton
# Only Windows 10 and above are supported
if int_conv(platform.release()) < 10:
raise OSError
# Set a custom AUMID to display the right icon, it is written to the registry by the installer
shell.SetCurrentProcessExplicitAppUserModelID("SABnzbd")
_HAVE_WINDOWS_TOASTER = True
except:
# Only supported on Windows 10 and above
# Sending toasts on non-supported platforms results in segfaults
_HAVE_WINDOWS_TOASTER = False
try:
@@ -210,6 +215,7 @@ def send_notify_osd(title, message):
def send_notification_center(title: str, msg: str, notification_type: str, actions: Optional[Dict[str, str]] = None):
"""Send message to macOS Notification Center.
Only 1 button is possible on macOS!"""
logging.debug("Sending macOS notification")
try:
subtitle = T(NOTIFICATION_TYPES.get(notification_type, "other"))
button_text = button_action = None
@@ -228,7 +234,7 @@ def send_notification_center(title: str, msg: str, notification_type: str, actio
def send_prowl(title, msg, notification_type, force=False, test=None):
"""Send message to Prowl"""
logging.debug("Sending Prowl notification")
if test:
apikey = test.get("prowl_apikey")
else:
@@ -261,7 +267,7 @@ def send_prowl(title, msg, notification_type, force=False, test=None):
def send_pushover(title, msg, notification_type, force=False, test=None):
"""Send message to pushover"""
logging.debug("Sending Pushover notification")
if test:
apikey = test.get("pushover_token")
userkey = test.get("pushover_userkey")
@@ -328,7 +334,7 @@ def do_send_pushover(body):
def send_pushbullet(title, msg, notification_type, force=False, test=None):
"""Send message to Pushbullet"""
logging.debug("Sending Pushbullet notification")
if test:
apikey = test.get("pushbullet_apikey")
device = test.get("pushbullet_device")
@@ -363,6 +369,7 @@ def send_pushbullet(title, msg, notification_type, force=False, test=None):
def send_nscript(title, msg, notification_type, force=False, test=None):
"""Run user's notification script"""
logging.debug("Sending notification script notification")
if test:
script = test.get("nscript_script")
env_params = {"notification_parameters": test.get("nscript_parameters")}
@@ -411,6 +418,7 @@ def send_windows(title: str, msg: str, notification_type: str, actions: Optional
if sabnzbd.WIN_SERVICE:
return None
logging.debug("Sending Windows notification")
try:
if _HAVE_WINDOWS_TOASTER:
notification_sender = InteractableWindowsToaster("SABnzbd", notifierAUMID="SABnzbd")

View File

@@ -1884,11 +1884,15 @@ class NzbObject(TryList):
return None, None, None
# Only a subset we want to apply directly to the NZO
for attrib in ("final_name", "priority", "password", "url"):
for attrib in ("final_name", "priority", "url"):
# Only set if it is present and has a value
if attribs.get(attrib):
setattr(self, attrib, attribs[attrib])
# Only set password if it wasn't already set
if not self.password and attribs.get("password"):
self.password = attribs["password"]
# Rest is to be used directly in the NZO-init flow
return attribs["cat"], attribs["pp"], attribs["script"]

View File

@@ -92,7 +92,7 @@ def osx_shutdown():
def osx_standby():
"""Make macOS system sleep, returns after wakeup"""
try:
subprocess.call(["osascript", "-e", 'tell app "System Events" to sleep'])
subprocess.call(["pmset", "sleepnow"])
time.sleep(10)
except:
logging.error(T("Failed to standby system"))

View File

@@ -567,8 +567,6 @@ SKIN_TEXT = {
"button-clrServer": TT("Clear Counters"), #: Button: Clear server's byte counters
"srv-testing": TT("Testing server details..."),
"srv-bandwidth": TT("Bandwidth"),
"srv-send_group": TT("Send Group"),
"srv-explain-send_group": TT("Send group command before requesting articles."),
"srv-notes": TT("Personal notes"),
"srv-article-availability": TT("Article availability"),
"srv-articles-tried": TT(

View File

@@ -29,7 +29,7 @@ def generate_key(key_size=2048, output_file="key.pem"):
private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
encryption_algorithm=serialization.NoEncryption(),
# encryption_algorithm=serialization.BestAvailableEncryption(b"passphrase")
)
)

View File

@@ -241,7 +241,6 @@ kDNSServiceInterfaceIndexLocalOnly = -1
class BonjourError(Exception):
"""
Exception representing an error returned by the DNS-SD library.
@@ -321,7 +320,6 @@ _DNSServiceErrorType = ctypes.c_int32
class DNSRecordRef(ctypes.c_void_p):
"""
A DNSRecordRef pointer. DO NOT CREATE INSTANCES OF THIS CLASS!
@@ -367,7 +365,6 @@ class _DNSRecordRef_or_null(DNSRecordRef):
class DNSServiceRef(DNSRecordRef):
"""
A DNSServiceRef pointer. DO NOT CREATE INSTANCES OF THIS CLASS!
@@ -1740,7 +1737,6 @@ def DNSServiceConstructFullName(service=None, regtype=_NO_DEFAULT, domain=_NO_DE
class TXTRecord(object):
"""
A mapping representing a DNS TXT record. The TXT record's

View File

@@ -20,9 +20,8 @@ sabnzbd.utils.servertests - Debugging server connections. Currently only NNTP se
"""
import socket
import sys
from sabnzbd.constants import DEF_TIMEOUT
from sabnzbd.constants import DEF_TEST_TIMEOUT
from sabnzbd.newswrapper import NewsWrapper, NNTPPermanentError
from sabnzbd.downloader import Server, clues_login, clues_too_many
from sabnzbd.config import get_servers
@@ -37,7 +36,7 @@ def test_nntp_server_dict(kwargs):
password = kwargs.get("password", "").strip()
server = kwargs.get("server", "").strip()
connections = int_conv(kwargs.get("connections", 0))
timeout = int_conv(kwargs.get("timeout", DEF_TIMEOUT))
timeout = int_conv(kwargs.get("timeout", DEF_TEST_TIMEOUT))
ssl = int_conv(kwargs.get("ssl", 0))
ssl_verify = int_conv(kwargs.get("ssl_verify", 1))
ssl_ciphers = kwargs.get("ssl_ciphers", "").strip()
@@ -56,7 +55,7 @@ def test_nntp_server_dict(kwargs):
if not timeout:
# Lower value during new server testing
timeout = 10
timeout = DEF_TEST_TIMEOUT
if "*" in password and not password.strip("*"):
# If the password is masked, try retrieving it from the config
@@ -78,7 +77,6 @@ def test_nntp_server_dict(kwargs):
use_ssl=ssl,
ssl_verify=ssl_verify,
ssl_ciphers=ssl_ciphers,
send_group=False,
username=username,
password=password,
)

View File

@@ -6,5 +6,5 @@
# You MUST use double quotes (so " and not ')
# Do not forget to update the appdata file for every major release!
__version__ = "4.2.2RC1"
__version__ = "4.2.3RC2"
__baseline__ = "unknown"

View File

@@ -769,7 +769,10 @@ class TestQueueApi(ApiTestFunctions):
("my_scripted_script_.py", True, True),
("유닉스.py", True, True),
pytest.param(
"유닉스.sh", True, True, marks=pytest.mark.skipif(sys.platform.startswith("win"), reason="Not for Windows")
"유닉스.sh",
True,
True,
marks=pytest.mark.skipif(sys.platform.startswith("win"), reason="Not for Windows"),
),
pytest.param(
"لغة برمجة نصية",

View File

@@ -150,11 +150,11 @@ class TestPostProc:
"order": 0,
"pp": None,
"script": None,
"dir": os.path.join(
SAB_CACHE_DIR, ("category_dir_for_" + category + ("*" if not has_jobdir else ""))
)
if has_catdir
else None,
"dir": (
os.path.join(SAB_CACHE_DIR, ("category_dir_for_" + category + ("*" if not has_jobdir else "")))
if has_catdir
else None
),
"newzbin": "",
"priority": 0,
},