mirror of
https://github.com/nathom/streamrip.git
synced 2025-05-15 07:34:48 -04:00
Formatting
Signed-off-by: nathom <nathanthomas707@gmail.com>
This commit is contained in:
parent
76ba2d413b
commit
7698ad7a2e
5 changed files with 39 additions and 112 deletions
|
@ -7,7 +7,7 @@ from getpass import getpass
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import requests # type: ignore
|
import requests
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from .clients import TidalClient
|
from .clients import TidalClient
|
||||||
|
@ -15,7 +15,6 @@ from .config import Config
|
||||||
from .constants import CACHE_DIR, CONFIG_DIR, CONFIG_PATH, QOBUZ_FEATURED_KEYS
|
from .constants import CACHE_DIR, CONFIG_DIR, CONFIG_PATH, QOBUZ_FEATURED_KEYS
|
||||||
from .core import MusicDL
|
from .core import MusicDL
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig(level="WARNING")
|
logging.basicConfig(level="WARNING")
|
||||||
logger = logging.getLogger("streamrip")
|
logger = logging.getLogger("streamrip")
|
||||||
|
|
||||||
|
@ -26,9 +25,7 @@ if not os.path.isdir(CACHE_DIR):
|
||||||
|
|
||||||
|
|
||||||
@click.group(invoke_without_command=True)
|
@click.group(invoke_without_command=True)
|
||||||
@click.option(
|
@click.option("-c", "--convert", metavar="CODEC", help="alac, mp3, flac, or ogg")
|
||||||
"-c", "--convert", metavar="CODEC", help="alac, mp3, flac, or ogg"
|
|
||||||
)
|
|
||||||
@click.option(
|
@click.option(
|
||||||
"-u",
|
"-u",
|
||||||
"--urls",
|
"--urls",
|
||||||
|
@ -141,9 +138,7 @@ def filter_discography(ctx, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option(
|
@click.option("-t", "--type", default="album", help="album, playlist, track, or artist")
|
||||||
"-t", "--type", default="album", help="album, playlist, track, or artist"
|
|
||||||
)
|
|
||||||
@click.option(
|
@click.option(
|
||||||
"-s",
|
"-s",
|
||||||
"--source",
|
"--source",
|
||||||
|
@ -264,9 +259,7 @@ def lastfm(ctx, source, url):
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option("-o", "--open", is_flag=True, help="Open the config file")
|
@click.option("-o", "--open", is_flag=True, help="Open the config file")
|
||||||
@click.option(
|
@click.option("-d", "--directory", is_flag=True, help="Open the config directory")
|
||||||
"-d", "--directory", is_flag=True, help="Open the config directory"
|
|
||||||
)
|
|
||||||
@click.option("-q", "--qobuz", is_flag=True, help="Set Qobuz credentials")
|
@click.option("-q", "--qobuz", is_flag=True, help="Set Qobuz credentials")
|
||||||
@click.option("-t", "--tidal", is_flag=True, help="Re-login into Tidal")
|
@click.option("-t", "--tidal", is_flag=True, help="Re-login into Tidal")
|
||||||
@click.option("--reset", is_flag=True, help="RESET the config file")
|
@click.option("--reset", is_flag=True, help="RESET the config file")
|
||||||
|
@ -310,9 +303,7 @@ def config(ctx, **kwargs):
|
||||||
click.launch(config_dir)
|
click.launch(config_dir)
|
||||||
|
|
||||||
if kwargs["qobuz"]:
|
if kwargs["qobuz"]:
|
||||||
config.file["qobuz"]["email"] = input(
|
config.file["qobuz"]["email"] = input(click.style("Qobuz email: ", fg="blue"))
|
||||||
click.style("Qobuz email: ", fg="blue")
|
|
||||||
)
|
|
||||||
|
|
||||||
click.secho("Qobuz password (will not show on screen):", fg="blue")
|
click.secho("Qobuz password (will not show on screen):", fg="blue")
|
||||||
config.file["qobuz"]["password"] = md5(
|
config.file["qobuz"]["password"] = md5(
|
||||||
|
@ -320,9 +311,7 @@ def config(ctx, **kwargs):
|
||||||
).hexdigest()
|
).hexdigest()
|
||||||
|
|
||||||
config.save()
|
config.save()
|
||||||
click.secho(
|
click.secho("Qobuz credentials hashed and saved to config.", fg="green")
|
||||||
"Qobuz credentials hashed and saved to config.", fg="green"
|
|
||||||
)
|
|
||||||
|
|
||||||
if kwargs["tidal"]:
|
if kwargs["tidal"]:
|
||||||
client = TidalClient()
|
client = TidalClient()
|
||||||
|
|
|
@ -216,9 +216,7 @@ class QobuzClient(Client):
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
page, status_code = self._api_request(epoint, params)
|
page, status_code = self._api_request(epoint, params)
|
||||||
logger.debug(
|
logger.debug("Keys returned from _gen_pages: %s", ", ".join(page.keys()))
|
||||||
"Keys returned from _gen_pages: %s", ", ".join(page.keys())
|
|
||||||
)
|
|
||||||
key = epoint.split("/")[0] + "s"
|
key = epoint.split("/")[0] + "s"
|
||||||
total = page.get(key, {})
|
total = page.get(key, {})
|
||||||
total = total.get("total") or total.get("items")
|
total = total.get("total") or total.get("items")
|
||||||
|
@ -242,9 +240,7 @@ class QobuzClient(Client):
|
||||||
for secret in self.secrets:
|
for secret in self.secrets:
|
||||||
if self._test_secret(secret):
|
if self._test_secret(secret):
|
||||||
self.sec = secret
|
self.sec = secret
|
||||||
logger.debug(
|
logger.debug("Working secret and app_id: %s - %s", secret, self.app_id)
|
||||||
"Working secret and app_id: %s - %s", secret, self.app_id
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
if not hasattr(self, "sec"):
|
if not hasattr(self, "sec"):
|
||||||
raise InvalidAppSecretError(f"Invalid secrets: {self.secrets}")
|
raise InvalidAppSecretError(f"Invalid secrets: {self.secrets}")
|
||||||
|
@ -278,15 +274,11 @@ class QobuzClient(Client):
|
||||||
|
|
||||||
response, status_code = self._api_request(epoint, params)
|
response, status_code = self._api_request(epoint, params)
|
||||||
if status_code != 200:
|
if status_code != 200:
|
||||||
raise Exception(
|
raise Exception(f'Error fetching metadata. "{response["message"]}"')
|
||||||
f'Error fetching metadata. "{response["message"]}"'
|
|
||||||
)
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def _api_search(
|
def _api_search(self, query: str, media_type: str, limit: int = 500) -> Generator:
|
||||||
self, query: str, media_type: str, limit: int = 500
|
|
||||||
) -> Generator:
|
|
||||||
"""Send a search request to the API.
|
"""Send a search request to the API.
|
||||||
|
|
||||||
:param query:
|
:param query:
|
||||||
|
@ -338,18 +330,14 @@ class QobuzClient(Client):
|
||||||
resp, status_code = self._api_request(epoint, params)
|
resp, status_code = self._api_request(epoint, params)
|
||||||
|
|
||||||
if status_code == 401:
|
if status_code == 401:
|
||||||
raise AuthenticationError(
|
raise AuthenticationError(f"Invalid credentials from params {params}")
|
||||||
f"Invalid credentials from params {params}"
|
|
||||||
)
|
|
||||||
elif status_code == 400:
|
elif status_code == 400:
|
||||||
raise InvalidAppIdError(f"Invalid app id from params {params}")
|
raise InvalidAppIdError(f"Invalid app id from params {params}")
|
||||||
else:
|
else:
|
||||||
logger.info("Logged in to Qobuz")
|
logger.info("Logged in to Qobuz")
|
||||||
|
|
||||||
if not resp["user"]["credential"]["parameters"]:
|
if not resp["user"]["credential"]["parameters"]:
|
||||||
raise IneligibleError(
|
raise IneligibleError("Free accounts are not eligible to download tracks.")
|
||||||
"Free accounts are not eligible to download tracks."
|
|
||||||
)
|
|
||||||
|
|
||||||
self.uat = resp["user_auth_token"]
|
self.uat = resp["user_auth_token"]
|
||||||
self.session.headers.update({"X-User-Auth-Token": self.uat})
|
self.session.headers.update({"X-User-Auth-Token": self.uat})
|
||||||
|
@ -398,9 +386,7 @@ class QobuzClient(Client):
|
||||||
}
|
}
|
||||||
response, status_code = self._api_request("track/getFileUrl", params)
|
response, status_code = self._api_request("track/getFileUrl", params)
|
||||||
if status_code == 400:
|
if status_code == 400:
|
||||||
raise InvalidAppSecretError(
|
raise InvalidAppSecretError("Invalid app secret from params %s" % params)
|
||||||
"Invalid app secret from params %s" % params
|
|
||||||
)
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -418,9 +404,7 @@ class QobuzClient(Client):
|
||||||
try:
|
try:
|
||||||
return r.json(), r.status_code
|
return r.json(), r.status_code
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error(
|
logger.error("Problem getting JSON. Status code: %s", r.status_code)
|
||||||
"Problem getting JSON. Status code: %s", r.status_code
|
|
||||||
)
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _test_secret(self, secret: str) -> bool:
|
def _test_secret(self, secret: str) -> bool:
|
||||||
|
@ -451,9 +435,7 @@ class DeezerClient(Client):
|
||||||
# no login required
|
# no login required
|
||||||
self.logged_in = True
|
self.logged_in = True
|
||||||
|
|
||||||
def search(
|
def search(self, query: str, media_type: str = "album", limit: int = 200) -> dict:
|
||||||
self, query: str, media_type: str = "album", limit: int = 200
|
|
||||||
) -> dict:
|
|
||||||
"""Search API for query.
|
"""Search API for query.
|
||||||
|
|
||||||
:param query:
|
:param query:
|
||||||
|
@ -490,9 +472,7 @@ class DeezerClient(Client):
|
||||||
url = f"{DEEZER_BASE}/{media_type}/{meta_id}"
|
url = f"{DEEZER_BASE}/{media_type}/{meta_id}"
|
||||||
item = self.session.get(url).json()
|
item = self.session.get(url).json()
|
||||||
if media_type in ("album", "playlist"):
|
if media_type in ("album", "playlist"):
|
||||||
tracks = self.session.get(
|
tracks = self.session.get(f"{url}/tracks", params={"limit": 1000}).json()
|
||||||
f"{url}/tracks", params={"limit": 1000}
|
|
||||||
).json()
|
|
||||||
item["tracks"] = tracks["data"]
|
item["tracks"] = tracks["data"]
|
||||||
item["track_total"] = len(tracks["data"])
|
item["track_total"] = len(tracks["data"])
|
||||||
elif media_type == "artist":
|
elif media_type == "artist":
|
||||||
|
@ -588,9 +568,7 @@ class TidalClient(Client):
|
||||||
logger.debug(resp)
|
logger.debug(resp)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def search(
|
def search(self, query: str, media_type: str = "album", limit: int = 100) -> dict:
|
||||||
self, query: str, media_type: str = "album", limit: int = 100
|
|
||||||
) -> dict:
|
|
||||||
"""Search for a query.
|
"""Search for a query.
|
||||||
|
|
||||||
:param query:
|
:param query:
|
||||||
|
@ -619,19 +597,13 @@ class TidalClient(Client):
|
||||||
return self._get_video_stream_url(track_id)
|
return self._get_video_stream_url(track_id)
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
"audioquality": get_quality(
|
"audioquality": get_quality(min(quality, TIDAL_MAX_Q), self.source),
|
||||||
min(quality, TIDAL_MAX_Q), self.source
|
|
||||||
),
|
|
||||||
"playbackmode": "STREAM",
|
"playbackmode": "STREAM",
|
||||||
"assetpresentation": "FULL",
|
"assetpresentation": "FULL",
|
||||||
}
|
}
|
||||||
resp = self._api_request(
|
resp = self._api_request(f"tracks/{track_id}/playbackinfopostpaywall", params)
|
||||||
f"tracks/{track_id}/playbackinfopostpaywall", params
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
manifest = json.loads(
|
manifest = json.loads(base64.b64decode(resp["manifest"]).decode("utf-8"))
|
||||||
base64.b64decode(resp["manifest"]).decode("utf-8")
|
|
||||||
)
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise Exception(resp["userMessage"])
|
raise Exception(resp["userMessage"])
|
||||||
|
|
||||||
|
@ -837,9 +809,7 @@ class TidalClient(Client):
|
||||||
offset += 100
|
offset += 100
|
||||||
tracks_left -= 100
|
tracks_left -= 100
|
||||||
resp["items"].extend(
|
resp["items"].extend(
|
||||||
self._api_request(f"{url}/items", {"offset": offset})[
|
self._api_request(f"{url}/items", {"offset": offset})["items"]
|
||||||
"items"
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
item["tracks"] = [item["item"] for item in resp["items"]]
|
item["tracks"] = [item["item"] for item in resp["items"]]
|
||||||
|
@ -884,9 +854,7 @@ class TidalClient(Client):
|
||||||
r'#EXT-X-STREAM-INF:BANDWIDTH=\d+,AVERAGE-BANDWIDTH=\d+,CODECS="[^"]+"'
|
r'#EXT-X-STREAM-INF:BANDWIDTH=\d+,AVERAGE-BANDWIDTH=\d+,CODECS="[^"]+"'
|
||||||
r",RESOLUTION=\d+x\d+\n(.+)"
|
r",RESOLUTION=\d+x\d+\n(.+)"
|
||||||
)
|
)
|
||||||
manifest = json.loads(
|
manifest = json.loads(base64.b64decode(resp["manifest"]).decode("utf-8"))
|
||||||
base64.b64decode(resp["manifest"]).decode("utf-8")
|
|
||||||
)
|
|
||||||
available_urls = self.session.get(manifest["urls"][0])
|
available_urls = self.session.get(manifest["urls"][0])
|
||||||
url_info = re.findall(stream_url_regex, available_urls.text)
|
url_info = re.findall(stream_url_regex, available_urls.text)
|
||||||
|
|
||||||
|
@ -965,10 +933,7 @@ class SoundCloudClient(Client):
|
||||||
url = None
|
url = None
|
||||||
for tc in track["media"]["transcodings"]:
|
for tc in track["media"]["transcodings"]:
|
||||||
fmt = tc["format"]
|
fmt = tc["format"]
|
||||||
if (
|
if fmt["protocol"] == "hls" and fmt["mime_type"] == "audio/mpeg":
|
||||||
fmt["protocol"] == "hls"
|
|
||||||
and fmt["mime_type"] == "audio/mpeg"
|
|
||||||
):
|
|
||||||
url = tc["url"]
|
url = tc["url"]
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
@ -335,9 +335,7 @@ class MusicDL(list):
|
||||||
|
|
||||||
parsed.extend(self.url_parse.findall(url)) # Qobuz, Tidal, Dezer
|
parsed.extend(self.url_parse.findall(url)) # Qobuz, Tidal, Dezer
|
||||||
soundcloud_urls = self.soundcloud_url_parse.findall(url)
|
soundcloud_urls = self.soundcloud_url_parse.findall(url)
|
||||||
soundcloud_items = [
|
soundcloud_items = [self.clients["soundcloud"].get(u) for u in soundcloud_urls]
|
||||||
self.clients["soundcloud"].get(u) for u in soundcloud_urls
|
|
||||||
]
|
|
||||||
|
|
||||||
parsed.extend(
|
parsed.extend(
|
||||||
("soundcloud", item["kind"], url)
|
("soundcloud", item["kind"], url)
|
||||||
|
@ -369,15 +367,11 @@ class MusicDL(list):
|
||||||
|
|
||||||
# For testing:
|
# For testing:
|
||||||
# https://www.last.fm/user/nathan3895/playlists/12058911
|
# https://www.last.fm/user/nathan3895/playlists/12058911
|
||||||
user_regex = re.compile(
|
user_regex = re.compile(r"https://www\.last\.fm/user/([^/]+)/playlists/\d+")
|
||||||
r"https://www\.last\.fm/user/([^/]+)/playlists/\d+"
|
|
||||||
)
|
|
||||||
lastfm_urls = self.lastfm_url_parse.findall(urls)
|
lastfm_urls = self.lastfm_url_parse.findall(urls)
|
||||||
try:
|
try:
|
||||||
lastfm_source = self.config.session["lastfm"]["source"]
|
lastfm_source = self.config.session["lastfm"]["source"]
|
||||||
lastfm_fallback_source = self.config.session["lastfm"][
|
lastfm_fallback_source = self.config.session["lastfm"]["fallback_source"]
|
||||||
"fallback_source"
|
|
||||||
]
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self._config_updating_message()
|
self._config_updating_message()
|
||||||
self.config.update()
|
self.config.update()
|
||||||
|
@ -407,9 +401,7 @@ class MusicDL(list):
|
||||||
except (NoResultsFound, StopIteration):
|
except (NoResultsFound, StopIteration):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
track = try_search(lastfm_source) or try_search(
|
track = try_search(lastfm_source) or try_search(lastfm_fallback_source)
|
||||||
lastfm_fallback_source
|
|
||||||
)
|
|
||||||
if track is None:
|
if track is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -431,9 +423,7 @@ class MusicDL(list):
|
||||||
pl.creator = creator_match.group(1)
|
pl.creator = creator_match.group(1)
|
||||||
|
|
||||||
tracks_not_found = 0
|
tracks_not_found = 0
|
||||||
with concurrent.futures.ThreadPoolExecutor(
|
with concurrent.futures.ThreadPoolExecutor(max_workers=15) as executor:
|
||||||
max_workers=15
|
|
||||||
) as executor:
|
|
||||||
futures = [
|
futures = [
|
||||||
executor.submit(search_query, title, artist, pl)
|
executor.submit(search_query, title, artist, pl)
|
||||||
for title, artist in queries
|
for title, artist in queries
|
||||||
|
@ -450,9 +440,7 @@ class MusicDL(list):
|
||||||
pl.loaded = True
|
pl.loaded = True
|
||||||
|
|
||||||
if tracks_not_found > 0:
|
if tracks_not_found > 0:
|
||||||
click.secho(
|
click.secho(f"{tracks_not_found} tracks not found.", fg="yellow")
|
||||||
f"{tracks_not_found} tracks not found.", fg="yellow"
|
|
||||||
)
|
|
||||||
self.append(pl)
|
self.append(pl)
|
||||||
|
|
||||||
def handle_txt(self, filepath: Union[str, os.PathLike]):
|
def handle_txt(self, filepath: Union[str, os.PathLike]):
|
||||||
|
@ -507,9 +495,7 @@ class MusicDL(list):
|
||||||
else:
|
else:
|
||||||
logger.debug("Not generator")
|
logger.debug("Not generator")
|
||||||
items = (
|
items = (
|
||||||
results.get("data")
|
results.get("data") or results.get("items") or results.get("collection")
|
||||||
or results.get("items")
|
|
||||||
or results.get("collection")
|
|
||||||
)
|
)
|
||||||
if items is None:
|
if items is None:
|
||||||
raise NoResultsFound(query)
|
raise NoResultsFound(query)
|
||||||
|
@ -549,9 +535,7 @@ class MusicDL(list):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
fields = (fname for _, fname, _, _ in Formatter().parse(fmt) if fname)
|
fields = (fname for _, fname, _, _ in Formatter().parse(fmt) if fname)
|
||||||
ret = fmt.format(
|
ret = fmt.format(**{k: media.get(k, default="Unknown") for k in fields})
|
||||||
**{k: media.get(k, default="Unknown") for k in fields}
|
|
||||||
)
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def interactive_search( # noqa
|
def interactive_search( # noqa
|
||||||
|
@ -682,9 +666,7 @@ class MusicDL(list):
|
||||||
playlist_title = html.unescape(playlist_title_match.group(1))
|
playlist_title = html.unescape(playlist_title_match.group(1))
|
||||||
|
|
||||||
if remaining_tracks > 0:
|
if remaining_tracks > 0:
|
||||||
with concurrent.futures.ThreadPoolExecutor(
|
with concurrent.futures.ThreadPoolExecutor(max_workers=15) as executor:
|
||||||
max_workers=15
|
|
||||||
) as executor:
|
|
||||||
last_page = int(remaining_tracks // 50) + int(
|
last_page = int(remaining_tracks // 50) + int(
|
||||||
remaining_tracks % 50 != 0
|
remaining_tracks % 50 != 0
|
||||||
)
|
)
|
||||||
|
@ -726,7 +708,8 @@ class MusicDL(list):
|
||||||
|
|
||||||
self.config.save()
|
self.config.save()
|
||||||
click.secho(
|
click.secho(
|
||||||
f'Credentials saved to config file at "{self.config._path}"'
|
f'Credentials saved to config file at "{self.config._path}"',
|
||||||
|
fg="green",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise Exception
|
raise Exception
|
||||||
|
|
|
@ -25,9 +25,7 @@ class MusicDB:
|
||||||
"""Create a database at `self.path`."""
|
"""Create a database at `self.path`."""
|
||||||
with sqlite3.connect(self.path) as conn:
|
with sqlite3.connect(self.path) as conn:
|
||||||
try:
|
try:
|
||||||
conn.execute(
|
conn.execute("CREATE TABLE downloads (id TEXT UNIQUE NOT NULL);")
|
||||||
"CREATE TABLE downloads (id TEXT UNIQUE NOT NULL);"
|
|
||||||
)
|
|
||||||
logger.debug("Download-IDs database created: %s", self.path)
|
logger.debug("Download-IDs database created: %s", self.path)
|
||||||
except sqlite3.OperationalError:
|
except sqlite3.OperationalError:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -82,9 +82,7 @@ def get_quality(quality_id: int, source: str) -> Union[str, int]:
|
||||||
raise InvalidSourceError(source)
|
raise InvalidSourceError(source)
|
||||||
|
|
||||||
possible_keys = set(q_map.keys())
|
possible_keys = set(q_map.keys())
|
||||||
assert (
|
assert quality_id in possible_keys, f"{quality_id} must be in {possible_keys}"
|
||||||
quality_id in possible_keys
|
|
||||||
), f"{quality_id} must be in {possible_keys}"
|
|
||||||
return q_map[quality_id]
|
return q_map[quality_id]
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,9 +123,7 @@ def get_stats_from_quality(
|
||||||
raise InvalidQuality(quality_id)
|
raise InvalidQuality(quality_id)
|
||||||
|
|
||||||
|
|
||||||
def tqdm_download(
|
def tqdm_download(url: str, filepath: str, params: dict = None, desc: str = None):
|
||||||
url: str, filepath: str, params: dict = None, desc: str = None
|
|
||||||
):
|
|
||||||
"""Download a file with a progress bar.
|
"""Download a file with a progress bar.
|
||||||
|
|
||||||
:param url: url to direct download
|
:param url: url to direct download
|
||||||
|
@ -199,9 +195,7 @@ def tidal_cover_url(uuid, size):
|
||||||
possibles = (80, 160, 320, 640, 1280)
|
possibles = (80, 160, 320, 640, 1280)
|
||||||
assert size in possibles, f"size must be in {possibles}"
|
assert size in possibles, f"size must be in {possibles}"
|
||||||
|
|
||||||
return TIDAL_COVER_URL.format(
|
return TIDAL_COVER_URL.format(uuid=uuid.replace("-", "/"), height=size, width=size)
|
||||||
uuid=uuid.replace("-", "/"), height=size, width=size
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def init_log(path: Optional[str] = None, level: str = "DEBUG"):
|
def init_log(path: Optional[str] = None, level: str = "DEBUG"):
|
||||||
|
@ -303,9 +297,7 @@ def gen_threadsafe_session(
|
||||||
headers = {}
|
headers = {}
|
||||||
|
|
||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
adapter = requests.adapters.HTTPAdapter(
|
adapter = requests.adapters.HTTPAdapter(pool_connections=100, pool_maxsize=100)
|
||||||
pool_connections=100, pool_maxsize=100
|
|
||||||
)
|
|
||||||
session.mount("https://", adapter)
|
session.mount("https://", adapter)
|
||||||
session.headers.update(headers)
|
session.headers.update(headers)
|
||||||
return session
|
return session
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue