Prowlarr non-category search fallback, bypass optimizations, and code cleanup (#379)

- Prowlarr: Added automatic fallback to search without category filter
when indexers return no results with book category (7000), improving
compatibility with indexers that don't support category filtering
- Prowlarr: Hide language filter in UI since Prowlarr has unreliable
language metadata
- Bypass: Refactored internal bypasser with code cleanup, extracted
helper functions, and added health check capability
  - Bypass: Added fingerprint module for screen size handling
- qBittorrent: Fixed connection test to use web API version instead of
app version
- Frontend: Added supported_filters config to control which filters
display per source
- Auth: Improved CWA database path validation (now uses Path object
properly)
This commit is contained in:
Alex
2025-12-30 23:19:25 +00:00
committed by GitHub
parent e870ada452
commit 91dd479edb
32 changed files with 881 additions and 800 deletions
+22 -10
View File
@@ -129,18 +129,22 @@ class TestSupportedFormats:
class TestContentTypeRouting:
"""Tests for content-type based directory routing."""
def test_download_paths_default_to_ingest_dir(self):
"""All content types should default to INGEST_DIR if not specified."""
from cwa_book_downloader.config.env import DOWNLOAD_PATHS, INGEST_DIR
def test_get_ingest_dir_returns_path(self):
"""get_ingest_dir should return a Path for all content types."""
from cwa_book_downloader.core.utils import get_ingest_dir, CONTENT_TYPES
# When no specific paths are set, all should default to INGEST_DIR
for content_type, path in DOWNLOAD_PATHS.items():
# Path should be INGEST_DIR or a custom path
# Default (no content type) should return a Path
default_path = get_ingest_dir()
assert isinstance(default_path, Path)
# All content types should return a Path
for content_type in CONTENT_TYPES:
path = get_ingest_dir(content_type)
assert isinstance(path, Path)
def test_content_type_routing_keys(self):
"""All expected content types should be present."""
from cwa_book_downloader.config.env import DOWNLOAD_PATHS
def test_content_types_list_complete(self):
"""All expected content types should be present in CONTENT_TYPES."""
from cwa_book_downloader.core.utils import CONTENT_TYPES
expected_types = [
"book (fiction)",
@@ -155,7 +159,15 @@ class TestContentTypeRouting:
]
for content_type in expected_types:
assert content_type in DOWNLOAD_PATHS, f"Missing content type: {content_type}"
assert content_type in CONTENT_TYPES, f"Missing content type: {content_type}"
def test_get_ingest_dir_unknown_type_returns_default(self):
"""Unknown content types should return the default ingest directory."""
from cwa_book_downloader.core.utils import get_ingest_dir
default_path = get_ingest_dir()
unknown_path = get_ingest_dir("unknown content type")
assert unknown_path == default_path
# =============================================================================
+12 -12
View File
@@ -10,9 +10,9 @@ import pytest
from cwa_book_downloader.release_sources.prowlarr.source import (
_parse_size,
_extract_format,
_get_protocol,
_extract_language,
)
from cwa_book_downloader.release_sources.prowlarr.utils import get_protocol_display
class TestParseSize:
@@ -116,51 +116,51 @@ class TestExtractFormat:
assert _extract_format("Literal Translation") is None
class TestGetProtocol:
"""Tests for the _get_protocol function."""
class TestGetProtocolDisplay:
"""Tests for the get_protocol_display function."""
def test_get_protocol_from_protocol_field_torrent(self):
"""Test extracting torrent protocol from protocol field."""
result = {"protocol": "torrent", "downloadUrl": "https://example.com"}
assert _get_protocol(result) == "torrent"
assert get_protocol_display(result) == "torrent"
def test_get_protocol_from_protocol_field_usenet(self):
"""Test extracting usenet protocol from protocol field."""
result = {"protocol": "usenet", "downloadUrl": "https://example.com"}
assert _get_protocol(result) == "nzb"
assert get_protocol_display(result) == "nzb"
def test_get_protocol_from_magnet_url(self):
"""Test inferring torrent from magnet URL."""
result = {"downloadUrl": "magnet:?xt=urn:btih:abc123"}
assert _get_protocol(result) == "torrent"
assert get_protocol_display(result) == "torrent"
def test_get_protocol_from_torrent_url(self):
"""Test inferring torrent from .torrent URL."""
result = {"downloadUrl": "https://example.com/file.torrent"}
assert _get_protocol(result) == "torrent"
assert get_protocol_display(result) == "torrent"
def test_get_protocol_from_nzb_url(self):
"""Test inferring NZB from .nzb URL."""
result = {"downloadUrl": "https://example.com/file.nzb"}
assert _get_protocol(result) == "nzb"
assert get_protocol_display(result) == "nzb"
def test_get_protocol_fallback_to_magnet_url(self):
"""Test fallback to magnetUrl field."""
result = {"magnetUrl": "magnet:?xt=urn:btih:abc123"}
assert _get_protocol(result) == "torrent"
assert get_protocol_display(result) == "torrent"
def test_get_protocol_unknown(self):
"""Test unknown protocol for unclear URLs."""
result = {"downloadUrl": "https://example.com/download"}
assert _get_protocol(result) == "unknown"
assert get_protocol_display(result) == "unknown"
def test_get_protocol_case_insensitive(self):
"""Test protocol detection is case insensitive."""
result = {"protocol": "TORRENT"}
assert _get_protocol(result) == "torrent"
assert get_protocol_display(result) == "torrent"
result = {"protocol": "Usenet"}
assert _get_protocol(result) == "nzb"
assert get_protocol_display(result) == "nzb"
class TestExtractLanguage: