mirror of
https://github.com/nathom/streamrip.git
synced 2025-05-12 22:26:16 -04:00
Only use click for secho, echo, style
This commit is contained in:
parent
9970ac548f
commit
286ff502b5
8 changed files with 129 additions and 129 deletions
138
rip/cli.py
138
rip/cli.py
|
@ -9,10 +9,10 @@ logging.basicConfig(level="WARNING")
|
||||||
logger = logging.getLogger("streamrip")
|
logger = logging.getLogger("streamrip")
|
||||||
|
|
||||||
|
|
||||||
class SkipArg(click.Group):
|
class SkipArg(Group):
|
||||||
def parse_args(self, ctx, args):
|
def parse_args(self, ctx, args):
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
click.echo(self.get_help(ctx))
|
echo(self.get_help(ctx))
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
if args[0] in self.commands:
|
if args[0] in self.commands:
|
||||||
|
@ -25,27 +25,27 @@ class SkipArg(click.Group):
|
||||||
super(SkipArg, self).parse_args(ctx, args)
|
super(SkipArg, self).parse_args(ctx, args)
|
||||||
|
|
||||||
|
|
||||||
# @click.option(
|
# @option(
|
||||||
# "-u",
|
# "-u",
|
||||||
# "--urls",
|
# "--urls",
|
||||||
# metavar="URLS",
|
# metavar="URLS",
|
||||||
# help="Url from Qobuz, Tidal, SoundCloud, or Deezer",
|
# help="Url from Qobuz, Tidal, SoundCloud, or Deezer",
|
||||||
# multiple=True,
|
# multiple=True,
|
||||||
# )
|
# )
|
||||||
@click.group(cls=SkipArg, invoke_without_command=True)
|
@group(cls=SkipArg, invoke_without_command=True)
|
||||||
@click.option("-c", "--convert", metavar="CODEC", help="alac, mp3, flac, or ogg")
|
@option("-c", "--convert", metavar="CODEC", help="alac, mp3, flac, or ogg")
|
||||||
@click.option(
|
@option(
|
||||||
"-q",
|
"-q",
|
||||||
"--quality",
|
"--quality",
|
||||||
metavar="INT",
|
metavar="INT",
|
||||||
help="0: < 320kbps, 1: 320 kbps, 2: 16 bit/44.1 kHz, 3: 24 bit/<=96 kHz, 4: 24 bit/<=192 kHz",
|
help="0: < 320kbps, 1: 320 kbps, 2: 16 bit/44.1 kHz, 3: 24 bit/<=96 kHz, 4: 24 bit/<=192 kHz",
|
||||||
)
|
)
|
||||||
@click.option("-t", "--text", metavar="PATH", help="Download urls from a text file.")
|
@option("-t", "--text", metavar="PATH", help="Download urls from a text file.")
|
||||||
@click.option("-nd", "--no-db", is_flag=True, help="Ignore the database.")
|
@option("-nd", "--no-db", is_flag=True, help="Ignore the database.")
|
||||||
@click.option("--debug", is_flag=True, help="Show debugging logs.")
|
@option("--debug", is_flag=True, help="Show debugging logs.")
|
||||||
@click.argument("URLS", nargs=1)
|
@argument("URLS", nargs=1)
|
||||||
@click.version_option(prog_name="rip", version=__version__)
|
@version_option(prog_name="rip", version=__version__)
|
||||||
@click.pass_context
|
@pass_context
|
||||||
def cli(ctx, **kwargs):
|
def cli(ctx, **kwargs):
|
||||||
"""Streamrip: The all-in-one Qobuz, Tidal, SoundCloud, and Deezer music
|
"""Streamrip: The all-in-one Qobuz, Tidal, SoundCloud, and Deezer music
|
||||||
downloader.
|
downloader.
|
||||||
|
@ -84,7 +84,7 @@ def cli(ctx, **kwargs):
|
||||||
|
|
||||||
if ctx.invoked_subcommand is None and not ctx.params["urls"]:
|
if ctx.invoked_subcommand is None and not ctx.params["urls"]:
|
||||||
print(dir(cli))
|
print(dir(cli))
|
||||||
click.echo(cli.get_help(ctx))
|
echo(cli.get_help(ctx))
|
||||||
|
|
||||||
if ctx.invoked_subcommand not in {
|
if ctx.invoked_subcommand not in {
|
||||||
None,
|
None,
|
||||||
|
@ -106,13 +106,13 @@ def cli(ctx, **kwargs):
|
||||||
r = requests.get("https://pypi.org/pypi/streamrip/json").json()
|
r = requests.get("https://pypi.org/pypi/streamrip/json").json()
|
||||||
newest = r["info"]["version"]
|
newest = r["info"]["version"]
|
||||||
if __version__ != newest:
|
if __version__ != newest:
|
||||||
click.secho(
|
secho(
|
||||||
"A new version of streamrip is available! "
|
"A new version of streamrip is available! "
|
||||||
"Run `pip3 install streamrip --upgrade` to update.",
|
"Run `pip3 install streamrip --upgrade` to update.",
|
||||||
fg="yellow",
|
fg="yellow",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
click.secho("streamrip is up-to-date!", fg="green")
|
secho("streamrip is up-to-date!", fg="green")
|
||||||
|
|
||||||
if kwargs["no_db"]:
|
if kwargs["no_db"]:
|
||||||
config.session["database"]["enabled"] = False
|
config.session["database"]["enabled"] = False
|
||||||
|
@ -124,7 +124,7 @@ def cli(ctx, **kwargs):
|
||||||
if kwargs["quality"] is not None:
|
if kwargs["quality"] is not None:
|
||||||
quality = int(kwargs["quality"])
|
quality = int(kwargs["quality"])
|
||||||
if quality not in range(5):
|
if quality not in range(5):
|
||||||
click.secho("Invalid quality", fg="red")
|
secho("Invalid quality", fg="red")
|
||||||
return
|
return
|
||||||
|
|
||||||
config.session["qobuz"]["quality"] = quality
|
config.session["qobuz"]["quality"] = quality
|
||||||
|
@ -142,21 +142,21 @@ def cli(ctx, **kwargs):
|
||||||
logger.debug(f"Handling {kwargs['text']}")
|
logger.debug(f"Handling {kwargs['text']}")
|
||||||
core.handle_txt(kwargs["text"])
|
core.handle_txt(kwargs["text"])
|
||||||
else:
|
else:
|
||||||
click.secho(f"Text file {kwargs['text']} does not exist.")
|
secho(f"Text file {kwargs['text']} does not exist.")
|
||||||
|
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
core.download()
|
core.download()
|
||||||
|
|
||||||
|
|
||||||
@cli.command(name="filter")
|
@cli.command(name="filter")
|
||||||
@click.option("--repeats", is_flag=True)
|
@option("--repeats", is_flag=True)
|
||||||
@click.option("--non-albums", is_flag=True)
|
@option("--non-albums", is_flag=True)
|
||||||
@click.option("--extras", is_flag=True)
|
@option("--extras", is_flag=True)
|
||||||
@click.option("--features", is_flag=True)
|
@option("--features", is_flag=True)
|
||||||
@click.option("--non-studio-albums", is_flag=True)
|
@option("--non-studio-albums", is_flag=True)
|
||||||
@click.option("--non-remasters", is_flag=True)
|
@option("--non-remasters", is_flag=True)
|
||||||
@click.argument("URLS", nargs=-1)
|
@argument("URLS", nargs=-1)
|
||||||
@click.pass_context
|
@pass_context
|
||||||
def filter_discography(ctx, **kwargs):
|
def filter_discography(ctx, **kwargs):
|
||||||
"""Filter an artists discography (qobuz only).
|
"""Filter an artists discography (qobuz only).
|
||||||
|
|
||||||
|
@ -176,22 +176,22 @@ def filter_discography(ctx, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option(
|
@option(
|
||||||
"-t",
|
"-t",
|
||||||
"--type",
|
"--type",
|
||||||
default="album",
|
default="album",
|
||||||
help="album, playlist, track, or artist",
|
help="album, playlist, track, or artist",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
)
|
)
|
||||||
@click.option(
|
@option(
|
||||||
"-s",
|
"-s",
|
||||||
"--source",
|
"--source",
|
||||||
default="qobuz",
|
default="qobuz",
|
||||||
help="qobuz, tidal, soundcloud, deezer, or deezloader",
|
help="qobuz, tidal, soundcloud, deezer, or deezloader",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
)
|
)
|
||||||
@click.argument("QUERY", nargs=-1)
|
@argument("QUERY", nargs=-1)
|
||||||
@click.pass_context
|
@pass_context
|
||||||
def search(ctx, **kwargs):
|
def search(ctx, **kwargs):
|
||||||
"""Search and download media in interactive mode.
|
"""Search and download media in interactive mode.
|
||||||
|
|
||||||
|
@ -223,18 +223,18 @@ def search(ctx, **kwargs):
|
||||||
if core.interactive_search(query, kwargs["source"], kwargs["type"]):
|
if core.interactive_search(query, kwargs["source"], kwargs["type"]):
|
||||||
core.download()
|
core.download()
|
||||||
else:
|
else:
|
||||||
click.secho("No items chosen, exiting.", fg="bright_red")
|
secho("No items chosen, exiting.", fg="bright_red")
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option("-l", "--list", default="ideal-discography", show_default=True)
|
@option("-l", "--list", default="ideal-discography", show_default=True)
|
||||||
@click.option(
|
@option(
|
||||||
"-s", "--scrape", is_flag=True, help="Download all of the items in a list."
|
"-s", "--scrape", is_flag=True, help="Download all of the items in a list."
|
||||||
)
|
)
|
||||||
@click.option(
|
@option(
|
||||||
"-n", "--num-items", default=50, help="Number of items to parse.", show_default=True
|
"-n", "--num-items", default=50, help="Number of items to parse.", show_default=True
|
||||||
)
|
)
|
||||||
@click.pass_context
|
@pass_context
|
||||||
def discover(ctx, **kwargs):
|
def discover(ctx, **kwargs):
|
||||||
"""Search for albums in Qobuz's featured lists.
|
"""Search for albums in Qobuz's featured lists.
|
||||||
|
|
||||||
|
@ -278,13 +278,13 @@ def discover(ctx, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option(
|
@option(
|
||||||
"-s",
|
"-s",
|
||||||
"--source",
|
"--source",
|
||||||
help="qobuz, tidal, deezer, deezloader, or soundcloud",
|
help="qobuz, tidal, deezer, deezloader, or soundcloud",
|
||||||
)
|
)
|
||||||
@click.argument("URL")
|
@argument("URL")
|
||||||
@click.pass_context
|
@pass_context
|
||||||
def lastfm(ctx, source, url):
|
def lastfm(ctx, source, url):
|
||||||
"""Search for tracks from a last.fm playlist on a given source.
|
"""Search for tracks from a last.fm playlist on a given source.
|
||||||
|
|
||||||
|
@ -312,25 +312,25 @@ def lastfm(ctx, source, url):
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option("-o", "--open", is_flag=True, help="Open the config file")
|
@option("-o", "--open", is_flag=True, help="Open the config file")
|
||||||
@click.option("-d", "--directory", is_flag=True, help="Open the config directory")
|
@option("-d", "--directory", is_flag=True, help="Open the config directory")
|
||||||
@click.option("-q", "--qobuz", is_flag=True, help="Set Qobuz credentials")
|
@option("-q", "--qobuz", is_flag=True, help="Set Qobuz credentials")
|
||||||
@click.option("-t", "--tidal", is_flag=True, help="Re-login into Tidal")
|
@option("-t", "--tidal", is_flag=True, help="Re-login into Tidal")
|
||||||
@click.option("-dz", "--deezer", is_flag=True, help="Set the Deezer ARL")
|
@option("-dz", "--deezer", is_flag=True, help="Set the Deezer ARL")
|
||||||
@click.option("--reset", is_flag=True, help="RESET the config file")
|
@option("--reset", is_flag=True, help="RESET the config file")
|
||||||
@click.option(
|
@option(
|
||||||
"--update",
|
"--update",
|
||||||
is_flag=True,
|
is_flag=True,
|
||||||
help="Reset the config file, keeping the credentials",
|
help="Reset the config file, keeping the credentials",
|
||||||
)
|
)
|
||||||
@click.option("-p", "--path", is_flag=True, help="Show the config file's path")
|
@option("-p", "--path", is_flag=True, help="Show the config file's path")
|
||||||
@click.option(
|
@option(
|
||||||
"-ov",
|
"-ov",
|
||||||
"--open-vim",
|
"--open-vim",
|
||||||
is_flag=True,
|
is_flag=True,
|
||||||
help="Open the config file in the nvim or vim text editor.",
|
help="Open the config file in the nvim or vim text editor.",
|
||||||
)
|
)
|
||||||
@click.pass_context
|
@pass_context
|
||||||
def config(ctx, **kwargs):
|
def config(ctx, **kwargs):
|
||||||
"""Manage the streamrip configuration file."""
|
"""Manage the streamrip configuration file."""
|
||||||
import os
|
import os
|
||||||
|
@ -350,11 +350,11 @@ def config(ctx, **kwargs):
|
||||||
config.update()
|
config.update()
|
||||||
|
|
||||||
if kwargs["path"]:
|
if kwargs["path"]:
|
||||||
click.echo(CONFIG_PATH)
|
echo(CONFIG_PATH)
|
||||||
|
|
||||||
if kwargs["open"]:
|
if kwargs["open"]:
|
||||||
click.secho(f"Opening {CONFIG_PATH}", fg="green")
|
secho(f"Opening {CONFIG_PATH}", fg="green")
|
||||||
click.launch(CONFIG_PATH)
|
launch(CONFIG_PATH)
|
||||||
|
|
||||||
if kwargs["open_vim"]:
|
if kwargs["open_vim"]:
|
||||||
if shutil.which("nvim") is not None:
|
if shutil.which("nvim") is not None:
|
||||||
|
@ -364,58 +364,58 @@ def config(ctx, **kwargs):
|
||||||
|
|
||||||
if kwargs["directory"]:
|
if kwargs["directory"]:
|
||||||
config_dir = os.path.dirname(CONFIG_PATH)
|
config_dir = os.path.dirname(CONFIG_PATH)
|
||||||
click.secho(f"Opening {config_dir}", fg="green")
|
secho(f"Opening {config_dir}", fg="green")
|
||||||
click.launch(config_dir)
|
launch(config_dir)
|
||||||
|
|
||||||
if kwargs["qobuz"]:
|
if kwargs["qobuz"]:
|
||||||
config.file["qobuz"]["email"] = input(click.style("Qobuz email: ", fg="blue"))
|
config.file["qobuz"]["email"] = input(style("Qobuz email: ", fg="blue"))
|
||||||
|
|
||||||
click.secho("Qobuz password (will not show on screen):", fg="blue")
|
secho("Qobuz password (will not show on screen):", fg="blue")
|
||||||
config.file["qobuz"]["password"] = md5(
|
config.file["qobuz"]["password"] = md5(
|
||||||
getpass(prompt="").encode("utf-8")
|
getpass(prompt="").encode("utf-8")
|
||||||
).hexdigest()
|
).hexdigest()
|
||||||
|
|
||||||
config.save()
|
config.save()
|
||||||
click.secho("Qobuz credentials hashed and saved to config.", fg="green")
|
secho("Qobuz credentials hashed and saved to config.", fg="green")
|
||||||
|
|
||||||
if kwargs["tidal"]:
|
if kwargs["tidal"]:
|
||||||
client = TidalClient()
|
client = TidalClient()
|
||||||
client.login()
|
client.login()
|
||||||
config.file["tidal"].update(client.get_tokens())
|
config.file["tidal"].update(client.get_tokens())
|
||||||
config.save()
|
config.save()
|
||||||
click.secho("Credentials saved to config.", fg="green")
|
secho("Credentials saved to config.", fg="green")
|
||||||
|
|
||||||
if kwargs["deezer"]:
|
if kwargs["deezer"]:
|
||||||
click.secho(
|
secho(
|
||||||
"If you're not sure how to find the ARL cookie, see the instructions at ",
|
"If you're not sure how to find the ARL cookie, see the instructions at ",
|
||||||
italic=True,
|
italic=True,
|
||||||
nl=False,
|
nl=False,
|
||||||
dim=True,
|
dim=True,
|
||||||
)
|
)
|
||||||
click.secho(
|
secho(
|
||||||
"https://github.com/nathom/streamrip/wiki/Finding-your-Deezer-ARL-Cookie",
|
"https://github.com/nathom/streamrip/wiki/Finding-your-Deezer-ARL-Cookie",
|
||||||
underline=True,
|
underline=True,
|
||||||
italic=True,
|
italic=True,
|
||||||
fg="blue",
|
fg="blue",
|
||||||
)
|
)
|
||||||
config.file["deezer"]["arl"] = input(click.style("ARL: ", fg="green"))
|
config.file["deezer"]["arl"] = input(style("ARL: ", fg="green"))
|
||||||
config.save()
|
config.save()
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option(
|
@option(
|
||||||
"-sr", "--sampling-rate", help="Downsample the tracks to this rate, in Hz."
|
"-sr", "--sampling-rate", help="Downsample the tracks to this rate, in Hz."
|
||||||
)
|
)
|
||||||
@click.option("-bd", "--bit-depth", help="Downsample the tracks to this bit depth.")
|
@option("-bd", "--bit-depth", help="Downsample the tracks to this bit depth.")
|
||||||
@click.option(
|
@option(
|
||||||
"-k",
|
"-k",
|
||||||
"--keep-source",
|
"--keep-source",
|
||||||
is_flag=True,
|
is_flag=True,
|
||||||
help="Do not delete the old file after conversion.",
|
help="Do not delete the old file after conversion.",
|
||||||
)
|
)
|
||||||
@click.argument("CODEC")
|
@argument("CODEC")
|
||||||
@click.argument("PATH")
|
@argument("PATH")
|
||||||
@click.pass_context
|
@pass_context
|
||||||
def convert(ctx, **kwargs):
|
def convert(ctx, **kwargs):
|
||||||
"""Batch convert audio files.
|
"""Batch convert audio files.
|
||||||
|
|
||||||
|
@ -498,14 +498,14 @@ def convert(ctx, **kwargs):
|
||||||
elif os.path.isfile(kwargs["path"]):
|
elif os.path.isfile(kwargs["path"]):
|
||||||
codec_map[codec](filename=kwargs["path"], **converter_args).convert()
|
codec_map[codec](filename=kwargs["path"], **converter_args).convert()
|
||||||
else:
|
else:
|
||||||
click.secho(f"File {kwargs['path']} does not exist.", fg="red")
|
secho(f"File {kwargs['path']} does not exist.", fg="red")
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option(
|
@option(
|
||||||
"-n", "--num-items", help="The number of items to atttempt downloads for."
|
"-n", "--num-items", help="The number of items to atttempt downloads for."
|
||||||
)
|
)
|
||||||
@click.pass_context
|
@pass_context
|
||||||
def repair(ctx, **kwargs):
|
def repair(ctx, **kwargs):
|
||||||
"""Retry failed downloads.
|
"""Retry failed downloads.
|
||||||
|
|
||||||
|
@ -520,7 +520,7 @@ def repair(ctx, **kwargs):
|
||||||
|
|
||||||
def none_chosen():
|
def none_chosen():
|
||||||
"""Print message if nothing was chosen."""
|
"""Print message if nothing was chosen."""
|
||||||
click.secho("No items chosen, exiting.", fg="bright_red")
|
secho("No items chosen, exiting.", fg="bright_red")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
|
@ -7,7 +7,7 @@ from pathlib import Path
|
||||||
from click import style, secho
|
from click import style, secho
|
||||||
|
|
||||||
APPNAME = "streamrip"
|
APPNAME = "streamrip"
|
||||||
APP_DIR = click.get_app_dir(APPNAME)
|
APP_DIR = get_app_dir(APPNAME)
|
||||||
HOME = Path.home()
|
HOME = Path.home()
|
||||||
|
|
||||||
LOG_DIR = CACHE_DIR = CONFIG_DIR = APP_DIR
|
LOG_DIR = CACHE_DIR = CONFIG_DIR = APP_DIR
|
||||||
|
|
52
rip/core.py
52
rip/core.py
|
@ -162,8 +162,8 @@ class RipCore(list):
|
||||||
if not parsed and len(self) == 0:
|
if not parsed and len(self) == 0:
|
||||||
if "last.fm" in url:
|
if "last.fm" in url:
|
||||||
message = (
|
message = (
|
||||||
f"For last.fm urls, use the {click.style('lastfm', fg='yellow')} "
|
f"For last.fm urls, use the {style('lastfm', fg='yellow')} "
|
||||||
f"command. See {click.style('rip lastfm --help', fg='yellow')}."
|
f"command. See {style('rip lastfm --help', fg='yellow')}."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
message = f"Cannot find urls in text: {url}"
|
message = f"Cannot find urls in text: {url}"
|
||||||
|
@ -175,7 +175,7 @@ class RipCore(list):
|
||||||
logger.info(
|
logger.info(
|
||||||
f"ID {item_id} already downloaded, use --no-db to override."
|
f"ID {item_id} already downloaded, use --no-db to override."
|
||||||
)
|
)
|
||||||
click.secho(
|
secho(
|
||||||
f"ID {item_id} already downloaded, use --no-db to override.",
|
f"ID {item_id} already downloaded, use --no-db to override.",
|
||||||
fg="magenta",
|
fg="magenta",
|
||||||
)
|
)
|
||||||
|
@ -248,7 +248,7 @@ class RipCore(list):
|
||||||
max_items = float("inf")
|
max_items = float("inf")
|
||||||
|
|
||||||
if self.failed_db.is_dummy:
|
if self.failed_db.is_dummy:
|
||||||
click.secho(
|
secho(
|
||||||
"Failed downloads database must be enabled in the config file "
|
"Failed downloads database must be enabled in the config file "
|
||||||
"to repair!",
|
"to repair!",
|
||||||
fg="red",
|
fg="red",
|
||||||
|
@ -304,7 +304,7 @@ class RipCore(list):
|
||||||
item.load_meta(**arguments)
|
item.load_meta(**arguments)
|
||||||
except NonStreamable:
|
except NonStreamable:
|
||||||
self.failed_db.add((item.client.source, item.type, item.id))
|
self.failed_db.add((item.client.source, item.type, item.id))
|
||||||
click.secho(f"{item!s} is not available, skipping.", fg="red")
|
secho(f"{item!s} is not available, skipping.", fg="red")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -321,7 +321,7 @@ class RipCore(list):
|
||||||
self.failed_db.add(failed_item_info)
|
self.failed_db.add(failed_item_info)
|
||||||
continue
|
continue
|
||||||
except ItemExists as e:
|
except ItemExists as e:
|
||||||
click.secho(f'"{e!s}" already exists. Skipping.', fg="yellow")
|
secho(f'"{e!s}" already exists. Skipping.', fg="yellow")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if hasattr(item, "id"):
|
if hasattr(item, "id"):
|
||||||
|
@ -366,13 +366,13 @@ class RipCore(list):
|
||||||
creds = self.config.creds(client.source)
|
creds = self.config.creds(client.source)
|
||||||
if client.source == "deezer" and creds["arl"] == "":
|
if client.source == "deezer" and creds["arl"] == "":
|
||||||
if self.config.session["deezer"]["deezloader_warnings"]:
|
if self.config.session["deezer"]["deezloader_warnings"]:
|
||||||
click.secho(
|
secho(
|
||||||
"Falling back to Deezloader (max 320kbps MP3). If you have a subscription, run ",
|
"Falling back to Deezloader (max 320kbps MP3). If you have a subscription, run ",
|
||||||
nl=False,
|
nl=False,
|
||||||
fg="yellow",
|
fg="yellow",
|
||||||
)
|
)
|
||||||
click.secho("rip config --deezer ", nl=False, bold=True)
|
secho("rip config --deezer ", nl=False, bold=True)
|
||||||
click.secho("to download FLAC files.\n\n", fg="yellow")
|
secho("to download FLAC files.\n\n", fg="yellow")
|
||||||
raise DeezloaderFallback
|
raise DeezloaderFallback
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
@ -380,7 +380,7 @@ class RipCore(list):
|
||||||
client.login(**creds)
|
client.login(**creds)
|
||||||
break
|
break
|
||||||
except AuthenticationError:
|
except AuthenticationError:
|
||||||
click.secho("Invalid credentials, try again.", fg="yellow")
|
secho("Invalid credentials, try again.", fg="yellow")
|
||||||
self.prompt_creds(client.source)
|
self.prompt_creds(client.source)
|
||||||
creds = self.config.creds(client.source)
|
creds = self.config.creds(client.source)
|
||||||
except MissingCredentials:
|
except MissingCredentials:
|
||||||
|
@ -419,7 +419,7 @@ class RipCore(list):
|
||||||
|
|
||||||
interpreter_urls = QOBUZ_INTERPRETER_URL_REGEX.findall(url)
|
interpreter_urls = QOBUZ_INTERPRETER_URL_REGEX.findall(url)
|
||||||
if interpreter_urls:
|
if interpreter_urls:
|
||||||
click.secho(
|
secho(
|
||||||
"Extracting IDs from Qobuz interpreter urls. Use urls "
|
"Extracting IDs from Qobuz interpreter urls. Use urls "
|
||||||
"that include the artist ID for faster preprocessing.",
|
"that include the artist ID for faster preprocessing.",
|
||||||
fg="yellow",
|
fg="yellow",
|
||||||
|
@ -432,7 +432,7 @@ class RipCore(list):
|
||||||
|
|
||||||
dynamic_urls = DEEZER_DYNAMIC_LINK_REGEX.findall(url)
|
dynamic_urls = DEEZER_DYNAMIC_LINK_REGEX.findall(url)
|
||||||
if dynamic_urls:
|
if dynamic_urls:
|
||||||
click.secho(
|
secho(
|
||||||
"Extracting IDs from Deezer dynamic link. Use urls "
|
"Extracting IDs from Deezer dynamic link. Use urls "
|
||||||
"of the form https://www.deezer.com/{country}/{type}/{id} for "
|
"of the form https://www.deezer.com/{country}/{type}/{id} for "
|
||||||
"faster processing.",
|
"faster processing.",
|
||||||
|
@ -526,7 +526,7 @@ class RipCore(list):
|
||||||
from streamrip.utils import TQDM_BAR_FORMAT
|
from streamrip.utils import TQDM_BAR_FORMAT
|
||||||
|
|
||||||
for purl in lastfm_urls:
|
for purl in lastfm_urls:
|
||||||
click.secho(f"Fetching playlist at {purl}", fg="blue")
|
secho(f"Fetching playlist at {purl}", fg="blue")
|
||||||
title, queries = self.get_lastfm_playlist(purl)
|
title, queries = self.get_lastfm_playlist(purl)
|
||||||
|
|
||||||
pl = Playlist(client=self.get_client(lastfm_source), name=title)
|
pl = Playlist(client=self.get_client(lastfm_source), name=title)
|
||||||
|
@ -555,7 +555,7 @@ class RipCore(list):
|
||||||
pl.loaded = True
|
pl.loaded = True
|
||||||
|
|
||||||
if tracks_not_found > 0:
|
if tracks_not_found > 0:
|
||||||
click.secho(f"{tracks_not_found} tracks not found.", fg="yellow")
|
secho(f"{tracks_not_found} tracks not found.", fg="yellow")
|
||||||
|
|
||||||
self.append(pl)
|
self.append(pl)
|
||||||
|
|
||||||
|
@ -817,9 +817,9 @@ class RipCore(list):
|
||||||
:type source: str
|
:type source: str
|
||||||
"""
|
"""
|
||||||
if source == "qobuz":
|
if source == "qobuz":
|
||||||
click.secho("Enter Qobuz email:", fg="green")
|
secho("Enter Qobuz email:", fg="green")
|
||||||
self.config.file[source]["email"] = input()
|
self.config.file[source]["email"] = input()
|
||||||
click.secho(
|
secho(
|
||||||
"Enter Qobuz password (will not show on screen):",
|
"Enter Qobuz password (will not show on screen):",
|
||||||
fg="green",
|
fg="green",
|
||||||
)
|
)
|
||||||
|
@ -828,27 +828,27 @@ class RipCore(list):
|
||||||
).hexdigest()
|
).hexdigest()
|
||||||
|
|
||||||
self.config.save()
|
self.config.save()
|
||||||
click.secho(
|
secho(
|
||||||
f'Credentials saved to config file at "{self.config._path}"',
|
f'Credentials saved to config file at "{self.config._path}"',
|
||||||
fg="green",
|
fg="green",
|
||||||
)
|
)
|
||||||
elif source == "deezer":
|
elif source == "deezer":
|
||||||
click.secho(
|
secho(
|
||||||
"If you're not sure how to find the ARL cookie, see the instructions at ",
|
"If you're not sure how to find the ARL cookie, see the instructions at ",
|
||||||
italic=True,
|
italic=True,
|
||||||
nl=False,
|
nl=False,
|
||||||
dim=True,
|
dim=True,
|
||||||
)
|
)
|
||||||
click.secho(
|
secho(
|
||||||
"https://github.com/nathom/streamrip/wiki/Finding-your-Deezer-ARL-Cookie",
|
"https://github.com/nathom/streamrip/wiki/Finding-your-Deezer-ARL-Cookie",
|
||||||
underline=True,
|
underline=True,
|
||||||
italic=True,
|
italic=True,
|
||||||
fg="blue",
|
fg="blue",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.config.file["deezer"]["arl"] = input(click.style("ARL: ", fg="green"))
|
self.config.file["deezer"]["arl"] = input(style("ARL: ", fg="green"))
|
||||||
self.config.save()
|
self.config.save()
|
||||||
click.secho(
|
secho(
|
||||||
f'Credentials saved to config file at "{self.config._path}"',
|
f'Credentials saved to config file at "{self.config._path}"',
|
||||||
fg="green",
|
fg="green",
|
||||||
)
|
)
|
||||||
|
@ -856,19 +856,19 @@ class RipCore(list):
|
||||||
raise Exception
|
raise Exception
|
||||||
|
|
||||||
def _config_updating_message(self):
|
def _config_updating_message(self):
|
||||||
click.secho(
|
secho(
|
||||||
"Updating config file... Some settings may be lost. Please run the "
|
"Updating config file... Some settings may be lost. Please run the "
|
||||||
"command again.",
|
"command again.",
|
||||||
fg="magenta",
|
fg="magenta",
|
||||||
)
|
)
|
||||||
|
|
||||||
def _config_corrupted_message(self, err: Exception):
|
def _config_corrupted_message(self, err: Exception):
|
||||||
click.secho(
|
secho(
|
||||||
"There was a problem with your config file. This happens "
|
"There was a problem with your config file. This happens "
|
||||||
"sometimes after updates. Run ",
|
"sometimes after updates. Run ",
|
||||||
nl=False,
|
nl=False,
|
||||||
fg="red",
|
fg="red",
|
||||||
)
|
)
|
||||||
click.secho("rip config --reset ", fg="yellow", nl=False)
|
secho("rip config --reset ", fg="yellow", nl=False)
|
||||||
click.secho("to reset it. You will need to log in again.", fg="red")
|
secho("to reset it. You will need to log in again.", fg="red")
|
||||||
click.secho(str(err), fg="red")
|
secho(str(err), fg="red")
|
||||||
|
|
|
@ -855,7 +855,7 @@ class TidalClient(Client):
|
||||||
fg="blue",
|
fg="blue",
|
||||||
)
|
)
|
||||||
if launch:
|
if launch:
|
||||||
click.launch(login_link)
|
launch(login_link)
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
elapsed = 0.0
|
elapsed = 0.0
|
||||||
|
|
|
@ -50,7 +50,7 @@ class NonStreamable(Exception):
|
||||||
|
|
||||||
:param item:
|
:param item:
|
||||||
"""
|
"""
|
||||||
click.echo(self.print_msg(item))
|
echo(self.print_msg(item))
|
||||||
|
|
||||||
def print_msg(self, item) -> str:
|
def print_msg(self, item) -> str:
|
||||||
"""Return a generic readable message.
|
"""Return a generic readable message.
|
||||||
|
@ -59,12 +59,12 @@ class NonStreamable(Exception):
|
||||||
:type item: Media
|
:type item: Media
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
base_msg = [click.style(f"Unable to stream {item!s}.", fg="yellow")]
|
base_msg = [style(f"Unable to stream {item!s}.", fg="yellow")]
|
||||||
if self.message:
|
if self.message:
|
||||||
base_msg.extend(
|
base_msg.extend(
|
||||||
(
|
(
|
||||||
click.style("Message:", fg="yellow"),
|
style("Message:", fg="yellow"),
|
||||||
click.style(self.message, fg="red"),
|
style(self.message, fg="red"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -270,7 +270,7 @@ class Track(Media):
|
||||||
:type progress_bar: bool
|
:type progress_bar: bool
|
||||||
"""
|
"""
|
||||||
if not self.part_of_tracklist:
|
if not self.part_of_tracklist:
|
||||||
click.secho(f"Downloading {self!s}\n", bold=True)
|
secho(f"Downloading {self!s}\n", bold=True)
|
||||||
|
|
||||||
self._prepare_download(
|
self._prepare_download(
|
||||||
quality=quality,
|
quality=quality,
|
||||||
|
@ -311,7 +311,7 @@ class Track(Media):
|
||||||
words[0] + " " + " ".join(map(str.lower, words[1:])) + "."
|
words[0] + " " + " ".join(map(str.lower, words[1:])) + "."
|
||||||
)
|
)
|
||||||
|
|
||||||
click.secho(f"Panic: {e} dl_info = {dl_info}", fg="red")
|
secho(f"Panic: {e} dl_info = {dl_info}", fg="red")
|
||||||
raise NonStreamable
|
raise NonStreamable
|
||||||
|
|
||||||
_quick_download(download_url, self.path, desc=self._progress_desc)
|
_quick_download(download_url, self.path, desc=self._progress_desc)
|
||||||
|
@ -461,7 +461,7 @@ class Track(Media):
|
||||||
|
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
return click.style(f"Track {self.meta.tracknumber:02}", fg="blue")
|
return style(f"Track {self.meta.tracknumber:02}", fg="blue")
|
||||||
|
|
||||||
def download_cover(self, width=999999, height=999999):
|
def download_cover(self, width=999999, height=999999):
|
||||||
"""Download the cover art, if cover_url is given."""
|
"""Download the cover art, if cover_url is given."""
|
||||||
|
@ -666,7 +666,7 @@ class Track(Media):
|
||||||
"""
|
"""
|
||||||
if not self.downloaded:
|
if not self.downloaded:
|
||||||
logger.debug("Track not downloaded, skipping conversion")
|
logger.debug("Track not downloaded, skipping conversion")
|
||||||
click.secho("Track not downloaded, skipping conversion", fg="magenta")
|
secho("Track not downloaded, skipping conversion", fg="magenta")
|
||||||
return
|
return
|
||||||
|
|
||||||
CONV_CLASS = {
|
CONV_CLASS = {
|
||||||
|
@ -683,7 +683,7 @@ class Track(Media):
|
||||||
try:
|
try:
|
||||||
self.container = codec.upper()
|
self.container = codec.upper()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
click.secho("Error: No audio codec chosen to convert to.", fg="red")
|
secho("Error: No audio codec chosen to convert to.", fg="red")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
if not hasattr(self, "final_path"):
|
if not hasattr(self, "final_path"):
|
||||||
|
@ -691,7 +691,7 @@ class Track(Media):
|
||||||
|
|
||||||
if not os.path.isfile(self.path):
|
if not os.path.isfile(self.path):
|
||||||
logger.info("File %s does not exist. Skipping conversion.", self.path)
|
logger.info("File %s does not exist. Skipping conversion.", self.path)
|
||||||
click.secho(f"{self!s} does not exist. Skipping conversion.", fg="red")
|
secho(f"{self!s} does not exist. Skipping conversion.", fg="red")
|
||||||
return
|
return
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
|
@ -809,7 +809,7 @@ class Video(Media):
|
||||||
|
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
"""
|
"""
|
||||||
click.secho(
|
secho(
|
||||||
f"Downloading {self.title} (Video). This may take a while.",
|
f"Downloading {self.title} (Video). This may take a while.",
|
||||||
fg="blue",
|
fg="blue",
|
||||||
)
|
)
|
||||||
|
@ -944,7 +944,7 @@ class YoutubeVideo(Media):
|
||||||
:type youtube_video_downloads_folder: str
|
:type youtube_video_downloads_folder: str
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
"""
|
"""
|
||||||
click.secho(f"Downloading url {self.id}", fg="blue")
|
secho(f"Downloading url {self.id}", fg="blue")
|
||||||
filename_formatter = "%(track_number)s.%(track)s.%(container)s"
|
filename_formatter = "%(track_number)s.%(track)s.%(container)s"
|
||||||
filename = os.path.join(parent_folder, filename_formatter)
|
filename = os.path.join(parent_folder, filename_formatter)
|
||||||
|
|
||||||
|
@ -964,7 +964,7 @@ class YoutubeVideo(Media):
|
||||||
)
|
)
|
||||||
|
|
||||||
if download_youtube_videos:
|
if download_youtube_videos:
|
||||||
click.secho("Downloading video stream", fg="blue")
|
secho("Downloading video stream", fg="blue")
|
||||||
pv = subprocess.Popen(
|
pv = subprocess.Popen(
|
||||||
[
|
[
|
||||||
"youtube-dl",
|
"youtube-dl",
|
||||||
|
@ -1080,7 +1080,7 @@ class Tracklist(list):
|
||||||
# TODO: make this function return the items that have not been downloaded
|
# TODO: make this function return the items that have not been downloaded
|
||||||
failed_downloads: List[Tuple[str, str, str]] = []
|
failed_downloads: List[Tuple[str, str, str]] = []
|
||||||
if kwargs.get("concurrent_downloads", True):
|
if kwargs.get("concurrent_downloads", True):
|
||||||
click.echo() # To separate cover progress bars and the rest
|
echo() # To separate cover progress bars and the rest
|
||||||
with concurrent.futures.ThreadPoolExecutor(
|
with concurrent.futures.ThreadPoolExecutor(
|
||||||
kwargs.get("max_connections", 3)
|
kwargs.get("max_connections", 3)
|
||||||
) as executor:
|
) as executor:
|
||||||
|
@ -1101,7 +1101,7 @@ class Tracklist(list):
|
||||||
|
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
executor.shutdown()
|
executor.shutdown()
|
||||||
click.echo("Aborted! May take some time to shutdown.")
|
echo("Aborted! May take some time to shutdown.")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -1109,11 +1109,11 @@ class Tracklist(list):
|
||||||
if self.client.source != "soundcloud":
|
if self.client.source != "soundcloud":
|
||||||
# soundcloud only gets metadata after `target` is called
|
# soundcloud only gets metadata after `target` is called
|
||||||
# message will be printed in `target`
|
# message will be printed in `target`
|
||||||
click.secho(f'\nDownloading "{item!s}"', bold=True, fg="green")
|
secho(f'\nDownloading "{item!s}"', bold=True, fg="green")
|
||||||
try:
|
try:
|
||||||
target(item, **kwargs)
|
target(item, **kwargs)
|
||||||
except ItemExists:
|
except ItemExists:
|
||||||
click.secho(f"{item!s} exists. Skipping.", fg="yellow")
|
secho(f"{item!s} exists. Skipping.", fg="yellow")
|
||||||
except NonStreamable as e:
|
except NonStreamable as e:
|
||||||
e.print(item)
|
e.print(item)
|
||||||
failed_downloads.append((item.client.source, item.type, item.id))
|
failed_downloads.append((item.client.source, item.type, item.id))
|
||||||
|
@ -1259,7 +1259,7 @@ class Tracklist(list):
|
||||||
|
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
click.secho(
|
secho(
|
||||||
f"\n\nDownloading {self.title} ({self.__class__.__name__})\n",
|
f"\n\nDownloading {self.title} ({self.__class__.__name__})\n",
|
||||||
fg="magenta",
|
fg="magenta",
|
||||||
bold=True,
|
bold=True,
|
||||||
|
@ -1417,7 +1417,7 @@ class Album(Tracklist, Media):
|
||||||
self.download_message()
|
self.download_message()
|
||||||
|
|
||||||
# choose optimal cover size and download it
|
# choose optimal cover size and download it
|
||||||
click.secho("Downloading cover art", bold=True)
|
secho("Downloading cover art", bold=True)
|
||||||
cover_path = os.path.join(gettempdir(), f"cover_{hash(self)}.jpg")
|
cover_path = os.path.join(gettempdir(), f"cover_{hash(self)}.jpg")
|
||||||
embed_cover_size = kwargs.get("embed_cover_size", "large")
|
embed_cover_size = kwargs.get("embed_cover_size", "large")
|
||||||
|
|
||||||
|
@ -1441,7 +1441,7 @@ class Album(Tracklist, Media):
|
||||||
|
|
||||||
cover_size = os.path.getsize(cover_path)
|
cover_size = os.path.getsize(cover_path)
|
||||||
if cover_size > FLAC_MAX_BLOCKSIZE: # 16.77 MB
|
if cover_size > FLAC_MAX_BLOCKSIZE: # 16.77 MB
|
||||||
click.secho(
|
secho(
|
||||||
"Downgrading embedded cover size, too large ({cover_size}).",
|
"Downgrading embedded cover size, too large ({cover_size}).",
|
||||||
fg="bright_yellow",
|
fg="bright_yellow",
|
||||||
)
|
)
|
||||||
|
@ -1468,7 +1468,7 @@ class Album(Tracklist, Media):
|
||||||
and kwargs.get("download_booklets", True)
|
and kwargs.get("download_booklets", True)
|
||||||
and not any(f.endswith(".pdf") for f in os.listdir(self.folder))
|
and not any(f.endswith(".pdf") for f in os.listdir(self.folder))
|
||||||
):
|
):
|
||||||
click.secho("\nDownloading booklets", bold=True)
|
secho("\nDownloading booklets", bold=True)
|
||||||
for item in self.booklets:
|
for item in self.booklets:
|
||||||
Booklet(item).download(parent_folder=self.folder)
|
Booklet(item).download(parent_folder=self.folder)
|
||||||
|
|
||||||
|
@ -1778,7 +1778,7 @@ class Playlist(Tracklist, Media):
|
||||||
kwargs["parent_folder"] = self.folder
|
kwargs["parent_folder"] = self.folder
|
||||||
if self.client.source == "soundcloud":
|
if self.client.source == "soundcloud":
|
||||||
item.load_meta()
|
item.load_meta()
|
||||||
click.secho(f"Downloading {item!s}", fg="blue")
|
secho(f"Downloading {item!s}", fg="blue")
|
||||||
|
|
||||||
if kwargs.get("set_playlist_to_album", False):
|
if kwargs.get("set_playlist_to_album", False):
|
||||||
item.meta.album = self.name
|
item.meta.album = self.name
|
||||||
|
@ -2211,4 +2211,4 @@ def _quick_download(url: str, path: str, desc: str = None):
|
||||||
|
|
||||||
|
|
||||||
def _cover_download(url: str, path: str):
|
def _cover_download(url: str, path: str):
|
||||||
_quick_download(url, path, click.style("Cover", fg="blue"))
|
_quick_download(url, path, style("Cover", fg="blue"))
|
||||||
|
|
|
@ -299,13 +299,13 @@ def decrypt_mqa_file(in_path, out_path, encryption_key):
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from Crypto.Util import Counter
|
from Crypto.Util import Counter
|
||||||
except (ImportError, ModuleNotFoundError):
|
except (ImportError, ModuleNotFoundError):
|
||||||
click.secho(
|
secho(
|
||||||
"To download this item in MQA, you need to run ",
|
"To download this item in MQA, you need to run ",
|
||||||
fg="yellow",
|
fg="yellow",
|
||||||
nl=False,
|
nl=False,
|
||||||
)
|
)
|
||||||
click.secho("pip3 install pycryptodome --upgrade", fg="blue", nl=False)
|
secho("pip3 install pycryptodome --upgrade", fg="blue", nl=False)
|
||||||
click.secho(".")
|
secho(".")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
# Do not change this
|
# Do not change this
|
||||||
|
@ -385,7 +385,7 @@ def decho(message, fg=None):
|
||||||
:param fg: ANSI color with which to display the message on the
|
:param fg: ANSI color with which to display the message on the
|
||||||
screen
|
screen
|
||||||
"""
|
"""
|
||||||
click.secho(message, fg=fg)
|
secho(message, fg=fg)
|
||||||
logger.debug(message)
|
logger.debug(message)
|
||||||
|
|
||||||
|
|
||||||
|
@ -479,9 +479,9 @@ TQDM_THEMES = {
|
||||||
"plain": None,
|
"plain": None,
|
||||||
"dainty": (
|
"dainty": (
|
||||||
"{desc} |{bar}| "
|
"{desc} |{bar}| "
|
||||||
+ click.style("{remaining}", fg="magenta")
|
+ style("{remaining}", fg="magenta")
|
||||||
+ " left at "
|
+ " left at "
|
||||||
+ click.style("{rate_fmt}{postfix} ", fg="cyan", bold=True)
|
+ style("{rate_fmt}{postfix} ", fg="cyan", bold=True)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,14 +44,14 @@ def download_albums():
|
||||||
procs.append(subprocess.run([*rip_url, url]))
|
procs.append(subprocess.run([*rip_url, url]))
|
||||||
|
|
||||||
for p in procs:
|
for p in procs:
|
||||||
click.echo(p)
|
echo(p)
|
||||||
|
|
||||||
|
|
||||||
def check_album_dl_success(folder, correct):
|
def check_album_dl_success(folder, correct):
|
||||||
if set(os.listdir(folder)) != set(correct):
|
if set(os.listdir(folder)) != set(correct):
|
||||||
click.secho(f"Check for {folder} failed!", fg="red")
|
secho(f"Check for {folder} failed!", fg="red")
|
||||||
else:
|
else:
|
||||||
click.secho(f"Check for {folder} succeeded!", fg="green")
|
secho(f"Check for {folder} succeeded!", fg="green")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue