Skip to content

Prowlarr parallel requests and rank torrent name fix#362

Open
lucamaia9 wants to merge 5 commits intog0ldyy:mainfrom
lucamaia9:main
Open

Prowlarr parallel requests and rank torrent name fix#362
lucamaia9 wants to merge 5 commits intog0ldyy:mainfrom
lucamaia9:main

Conversation

@lucamaia9
Copy link

@lucamaia9 lucamaia9 commented Oct 18, 2025

Prowlarr scrapper can use parallel requests so the timeout works returning the indexers that successfully returned values. Also fix to "rank-torrent-name==1.9.0", as the new version (1.9.2) causes import issues and would require further code changes.

Summary by CodeRabbit

  • Bug Fixes

    • Implemented explicit timeout handling for search requests and torrent downloads to prevent hanging operations and improve overall reliability across all operations
  • Refactor

    • Restructured search indexing system to perform concurrent operations for significantly improved search performance and faster result aggregation
  • Chores

    • Pinned a dependency version for improved consistency and stability across different deployment environments

@coderabbitai
Copy link

coderabbitai bot commented Oct 18, 2025

Walkthrough

The pull request refactors Prowlarr search execution from a single aggregated HTTP request to parallel per-indexer concurrent requests with individual timeouts, adds explicit asyncio.TimeoutError handling to torrent downloads, and pins the rank-torrent-name dependency to version 1.9.0.

Changes

Cohort / File(s) Summary
Prowlarr concurrent search
comet/scrapers/prowlarr.py
Introduces fetch_prowlarr_results() function to fetch results from individual indexers with per-call timeout and error handling. Modifies get_prowlarr() to use asyncio.gather() for concurrent per-indexer searches instead of single aggregated request.
Timeout error handling
comet/utils/torrent.py
Adds explicit except clause for asyncio.TimeoutError in download_torrent() to log timeouts and return (None, None, None), separating timeout handling from general exception path.
Dependency pinning
pyproject.toml
Pins rank-torrent-name dependency to exact version 1.9.0 (previously unconstrained).

Sequence Diagram

sequenceDiagram
    participant User
    participant get_prowlarr
    participant Fetch as fetch_prowlarr_results
    participant Prowlarr as Prowlarr API
    participant Results as Result Aggregation

    Note over User,Results: Previous Flow (Single Request)
    User->>get_prowlarr: search query
    get_prowlarr->>Prowlarr: single aggregated search
    Prowlarr-->>get_prowlarr: combined results
    get_prowlarr->>Results: process & filter
    Results-->>User: final results

    Note over User,Results: New Flow (Parallel Requests)
    User->>get_prowlarr: search query
    get_prowlarr->>get_prowlarr: collect indexer IDs
    rect rgb(200, 220, 240)
        Note over get_prowlarr,Prowlarr: asyncio.gather() — parallel per-indexer requests
        par Indexer 1
            get_prowlarr->>Fetch: indexer_id=1, query
            Fetch->>Prowlarr: search (timeout)
            Prowlarr-->>Fetch: results
            Fetch-->>get_prowlarr: results[1]
        and Indexer 2
            get_prowlarr->>Fetch: indexer_id=2, query
            Fetch->>Prowlarr: search (timeout)
            Prowlarr-->>Fetch: results
            Fetch-->>get_prowlarr: results[2]
        and Indexer N
            get_prowlarr->>Fetch: indexer_id=N, query
            Fetch->>Prowlarr: search (timeout)
            Prowlarr-->>Fetch: results
            Fetch-->>get_prowlarr: results[N]
        end
    end
    get_prowlarr->>Results: flatten & aggregate
    get_prowlarr->>Results: process & filter
    Results-->>User: final results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The changes introduce new async concurrency logic in prowlarr.py requiring careful review of asyncio.gather() patterns and error propagation, while the timeout handling addition and dependency pin are straightforward. Mixed complexity across multiple areas justifies moderate effort.

Poem

🐰 Hop, hop—parallelism's the way!
No more waiting for searches to sway,
Each indexer springs forth with its might,
While timeouts catch errors outright,
Dependencies locked, we run smooth today!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Prowlarr parallel requests and rank torrent name fix" accurately reflects both major changes in the pull request: the refactoring of the Prowlarr scraper to use parallel concurrent requests instead of a single aggregated request, and the pinning of the rank-torrent-name dependency to version 1.9.0. The title is clear, specific, and concise at 52 characters, avoiding vague terms and effectively communicating the primary objective (parallel requests) first before the secondary change. Both components mentioned in the title are substantive parts of the actual changeset and directly relate to the PR's stated objectives.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f29ec84 and 0018ca1.

📒 Files selected for processing (3)
  • comet/scrapers/prowlarr.py (2 hunks)
  • comet/utils/torrent.py (1 hunks)
  • pyproject.toml (1 hunks)
🔇 Additional comments (3)
comet/utils/torrent.py (1)

53-56: LGTM! Explicit timeout handling improves observability.

The dedicated asyncio.TimeoutError handler provides clear logging for timeout scenarios, which complements the parallel request changes in the Prowlarr scraper.

