Compare commits

...

9 Commits

Author SHA1 Message Date
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
renovate[bot]
bf52430da8 Update all dependencies 2024-01-22 01:31:55 +00:00
Safihre
7005b3ee86 Do not trigger Duplicate Check twice on URL's 2024-01-21 22:00:12 +01:00
Safihre
8f2ea239c5 Do not send Windows notifications if ran as Service 2024-01-21 20:35:27 +01:00
Safihre
9ee2a8a98c Add hover to Night style buttons 2024-01-19 13:56:43 +01:00
jcfp
6f0daf9d1b Use local ipv6 if public fails and is rout able (#2783)
* use local ipv6 if routable

* always try a remote lookup anyway, for statistics purposes
2024-01-19 13:01:18 +01:00
Safihre
28ed424fa8 Cache CPU name for 1 hour 2024-01-18 21:40:08 +01:00
SABnzbd Automation
fe3e20b108 Update translatable texts
[skip ci]
2024-01-17 15:11:46 +00:00
18 changed files with 101 additions and 75 deletions

View File

@@ -1,4 +1,4 @@
Release Notes - SABnzbd 4.2.2 Release Candidate 1
Release Notes - SABnzbd 4.2.2 Release Candidate 2
=========================================================
This is the second bug-fix release of SABnzbd 4.2.0.
@@ -6,9 +6,11 @@ This is the second bug-fix release of SABnzbd 4.2.0.
## 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.
* **Changes:**
* Parsing of filenames from the NZB was extended to allow more exotic formatting.

View File

@@ -2,7 +2,7 @@
# Note that not all sub-dependencies are listed, but only ones we know could cause trouble
pyinstaller==5.13.2
packaging==23.2
pyinstaller-hooks-contrib==2023.12
pyinstaller-hooks-contrib==2024.0
altgraph==0.17.4
wrapt==1.16.0
setuptools==69.0.3
@@ -14,7 +14,7 @@ importlib_resources==6.1.1; python_version < '3.10'
zipp==3.17.0; python_version < '3.10'
# orjson does not support 32bit Windows, also exclude based on Python-version
orjson==3.9.10; python_version > '3.8'
orjson==3.9.12; python_version > '3.8'
# For the Windows build
pefile==2023.2.7; sys_platform == 'win32'

View File

@@ -140,6 +140,12 @@ select.form-control,
color: #EBEBEB;
}
.btn-default:not(.navbar-btn):hover,
select:hover,
input:hover {
background-color: #666666;
}
/* Needed to force the text-color */
.table-striped>tbody>tr:nth-child(odd)>td,
.table>tbody>tr:nth-child(odd)>td,

View File

@@ -30,7 +30,7 @@
<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.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

@@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.2Beta1\n"
"Project-Id-Version: SABnzbd-4.2.2RC1\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: team@sabnzbd.org\n"
"Language-Team: SABnzbd <team@sabnzbd.org>\n"

View File

@@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.2Beta1\n"
"Project-Id-Version: SABnzbd-4.2.2RC1\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: team@sabnzbd.org\n"
"Language-Team: SABnzbd <team@sabnzbd.org>\n"

View File

@@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.2Beta1\n"
"Project-Id-Version: SABnzbd-4.2.2RC1\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: team@sabnzbd.org\n"
"Language-Team: SABnzbd <team@sabnzbd.org>\n"

View File

@@ -171,6 +171,7 @@ class DuplicateStatus:
DUPLICATE_ALTERNATIVE = "Duplicate Alternative" # Alternative duplicate for a queued job
SMART_DUPLICATE = "Smart Duplicate" # Simple Series duplicate
SMART_DUPLICATE_ALTERNATIVE = "Smart Duplicate Alternative" # Alternative duplicate for a queued job
DUPLICATE_IGNORED = "Duplicate Ignored"
class AddNzbFileResult:

View File

@@ -168,5 +168,8 @@ def local_ipv6():
def public_ipv6():
if local_ipv6():
return public_ip(family=socket.AF_INET6)
if local_address := local_ipv6():
if public_address := public_ip(family=socket.AF_INET6):
return public_address
elif not sabnzbd.misc.is_lan_addr(local_address):
return local_address

View File

