perf: parallelize lrclib search queries

This commit is contained in:
2026-04-04 09:02:31 +02:00
parent 303eeaa661
commit 1c7456c588
3 changed files with 25 additions and 18 deletions
+23 -16
View File
@@ -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. Selects the best match by duration when track length is known.
""" """
import asyncio
import httpx import httpx
from typing import Optional from typing import Optional
from loguru import logger from loguru import logger
@@ -80,29 +81,35 @@ class LrclibSearchFetcher(BaseFetcher):
try: try:
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client: 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)}" url = f"{LRCLIB_SEARCH_URL}?{urlencode(params)}"
logger.debug(f"LRCLIB-search: query {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: if resp.status_code != 200:
logger.error(f"LRCLIB-search: API returned {resp.status_code}") logger.error(f"LRCLIB-search: API returned {resp.status_code}")
had_error = True return [], True
continue
data = resp.json() data = resp.json()
if not isinstance(data, list): if not isinstance(data, list):
continue return [], False
return [item for item in data if isinstance(item, dict)], False
for item in data: all_results = await asyncio.gather(*(_query(p) for p in queries))
if not isinstance(item, dict):
continue for items, err in all_results:
item_id = item.get("id") if err:
if item_id is not None and item_id in seen_ids: had_error = True
continue for item in items:
if item_id is not None: item_id = item.get("id")
seen_ids.add(item_id) if item_id is not None and item_id in seen_ids:
candidates.append(item) continue
if item_id is not None:
seen_ids.add(item_id)
candidates.append(item)
if not candidates: if not candidates:
if had_error: if had_error:
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "lrx-cli" name = "lrx-cli"
version = "0.4.2" version = "0.4.3"
description = "Fetch line-synced lyrics for your music player." description = "Fetch line-synced lyrics for your music player."
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
Generated
+1 -1
View File
@@ -153,7 +153,7 @@ wheels = [
[[package]] [[package]]
name = "lrx-cli" name = "lrx-cli"
version = "0.4.1" version = "0.4.2"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "cyclopts" }, { name = "cyclopts" },