diff --git a/SABnzbd.py b/SABnzbd.py index 7433043b2..b4031649d 100755 --- a/SABnzbd.py +++ b/SABnzbd.py @@ -17,7 +17,6 @@ import sys - # Trick to show a better message on older Python # releases that don't support walrus operator if Python_39_is_required_to_run_SABnzbd := sys.hexversion < 0x03090000: diff --git a/builder/requirements.txt b/builder/requirements.txt index a23ad60c4..51b36852d 100644 --- a/builder/requirements.txt +++ b/builder/requirements.txt @@ -9,12 +9,12 @@ setuptools==82.0.1 markdown==3.10.2 # Required on 32bit Windows, exclude it based on Python-version -importlib_metadata==8.5.0; python_version < '3.10' -importlib_resources==6.4.5; python_version < '3.10' -zipp==3.21.0; python_version < '3.10' +importlib_metadata==8.7.0; python_version < '3.10' +importlib_resources==6.5.2; python_version < '3.10' +zipp==3.23.0; python_version < '3.10' # orjson does not support 32bit Windows, also exclude based on Python-version -orjson==3.10.12; python_version > '3.8' +orjson==3.11.3; python_version > '3.8' # For the Windows build pefile==2024.8.26; sys_platform == 'win32' diff --git a/interfaces/Config/templates/config_cat.tmpl b/interfaces/Config/templates/config_cat.tmpl index e97f58679..5e83fb7e4 100644 --- a/interfaces/Config/templates/config_cat.tmpl +++ b/interfaces/Config/templates/config_cat.tmpl @@ -10,7 +10,7 @@
$T('explain-relFolder'): $defdir
-
class="sorting-row"> + class="sorting-row"> @@ -108,7 +108,7 @@ jQuery(document).ready(function() { jQuery('.delCat').click(function() { var theForm = jQuery(this).closest("form"); - theForm.attr("action", "delete").submit(); + theForm.attr("action", "categories/delete").submit(); }); // Add autocomplete and file-browser diff --git a/interfaces/Config/templates/config_folders.tmpl b/interfaces/Config/templates/config_folders.tmpl index 6ebeb5b74..9f5ddc001 100644 --- a/interfaces/Config/templates/config_folders.tmpl +++ b/interfaces/Config/templates/config_folders.tmpl @@ -8,7 +8,7 @@ $T('button-advanced') - + diff --git a/interfaces/Config/templates/config_general.tmpl b/interfaces/Config/templates/config_general.tmpl index 84ffef906..1d31c79ff 100644 --- a/interfaces/Config/templates/config_general.tmpl +++ b/interfaces/Config/templates/config_general.tmpl @@ -8,7 +8,7 @@ $T('button-advanced') - + diff --git a/interfaces/Config/templates/config_special.tmpl b/interfaces/Config/templates/config_special.tmpl index bf11db7d0..559f67484 100644 --- a/interfaces/Config/templates/config_special.tmpl +++ b/interfaces/Config/templates/config_special.tmpl @@ -3,7 +3,7 @@
- +

$T('explain-special')

diff --git a/interfaces/Config/templates/config_switches.tmpl b/interfaces/Config/templates/config_switches.tmpl index d1130d53c..3e89351f4 100644 --- a/interfaces/Config/templates/config_switches.tmpl +++ b/interfaces/Config/templates/config_switches.tmpl @@ -8,7 +8,7 @@ $T('button-advanced')
- + diff --git a/requirements.txt b/requirements.txt index 0ff5f53c2..1ad7a4c9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,11 +33,11 @@ babelfish==0.6.1 rebulk==3.2.0 # New for starlette/uvicorn -starlette==0.47.3 -anyio==4.10.0 +starlette==0.48.0 +anyio==4.11.0 sniffio==1.3.1 -uvicorn==0.35.0 -click==8.1.7 +uvicorn==0.37.0 +click==8.3.0 h11==0.16.0 python-multipart==0.0.20 httptools==0.6.4 diff --git a/sabnzbd/interface.py b/sabnzbd/interface.py index d9227f4ac..6986393f5 100644 --- a/sabnzbd/interface.py +++ b/sabnzbd/interface.py @@ -136,7 +136,6 @@ def secured_expose( @functools.wraps(wrap_func) async def internal_wrap(*args, **kwargs): - # All good, cool! return await wrap_func(*args, **kwargs) return internal_wrap @@ -963,7 +962,7 @@ async def config_general_save(request: Request): # Handle general options for kw in GENERAL_LIST: if msg := config.get_config("misc", kw).set(request.query_params.get(kw)): - return badParameterResponse(msg, ajax=request.query_params.get("ajax")) + return sabnzbd.api.report(request.query_params, error=msg) # Handle special options cfg.password.set(request.query_params.get("password")) @@ -972,13 +971,7 @@ async def config_general_save(request: Request): change_web_dir(web_dir) config.save_config() - - # Update CherryPy authentication - set_auth(cherrypy.config) - if request.query_params.get("ajax"): - return sabnzbd.api.report(data={"success": True, "restart_req": sabnzbd.RESTART_REQ}) - else: - raise Raiser("/config") + return sabnzbd.api.report(request.query_params, data={"success": True, "restart_req": sabnzbd.RESTART_REQ}) @secured_expose(route="/config/general/uploadConfig", check_api_key=True, check_configlock=True) @@ -1706,64 +1699,76 @@ class ConfigScheduling: ############################################################################## -class ConfigCats: - def __init__(self, root): - self.__root = root +# Page definitions - Config - Categories +############################################################################## - @secured_expose(check_configlock=True) - def index(request: Request): - conf = build_header(sabnzbd.WEB_DIR_CONFIG) +@secured_expose(route="/config/categories", check_configlock=True) +async def index_config_categories(request: Request): + conf = build_header(sabnzbd.WEB_DIR_CONFIG) - conf["scripts"] = list_scripts(default=True) - conf["defdir"] = cfg.complete_dir.get_clipped_path() + conf["scripts"] = list_scripts(default=True) + conf["defdir"] = cfg.complete_dir.get_clipped_path() - categories = config.get_ordered_categories() - new_cat_order = max(cat["order"] for cat in categories) + 1 + categories = config.get_ordered_categories() + new_cat_order = max(cat["order"] for cat in categories) + 1 - # Add empty line to add new categories - empty = { - "name": "", - "order": str(new_cat_order), - "pp": "-1", - "script": "", - "dir": "", - "newzbin": "", - "priority": DEFAULT_PRIORITY, - } - categories.insert(1, empty) - conf["slotinfo"] = categories + # Add empty line to add new categories + empty = { + "name": "", + "order": str(new_cat_order), + "pp": "-1", + "script": "", + "dir": "", + "newzbin": "", + "priority": DEFAULT_PRIORITY, + } + categories.insert(1, empty) + conf["slotinfo"] = categories - return template_filtered_response( - file=os.path.join(sabnzbd.WEB_DIR_CONFIG, "config_cat.tmpl"), - search_list=conf, - ) + return template_filtered_response( + file=os.path.join(sabnzbd.WEB_DIR_CONFIG, "config_cat.tmpl"), + search_list=conf, + ) - @secured_expose(check_api_key=True, check_configlock=True) - def delete(request: Request): - kwargs["section"] = "categories" - kwargs["keyword"] = request.query_params.get("name") - del_from_section(kwargs) - raise Raiser(self.__root) - @secured_expose(check_api_key=True, check_configlock=True) - def save(request: Request): - name = request.query_params.get("name", "*") - newname = request.query_params.get("newname", "") - if name == "*": - newname = name +@secured_expose(route="/config/categories/delete", check_api_key=True, check_configlock=True) +async def config_categories_delete(request: Request): + kw = { + "section": "categories", + "keyword": request.query_params.get("name"), + } + del_from_section(kw) + # Keep API response pattern consistent + return sabnzbd.api.report(request.query_params) - if newname: - # Check if this cat-dir is not sub-folder of incomplete - if same_directory(cfg.download_dir.get_path(), real_path(cfg.complete_dir.get_path(), kwargs["dir"])): - return T("Category folder cannot be a subfolder of the Temporary Download Folder.") - # Delete current one and replace with new one - if name: - config.delete("categories", name) - config.ConfigCat(newname.lower(), kwargs) +@secured_expose(route="/config/categories/save", check_api_key=True, check_configlock=True) +async def config_categories_save(request: Request): + name = request.query_params.get("name", "*") + newname = request.query_params.get("newname", "") + if name == "*": + newname = name - config.save_config() - raise Raiser(self.__root) + if newname: + # Build params dict for category configuration + cat_params = dict(request.query_params) + # Validate directory not under incomplete + if same_directory( + cfg.download_dir.get_path(), + real_path(cfg.complete_dir.get_path(), cat_params.get("dir", "")), + ): + return sabnzbd.api.report( + request.query_params, + error=T("Category folder cannot be a subfolder of the Temporary Download Folder."), + ) + + # Delete current one and replace with new one + if name: + config.delete("categories", name) + config.ConfigCat(newname.lower(), cat_params) + + config.save_config() + return sabnzbd.api.report(request.query_params) ##############################################################################