comet/scrapers/prowlarr.py (1)

80-102: LGTM! Well-structured per-indexer fetch with proper timeout and error handling.

The function correctly isolates failures to individual indexers and returns partial results when some indexers timeout or fail.

pyproject.toml (1)

26-26: Pinned version is valid and secure.

Verification confirms that rank-torrent-name version 1.9.0 exists on PyPI and has no known security vulnerabilities.

Comment on lines +110 to +115
get_indexers_response = await session.get(
f"{settings.INDEXER_MANAGER_URL}/api/v1/indexer",
headers={"X-Api-Key": settings.INDEXER_MANAGER_API_KEY},
timeout=aiohttp.ClientTimeout(total=settings.INDEXER_MANAGER_TIMEOUT),
)
get_indexers = await get_indexers.json()
get_indexers = await get_indexers_response.json()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add status check before parsing JSON response.

The code calls json() without verifying the HTTP response status. If the indexer list fetch fails with a non-2xx status, this could raise an exception.

Apply this diff to check the status:

 get_indexers_response = await session.get(
     f"{settings.INDEXER_MANAGER_URL}/api/v1/indexer",
     headers={"X-Api-Key": settings.INDEXER_MANAGER_API_KEY},
     timeout=aiohttp.ClientTimeout(total=settings.INDEXER_MANAGER_TIMEOUT),
 )
+get_indexers_response.raise_for_status()
 get_indexers = await get_indexers_response.json()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
get_indexers_response = await session.get(
f"{settings.INDEXER_MANAGER_URL}/api/v1/indexer",
headers={"X-Api-Key": settings.INDEXER_MANAGER_API_KEY},
timeout=aiohttp.ClientTimeout(total=settings.INDEXER_MANAGER_TIMEOUT),
)
get_indexers = await get_indexers.json()
get_indexers = await get_indexers_response.json()
get_indexers_response = await session.get(
f"{settings.INDEXER_MANAGER_URL}/api/v1/indexer",
headers={"X-Api-Key": settings.INDEXER_MANAGER_API_KEY},
timeout=aiohttp.ClientTimeout(total=settings.INDEXER_MANAGER_TIMEOUT),
)
get_indexers_response.raise_for_status()
get_indexers = await get_indexers_response.json()
🤖 Prompt for AI Agents
In comet/scrapers/prowlarr.py around lines 110 to 115, the code calls
get_indexers_response.json() without checking the HTTP status; add a status
check on get_indexers_response.status and handle non-2xx responses before
parsing JSON (e.g., raise or log an error and return/raise an exception) so you
only call .json() when the response is successful; ensure you include the status
and response text/body in the error handling for debugging and use the same
timeout and headers already present.

Comment on lines +126 to 134
tasks = [
fetch_prowlarr_results(session, indexer_id, title)
for indexer_id in indexers_id
]
all_results_lists = await asyncio.gather(*tasks)

# Flatten the list of lists into a single list of results
response = [result for sublist in all_results_lists for result in sublist]

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add return_exceptions=True to asyncio.gather for graceful degradation.

Without return_exceptions=True, an unexpected exception from any indexer task (not caught by fetch_prowlarr_results) will cancel all other tasks and lose partial results, defeating the purpose of parallel requests with timeout tolerance.

Apply this diff:

 tasks = [
     fetch_prowlarr_results(session, indexer_id, title)
     for indexer_id in indexers_id
 ]
-all_results_lists = await asyncio.gather(*tasks)
+all_results_lists = await asyncio.gather(*tasks, return_exceptions=True)

 # Flatten the list of lists into a single list of results
-response = [result for sublist in all_results_lists for result in sublist]
+response = [
+    result 
+    for sublist in all_results_lists 
+    if not isinstance(sublist, Exception)
+    for result in sublist
+]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tasks = [
fetch_prowlarr_results(session, indexer_id, title)
for indexer_id in indexers_id
]
all_results_lists = await asyncio.gather(*tasks)
# Flatten the list of lists into a single list of results
response = [result for sublist in all_results_lists for result in sublist]
tasks = [
fetch_prowlarr_results(session, indexer_id, title)
for indexer_id in indexers_id
]
all_results_lists = await asyncio.gather(*tasks, return_exceptions=True)
# Flatten the list of lists into a single list of results
response = [
result
for sublist in all_results_lists
if not isinstance(sublist, Exception)
for result in sublist
]
🤖 Prompt for AI Agents
In comet/scrapers/prowlarr.py around lines 126 to 134, asyncio.gather is called
without return_exceptions=True which will cancel all tasks if any task raises;
change the gather call to asyncio.gather(*tasks, return_exceptions=True) and
then post-process all_results_lists to filter out exceptions (e.g., iterate
results, if isinstance(item, Exception) log or ignore it, otherwise treat as the
expected list), finally flatten only the successful result lists into response
so partial results are preserved.

@g0ldyy
Copy link
Owner

g0ldyy commented Oct 18, 2025

rtn fixed in 831a0cf
I will give a try to your prowlarr PR in a few hours

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants