mirror of
https://github.com/nathom/streamrip.git
synced 2025-05-14 23:24:52 -04:00
Merge branch 'dev' into fix-regex-appid-appsecret
This commit is contained in:
commit
c5bbd11414
7 changed files with 51 additions and 33 deletions
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "streamrip"
|
||||
version = "1.9.5"
|
||||
version = "1.9.6"
|
||||
description = "A fast, all-in-one music ripper for Qobuz, Deezer, Tidal, and SoundCloud"
|
||||
authors = ["nathom <nathanthomas707@gmail.com>"]
|
||||
license = "GPL-3.0-only"
|
||||
|
|
13
rip/cli.py
13
rip/cli.py
|
@ -20,7 +20,7 @@ logging.basicConfig(level="WARNING")
|
|||
logger = logging.getLogger("streamrip")
|
||||
|
||||
outdated = False
|
||||
newest_version = __version__
|
||||
newest_version: Optional[str] = None
|
||||
|
||||
|
||||
class DownloadCommand(Command):
|
||||
|
@ -126,6 +126,7 @@ class DownloadCommand(Command):
|
|||
self.line("<error>Must pass arguments. See </><cmd>rip url -h</cmd>.")
|
||||
|
||||
update_check.join()
|
||||
|
||||
if outdated:
|
||||
import re
|
||||
import subprocess
|
||||
|
@ -814,7 +815,15 @@ def is_outdated():
|
|||
global newest_version
|
||||
r = requests.get("https://pypi.org/pypi/streamrip/json").json()
|
||||
newest_version = r["info"]["version"]
|
||||
outdated = newest_version != __version__
|
||||
|
||||
# Compare versions
|
||||
curr_version_parsed = map(int, __version__.split("."))
|
||||
newest_version_parsed = map(int, newest_version.split("."))
|
||||
outdated = False
|
||||
for c, n in zip(curr_version_parsed, newest_version_parsed):
|
||||
outdated = c < n
|
||||
if c != n:
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -153,6 +153,9 @@ folder_format = "{albumartist} - {title} ({year}) [{container}] [{bit_depth}B-{s
|
|||
track_format = "{tracknumber}. {artist} - {title}{explicit}"
|
||||
# Only allow printable ASCII characters in filenames.
|
||||
restrict_characters = false
|
||||
# Truncate the filename if it is greater than 120 characters
|
||||
# Setting this to false may cause downloads to fail on some systems
|
||||
truncate = true
|
||||
|
||||
|
||||
# Last.fm playlists are downloaded by searching for the titles of the tracks
|
||||
|
@ -169,4 +172,4 @@ progress_bar = "dainty"
|
|||
|
||||
[misc]
|
||||
# Metadata to identify this config file. Do not change.
|
||||
version = "1.9.2"
|
||||
version = "1.9.6"
|
||||
|
|
15
rip/core.py
15
rip/core.py
|
@ -216,6 +216,7 @@ class RipCore(list):
|
|||
concurrency = session["downloads"]["concurrency"]
|
||||
return {
|
||||
"restrict_filenames": filepaths["restrict_characters"],
|
||||
"truncate_filenames": filepaths["truncate"],
|
||||
"parent_folder": session["downloads"]["folder"],
|
||||
"folder_format": filepaths["folder_format"],
|
||||
"track_format": filepaths["track_format"],
|
||||
|
@ -312,7 +313,8 @@ class RipCore(list):
|
|||
try:
|
||||
item.download(**arguments)
|
||||
for item_id in item.downloaded_ids:
|
||||
self.db.add([item_id])
|
||||
# Add items row by row
|
||||
self.db.add((item_id,))
|
||||
except NonStreamable as e:
|
||||
e.print(item)
|
||||
self.failed_db.add((item.client.source, item.type, item.id))
|
||||
|
@ -815,7 +817,7 @@ class RipCore(list):
|
|||
|
||||
info = []
|
||||
words = re.compile(r"[\w\s]+")
|
||||
title_tags = re.compile('title="([^"]+)"')
|
||||
title_tags = re.compile(r'<a\s+href="[^"]+"\s+title="([^"]+)"')
|
||||
|
||||
def essence(s):
|
||||
s = re.sub(r"&#\d+;", "", s) # remove HTML entities
|
||||
|
@ -823,7 +825,7 @@ class RipCore(list):
|
|||
return "".join(words.findall(s))
|
||||
|
||||
def get_titles(s):
|
||||
titles = title_tags.findall(s)[2:]
|
||||
titles = title_tags.findall(s) # [2:]
|
||||
for i in range(0, len(titles) - 1, 2):
|
||||
info.append((essence(titles[i]), essence(titles[i + 1])))
|
||||
|
||||
|
@ -850,13 +852,14 @@ class RipCore(list):
|
|||
|
||||
if remaining_tracks > 0:
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=15) as executor:
|
||||
last_page = int(remaining_tracks // 50) + int(
|
||||
remaining_tracks % 50 != 0
|
||||
last_page = (
|
||||
1 + int(remaining_tracks // 50) + int(remaining_tracks % 50 != 0)
|
||||
)
|
||||
logger.debug("Fetching up to page %d", last_page)
|
||||
|
||||
futures = [
|
||||
executor.submit(requests.get, f"{url}?page={page}")
|
||||
for page in range(1, last_page + 1)
|
||||
for page in range(2, last_page + 1)
|
||||
]
|
||||
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""streamrip: the all in one music downloader."""
|
||||
|
||||
__version__ = "1.9.5"
|
||||
__version__ = "1.9.6"
|
||||
|
||||
from . import clients, constants, converter, downloadtools, media
|
||||
|
|
|
@ -29,12 +29,7 @@ from pathvalidate import sanitize_filepath
|
|||
|
||||
from . import converter
|
||||
from .clients import Client, DeezloaderClient
|
||||
from .constants import (
|
||||
ALBUM_KEYS,
|
||||
FLAC_MAX_BLOCKSIZE,
|
||||
FOLDER_FORMAT,
|
||||
TRACK_FORMAT,
|
||||
)
|
||||
from .constants import ALBUM_KEYS, FLAC_MAX_BLOCKSIZE, FOLDER_FORMAT, TRACK_FORMAT
|
||||
from .downloadtools import DownloadPool, DownloadStream
|
||||
from .exceptions import (
|
||||
InvalidQuality,
|
||||
|
@ -1516,7 +1511,9 @@ class Album(Tracklist, Media):
|
|||
parent_folder = kwargs.get("parent_folder", "StreamripDownloads")
|
||||
if self.folder_format:
|
||||
self.folder = self._get_formatted_folder(
|
||||
parent_folder, restrict=kwargs.get("restrict_filenames", False)
|
||||
parent_folder,
|
||||
restrict=kwargs.get("restrict_filenames", False),
|
||||
truncate=kwargs.get("truncate_filenames", True),
|
||||
)
|
||||
else:
|
||||
self.folder = parent_folder
|
||||
|
@ -1525,7 +1522,7 @@ class Album(Tracklist, Media):
|
|||
|
||||
self.download_message()
|
||||
|
||||
cover_path = (
|
||||
cover_path: Optional[str] = (
|
||||
_choose_and_download_cover(
|
||||
self.cover_urls,
|
||||
kwargs.get("embed_cover_size", "large"),
|
||||
|
@ -1660,7 +1657,9 @@ class Album(Tracklist, Media):
|
|||
logger.debug("Formatter: %s", fmt)
|
||||
return fmt
|
||||
|
||||
def _get_formatted_folder(self, parent_folder: str, restrict: bool = False) -> str:
|
||||
def _get_formatted_folder(
|
||||
self, parent_folder: str, restrict: bool = False, truncate: bool = True
|
||||
) -> str:
|
||||
"""Generate the folder name for this album.
|
||||
|
||||
:param parent_folder:
|
||||
|
@ -1675,8 +1674,8 @@ class Album(Tracklist, Media):
|
|||
self._get_formatter(),
|
||||
restrict=restrict,
|
||||
)
|
||||
if len(formatted_folder) > 120:
|
||||
formatted_folder = f"{formatted_folder[:120]}..."
|
||||
if truncate and len(formatted_folder) > 120:
|
||||
formatted_folder = formatted_folder[:120]
|
||||
|
||||
return os.path.join(parent_folder, formatted_folder)
|
||||
|
||||
|
@ -1840,13 +1839,13 @@ class Playlist(Tracklist, Media):
|
|||
self.append(Track(self.client, id=track["id"]))
|
||||
else:
|
||||
for track in tracklist:
|
||||
# TODO: This should be managed with .m3u files and alike. Arbitrary
|
||||
# tracknumber tags might cause conflicts if the playlist files are
|
||||
# inside of a library folder
|
||||
meta = TrackMetadata(track=track, source=self.client.source)
|
||||
cover_url = get_cover_urls(track["album"], self.client.source)[
|
||||
kwargs.get("embed_cover_size", "large")
|
||||
]
|
||||
cover_urls = get_cover_urls(track["album"], self.client.source)
|
||||
cover_url = (
|
||||
cover_urls[kwargs.get("embed_cover_size", "large")]
|
||||
if cover_urls is not None
|
||||
else None
|
||||
)
|
||||
|
||||
self.append(
|
||||
Track(
|
||||
|
@ -2052,7 +2051,7 @@ class Artist(Tracklist, Media):
|
|||
else:
|
||||
self.folder = parent_folder
|
||||
|
||||
logger.debug("Artist folder: %s", folder)
|
||||
logger.debug("Artist folder: %s", self.folder)
|
||||
logger.debug("Length of tracklist %d", len(self))
|
||||
logger.debug("Filters: %s", filters)
|
||||
|
||||
|
@ -2322,7 +2321,7 @@ def _choose_and_download_cover(
|
|||
directory: str,
|
||||
keep_hires_cover: bool = True,
|
||||
downsize: Tuple[int, int] = (999999, 999999),
|
||||
) -> str:
|
||||
) -> Optional[str]:
|
||||
# choose optimal cover size and download it
|
||||
|
||||
hashcode: str = hashlib.md5(
|
||||
|
@ -2346,12 +2345,16 @@ def _choose_and_download_cover(
|
|||
), f"Invalid cover size. Must be in {cover_urls.keys()}"
|
||||
|
||||
embed_cover_url = cover_urls[preferred_size]
|
||||
|
||||
logger.debug("Chosen cover url: %s", embed_cover_url)
|
||||
if not os.path.exists(temp_cover_path):
|
||||
# Sometimes a size isn't available. When this is the case, find
|
||||
# the first `not None` url.
|
||||
if embed_cover_url is None:
|
||||
embed_cover_url = next(filter(None, cover_urls.values()))
|
||||
urls = tuple(filter(None, cover_urls.values()))
|
||||
if len(urls) == 0:
|
||||
return None
|
||||
embed_cover_url = urls[0]
|
||||
|
||||
logger.debug("Downloading cover from url %s", embed_cover_url)
|
||||
|
||||
|
|
|
@ -306,7 +306,7 @@ def get_container(quality: int, source: str) -> str:
|
|||
return "MP3"
|
||||
|
||||
|
||||
def get_cover_urls(resp: dict, source: str) -> dict:
|
||||
def get_cover_urls(resp: dict, source: str) -> Optional[dict]:
|
||||
"""Parse a response dict containing cover info according to the source.
|
||||
|
||||
:param resp:
|
||||
|
@ -318,7 +318,7 @@ def get_cover_urls(resp: dict, source: str) -> dict:
|
|||
|
||||
if source == "qobuz":
|
||||
cover_urls = resp["image"]
|
||||
cover_urls["original"] = cover_urls["large"].replace("600", "org")
|
||||
cover_urls["original"] = "org".join(cover_urls["large"].rsplit('600', 1))
|
||||
return cover_urls
|
||||
|
||||
if source == "tidal":
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue