diff --git a/comet/scrapers/prowlarr.py b/comet/scrapers/prowlarr.py index 73f8f9cd..5b06fed3 100644 --- a/comet/scrapers/prowlarr.py +++ b/comet/scrapers/prowlarr.py @@ -77,16 +77,42 @@ async def process_torrent( return torrents +async def fetch_prowlarr_results( + session: aiohttp.ClientSession, indexer_id: int, query: str +): + """ + Fetches results from a single Prowlarr indexer with a configured timeout. + """ + try: + search_url = f"{settings.INDEXER_MANAGER_URL}/api/v1/search?query={query}&indexerIds={indexer_id}&type=search" + response = await session.get( + search_url, + headers={"X-Api-Key": settings.INDEXER_MANAGER_API_KEY}, + # This is the key change, mirroring the Jackett implementation + timeout=aiohttp.ClientTimeout(total=settings.INDEXER_MANAGER_TIMEOUT), + ) + response.raise_for_status() + return await response.json() + except asyncio.TimeoutError: + logger.warning(f"Prowlarr search timed out for indexer ID {indexer_id}") + return [] + except aiohttp.ClientError as e: + logger.warning(f"Prowlarr search failed for indexer ID {indexer_id}: {e}") + return [] + + async def get_prowlarr(manager, session: aiohttp.ClientSession, title: str, seen: set): torrents = [] try: + # Get all configured indexer IDs from Prowlarr indexers = [indexer.lower() for indexer in settings.INDEXER_MANAGER_INDEXERS] - - get_indexers = await session.get( + + 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() indexers_id = [] for indexer in get_indexers: @@ -96,12 +122,17 @@ async def get_prowlarr(manager, session: aiohttp.ClientSession, title: str, seen ): indexers_id.append(indexer["id"]) - response = await session.get( - f"{settings.INDEXER_MANAGER_URL}/api/v1/search?query={title}&indexerIds={'&indexerIds='.join(str(indexer_id) for indexer_id in indexers_id)}&type=search", - headers={"X-Api-Key": settings.INDEXER_MANAGER_API_KEY}, - ) - response = await response.json() + # Create a search task for each indexer ID + 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] + # Process the combined results torrent_tasks = [] for result in response: if result["infoUrl"] in seen: diff --git a/comet/utils/torrent.py b/comet/utils/torrent.py index c28da50b..a28daa5a 100644 --- a/comet/utils/torrent.py +++ b/comet/utils/torrent.py @@ -50,6 +50,10 @@ async def download_torrent(session: aiohttp.ClientSession, url: str): ).decode("utf-8") return (None, info_hash, location) return (None, None, None) + except asyncio.TimeoutError: + # Log the specific message for a timeout + logger.warning(f"Timeout while trying to download torrent from {url}") + return (None, None, None) except Exception as e: logger.warning( f"Failed to download torrent from {url}: {e} (in most cases, you can ignore this error)" diff --git a/pyproject.toml b/pyproject.toml index 2443313a..986d100d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,6 @@ dependencies = [ "orjson", "pydantic-settings", "python-multipart", - "rank-torrent-name", + "rank-torrent-name==1.9.0", "uvicorn", ]