LRCLib: Improve exception handling

This commit is contained in:
Šarūnas Nejus 2024-08-27 18:14:38 +01:00
parent 8d4a569291
commit a398fbe62d
No known key found for this signature in database
GPG key ID: DD28F6704DBE3435
2 changed files with 49 additions and 18 deletions

View file

@ -284,6 +284,19 @@ class LRCLibItem(TypedDict):
class LRCLib(Backend):
base_url = "https://lrclib.net/api/search"
def warn(self, message: str, *args) -> None:
"""Log a warning message with the class name."""
self._log.warning(f"{self.__class__.__name__}: {message}", *args)
def fetch_json(self, *args, **kwargs):
"""Wrap the request method to raise an exception on HTTP errors."""
kwargs.setdefault("timeout", 10)
kwargs.setdefault("headers", {"User-Agent": USER_AGENT})
r = requests.get(*args, **kwargs)
r.raise_for_status()
return r.json()
@staticmethod
def get_rank(
target_duration: float, item: LRCLibItem
@ -327,25 +340,21 @@ class LRCLib(Backend):
params["duration"] = length
try:
response = requests.get(
self.base_url,
params=params,
timeout=10,
)
data: list[LRCLibItem] = response.json()
except (requests.RequestException, json.decoder.JSONDecodeError) as exc:
self._log.debug("LRCLib API request failed: {0}", exc)
return None
data = self.fetch_json(self.base_url, params=params)
except requests.JSONDecodeError:
self.warn("Could not decode response JSON data")
except requests.RequestException as exc:
self.warn("Request error: {}", exc)
else:
if data:
item = self.pick_lyrics(length, data)
if not data:
return None
if self.config["synced"] and (synced := item["syncedLyrics"]):
return synced
item = self.pick_lyrics(length, data)
return item["plainLyrics"]
if self.config["synced"] and (synced_lyrics := item["syncedLyrics"]):
return synced_lyrics
return item["plainLyrics"]
return None
class DirectBackend(Backend):

View file

@ -14,7 +14,9 @@
"""Tests for the 'lyrics' plugin."""
import re
from functools import partial
from http import HTTPStatus
import pytest
@ -360,8 +362,12 @@ class TestLRCLibLyrics(LyricsBackendTest):
return "lrclib"
@pytest.fixture
def fetch_lyrics(self, backend, requests_mock, response_data):
requests_mock.get(backend.base_url, json=response_data)
def request_kwargs(self, response_data):
return {"json": response_data}
@pytest.fixture
def fetch_lyrics(self, backend, requests_mock, request_kwargs):
requests_mock.get(backend.base_url, **request_kwargs)
return partial(backend.fetch, "la", "la", "la", self.ITEM_DURATION)
@ -407,3 +413,19 @@ class TestLRCLibLyrics(LyricsBackendTest):
@pytest.mark.parametrize("plugin_config", [{"synced": True}])
def test_fetch_lyrics(self, fetch_lyrics, expected_lyrics):
assert fetch_lyrics() == expected_lyrics
@pytest.mark.parametrize(
"request_kwargs, expected_log_match",
[
(
{"status_code": HTTPStatus.BAD_GATEWAY},
r"LRCLib: Request error: 502",
),
({"text": "invalid"}, r"LRCLib: Could not decode.*JSON"),
],
)
def test_error(self, caplog, fetch_lyrics, expected_log_match):
assert fetch_lyrics() is None
assert caplog.messages
assert (last_log := caplog.messages[-1])
assert re.search(expected_log_match, last_log, re.I)