mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2025-12-24 08:08:37 -05:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76c58953df | ||
|
|
4ddc5caa49 | ||
|
|
694663bd95 | ||
|
|
62aba5844e | ||
|
|
d0d60cef05 | ||
|
|
3d293fdcb0 | ||
|
|
96e9528046 | ||
|
|
4ea24b3203 | ||
|
|
e586ead024 | ||
|
|
14c80bf1dc | ||
|
|
bdd56e794a | ||
|
|
a544548934 | ||
|
|
e06c1d61fb | ||
|
|
600c5209c6 | ||
|
|
bee90366ee | ||
|
|
e9bc4e9417 | ||
|
|
f01ff15761 | ||
|
|
356ada159d |
14
.github/dependabot.yml
vendored
Normal file
14
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/builder"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/builder/osx"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
6
.github/workflows/build_release.yml
vendored
6
.github/workflows/build_release.yml
vendored
@@ -73,7 +73,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.10.1"
|
||||
PYTHON_VERSION: "3.10.2"
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.9"
|
||||
# We need to force compile for universal2 support
|
||||
CFLAGS: -arch arm64 -arch x86_64
|
||||
@@ -108,8 +108,8 @@ jobs:
|
||||
pip3 install --upgrade -r requirements.txt --no-binary sabyenc3
|
||||
|
||||
pip3 uninstall cryptography -y
|
||||
pip3 download cryptography --platform macosx_10_10_universal2 --only-binary :all: --no-deps --dest .
|
||||
pip3 install cryptography --no-cache-dir --no-index --find-links .
|
||||
pip3 download -r builder/osx/requirements.txt --platform macosx_10_10_universal2 --only-binary :all: --no-deps --dest .
|
||||
pip3 install -r builder/osx/requirements.txt --no-cache-dir --no-index --find-links .
|
||||
|
||||
PYINSTALLER_COMPILE_BOOTLOADER=1 pip3 install --upgrade -r builder/requirements.txt --no-binary pyinstaller
|
||||
- name: Import macOS codesign certificates
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -1,7 +1,7 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 3.5.0RC1
|
||||
Summary: SABnzbd-3.5.0RC1
|
||||
Version: 3.5.1RC1
|
||||
Summary: SABnzbd-3.5.1RC1
|
||||
Home-page: https://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
|
||||
21
README.mkd
21
README.mkd
@@ -1,6 +1,11 @@
|
||||
Release Notes - SABnzbd 3.5.0 Release Candidate 1
|
||||
Release Notes - SABnzbd 3.5.1 Release Candidate 1
|
||||
=========================================================
|
||||
|
||||
## Changes and bugfixes since 3.5.0
|
||||
- Small changes in file assembly and Direct Unpack processing.
|
||||
- RSS feeds with HTML-characters in the name resulted in crashes.
|
||||
- macOS: failed to start on older macOS versions.
|
||||
|
||||
## Changes since 3.4.2
|
||||
- Removed Python 3.6 support.
|
||||
- SOCKS5 proxy support for all outgoing connections.
|
||||
@@ -8,31 +13,33 @@ Release Notes - SABnzbd 3.5.0 Release Candidate 1
|
||||
- `Required` server option: in case of connection failures, the queue
|
||||
will be paused for a few minutes instead of skipping the server.
|
||||
- Added Special option to preserve paused state after a restart.
|
||||
- Show an estimated time-left indicator for repair and unpacking.
|
||||
- Show an estimated time-left indicator for repair and unpacking.
|
||||
- Require TLS version 1.2 or higher for SSL news server connections.
|
||||
- Setting custom ciphers forces the maximum TLS version to 1.2.
|
||||
- Print low-level Windows status error on `IOError`.
|
||||
- Reduced memory usage during and after parsing `.nzb` files.
|
||||
- Handle multiple passwords stored in NZB-file.
|
||||
- macOS/Linux: `Permissions` are only applied if any are set.
|
||||
- macOS/Windows: updated to Python 3.10.1.
|
||||
- macOS: run native on M1 systems. However, included tools
|
||||
- macOS/Windows: updated to Python 3.10.2.
|
||||
- macOS: run native on M1 systems. However, included tools
|
||||
(`par2`, `unrar` and `7za`) still require Rosetta emulation.
|
||||
- Snap: updated to `core20` base and restore 7zip support.
|
||||
|
||||
## Bugfixes since 3.4.2
|
||||
- Global interface settings would not always be applied correctly.
|
||||
- Email notification setting was not shown correctly.
|
||||
- Improvements and fixes for `Defobfuscate final filenames`.
|
||||
- `Post-Process Only Verified Jobs` would not always work as intended.
|
||||
- Correctly detect too little disk space when unpacking 7zip's.
|
||||
- Improvements to handling of repair by MultiPar and par2cmdline.
|
||||
- HTML characters in configuration fields were shown incorrectly.
|
||||
- On Retry the number of downloaded bytes could exceed the total bytes.
|
||||
- `unrar` logging of Direct Unpack was not logged if it was aborted.
|
||||
- Windows: `portable.cmd` was not included in the release.
|
||||
- Windows: print low-level Windows error on `IOError`.
|
||||
|
||||
## Upgrade notices
|
||||
- The download statistics file `totals10.sab` is updated in 3.2.x
|
||||
version. If you downgrade to 3.1.x or lower, detailed download
|
||||
- The download statistics file `totals10.sab` is updated in 3.2.x
|
||||
version. If you downgrade to 3.1.x or lower, detailed download
|
||||
statistics will be lost.
|
||||
|
||||
## Known problems and solutions
|
||||
|
||||
3
builder/osx/requirements.txt
Normal file
3
builder/osx/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# Special requirements for macOS universal2 binary release
|
||||
# This way dependabot can auto-update them
|
||||
cryptography==36.0.1
|
||||
@@ -1,9 +1,13 @@
|
||||
# Basic build requirements
|
||||
pyinstaller>=4.8
|
||||
setuptools
|
||||
# Note that not all sub-dependencies are listed, but only ones we know could cause trouble
|
||||
pyinstaller==4.8
|
||||
pyinstaller-hooks-contrib==2022.0
|
||||
altgraph==0.17.2
|
||||
wrapt==1.13.3
|
||||
setuptools==60.6.0
|
||||
pkginfo
|
||||
certifi
|
||||
pygithub
|
||||
|
||||
# For the OSX build specific
|
||||
dmgbuild; sys_platform == 'darwin'
|
||||
dmgbuild==1.5.2; sys_platform == 'darwin'
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
</td>
|
||||
<td class="title">
|
||||
<a href="?feed=$rss[$feed_item]['link']" class="subscription-title path feed <!--#if int($rss[$feed_item]['enable']) != 0 then 'feed_enabled' else 'feed_disabled'#-->">
|
||||
$feed_item
|
||||
$feed_item_html
|
||||
</a>
|
||||
</td>
|
||||
<td class="controls">
|
||||
@@ -102,7 +102,7 @@
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<!--#if $active_feed#-->
|
||||
<!--#set $feed = $active_feed#-->
|
||||
<!--#set $feed = html.unescape($active_feed)#-->
|
||||
<div class="section rss-section">
|
||||
<div class="padTable">
|
||||
<a class="main-helplink" href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
@@ -113,12 +113,12 @@
|
||||
<!--#if $error#-->
|
||||
<div class="alert alert-danger">
|
||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||
<!--#echo html.escape($error)#-->
|
||||
$error
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<form action="upd_rss_feed" method="post">
|
||||
<input type="hidden" name="apikey" value="$apikey" />
|
||||
<input type="hidden" name="feed" value="$feed" />
|
||||
<input type="hidden" name="feed" value="$active_feed" />
|
||||
<input type="hidden" name="uri" value="$rss[$feed]['uris']" />
|
||||
<table class="catTable">
|
||||
<thead>
|
||||
@@ -210,7 +210,7 @@
|
||||
<form action="upd_rss_filter" method="post">
|
||||
<input type="hidden" name="apikey" value="$apikey" />
|
||||
<input type="hidden" name="index" value="$rss[$feed]['filtercount']" />
|
||||
<input type="hidden" name="feed" value="$feed" />
|
||||
<input type="hidden" name="feed" value="$active_feed" />
|
||||
<table class="catTable">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -286,7 +286,7 @@
|
||||
<form action="upd_rss_filter" method="post" autocomplete="off">
|
||||
<input type="hidden" name="apikey" value="$apikey" />
|
||||
<input type="hidden" name="index" value="$fnum" />
|
||||
<input type="hidden" name="feed" value="$feed" />
|
||||
<input type="hidden" name="feed" value="$active_feed" />
|
||||
<table class="catTable">
|
||||
<tbody>
|
||||
<tr class="<!--#if $odd then " alt " else " "#--> <!--#if $filter[3]!="A" and $filter[3]!="S" then 'disabled_options_rule' else ""#-->">
|
||||
@@ -363,13 +363,13 @@
|
||||
<!--#end for#-->
|
||||
<form action="download_rss_feed" method="post">
|
||||
<input type="hidden" name="apikey" value="$apikey" />
|
||||
<input type="hidden" name="feed" value="$feed" />
|
||||
<input type="hidden" name="feed" value="$active_feed" />
|
||||
<div class="padding">
|
||||
<button type="button" class="btn btn-default testFeed" rel="$feed"><span class="glyphicon glyphicon-sort"></span> $T('button-preFeed')</button>
|
||||
<button type="button" class="btn btn-default testFeed" rel="$active_feed"><span class="glyphicon glyphicon-sort"></span> $T('button-preFeed')</button>
|
||||
<button type="submit" class="btn btn-default Save"><span class="glyphicon glyphicon-forward"></span> $T('button-forceFeed')</button>
|
||||
<button type="button" class="btn btn-default cleanFeed"><span class="glyphicon glyphicon-trash"></span> $T('button-clear') $T('rss-done')</button>
|
||||
<!--#if $evalButton#-->
|
||||
<button type="button" class="btn btn-default evalFeed" rel="$feed"><span class="glyphicon glyphicon-ok-circle"></span> $T('button-evalFeed')</button>
|
||||
<button type="button" class="btn btn-default evalFeed" rel="$active_feed"><span class="glyphicon glyphicon-ok-circle"></span> $T('button-evalFeed')</button>
|
||||
<!--#end if#-->
|
||||
</div>
|
||||
</form>
|
||||
@@ -402,7 +402,7 @@
|
||||
<tr class="infoTableSeperator">
|
||||
<td>
|
||||
<form action="download" method="get">
|
||||
<input type="hidden" value="$feed" name="feed" />
|
||||
<input type="hidden" value="$active_feed" name="feed" />
|
||||
<input type="hidden" name="apikey" value="$apikey" />
|
||||
<input type="hidden" name="url" value="$job['url']" />
|
||||
<input type="hidden" name="nzbname" value="$job['nzbname']" />
|
||||
@@ -446,7 +446,7 @@
|
||||
<tr class="infoTableSeperator">
|
||||
<td>
|
||||
<form action="download" method="get">
|
||||
<input type="hidden" value="$feed" name="feed" />
|
||||
<input type="hidden" value="$active_feed" name="feed" />
|
||||
<input type="hidden" name="apikey" value="$apikey" />
|
||||
<input type="hidden" name="url" value="$job['url']" />
|
||||
<input type="hidden" name="nzbname" value="$job['nzbname']" />
|
||||
@@ -475,7 +475,7 @@
|
||||
<div class="tab-pane padTable" id="rss-tab-done">
|
||||
<!--#if $downloaded#-->
|
||||
<form action="clean_rss_jobs" method="post">
|
||||
<input type="hidden" value="$feed" name="feed" />
|
||||
<input type="hidden" value="$active_feed" name="feed" />
|
||||
<input type="hidden" name="apikey" value="$apikey" />
|
||||
<table class="catTable">
|
||||
<thead>
|
||||
|
||||
@@ -1,22 +1,36 @@
|
||||
sabyenc3>=4.0.0
|
||||
cheetah3>=3.0.0
|
||||
cryptography
|
||||
feedparser>=6.0.0
|
||||
configobj
|
||||
cheroot
|
||||
cherrypy
|
||||
portend
|
||||
chardet
|
||||
notify2
|
||||
PySocks
|
||||
puremagic
|
||||
guessit>=3.1.0
|
||||
# Main requirements
|
||||
# Note that not all sub-dependencies are listed, but only ones we know could cause trouble
|
||||
sabyenc3==4.0.2
|
||||
cheetah3==3.2.6
|
||||
cryptography==36.0.1
|
||||
cffi==1.15
|
||||
pycparser==2.21
|
||||
feedparser==6.0.8
|
||||
configobj==5.0.6
|
||||
cheroot==8.6.0
|
||||
cherrypy==18.6.1
|
||||
jaraco.functools==3.5.0
|
||||
jaraco.collections==3.5.1
|
||||
jaraco.text==3.7.0
|
||||
jaraco.classes==3.2.1
|
||||
jaraco.context==4.1.1
|
||||
more-itertools==8.12.0
|
||||
tempora==5.0.1
|
||||
portend==3.1.0
|
||||
chardet==4.0.0
|
||||
PySocks==1.7.1
|
||||
puremagic==1.11
|
||||
guessit==3.4.3
|
||||
rebulk==3.1.0
|
||||
|
||||
# Windows system integration
|
||||
pywin32>=227; sys_platform == 'win32'
|
||||
pywin32==303; sys_platform == 'win32'
|
||||
|
||||
# macOS system calls
|
||||
pyobjc; sys_platform == 'darwin'
|
||||
pyobjc==8.1; sys_platform == 'darwin'
|
||||
|
||||
# Linux notifications
|
||||
notify2==0.3.1; sys_platform != 'win32' and sys_platform != 'darwin'
|
||||
|
||||
# Optional support for *nix tray icon.
|
||||
# Note that pygobject depends on pycairo, which requires pkg-config and cairo headers.
|
||||
|
||||
@@ -119,7 +119,7 @@ class Assembler(Thread):
|
||||
# Log traceback
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
if sabnzbd.WIN32:
|
||||
logging.info("Winerror: %s", hex(ctypes.windll.ntdll.RtlGetLastNtStatus() + 2 ** 32))
|
||||
logging.info("Winerror: %s", hex(ctypes.windll.ntdll.RtlGetLastNtStatus() + 2**32))
|
||||
# Pause without saving
|
||||
sabnzbd.Downloader.pause()
|
||||
continue
|
||||
@@ -214,7 +214,8 @@ class Assembler(Thread):
|
||||
if not nzf.md5:
|
||||
nzf.md5 = hashlib.md5()
|
||||
|
||||
with open(nzf.filepath, "ab") as fout:
|
||||
# We write large article-sized chunks, so we can safely skip the buffering of Python
|
||||
with open(nzf.filepath, "ab", buffering=0) as fout:
|
||||
for article in nzf.decodetable:
|
||||
# Break if deleted during writing
|
||||
if nzf.nzo.status is Status.DELETED:
|
||||
|
||||
@@ -401,7 +401,7 @@ class ConfigServer:
|
||||
|
||||
self.displayname = OptionStr(name, "displayname", add=False)
|
||||
self.host = OptionStr(name, "host", add=False)
|
||||
self.port = OptionNumber(name, "port", 119, 0, 2 ** 16 - 1, add=False)
|
||||
self.port = OptionNumber(name, "port", 119, 0, 2**16 - 1, add=False)
|
||||
self.timeout = OptionNumber(name, "timeout", 60, 20, 240, add=False)
|
||||
self.username = OptionStr(name, "username", add=False)
|
||||
self.password = OptionPassword(name, "password", add=False)
|
||||
|
||||
@@ -41,9 +41,9 @@ ANFO = namedtuple("ANFO", "article_sum cache_size cache_limit")
|
||||
DEF_FOLDER_MAX = 256 - 10
|
||||
DEF_FILE_MAX = 255 - 10 # max filename length on modern filesystems, minus some room for extra chars later on
|
||||
|
||||
GIGI = float(2 ** 30)
|
||||
MEBI = float(2 ** 20)
|
||||
KIBI = float(2 ** 10)
|
||||
GIGI = float(2**30)
|
||||
MEBI = float(2**20)
|
||||
KIBI = float(2**10)
|
||||
|
||||
BYTES_FILE_NAME_OLD = "totals9.sab"
|
||||
BYTES_FILE_NAME = "totals10.sab"
|
||||
|
||||
@@ -54,20 +54,20 @@ class DirectUnpacker(threading.Thread):
|
||||
|
||||
self.nzo: NzbObject = nzo
|
||||
self.active_instance: Optional[subprocess.Popen] = None
|
||||
self.killed = False
|
||||
self.killed: bool = False
|
||||
self.next_file_lock = threading.Condition(threading.RLock())
|
||||
|
||||
self.unpack_dir_info = None
|
||||
self.rarfile_nzf: Optional[NzbFile] = None
|
||||
self.cur_setname = None
|
||||
self.cur_volume = 0
|
||||
self.total_volumes = {}
|
||||
self.unpack_time = 0.0
|
||||
self.cur_setname: Optional[str] = None
|
||||
self.cur_volume: int = 0
|
||||
self.total_volumes: Dict[str, int] = {}
|
||||
self.unpack_time: float = 0.0
|
||||
|
||||
self.success_sets: Dict[str, Tuple[List[str], List[str]]] = {}
|
||||
self.next_sets = []
|
||||
self.next_sets: List[NzbFile] = []
|
||||
|
||||
self.duplicate_lines = 0
|
||||
self.duplicate_lines: int = 0
|
||||
|
||||
nzo.direct_unpacker = self
|
||||
|
||||
@@ -292,9 +292,13 @@ class DirectUnpacker(threading.Thread):
|
||||
|
||||
# Possible that the instance was deleted while locked
|
||||
if not self.killed:
|
||||
# Sometimes the assembler is still working on the file, resulting in "Unexpected end of archive".
|
||||
# So we delay a tiny bit before we continue. This is not the cleanest solution, but it works.
|
||||
time.sleep(0.1)
|
||||
|
||||
# If unrar stopped or is killed somehow, writing will cause a crash
|
||||
try:
|
||||
# Give unrar some time to do it's thing
|
||||
# Give unrar some time to do its thing
|
||||
self.active_instance.stdin.write(b"C\n")
|
||||
start_time = time.time()
|
||||
time.sleep(0.1)
|
||||
@@ -389,7 +393,7 @@ class DirectUnpacker(threading.Thread):
|
||||
# The first NZF
|
||||
self.rarfile_nzf = self.have_next_volume()
|
||||
|
||||
# Ignore if maybe this set is not there any more
|
||||
# Ignore if maybe this set is not there anymore
|
||||
# This can happen due to race/timing issues when creating the sets
|
||||
if not self.rarfile_nzf:
|
||||
return
|
||||
|
||||
@@ -890,7 +890,7 @@ def renamer(old: str, new: str, create_local_directories: bool = False) -> str:
|
||||
time.sleep(2)
|
||||
else:
|
||||
raise
|
||||
raise OSError("Failed to rename (Winerr %s)" % hex(ctypes.windll.ntdll.RtlGetLastNtStatus() + 2 ** 32))
|
||||
raise OSError("Failed to rename (Winerr %s)" % hex(ctypes.windll.ntdll.RtlGetLastNtStatus() + 2**32))
|
||||
else:
|
||||
shutil.move(old, new)
|
||||
return new
|
||||
|
||||
@@ -30,6 +30,7 @@ import hashlib
|
||||
import socket
|
||||
import ssl
|
||||
import functools
|
||||
import copy
|
||||
from random import randint
|
||||
from xml.sax.saxutils import escape
|
||||
from Cheetah.Template import Template
|
||||
@@ -367,8 +368,11 @@ def check_apikey(kwargs):
|
||||
|
||||
def template_filtered_response(file: str, search_list: Dict[str, Any]):
|
||||
"""Wrapper for Cheetah response"""
|
||||
recursive_html_escape(search_list, exclude_items=("webdir",))
|
||||
return Template(file=file, searchList=[search_list], compilerSettings=CHEETAH_DIRECTIVES).respond()
|
||||
# We need a copy, because otherwise source-dicts might be modified
|
||||
search_list_copy = copy.deepcopy(search_list)
|
||||
# 'filters' is excluded because the RSS-filters are listed twice
|
||||
recursive_html_escape(search_list_copy, exclude_items=("webdir", "filters"))
|
||||
return Template(file=file, searchList=[search_list_copy], compilerSettings=CHEETAH_DIRECTIVES).respond()
|
||||
|
||||
|
||||
def log_warning_and_ip(txt):
|
||||
@@ -1445,7 +1449,7 @@ class ConfigRss:
|
||||
|
||||
if filt:
|
||||
feed_cfg.filters.update(
|
||||
int(kwargs.get("index", 0)), (cat, pp, script, kwargs.get("filter_type"), filt, prio, enabled)
|
||||
int(kwargs.get("index", 0)), [cat, pp, script, kwargs.get("filter_type"), filt, prio, enabled]
|
||||
)
|
||||
|
||||
# Move filter if requested
|
||||
|
||||
@@ -733,7 +733,7 @@ def loadavg():
|
||||
return p
|
||||
|
||||
|
||||
def format_time_string(seconds):
|
||||
def format_time_string(seconds: float) -> str:
|
||||
"""Return a formatted and translated time string"""
|
||||
|
||||
def unit(single, n):
|
||||
@@ -1080,11 +1080,13 @@ def recursive_html_escape(input_dict_or_list: Union[Dict[str, Any], List], exclu
|
||||
iterator = enumerate(input_dict_or_list)
|
||||
|
||||
for key, value in iterator:
|
||||
# We ignore any other than str and those on the exclude_items-list
|
||||
if isinstance(value, str) and key not in exclude_items:
|
||||
input_dict_or_list[key] = html.escape(value, quote=True)
|
||||
if isinstance(value, (dict, list)):
|
||||
recursive_html_escape(value)
|
||||
# Ignore any keys that are not safe to convert
|
||||
if key not in exclude_items:
|
||||
# We ignore any other than str
|
||||
if isinstance(value, str):
|
||||
input_dict_or_list[key] = html.escape(value, quote=True)
|
||||
if isinstance(value, (dict, list)):
|
||||
recursive_html_escape(value, exclude_items=exclude_items)
|
||||
else:
|
||||
raise ValueError("Expected dict or str, got %s" % type(input_dict_or_list))
|
||||
|
||||
|
||||
@@ -970,6 +970,8 @@ def unseven(nzo: NzbObject, workdir_complete: str, one_folder: bool, sevens: Lis
|
||||
logging.info("Starting extract on 7zip set/file: %s ", seven_set)
|
||||
nzo.set_action_line(T("Unpacking"), setname_from_path(seven_set))
|
||||
|
||||
# Sort, so that x.001 is the first one
|
||||
seven_sets[seven_set].sort()
|
||||
seven_path = seven_sets[seven_set][0]
|
||||
|
||||
if workdir_complete and seven_path.startswith(nzo.download_path):
|
||||
@@ -978,7 +980,9 @@ def unseven(nzo: NzbObject, workdir_complete: str, one_folder: bool, sevens: Lis
|
||||
extraction_path = os.path.split(seven_path)[0]
|
||||
|
||||
res, new_files_set = seven_extract(nzo, seven_path, seven_set, extraction_path, one_folder)
|
||||
if not res and nzo.delete:
|
||||
if res:
|
||||
unseven_failed = True
|
||||
elif nzo.delete:
|
||||
for seven in seven_sets[seven_set]:
|
||||
try:
|
||||
remove_file(seven)
|
||||
@@ -1284,7 +1288,7 @@ def par2cmdline_verify(
|
||||
if line == "":
|
||||
continue
|
||||
|
||||
if not line.startswith(("Repairing:", "Scanning:", "Loading:")):
|
||||
if not line.startswith(("Repairing:", "Scanning:", "Loading:", "Solving:", "Constructing:")):
|
||||
lines.append(line)
|
||||
|
||||
if line.startswith(("Invalid option specified", "Invalid thread option", "Cannot specify recovery file count")):
|
||||
@@ -2046,12 +2050,14 @@ def unrar_check(rar: str) -> Tuple[int, bool]:
|
||||
|
||||
def sevenzip_check(sevenzip: str) -> str:
|
||||
"""Return version of 7zip, currently as a string"""
|
||||
try:
|
||||
seven_command_output = run_command([sevenzip])
|
||||
# Example: 7-Zip (z) 21.06 (x64) : Copyright (c) 1999-2021 Igor Pavlov : 2021-11-24
|
||||
return re.search(r"(\d+\.\d+).*Copyright", seven_command_output).group(1)
|
||||
except:
|
||||
return ""
|
||||
if sevenzip:
|
||||
try:
|
||||
seven_command_output = run_command([sevenzip])
|
||||
# Example: 7-Zip (z) 21.06 (x64) : Copyright (c) 1999-2021 Igor Pavlov : 2021-11-24
|
||||
return re.search(r"(\d+\.\d+).*Copyright", seven_command_output).group(1)
|
||||
except:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def par2_mt_check(par2_path: str) -> bool:
|
||||
|
||||
@@ -435,7 +435,7 @@ def nzbfile_parser(full_nzb_path: str, nzo):
|
||||
nzo.increase_bad_articles_counter("duplicate_articles")
|
||||
else:
|
||||
logging.info("Skipping duplicate article (%s)", article_id)
|
||||
elif segment_size <= 0 or segment_size >= 2 ** 23:
|
||||
elif segment_size <= 0 or segment_size >= 2**23:
|
||||
# Perform sanity check (not negative, 0 or larger than 8MB) on article size
|
||||
# We use this value later to allocate memory in cache and sabyenc
|
||||
logging.info("Skipping article %s due to strange size (%s)", article_id, segment_size)
|
||||
|
||||
@@ -34,7 +34,7 @@ def measure_speed_from_url(url: str) -> float:
|
||||
logging.debug("Downloaded bytes: %d", downloaded_bytes)
|
||||
logging.debug("Duration in seconds: %f", duration)
|
||||
|
||||
return downloaded_bytes / 1024 ** 2 / duration
|
||||
return downloaded_bytes / 1024**2 / duration
|
||||
|
||||
|
||||
def bytes_to_bits(megabytes_per_second: float) -> float:
|
||||
|
||||
@@ -264,7 +264,7 @@ class TestOtherApi(ApiTestFunctions):
|
||||
if round(limit_pct / 100 * linespeed_value) > 20:
|
||||
speed_abs = str(round(limit_pct / 100 * linespeed_value)) + "M"
|
||||
else:
|
||||
speed_abs = str(round(limit_pct * 2 ** 10 * linespeed_value / 100)) + "K"
|
||||
speed_abs = str(round(limit_pct * 2**10 * linespeed_value / 100)) + "K"
|
||||
else:
|
||||
speed_abs = str(round(limit_pct / 100 * from_units(linespeed)))
|
||||
assert self._get_api_json("config", extra_args={"name": "speedlimit", "value": speed_abs})["status"] is True
|
||||
@@ -615,7 +615,7 @@ class TestQueueApi(ApiTestFunctions):
|
||||
def size_in_bytes(size):
|
||||
# Helper function for list.sort() to deal with B/KB/MB in size values
|
||||
if size.endswith(" MB"):
|
||||
return float(size.strip(" MB")) * 1024 ** 2
|
||||
return float(size.strip(" MB")) * 1024**2
|
||||
if size.endswith(" KB"):
|
||||
return float(size.strip(" KB")) * 1024
|
||||
if size.endswith(" B"):
|
||||
|
||||
@@ -124,12 +124,12 @@ class TestMisc:
|
||||
assert "10.0 M" == misc.to_units(1024 * 1024 * 10)
|
||||
assert "100.0 M" == misc.to_units(1024 * 1024 * 100)
|
||||
assert "9.8 G" == misc.to_units(1024 * 1024 * 10000)
|
||||
assert "1024.0 P" == misc.to_units(1024 ** 6)
|
||||
assert "1024.0 P" == misc.to_units(1024**6)
|
||||
|
||||
def test_unit_back_and_forth(self):
|
||||
assert 100 == misc.from_units(misc.to_units(100))
|
||||
assert 1024 == misc.from_units(misc.to_units(1024))
|
||||
assert 1024 ** 3 == misc.from_units(misc.to_units(1024 ** 3))
|
||||
assert 1024**3 == misc.from_units(misc.to_units(1024**3))
|
||||
|
||||
def test_caller_name(self):
|
||||
@set_config({"log_level": 0})
|
||||
|
||||
@@ -208,21 +208,21 @@ class FakeHistoryDB(db.HistoryDB):
|
||||
nzo.status = choice([Status.COMPLETED, choice(self.status_options)])
|
||||
nzo.fail_msg = "¡Fracaso absoluto!" if nzo.status == Status.FAILED else ""
|
||||
nzo.nzo_id = "SABnzbd_nzo_%s" % ("".join(choice(ascii_lowercase + digits) for i in range(8)))
|
||||
nzo.bytes_downloaded = randint(1024, 1024 ** 4)
|
||||
nzo.bytes_downloaded = randint(1024, 1024**4)
|
||||
nzo.md5sum = "".join(choice("abcdef" + digits) for i in range(32))
|
||||
nzo.repair, nzo.unpack, nzo.delete = pp_to_opts(choice(list(db._PP_LOOKUP.keys()))) # for "pp"
|
||||
nzo.nzo_info = {"download_time": randint(1, 10 ** 4)}
|
||||
nzo.nzo_info = {"download_time": randint(1, 10**4)}
|
||||
nzo.unpack_info = {"unpack_info": "placeholder unpack_info line\r\n" * 3}
|
||||
nzo.futuretype = False # for "report", only True when fetching an URL
|
||||
nzo.download_path = os.path.join(os.path.dirname(db.HistoryDB.db_path), "placeholder_downpath")
|
||||
|
||||
# Mock time when calling add_history_db() to randomize completion times
|
||||
almost_time = mock.Mock(return_value=time.time() - randint(0, 10 ** 8))
|
||||
almost_time = mock.Mock(return_value=time.time() - randint(0, 10**8))
|
||||
with mock.patch("time.time", almost_time):
|
||||
self.add_history_db(
|
||||
nzo,
|
||||
storage=os.path.join(os.path.dirname(db.HistoryDB.db_path), "placeholder_workdir"),
|
||||
postproc_time=randint(1, 10 ** 3),
|
||||
postproc_time=randint(1, 10**3),
|
||||
script_output="",
|
||||
script_line="",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user