Compare commits

...

7 Commits

Author SHA1 Message Date
Safihre
46954165d2 Update text files for 4.3.3RC2 2024-08-17 15:08:57 +02:00
Safihre
58e7d520bf increase_bad_articles_counter should be called before register_article 2024-08-17 15:08:03 +02:00
Safihre
a4f8040324 Prevent excessive newswrapper data buffer size
Closes #2895
2024-08-17 15:07:57 +02:00
Safihre
8d5cc9a3e6 Do not use article_queue when resetting newswrapper
Relates to #2866
2024-08-17 15:07:52 +02:00
jcfp
4592ce4d55 Remove pyfakefs workarounds and put its new apply_umask option to good use (#2922) 2024-08-17 15:07:47 +02:00
Safihre
b62b38b5af Update text files for 4.3.3RC1 2024-08-13 10:29:25 +02:00
renovate[bot]
14b1d4630c Update all dependencies 2024-08-13 10:16:28 +02:00
13 changed files with 43 additions and 39 deletions

View File

@@ -69,7 +69,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.3"
PYTHON_VERSION: "3.12.5"
MACOSX_DEPLOYMENT_TARGET: "10.9"
# We need to force compile for universal2 support
CFLAGS: -arch x86_64 -arch arm64

View File

@@ -1,13 +1,15 @@
Release Notes - SABnzbd 4.3.3 Beta 2
Release Notes - SABnzbd 4.3.3 Release Candidate 2
=========================================================
This is the third bug fix release of SABnzbd 4.3.0.
## Bug fixes and changes since 4.3.2
* Jobs could get stuck at 99%.
* Reduced chance of jobs getting stuck at 99%.
* Prevent crash in case of invalid articles.
* Correct handling of empty or `Default` category when adding a job.
* History API-output could contain inconsistent variable types.
* Skip external IPv6 check if only link local addresses are available.
* Shortened timeouts when resolving addresses during checks.
* Windows: Could not repair or extract on ARM platforms.
* Windows: Add file version information to installer.

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==24.1
pyinstaller-hooks-contrib==2024.7
pyinstaller-hooks-contrib==2024.8
altgraph==0.17.4
wrapt==1.16.0
setuptools==72.1.0
@@ -10,10 +10,10 @@ setuptools==72.1.0
# Required on 32bit Windows, exclude it based on Python-version
importlib_metadata==8.2.0; python_version < '3.10'
importlib_resources==6.4.0; python_version < '3.10'
zipp==3.19.2; python_version < '3.10'
zipp==3.20.0; python_version < '3.10'
# orjson does not support 32bit Windows, also exclude based on Python-version
orjson==3.10.6; python_version > '3.8'
orjson==3.10.7; python_version > '3.8'
# For the Windows build
pefile==2023.2.7; sys_platform == 'win32'

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.3.3" date="2024-08-01" type="stable"/>
<release version="4.3.3" date="2024-08-20" type="stable"/>
<release version="4.3.2" date="2024-05-30" type="stable"/>
<release version="4.3.1" date="2024-05-03" type="stable"/>
<release version="4.3.0" date="2024-05-01" type="stable"/>

View File

@@ -3,7 +3,7 @@
apprise==1.8.1
sabctools==8.2.5
CT3==3.3.3.post1
cffi==1.16.0
cffi==1.17.0
pycparser==2.22
feedparser==6.0.11
configobj==5.0.8
@@ -15,7 +15,7 @@ jaraco.collections==5.0.0
jaraco.text==3.8.1 # Newer version introduces irrelevant extra dependencies
jaraco.classes==3.4.0
jaraco.context==4.3.0
more-itertools==10.3.0
more-itertools==10.4.0
zc.lockfile==3.0.post1
python-dateutil==2.9.0.post0
tempora==5.7.0
@@ -24,7 +24,7 @@ sgmllib3k==1.0.0
portend==3.2.0
chardet==5.2.0
PySocks==1.7.1
puremagic==1.26
puremagic==1.27
guessit==3.8.0
babelfish==0.6.1
rebulk==3.2.0
@@ -56,7 +56,7 @@ notify2==0.3.1; sys_platform != 'win32' and sys_platform != 'darwin'
# Apprise Requirements
requests==2.32.3
requests-oauthlib==2.0.0
PyYAML==6.0.1
PyYAML==6.0.2
markdown==3.6
paho-mqtt==1.6.1 # Pinned, newer versions don't work with AppRise yet

View File

@@ -101,6 +101,7 @@ SOFT_QUEUE_LIMIT = 0.5
# Percentage of cache to use before adding file to assembler
ASSEMBLER_WRITE_THRESHOLD = 5
NNTP_BUFFER_SIZE = int(800 * KIBI)
NTTP_MAX_BUFFER_SIZE = int(10 * MEBI)
REPAIR_PRIORITY = 3
FORCE_PRIORITY = 2

View File

@@ -510,8 +510,8 @@ class Downloader(Thread):
# Handle broken articles directly
if not data_view:
if not article.search_new_server():
sabnzbd.NzbQueue.register_article(article, success=False)
article.nzf.nzo.increase_bad_articles_counter("missing_articles")
sabnzbd.NzbQueue.register_article(article, success=False)
return
# Decode and send to article cache
@@ -704,9 +704,14 @@ class Downloader(Thread):
except ssl.SSLWantReadError:
return
except (ConnectionError, ConnectionAbortedError):
# The ConnectionAbortedError is thrown by sabctools in case of fatal SSL-layer problems
# The ConnectionAbortedError is also thrown by sabctools in case of fatal SSL-layer problems
self.__reset_nw(nw, "Server closed connection", wait=False)
return
except BufferError:
# The BufferError is thrown when exceeding maximum buffer size
# Make sure to discard the article
self.__reset_nw(nw, "Maximum data buffer size exceeded", wait=False, retry_article=False)
return
article = nw.article
server = nw.server
@@ -960,14 +965,9 @@ class Downloader(Thread):
self.decode(nw.article)
nw.article.tries = 0
else:
# Retry again with the same server
logging.debug(
"Re-adding article %s from %s to server %s",
nw.article.article,
nw.article.nzf.filename,
nw.article.fetcher,
)
nw.article.fetcher.article_queue.append(nw.article)
# Allow all servers again on this server
# Do not use the article_queue, as the server could already have been disabled when we get here!
sabnzbd.NzbQueue.reset_try_lists(nw.article)
# Reset connection object
nw.hard_reset(wait)

View File

@@ -30,7 +30,7 @@ from typing import Optional, Tuple, Union
import sabnzbd
import sabnzbd.cfg
from sabnzbd.constants import DEF_NETWORKING_TIMEOUT, NNTP_BUFFER_SIZE
from sabnzbd.constants import DEF_NETWORKING_TIMEOUT, NNTP_BUFFER_SIZE, NTTP_MAX_BUFFER_SIZE
from sabnzbd.encoding import utob, ubtou
from sabnzbd.happyeyeballs import AddrInfo
from sabnzbd.decorators import synchronized, DOWNLOADER_LOCK
@@ -230,6 +230,10 @@ class NewsWrapper:
def increase_data_buffer(self):
"""Resize the buffer in the extremely unlikely case that it overflows"""
# Sanity check before we go any further
if len(self.data) > NTTP_MAX_BUFFER_SIZE:
raise BufferError("Maximum data buffer size exceeded")
# Input needs to be integer, floats don't work
new_buffer = sabctools.bytearray_malloc(len(self.data) + NNTP_BUFFER_SIZE // 2)
new_buffer[: len(self.data)] = self.data

View File

@@ -898,8 +898,8 @@ class NzbQueue:
for article in nzf.articles[:]:
if article.all_servers_in_try_list(active_servers):
logging.debug("Removing article %s with bad trylist in file %s", article, nzf.filename)
sabnzbd.NzbQueue.register_article(article, success=False)
nzo.increase_bad_articles_counter("missing_articles")
sabnzbd.NzbQueue.register_article(article, success=False)
logging.info("Resetting bad trylist for file %s in job %s", nzf.filename, nzo.final_name)
nzf.reset_try_list()

View File

@@ -1592,7 +1592,8 @@ class NzbObject(TryList):
@synchronized(NZO_LOCK)
def increase_bad_articles_counter(self, bad_article_type: str):
"""Record information about bad articles"""
"""Record information about bad articles. Should be called before
register_article, which triggers the availability check."""
if bad_article_type not in self.nzo_info:
self.nzo_info[bad_article_type] = 0
self.nzo_info[bad_article_type] += 1

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.3.3Beta2"
__version__ = "4.3.3RC2"
__baseline__ = "unknown"

View File

@@ -3,7 +3,7 @@ pytest==7.4.4
setuptools
selenium
requests
pyfakefs>=5.4.0
pyfakefs>=5.6.0
werkzeug<2.1.0 # Breaks httpbin in newer versions
pytest-httpbin
pytest-httpserver

View File

@@ -912,11 +912,9 @@ class TestCreateAllDirs(ffs.TestCase, PermissionCheckerHelper):
def _permissions_runner(self, test_base, perms_base="0700", apply_permissions=True):
# Create base directory and set the base permissions
perms_base_int = int(perms_base, 8)
self.fs.create_dir(test_base, perms_base_int)
self.fs.create_dir(test_base, perms_base_int, apply_umask=False)
assert os.path.exists(test_base) is True
# Account for pyfakefs (>= 5.4.0) always applying the fake fs umask when creating dirs
perms_base_int_umasked = perms_base_int & ~self.fs.umask
self.assert_dir_perms(test_base, perms_base_int_umasked)
self.assert_dir_perms(test_base, perms_base_int)
# Create directories with permissions
new_dir = os.path.join(test_base, "se 1", "ep1")
@@ -930,7 +928,7 @@ class TestCreateAllDirs(ffs.TestCase, PermissionCheckerHelper):
# Get the current permissions, since os.mkdir masks that out
perms_test_int = int("0777", 8) & ~sabnzbd.ORG_UMASK
self.assert_dir_perms(new_dir, perms_test_int)
self.assert_dir_perms(test_base, perms_base_int_umasked)
self.assert_dir_perms(test_base, perms_base_int)
class TestSetPermissionsWin(ffs.TestCase):
@@ -947,9 +945,7 @@ class TestSetPermissions(ffs.TestCase, PermissionCheckerHelper):
self.setUpPyfakefs()
self.fs.path_separator = "/"
self.fs.is_case_sensitive = True
# All-permissive umask as a workaround for changes in pyfakefs (>= 5.4.0),
# causing it to always take the umask into account when creating dirs
self.fs.umask = int("0000", 8) # rwxrwxrwx
self.fs.umask = int("0022", 8) # rwxr-xr-x
def _runner(self, perms_before_test):
"""
@@ -970,7 +966,7 @@ class TestSetPermissions(ffs.TestCase, PermissionCheckerHelper):
# Setup and verify fake dir
test_dir = "/test"
self.fs.create_dir(test_dir, perms_before_test)
self.fs.create_dir(test_dir, perms_before_test, apply_umask=False)
assert os.path.exists(test_dir) is True
self.assert_dir_perms(test_dir, perms_before_test)
@@ -987,10 +983,10 @@ class TestSetPermissions(ffs.TestCase, PermissionCheckerHelper):
# Create the folder, so it has the expected permissions
if not os.path.exists(basefolder):
try:
self.fs.create_dir(basefolder, perms_before_test)
self.fs.create_dir(basefolder, perms_before_test, apply_umask=False)
except PermissionError:
ffs.set_uid(0)
self.fs.create_file(file, perms_before_test)
self.fs.create_file(file, perms_before_test, apply_umask=False)
assert os.path.exists(basefolder) is True
self.assert_dir_perms(basefolder, perms_before_test)
@@ -1001,10 +997,10 @@ class TestSetPermissions(ffs.TestCase, PermissionCheckerHelper):
# Then, create the file
try:
self.fs.create_file(file, file_perms_before_test)
self.fs.create_file(file, file_perms_before_test, apply_umask=False)
except PermissionError:
ffs.set_uid(0)
self.fs.create_file(file, file_perms_before_test)
self.fs.create_file(file, file_perms_before_test, apply_umask=False)
assert os.path.exists(file) is True
assert stat.filemode(os.stat(file).st_mode)[1:] == stat.filemode(file_perms_before_test)[1:]