mirror of
https://github.com/KevinMidboe/spotify-downloader.git
synced 2025-10-29 18:00:15 +00:00
Setup coloredlogs
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,7 @@ config.yml
|
|||||||
Music/
|
Music/
|
||||||
*.txt
|
*.txt
|
||||||
*.m3u
|
*.m3u
|
||||||
|
.cache-*
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
3
setup.py
3
setup.py
@@ -73,5 +73,6 @@ setup(
|
|||||||
"Topic :: Multimedia :: Sound/Audio",
|
"Topic :: Multimedia :: Sound/Audio",
|
||||||
"Topic :: Utilities",
|
"Topic :: Utilities",
|
||||||
],
|
],
|
||||||
entry_points={"console_scripts": ["spotdl = spotdl.command_line.__main__:main"]},
|
# entry_points={"console_scripts": ["spotdl = spotdl.command_line.__main__:main"]},
|
||||||
|
entry_points={"console_scripts": ["spotdl = spotdl:main"]},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
from spotdl.command_line.__main__ import main
|
||||||
|
|
||||||
from spotdl.version import __version__
|
from spotdl.version import __version__
|
||||||
from spotdl.command_line import Spotdl
|
from spotdl.command_line.lib import Spotdl
|
||||||
from spotdl.track import Track
|
from spotdl.track import Track
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
from spotdl.command_line import lib
|
|
||||||
from spotdl.command_line.arguments import get_arguments
|
|
||||||
from spotdl.command_line.lib import Spotdl
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,37 @@
|
|||||||
import logzero
|
import logging
|
||||||
|
for module in ("urllib3", "spotipy", "pytube",):
|
||||||
|
logging.getLogger(module).setLevel(logging.CRITICAL)
|
||||||
|
|
||||||
|
import coloredlogs
|
||||||
|
coloredlogs.DEFAULT_FIELD_STYLES = {
|
||||||
|
"levelname": {"bold": True, "color": "yellow"},
|
||||||
|
"name": {"color": "blue"},
|
||||||
|
"lineno": {"color": "magenta"},
|
||||||
|
}
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from spotdl import command_line
|
def set_logger(level):
|
||||||
|
if level == logging.DEBUG:
|
||||||
|
fmt = "%(levelname)s:%(name)s:%(lineno)d:\n%(message)s"
|
||||||
|
else:
|
||||||
|
fmt = "%(levelname)s: %(message)s"
|
||||||
|
logging.basicConfig(format=fmt, level=level)
|
||||||
|
logger = logging.getLogger(name=__name__)
|
||||||
|
coloredlogs.install(level=level, fmt=fmt, logger=logger)
|
||||||
|
return logger
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
arguments = command_line.get_arguments()
|
from spotdl.command_line.arguments import get_arguments
|
||||||
spotdl = command_line.Spotdl(arguments.__dict__)
|
arguments = get_arguments()
|
||||||
|
logger = set_logger(arguments["log_level"])
|
||||||
|
from spotdl.command_line.lib import Spotdl
|
||||||
|
spotdl = Spotdl(arguments)
|
||||||
try:
|
try:
|
||||||
spotdl.match_arguments()
|
spotdl.match_arguments()
|
||||||
except KeyboardInterrupt as e:
|
except KeyboardInterrupt as e:
|
||||||
logzero.logger.exception(e)
|
logger.exception(e)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from logzero import logger as log
|
|
||||||
import appdirs
|
import appdirs
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@@ -284,26 +283,26 @@ def run_errands(parser, parsed, config):
|
|||||||
|
|
||||||
encoder_exists = shutil.which(parsed.encoder)
|
encoder_exists = shutil.which(parsed.encoder)
|
||||||
if not encoder_exists:
|
if not encoder_exists:
|
||||||
# log.warn("Specified encoder () was not found. Will not encode to specified "
|
logger.warn("Specified encoder () was not found. Will not encode to specified "
|
||||||
# "output format".format(parsed.encoder))
|
"output format".format(parsed.encoder))
|
||||||
parsed.encoder = "null"
|
parsed.encoder = "null"
|
||||||
|
|
||||||
if parsed.output_file == "-" and parsed.no_metadata is False:
|
if parsed.output_file == "-" and parsed.no_metadata is False:
|
||||||
# log.warn(
|
logger.warn(
|
||||||
# "Cannot write metadata when target file is STDOUT. Pass "
|
"Cannot write metadata when target file is STDOUT. Pass "
|
||||||
# "--no-metadata explicitly to hide this warning."
|
"--no-metadata explicitly to hide this warning."
|
||||||
# )
|
)
|
||||||
parsed.no_metadata = True
|
parsed.no_metadata = True
|
||||||
elif os.path.isdir(parsed.output_file):
|
elif os.path.isdir(parsed.output_file):
|
||||||
adjusted_output_file = os.path.join(
|
adjusted_output_file = os.path.join(
|
||||||
parsed.output_file,
|
parsed.output_file,
|
||||||
config["output-file"]
|
config["output-file"]
|
||||||
)
|
)
|
||||||
# log.warn(
|
logger.warn(
|
||||||
# "Specified output file is a directory. Will write the filename as in
|
"Specified output file is a directory. Will write the filename as in "
|
||||||
# "default file format. Pass --output-file={} to hide this warning".format(
|
"default file format. Pass --output-file={} to hide this warning".format(
|
||||||
# adjusted_output_file
|
adjusted_output_file
|
||||||
# )
|
))
|
||||||
parsed.output_file = adjusted_output_file
|
parsed.output_file = adjusted_output_file
|
||||||
|
|
||||||
parsed.log_level = log_leveller(parsed.log_level)
|
parsed.log_level = log_leveller(parsed.log_level)
|
||||||
@@ -311,5 +310,5 @@ def run_errands(parser, parsed, config):
|
|||||||
# We're done dealing with configuration file here and don't need to use it later
|
# We're done dealing with configuration file here and don't need to use it later
|
||||||
del parsed.config
|
del parsed.config
|
||||||
|
|
||||||
return parsed
|
return parsed.__dict__
|
||||||
|
|
||||||
|
|||||||
@@ -20,18 +20,11 @@ import spotdl.config
|
|||||||
|
|
||||||
from spotdl.command_line.exceptions import NoYouTubeVideoError
|
from spotdl.command_line.exceptions import NoYouTubeVideoError
|
||||||
|
|
||||||
import logzero
|
|
||||||
import os
|
import os
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
|
import logging
|
||||||
def set_logger(level):
|
logger = logging.getLogger(name=__name__)
|
||||||
fmt = "%(color)s%(levelname)s:%(end_color)s %(message)s"
|
|
||||||
formatter = logzero.LogFormatter(fmt=fmt)
|
|
||||||
logzero.formatter(formatter)
|
|
||||||
logzero.loglevel(level)
|
|
||||||
return logzero.logger
|
|
||||||
|
|
||||||
|
|
||||||
def search_lyrics(query):
|
def search_lyrics(query):
|
||||||
provider = Genius()
|
provider = Genius()
|
||||||
@@ -116,8 +109,7 @@ def search_metadata(track, search_format="{artist} - {track-name} lyrics", manua
|
|||||||
return
|
return
|
||||||
if manual:
|
if manual:
|
||||||
youtube_video = prompt_for_youtube_search_result(youtube_videos)
|
youtube_video = prompt_for_youtube_search_result(youtube_videos)
|
||||||
else:
|
else: youtube_video = youtube_videos.bestmatch()["url"]
|
||||||
youtube_video = youtube_videos.bestmatch()["url"]
|
|
||||||
youtube_metadata = youtube.from_url(youtube_video)
|
youtube_metadata = youtube.from_url(youtube_video)
|
||||||
metadata = spotdl.util.merge(
|
metadata = spotdl.util.merge(
|
||||||
youtube_metadata,
|
youtube_metadata,
|
||||||
@@ -160,8 +152,6 @@ class Spotdl:
|
|||||||
client_id=self.arguments["spotify_client_id"],
|
client_id=self.arguments["spotify_client_id"],
|
||||||
client_secret=self.arguments["spotify_client_secret"]
|
client_secret=self.arguments["spotify_client_secret"]
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = set_logger(self.arguments["log_level"])
|
|
||||||
logger.debug(self.arguments)
|
logger.debug(self.arguments)
|
||||||
|
|
||||||
# youtube_tools.set_api_key()
|
# youtube_tools.set_api_key()
|
||||||
@@ -220,7 +210,7 @@ class Spotdl:
|
|||||||
metadata,
|
metadata,
|
||||||
output_extension=self.arguments["output_ext"],
|
output_extension=self.arguments["output_ext"],
|
||||||
)
|
)
|
||||||
# log.info(log_fmt)
|
logger.info(log_fmt)
|
||||||
self.download_track_from_metadata(metadata)
|
self.download_track_from_metadata(metadata)
|
||||||
|
|
||||||
def download_track_from_metadata(self, metadata):
|
def download_track_from_metadata(self, metadata):
|
||||||
@@ -232,7 +222,7 @@ class Spotdl:
|
|||||||
quality=self.arguments["quality"],
|
quality=self.arguments["quality"],
|
||||||
preftype=self.arguments["input_ext"],
|
preftype=self.arguments["input_ext"],
|
||||||
)
|
)
|
||||||
# log.info(stream)
|
logger.info(stream)
|
||||||
|
|
||||||
Encoder = {
|
Encoder = {
|
||||||
"ffmpeg": EncoderFFmpeg,
|
"ffmpeg": EncoderFFmpeg,
|
||||||
@@ -253,7 +243,7 @@ class Spotdl:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(filename)
|
print(filename)
|
||||||
# log.info(filename)
|
logger.info(filename)
|
||||||
|
|
||||||
to_skip = self.arguments["dry_run"]
|
to_skip = self.arguments["dry_run"]
|
||||||
if not to_skip and os.path.isfile(filename):
|
if not to_skip and os.path.isfile(filename):
|
||||||
@@ -282,14 +272,13 @@ class Spotdl:
|
|||||||
try:
|
try:
|
||||||
track.apply_metadata(filename, encoding=output_extension)
|
track.apply_metadata(filename, encoding=output_extension)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# log.warning("Cannot write metadata to given file")
|
logger.warning("Cannot write metadata to given file")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def download_tracks_from_file(self, path):
|
def download_tracks_from_file(self, path):
|
||||||
# log.info(
|
logger.info(
|
||||||
# "Checking and removing any duplicate tracks "
|
"Checking and removing any duplicate tracks in {}".format(path)
|
||||||
# "in reading {}".format(path)
|
)
|
||||||
# )
|
|
||||||
with open(path, "r") as fin:
|
with open(path, "r") as fin:
|
||||||
# Read tracks into a list and remove any duplicates
|
# Read tracks into a list and remove any duplicates
|
||||||
tracks = fin.read().splitlines()
|
tracks = fin.read().splitlines()
|
||||||
@@ -310,14 +299,14 @@ class Spotdl:
|
|||||||
try:
|
try:
|
||||||
metadata = search_metadata(track, self.arguments["search_format"])
|
metadata = search_metadata(track, self.arguments["search_format"])
|
||||||
log_fmt=(str(number) + ". {artist} - {track-name}")
|
log_fmt=(str(number) + ". {artist} - {track-name}")
|
||||||
# log.info(log_fmt)
|
logger.info(log_fmt)
|
||||||
self.download_track_from_metadata(metadata)
|
self.download_track_from_metadata(metadata)
|
||||||
except (urllib.request.URLError, TypeError, IOError) as e:
|
except (urllib.request.URLError, TypeError, IOError) as e:
|
||||||
# log.exception(e.args[0])
|
logger.exception(e.args[0])
|
||||||
# log.warning("Failed. Will retry after other songs\n")
|
logger.warning("Failed. Will retry after other songs\n")
|
||||||
tracks.append(track)
|
tracks.append(track)
|
||||||
except NoYouTubeVideoError:
|
except NoYouTubeVideoError:
|
||||||
# log.warning("Failed. No YouTube video found.\n")
|
logger.warning("Failed. No YouTube video found.\n")
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if self.arguments["write_successful"]:
|
if self.arguments["write_successful"]:
|
||||||
@@ -330,10 +319,9 @@ class Spotdl:
|
|||||||
def download_tracks_from_file_threaded(self, path):
|
def download_tracks_from_file_threaded(self, path):
|
||||||
# FIXME: Can we make this function cleaner?
|
# FIXME: Can we make this function cleaner?
|
||||||
|
|
||||||
# log.info(
|
logger.info(
|
||||||
# "Checking and removing any duplicate tracks "
|
"Checking and removing any duplicate tracks in {}".format(path)
|
||||||
# "in reading {}".format(path)
|
)
|
||||||
# )
|
|
||||||
with open(path, "r") as fin:
|
with open(path, "r") as fin:
|
||||||
# Read tracks into a list and remove any duplicates
|
# Read tracks into a list and remove any duplicates
|
||||||
tracks = fin.read().splitlines()
|
tracks = fin.read().splitlines()
|
||||||
@@ -378,15 +366,15 @@ class Spotdl:
|
|||||||
metadata["next_track"].start()
|
metadata["next_track"].start()
|
||||||
|
|
||||||
log_fmt=(str(current_iteration) + ". {artist} - {track-name}")
|
log_fmt=(str(current_iteration) + ". {artist} - {track-name}")
|
||||||
# log.info(log_fmt)
|
logger.info(log_fmt)
|
||||||
if metadata["current_track"] is None:
|
if metadata["current_track"] is None:
|
||||||
# log.warning("Something went wrong. Will retry after downloading remaining tracks")
|
logger.warning("Something went wrong. Will retry after downloading remaining tracks")
|
||||||
pass
|
pass
|
||||||
print(metadata["current_track"]["name"])
|
print(metadata["current_track"]["name"])
|
||||||
# self.download_track_from_metadata(metadata["current_track"])
|
# self.download_track_from_metadata(metadata["current_track"])
|
||||||
except (urllib.request.URLError, TypeError, IOError) as e:
|
except (urllib.request.URLError, TypeError, IOError) as e:
|
||||||
# log.exception(e.args[0])
|
logger.exception(e.args[0])
|
||||||
# log.warning("Failed. Will retry after other songs\n")
|
logger.warning("Failed. Will retry after other songs\n")
|
||||||
tracks.append(current_track)
|
tracks.append(current_track)
|
||||||
else:
|
else:
|
||||||
tracks_count -= 1
|
tracks_count -= 1
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import yaml
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import spotdl.util
|
import spotdl.util
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_CONFIGURATION = {
|
DEFAULT_CONFIGURATION = {
|
||||||
"spotify-downloader": {
|
"spotify-downloader": {
|
||||||
@@ -56,17 +58,17 @@ def get_config(config_file):
|
|||||||
config = DEFAULT_CONFIGURATION
|
config = DEFAULT_CONFIGURATION
|
||||||
dump_config(config_file, config=DEFAULT_CONFIGURATION)
|
dump_config(config_file, config=DEFAULT_CONFIGURATION)
|
||||||
|
|
||||||
# log.info("Writing default configuration to {0}:".format(config_file))
|
logger.info("Writing default configuration to {0}:".format(config_file))
|
||||||
|
|
||||||
# for line in yaml.dump(
|
for line in yaml.dump(
|
||||||
# DEFAULT_CONFIGURATION["spotify-downloader"], default_flow_style=False
|
DEFAULT_CONFIGURATION["spotify-downloader"], default_flow_style=False
|
||||||
# ).split("\n"):
|
).split("\n"):
|
||||||
# if line.strip():
|
if line.strip():
|
||||||
# log.info(line.strip())
|
log.info(line.strip())
|
||||||
# log.info(
|
logger.info(
|
||||||
# "Please note that command line arguments have higher priority "
|
"Please note that command line arguments have higher priority "
|
||||||
# "than their equivalents in the configuration file"
|
"than their equivalents in the configuration file"
|
||||||
# )
|
)
|
||||||
|
|
||||||
return config["spotify-downloader"]
|
return config["spotify-downloader"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
from logzero import logger as log
|
# from logzero import logger as log
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from spotdl.encode import EncoderBase
|
from spotdl.encode import EncoderBase
|
||||||
from spotdl.encode.exceptions import EncoderNotFoundError
|
from spotdl.encode.exceptions import EncoderNotFoundError
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
from logzero import logger as log
|
# from logzero import logger as log
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from spotdl.encode import EncoderBase
|
from spotdl.encode import EncoderBase
|
||||||
from spotdl.encode.exceptions import EncoderNotFoundError
|
from spotdl.encode.exceptions import EncoderNotFoundError
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
from spotdl.authorize.services import AuthorizeSpotify
|
from spotdl.authorize.services import AuthorizeSpotify
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class SpotifyHelpers:
|
class SpotifyHelpers:
|
||||||
def __init__(self, spotify=None):
|
def __init__(self, spotify=None):
|
||||||
if spotify is None:
|
if spotify is None:
|
||||||
@@ -28,7 +31,7 @@ class SpotifyHelpers:
|
|||||||
# in rare cases, playlists may not be found, so playlists['next']
|
# in rare cases, playlists may not be found, so playlists['next']
|
||||||
# is None. Skip these. Also see Issue #91.
|
# is None. Skip these. Also see Issue #91.
|
||||||
if playlist["name"] is not None:
|
if playlist["name"] is not None:
|
||||||
# log.info(
|
logger.info(
|
||||||
# u"{0:>5}. {1:<30} ({2} tracks)".format(
|
# u"{0:>5}. {1:<30} ({2} tracks)".format(
|
||||||
# check, playlist["name"], playlist["tracks"]["total"]
|
# check, playlist["name"], playlist["tracks"]["total"]
|
||||||
# )
|
# )
|
||||||
@@ -57,7 +60,7 @@ class SpotifyHelpers:
|
|||||||
)
|
)
|
||||||
except spotipy.client.SpotifyException:
|
except spotipy.client.SpotifyException:
|
||||||
# log.error("Unable to find playlist")
|
# log.error("Unable to find playlist")
|
||||||
# log.info("Make sure the playlist is set to publicly visible and then try again")
|
logger.info("Make sure the playlist is set to publicly visible and then try again")
|
||||||
sys.exit(11)
|
sys.exit(11)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
@@ -125,7 +128,7 @@ class SpotifyHelpers:
|
|||||||
write_album(album_base_url + album["id"], text_file=text_file)
|
write_album(album_base_url + album["id"], text_file=text_file)
|
||||||
|
|
||||||
def write_tracks(self, tracks, text_file):
|
def write_tracks(self, tracks, text_file):
|
||||||
# log.info(u"Writing {0} tracks to {1}".format(tracks["total"], text_file))
|
logger.info(u"Writing {0} tracks to {1}".format(tracks["total"], text_file))
|
||||||
track_urls = []
|
track_urls = []
|
||||||
with open(text_file, "a") as file_out:
|
with open(text_file, "a") as file_out:
|
||||||
while True:
|
while True:
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ from spotdl.metadata.exceptions import YouTubeMetadataNotFoundError
|
|||||||
|
|
||||||
import spotdl.util
|
import spotdl.util
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
BASE_SEARCH_URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q={}"
|
BASE_SEARCH_URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q={}"
|
||||||
HEADERS = [('Range', 'bytes=0-'),]
|
HEADERS = [('Range', 'bytes=0-'),]
|
||||||
|
|
||||||
@@ -96,7 +99,7 @@ class YouTubeSearch:
|
|||||||
def search(self, query, limit=10):
|
def search(self, query, limit=10):
|
||||||
""" Search and scrape YouTube to return a list of matching videos. """
|
""" Search and scrape YouTube to return a list of matching videos. """
|
||||||
search_url = self.generate_search_url(query)
|
search_url = self.generate_search_url(query)
|
||||||
# log.debug("Opening URL: {0}".format(search_url))
|
logger.debug("Opening URL: {0}".format(search_url))
|
||||||
html = self._fetch_response_html(search_url)
|
html = self._fetch_response_html(search_url)
|
||||||
|
|
||||||
videos = self._fetch_search_results(html)
|
videos = self._fetch_search_results(html)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
from logzero import logger as log
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import math
|
import math
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import winreg
|
import winreg
|
||||||
@@ -15,8 +16,8 @@ except ImportError:
|
|||||||
try:
|
try:
|
||||||
from slugify import SLUG_OK, slugify
|
from slugify import SLUG_OK, slugify
|
||||||
except ImportError:
|
except ImportError:
|
||||||
log.error("Oops! `unicode-slugify` was not found.")
|
logger.error("Oops! `unicode-slugify` was not found.")
|
||||||
log.info("Please remove any other slugify library and install `unicode-slugify`")
|
logger.info("Please remove any other slugify library and install `unicode-slugify`")
|
||||||
sys.exit(5)
|
sys.exit(5)
|
||||||
|
|
||||||
|
|
||||||
@@ -53,19 +54,16 @@ def prompt_user_for_selection(items):
|
|||||||
""" Let the user input a choice. """
|
""" Let the user input a choice. """
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
# log.info("Choose your number:")
|
logger.info("Choose your number:")
|
||||||
print("Choose your number:")
|
|
||||||
the_chosen_one = int(input("> "))
|
the_chosen_one = int(input("> "))
|
||||||
if 1 <= the_chosen_one <= len(items):
|
if 1 <= the_chosen_one <= len(items):
|
||||||
return items[the_chosen_one - 1]
|
return items[the_chosen_one - 1]
|
||||||
elif the_chosen_one == 0:
|
elif the_chosen_one == 0:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# log.warning("Choose a valid number!")
|
logger.warning("Choose a valid number!")
|
||||||
print("Chose a valid number!")
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# log.warning("Choose a valid number!")
|
logger.warning("Choose a valid number!")
|
||||||
print("Chose a valid number!")
|
|
||||||
|
|
||||||
|
|
||||||
def is_spotify(raw_song):
|
def is_spotify(raw_song):
|
||||||
|
|||||||
Reference in New Issue
Block a user