@@ -53,6 +53,7 @@ from sabnzbd.misc import (
helpful_warning,
recursive_html_escape,
is_none,
get_cpu_name,
)
from sabnzbd.happyeyeballs import happyeyeballs
from sabnzbd.filesystem import (
@@ -69,7 +70,6 @@ import sabnzbd.cfg as cfg
import sabnzbd.notifier as notifier
import sabnzbd.newsunpack
from sabnzbd.utils.servertests import test_nntp_server_dict
from sabnzbd.utils.getperformance import getcpu
import sabnzbd.utils.ssdp
from sabnzbd.constants import (
DEF_STD_CONFIG,
@@ -430,7 +430,7 @@ class MainPage:
info["have_rss_defined"] = bool(config.get_rss())
info["have_watched_dir"] = bool(cfg.dirscan_dir())
info["cpumodel"] = getcpu()
info["cpumodel"] = get_cpu_name()
info["cpusimd"] = sabnzbd.decoder.SABCTOOLS_SIMD
# Have logout only with HTML and if inet=5, only when we are external

View File

@@ -20,9 +20,11 @@ sabnzbd.misc - misc classes
"""
import os
import platform
import ssl
import sys
import logging
import functools
import urllib.request
import urllib.parse
import re
@@ -51,11 +53,13 @@ from sabnzbd.constants import (
)
import sabnzbd.config as config
import sabnzbd.cfg as cfg
from sabnzbd.encoding import ubtou
from sabnzbd.decorators import cache_maintainer
from sabnzbd.encoding import ubtou, platform_btou
from sabnzbd.filesystem import userxbit, make_script_path, remove_file
if sabnzbd.WIN32:
try:
import winreg
import win32process
import win32con
@@ -381,8 +385,6 @@ _SERVICE_PARM = "CommandLine"
def get_serv_parms(service):
"""Get the service command line parameters from Registry"""
import winreg
service_parms = []
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, _SERVICE_KEY + service)
@@ -402,8 +404,6 @@ def get_serv_parms(service):
def set_serv_parms(service, args):
"""Set the service command line parameters in Registry"""
import winreg
serv = []
for arg in args:
serv.append(arg[0])
@@ -739,6 +739,51 @@ def get_macos_memory():
return float(system_output.split()[1])
@cache_maintainer(clear_time=3600)
@functools.lru_cache(maxsize=None)
def get_cpu_name():
"""Find the CPU name (which needs a different method per OS), and return it
If none found, return platform.platform()"""
cputype = None
try:
if sabnzbd.WIN32:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0")
cputype = winreg.QueryValueEx(key, "ProcessorNameString")[0]
winreg.CloseKey(key)
elif sabnzbd.MACOS:
cputype = subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"]).strip()
else:
with open("/proc/cpuinfo") as fp:
for myline in fp.readlines():
if myline.startswith("model name"):
# Typical line:
# model name : Intel(R) Xeon(R) CPU E5335 @ 2.00GHz
cputype = myline.split(":", 1)[1] # get everything after the first ":"
break # we're done
cputype = platform_btou(cputype)
except:
# An exception, maybe due to a subprocess call gone wrong
pass
if cputype:
# OK, found. Remove unwanted spaces:
cputype = " ".join(cputype.split())
else:
try:
# Not found, so let's fall back to platform()
cputype = platform.platform()
except:
# Can fail on special platforms (like Snapcraft or embedded)
pass
logging.debug("CPU model = %s", cputype)
return cputype
def on_cleanup_list(filename: str, skip_nzb: bool = False) -> bool:
"""Return True if a filename matches the clean-up list"""
cleanup_list = cfg.cleanup_list()

View File

@@ -210,6 +210,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 +229,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 +262,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 +329,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 +364,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")}
@@ -406,6 +408,12 @@ def send_nscript(title, msg, notification_type, force=False, test=None):
def send_windows(title: str, msg: str, notification_type: str, actions: Optional[Dict[str, str]] = None):
"""Send Windows notifications, either fancy with buttons (Windows 10+) or basic ones"""
# Skip any notifications if ran as a Windows Service, it can result in crashes
if sabnzbd.WIN_SERVICE:
return None
logging.debug("Sending Windows notification")
try:
if _HAVE_WINDOWS_TOASTER:
notification_sender = InteractableWindowsToaster("SABnzbd", notifierAUMID="SABnzbd")

View File

@@ -62,6 +62,7 @@ def add_nzbfile(
reuse: Optional[str] = None,
password: Optional[str] = None,
nzo_id: Optional[str] = None,
dup_check: bool = True,
):
"""Add file, either a single NZB-file or an archive.
All other parameters are passed to the NZO-creation.
@@ -119,6 +120,7 @@ def add_nzbfile(
url=url,
password=password,
nzo_id=nzo_id,
dup_check=dup_check,
)
else:
return process_single_nzb(
@@ -136,6 +138,7 @@ def add_nzbfile(
url=url,
password=password,
nzo_id=nzo_id,
dup_check=dup_check,
)
@@ -151,10 +154,10 @@ def process_nzb_archive_file(
nzbname: Optional[str] = None,
reuse: Optional[str] = None,
nzo_info: Optional[Dict[str, Any]] = None,
dup_check: bool = True,
url: Optional[str] = None,
password: Optional[str] = None,
nzo_id: Optional[str] = None,
dup_check: bool = True,
) -> Tuple[AddNzbFileResult, List[str]]:
"""Analyse archive and create job(s).
Accepts archive files with ONLY nzb/nfo/folder files in it.
@@ -266,10 +269,10 @@ def process_single_nzb(
nzbname: Optional[str] = None,
reuse: Optional[str] = None,
nzo_info: Optional[Dict[str, Any]] = None,
dup_check: bool = True,
url: Optional[str] = None,
password: Optional[str] = None,
nzo_id: Optional[str] = None,
dup_check: bool = True,
) -> Tuple[AddNzbFileResult, List[str]]:
"""Analyze file and create a job from it
Supports NZB, NZB.BZ2, NZB.GZ and GZ.NZB-in-disguise

View File

@@ -979,7 +979,7 @@ class NzbQueue:
# Unfortunately we need a copy, since we might remove items from the list
for nzo in self.__nzo_list[:]:
if not nzo.duplicate:
if not nzo.duplicate or nzo.duplicate == DuplicateStatus.DUPLICATE_IGNORED:
continue
# URL's do not have an MD5!

View File

@@ -1433,12 +1433,13 @@ class NzbObject(TryList):
# If user resumes after encryption warning, no more auto-pauses
self.encrypted = 2
# If user resumes after warning, reset duplicate/oversized/incomplete/unwanted indicators
self.duplicate = None
self.oversized = False
self.incomplete = False
if self.unwanted_ext:
# If user resumes after "unwanted" warning, no more auto-pauses
self.unwanted_ext = 2
if self.duplicate:
self.duplicate = DuplicateStatus.DUPLICATE_IGNORED
@synchronized(NZO_LOCK)
def add_parfile(self, parfile: NzbFile) -> bool:

View File

@@ -34,7 +34,7 @@ import base64
from typing import Tuple, Optional, Union, List, Dict, Any
import sabnzbd
from sabnzbd.constants import DEF_TIMEOUT, FUTURE_Q_FOLDER, VALID_NZB_FILES, Status, VALID_ARCHIVES
from sabnzbd.constants import DEF_TIMEOUT, FUTURE_Q_FOLDER, VALID_NZB_FILES, Status, VALID_ARCHIVES, DuplicateStatus
import sabnzbd.misc as misc
import sabnzbd.filesystem
import sabnzbd.cfg as cfg
@@ -243,6 +243,10 @@ class URLGrabber(Thread):
# Check if nzb file
if sabnzbd.filesystem.get_ext(filename) in VALID_ARCHIVES + VALID_NZB_FILES:
# If the user resumed a duplicate detected URL, skip the check
dup_check = future_nzo.duplicate != DuplicateStatus.DUPLICATE_IGNORED
# Add the new job to the queue
res, _ = sabnzbd.nzbparser.add_nzbfile(
path,
pp=future_nzo.pp,
@@ -255,6 +259,7 @@ class URLGrabber(Thread):
keep=False,
password=future_nzo.password,
nzo_id=future_nzo.nzo_id,
dup_check=dup_check,
)
if res is AddNzbFileResult.RETRY:
logging.info("Incomplete NZB, retry after 5 min %s", url)

View File

@@ -1,56 +1,9 @@
import platform
import subprocess
import locale
import logging
import time
from .pystone import pystones
def getcpu():
# find the CPU name (which needs a different method per OS), and return it
# If none found, return platform.platform().
cputype = None
try:
if platform.system() == "Windows":
import winreg
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0")
cputype = winreg.QueryValueEx(key, "ProcessorNameString")[0]
winreg.CloseKey(key)
elif platform.system() == "Darwin":
cputype = subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"]).strip()
elif platform.system() == "Linux":
with open("/proc/cpuinfo") as fp:
for myline in fp.readlines():
if myline.startswith("model name"):
# Typical line:
# model name : Intel(R) Xeon(R) CPU E5335 @ 2.00GHz
cputype = myline.split(":", 1)[1] # get everything after the first ":"
break # we're done
cputype = cputype.decode(locale.getpreferredencoding())
except:
# An exception, maybe due to a subprocess call gone wrong
pass
if cputype:
# OK, found. Remove unwanted spaces:
cputype = " ".join(cputype.split())
else:
try:
# Not found, so let's fall back to platform()
cputype = platform.platform()
except:
# Can fail on special platforms (like Snapcraft or embedded)
pass
logging.debug("CPU model = %s", cputype)
return cputype
def getpystone():
# Start calculation
maxpystone = 0
@@ -69,4 +22,3 @@ def getpystone():
if __name__ == "__main__":
print(getpystone())
print(getcpu())

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.2RC2"
__baseline__ = "unknown"