mirror of
https://github.com/beetbox/beets.git
synced 2025-12-07 17:16:07 +01:00
Centralize requests setup with requests.Session
Improve requests performance with requests.Session which uses connection pooling for repeated requests to the same host. Additionally, this centralizes request configuration, making sure that we use the same timeout and provide beets user agent for all requests.
This commit is contained in:
parent
c40db1034a
commit
06eac79c0d
2 changed files with 28 additions and 44 deletions
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import atexit
|
||||||
import errno
|
import errno
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
|
|
@ -24,13 +25,12 @@ import os.path
|
||||||
import re
|
import re
|
||||||
import struct
|
import struct
|
||||||
import unicodedata
|
import unicodedata
|
||||||
import warnings
|
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import cached_property, partial, total_ordering
|
from functools import cached_property, partial, total_ordering
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import TYPE_CHECKING, ClassVar, Iterable, Iterator
|
from typing import TYPE_CHECKING, ClassVar, Iterable, Iterator
|
||||||
from urllib.parse import quote, urlencode, urlparse
|
from urllib.parse import quote, urlparse
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from typing_extensions import TypedDict
|
from typing_extensions import TypedDict
|
||||||
|
|
@ -106,6 +106,22 @@ class NotFoundError(requests.exceptions.HTTPError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TimeoutSession(requests.Session):
|
||||||
|
def request(self, *args, **kwargs):
|
||||||
|
kwargs.setdefault("timeout", 10)
|
||||||
|
return super().request(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
r_session = TimeoutSession()
|
||||||
|
r_session.headers.update({"User-Agent": USER_AGENT})
|
||||||
|
|
||||||
|
|
||||||
|
@atexit.register
|
||||||
|
def close_session():
|
||||||
|
"""Close the requests session on shut down."""
|
||||||
|
r_session.close()
|
||||||
|
|
||||||
|
|
||||||
# Utilities.
|
# Utilities.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -246,21 +262,7 @@ class Backend:
|
||||||
is unreachable.
|
is unreachable.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Disable the InsecureRequestWarning that comes from using
|
r = r_session.get(url)
|
||||||
# `verify=false`.
|
|
||||||
# https://github.com/kennethreitz/requests/issues/2214
|
|
||||||
# We're not overly worried about the NSA MITMing our lyrics scraper
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.simplefilter("ignore")
|
|
||||||
r = requests.get(
|
|
||||||
url,
|
|
||||||
verify=False,
|
|
||||||
headers={
|
|
||||||
"User-Agent": USER_AGENT,
|
|
||||||
},
|
|
||||||
timeout=10,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
except requests.RequestException as exc:
|
except requests.RequestException as exc:
|
||||||
self._log.debug("lyrics request failed: {0}", exc)
|
self._log.debug("lyrics request failed: {0}", exc)
|
||||||
return
|
return
|
||||||
|
|
@ -368,9 +370,7 @@ class LRCLib(Backend):
|
||||||
|
|
||||||
def fetch_json(self, *args, **kwargs):
|
def fetch_json(self, *args, **kwargs):
|
||||||
"""Wrap the request method to raise an exception on HTTP errors."""
|
"""Wrap the request method to raise an exception on HTTP errors."""
|
||||||
kwargs.setdefault("timeout", 10)
|
r = r_session.get(*args, **kwargs)
|
||||||
kwargs.setdefault("headers", {"User-Agent": USER_AGENT})
|
|
||||||
r = requests.get(*args, **kwargs)
|
|
||||||
if r.status_code == HTTPStatus.NOT_FOUND:
|
if r.status_code == HTTPStatus.NOT_FOUND:
|
||||||
raise NotFoundError("HTTP Error: Not Found", response=r)
|
raise NotFoundError("HTTP Error: Not Found", response=r)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
|
|
@ -535,10 +535,7 @@ class Genius(SearchBackend):
|
||||||
def __init__(self, config, log):
|
def __init__(self, config, log):
|
||||||
super().__init__(config, log)
|
super().__init__(config, log)
|
||||||
self.api_key = config["genius_api_key"].as_str()
|
self.api_key = config["genius_api_key"].as_str()
|
||||||
self.headers = {
|
self.headers = {"Authorization": f"Bearer {self.api_key}"}
|
||||||
"Authorization": "Bearer %s" % self.api_key,
|
|
||||||
"User-Agent": USER_AGENT,
|
|
||||||
}
|
|
||||||
|
|
||||||
def fetch(self, artist: str, title: str, *_) -> str | None:
|
def fetch(self, artist: str, title: str, *_) -> str | None:
|
||||||
"""Fetch lyrics from genius.com
|
"""Fetch lyrics from genius.com
|
||||||
|
|
@ -573,18 +570,13 @@ class Genius(SearchBackend):
|
||||||
search_url = self.base_url + "/search"
|
search_url = self.base_url + "/search"
|
||||||
data = {"q": title + " " + artist.lower()}
|
data = {"q": title + " " + artist.lower()}
|
||||||
try:
|
try:
|
||||||
response = requests.get(
|
r = r_session.get(search_url, params=data, headers=self.headers)
|
||||||
search_url,
|
|
||||||
params=data,
|
|
||||||
headers=self.headers,
|
|
||||||
timeout=10,
|
|
||||||
)
|
|
||||||
except requests.RequestException as exc:
|
except requests.RequestException as exc:
|
||||||
self._log.debug("Genius API request failed: {0}", exc)
|
self._log.debug("Genius API request failed: {0}", exc)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return response.json()
|
return r.json()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -979,13 +971,7 @@ class LyricsPlugin(plugins.BeetsPlugin):
|
||||||
}
|
}
|
||||||
|
|
||||||
oauth_url = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
|
oauth_url = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
|
||||||
oauth_token = json.loads(
|
oauth_token = r_session.post(oauth_url, params=params).json()
|
||||||
requests.post(
|
|
||||||
oauth_url,
|
|
||||||
data=urlencode(params),
|
|
||||||
timeout=10,
|
|
||||||
).content
|
|
||||||
)
|
|
||||||
if "access_token" in oauth_token:
|
if "access_token" in oauth_token:
|
||||||
return "Bearer " + oauth_token["access_token"]
|
return "Bearer " + oauth_token["access_token"]
|
||||||
else:
|
else:
|
||||||
|
|
@ -1202,10 +1188,8 @@ class LyricsPlugin(plugins.BeetsPlugin):
|
||||||
"https://api.microsofttranslator.com/v2/Http.svc/"
|
"https://api.microsofttranslator.com/v2/Http.svc/"
|
||||||
"Translate?text=%s&to=%s" % ("|".join(text_lines), to_lang)
|
"Translate?text=%s&to=%s" % ("|".join(text_lines), to_lang)
|
||||||
)
|
)
|
||||||
r = requests.get(
|
r = r_session.get(
|
||||||
url,
|
url, headers={"Authorization": self.bing_auth_token}
|
||||||
headers={"Authorization ": self.bing_auth_token},
|
|
||||||
timeout=10,
|
|
||||||
)
|
)
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
self._log.debug(
|
self._log.debug(
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ omit = beets/test/*
|
||||||
precision = 2
|
precision = 2
|
||||||
skip_empty = true
|
skip_empty = true
|
||||||
show_missing = true
|
show_missing = true
|
||||||
exclude_lines =
|
exclude_also =
|
||||||
pragma: no cover
|
@atexit.register
|
||||||
if TYPE_CHECKING
|
if TYPE_CHECKING
|
||||||
if typing.TYPE_CHECKING
|
if typing.TYPE_CHECKING
|
||||||
raise AssertionError
|
raise AssertionError
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue