Patch: Various additions (#564)

- Added rich Prowlarr search results for whitelisted indexers
- Added torznab query for whitelisted indexers
- Added flags for all Prowlarr indexers
- Added completed external download retry mechanism and "locating" state
- Added client side preference storage of Book/Audiobook search
preference
- Fixed reverse proxy base URL in edge cases
- Added gevent locking for I/O operations, keeps healthcheck alive on
intensive processing operations
- Added M4A supported audiobook option
- Improved file transfer counting and logging with hardlink fallback
warnings
- Fixed proxy auth header for REMOTE_USER scenario
- Dependency tweak for internal bypasser
This commit is contained in:
Alex
2026-01-31 12:53:11 +00:00
committed by GitHub
parent 86082c999c
commit e5ccabe1ef
35 changed files with 1867 additions and 563 deletions
+24
View File
@@ -0,0 +1,24 @@
def test_bypass_tries_all_methods_before_abort(monkeypatch):
"""Regression test for issue #524: don't abort before cycling through bypass methods."""
import shelfmark.bypass.internal_bypasser as internal_bypasser
calls: list[str] = []
def _make_method(name: str):
def _method(_sb) -> bool:
calls.append(name)
return False
_method.__name__ = name
return _method
methods = [_make_method(f"m{i}") for i in range(6)]
monkeypatch.setattr(internal_bypasser, "BYPASS_METHODS", methods)
monkeypatch.setattr(internal_bypasser, "_is_bypassed", lambda _sb, escape_emojis=True: False)
monkeypatch.setattr(internal_bypasser, "_detect_challenge_type", lambda _sb: "ddos_guard")
monkeypatch.setattr(internal_bypasser.time, "sleep", lambda _seconds: None)
monkeypatch.setattr(internal_bypasser.random, "uniform", lambda _a, _b: 0)
assert internal_bypasser._bypass(object(), max_retries=10) is False
assert calls == [f"m{i}" for i in range(6)]
@@ -0,0 +1,33 @@
from unittest.mock import MagicMock
def test_update_download_status_dedupes_identical_events(monkeypatch):
import shelfmark.download.orchestrator as orchestrator
book_id = "test-book-id"
# Ensure clean module-level state
orchestrator._last_activity.clear()
orchestrator._last_status_event.clear()
mock_queue = MagicMock()
monkeypatch.setattr(orchestrator, "book_queue", mock_queue)
monkeypatch.setattr(orchestrator, "queue_status", lambda: {})
mock_ws = MagicMock()
monkeypatch.setattr(orchestrator, "ws_manager", mock_ws)
times = iter([1.0, 2.0])
monkeypatch.setattr(orchestrator.time, "time", lambda: next(times))
orchestrator.update_download_status(book_id, "resolving", "Bypassing protection...")
orchestrator.update_download_status(book_id, "resolving", "Bypassing protection...")
# Status + message should only be applied/broadcast once.
assert mock_queue.update_status.call_count == 1
assert mock_queue.update_status_message.call_count == 1
assert mock_ws.broadcast_status_update.call_count == 1
# Activity timestamp should still be updated on the duplicate keep-alive call.
assert orchestrator._last_activity[book_id] == 2.0