From 3e35bca73c24e662ecaccd9873c2f0b183cb0265 Mon Sep 17 00:00:00 2001 From: Uyanide Date: Sat, 4 Apr 2026 09:02:31 +0200 Subject: [PATCH] perf: parallelize lrclib search queries --- lrx_cli/fetchers/lrclib_search.py | 39 ++++++++++++++++++------------- pyproject.toml | 2 +- uv.lock | 2 +- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/lrx_cli/fetchers/lrclib_search.py b/lrx_cli/fetchers/lrclib_search.py index 6cabb51..f65c52e 100644 --- a/lrx_cli/fetchers/lrclib_search.py +++ b/lrx_cli/fetchers/lrclib_search.py @@ -9,6 +9,7 @@ Used when metadata is incomplete (no album or duration) but title is available. Selects the best match by duration when track length is known. """ +import asyncio import httpx from typing import Optional from loguru import logger @@ -80,29 +81,35 @@ class LrclibSearchFetcher(BaseFetcher): try: async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client: - for params in queries: + + async def _query(params: dict[str, str]) -> tuple[list[dict], bool]: url = f"{LRCLIB_SEARCH_URL}?{urlencode(params)}" logger.debug(f"LRCLIB-search: query {params}") - resp = await client.get(url, headers={"User-Agent": UA_LRX}) - + try: + resp = await client.get(url, headers={"User-Agent": UA_LRX}) + except httpx.HTTPError as e: + logger.error(f"LRCLIB-search: HTTP error: {e}") + return [], True if resp.status_code != 200: logger.error(f"LRCLIB-search: API returned {resp.status_code}") - had_error = True - continue - + return [], True data = resp.json() if not isinstance(data, list): - continue + return [], False + return [item for item in data if isinstance(item, dict)], False - for item in data: - if not isinstance(item, dict): - continue - item_id = item.get("id") - if item_id is not None and item_id in seen_ids: - continue - if item_id is not None: - seen_ids.add(item_id) - candidates.append(item) + all_results = await asyncio.gather(*(_query(p) for p in queries)) + + for items, err in all_results: + if err: + had_error = True + for item in items: + item_id = item.get("id") + if item_id is not None and item_id in seen_ids: + continue + if item_id is not None: + seen_ids.add(item_id) + candidates.append(item) if not candidates: if had_error: diff --git a/pyproject.toml b/pyproject.toml index b0a2b93..ea32fc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "lrx-cli" -version = "0.4.2" +version = "0.4.3" description = "Fetch line-synced lyrics for your music player." readme = "README.md" requires-python = ">=3.13" diff --git a/uv.lock b/uv.lock index 1a6cd93..8b80d53 100644 --- a/uv.lock +++ b/uv.lock @@ -153,7 +153,7 @@ wheels = [ [[package]] name = "lrx-cli" -version = "0.4.1" +version = "0.4.2" source = { editable = "." } dependencies = [ { name = "cyclopts" },