Files
shelfmark/shelfmark/release_sources/newznab/settings.py
T
CaliBrain 2b8b35bb52 fix(newznab): make indexer book categories configurable (#1214)
Newznab searches hardcoded category 7000 for ebooks and 3030 for
audiobooks,
so indexers using custom IDs returned no results or the wrong ones. Add
NEWZNAB_EBOOK_CATEGORIES and NEWZNAB_AUDIOBOOK_CATEGORIES (tag lists,
defaulting to 7000 and 3030) and resolve the search categories from
config.

Values are parsed leniently — list or comma/whitespace separated,
non-numeric
entries skipped, duplicates dropped — and fall back to the standard IDs
when
empty, so a cleared field can't silently widen the search to every
category.
NEWZNAB_AUTO_EXPAND remains the way to do that on purpose.

Results carrying a custom ID outside the standard 7000-7999 / 3030
ranges were
typed as "other", which routed custom-category audiobooks as ebooks.
Trust the
searched content type when a result carries a category we explicitly
asked for.

Also drop the unused NEWZNAB_BOOKS / NEWZNAB_AUDIOBOOKS constants from
api.py —
a third copy of the same hardcoding.

Closes #1208
2026-08-15 11:48:10 -04:00

122 lines
4.2 KiB
Python

"""Newznab settings registration."""
from typing import Any
from shelfmark.core.settings_registry import (
ActionButton,
CheckboxField,
HeadingField,
PasswordField,
SettingsField,
TagListField,
TextField,
register_settings,
)
from shelfmark.core.utils import normalize_http_url
def _test_newznab_connection(current_values: dict[str, Any] | None = None) -> dict[str, Any]:
"""Test the Newznab connection using current form values."""
from shelfmark.core.config import config
from shelfmark.release_sources.newznab.api import NewznabClient
current_values = current_values or {}
raw_url = str(current_values.get("NEWZNAB_URL") or config.get("NEWZNAB_URL", "") or "")
api_key = str(current_values.get("NEWZNAB_API_KEY") or config.get("NEWZNAB_API_KEY", "") or "")
if not raw_url:
return {"success": False, "message": "Newznab URL is required"}
url = normalize_http_url(raw_url)
if not url:
return {"success": False, "message": "Newznab URL is invalid"}
try:
client = NewznabClient(url, api_key)
success, message = client.test_connection()
except Exception as e: # noqa: BLE001 — surface any unexpected error to the UI
return {"success": False, "message": f"Connection failed: {e!s}"}
else:
return {"success": success, "message": message}
@register_settings(
name="newznab_config",
display_name="Newznab",
icon="download",
order=42,
)
def newznab_config_settings() -> list[SettingsField]:
"""Newznab connection settings."""
return [
HeadingField(
key="newznab_heading",
title="Newznab Integration",
description=(
"Search for books via any Newznab-compatible indexer or aggregator "
"(e.g. NZBHydra2, NZBGeek, Drunkenslug)."
),
),
CheckboxField(
key="NEWZNAB_ENABLED",
label="Enable Newznab source",
default=False,
description="Enable searching for books via a Newznab-compatible indexer",
),
TextField(
key="NEWZNAB_URL",
label="Newznab URL",
description="Base URL of your Newznab indexer or aggregator",
placeholder="http://nzbhydra:5076",
required=True,
show_when={"field": "NEWZNAB_ENABLED", "value": True},
),
PasswordField(
key="NEWZNAB_API_KEY",
label="API Key",
description="Your Newznab API key (leave blank if not required)",
required=False,
show_when={"field": "NEWZNAB_ENABLED", "value": True},
),
ActionButton(
key="test_newznab",
label="Test Connection",
description="Verify your Newznab configuration",
style="primary",
callback=_test_newznab_connection,
show_when={"field": "NEWZNAB_ENABLED", "value": True},
),
TagListField(
key="NEWZNAB_EBOOK_CATEGORIES",
label="Ebook Categories",
description=(
"Newznab category IDs searched for ebooks. Most indexers use the standard 7000, "
"but some use custom IDs. Leave empty to use 7000."
),
placeholder="7000",
default=["7000"],
normalize_urls=False,
show_when={"field": "NEWZNAB_ENABLED", "value": True},
),
TagListField(
key="NEWZNAB_AUDIOBOOK_CATEGORIES",
label="Audiobook Categories",
description=(
"Newznab category IDs searched for audiobooks. Most indexers use the standard "
"3030, but some use custom IDs. Leave empty to use 3030."
),
placeholder="3030",
default=["3030"],
normalize_urls=False,
show_when={"field": "NEWZNAB_ENABLED", "value": True},
),
CheckboxField(
key="NEWZNAB_AUTO_EXPAND",
label="Auto-expand search on no results",
default=False,
description="Automatically retry search without category filtering if no results are found",
show_when={"field": "NEWZNAB_ENABLED", "value": True},
),
]