fix(network): apply HTTP proxy to Anna's Archive download-count lookups (#1686)

Fixes #1685

`_fetch_download_count_inline` fetches `/dyn/md5/inline_info/<md5>` for
every search result (5 workers) through a module-level
`requests.Session`, with no `proxies=`. `get_proxies()` reads the app
config rather than the environment, so with the proxy set in Settings →
Network the search page and the bypasser used it while these requests
left from the host's own IP, about 50 per search. It now passes
`network.get_proxies(url)` on each request, as the Libgen fast path in
the same file already does. The session stays module-level for
keep-alive, and proxies are resolved per request, so a settings change
applies without a restart.

Verification (macOS, `uv run --frozen pytest`):
- New `tests/direct_download/test_download_count_proxy.py` passes. With
the source change reverted it fails (`proxies` is `None`).
- `tests/direct_download`: 135 passed, including
`test_download_count_reuse.py`.
- `ruff check` and `ruff format --check` on the changed files pass.
- Not re-run live with this branch built. The behaviour before the
change was observed on a running v1.6.2 container: 5 sockets from the
`ThreadPoolExecutor` threads straight to the AA mirror's IP on :443, and
1 to the proxy.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Bug Fixes**
* Anna’s Archive download counts are now retrieved using the configured
network proxies. This improves access to download-count information in
environments where requests need a proxy, while preserving the existing
response handling and timeout behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Kris AndersonandClaude Sonnet 5.5 authored and GitHub committed 2026-11-09 17:58:01 -06:00
1 parent bb13fae0bb
commit 0d97110f01
2 files changed
+49 -1

No files matched your search

@@ -840,7 +840,12 @@ def _fetch_download_count_inline(book_id: str) -> int | None:
return cached return cached
try: try:
url = f"{network.get_aa_base_url()}/dyn/md5/inline_info/{book_id}" url = f"{network.get_aa_base_url()}/dyn/md5/inline_info/{book_id}"
resp = _inline_info_session.get(url, timeout=5, headers={"Accept": "application/json"}) resp = _inline_info_session.get(
url,
timeout=5,
headers={"Accept": "application/json"},
proxies=network.get_proxies(url),
)
if resp.status_code == 200: if resp.status_code == 200:
data = resp.json() data = resp.json()
count = data.get("downloads_total") count = data.get("downloads_total")
@@ -0,0 +1,43 @@
"""AA download-count lookups must honor the Network proxy settings.
`_fetch_download_count_inline` runs for every search result, on its own session, so a
lookup that skips `get_proxies` sends the whole list straight from the host's IP while
the search page itself goes through the configured proxy.
"""
from unittest.mock import MagicMock
import pytest
import requests
from shelfmark.release_sources.direct_download import annas_archive as aa
_PROXIES = {"http": "http://10.0.80.3:8118", "https": "http://10.0.80.3:8118"}
@pytest.fixture(autouse=True)
def _fresh_cache(monkeypatch):
aa._download_count_cache.clear()
monkeypatch.setattr(aa.network, "get_aa_base_url", lambda: "https://annas-archive.gl")
yield
aa._download_count_cache.clear()
def test_download_count_lookup_passes_configured_proxies(monkeypatch):
asked_for: list[str] = []
captured: dict[str, object] = {}
def fake_get_proxies(url=""):
asked_for.append(url)
return _PROXIES
def fake_request(_self, _method, _url, *_args, **kwargs):
captured.update(kwargs)
return MagicMock(status_code=200, json=MagicMock(return_value={"downloads_total": 7}))
monkeypatch.setattr(aa.network, "get_proxies", fake_get_proxies)
monkeypatch.setattr(requests.Session, "request", fake_request)
assert aa._fetch_download_count_inline("aaa") == 7
assert captured.get("proxies") == _PROXIES
assert asked_for == ["https://annas-archive.gl/dyn/md5/inline_info/aaa"]