mirror of
https://github.com/KevinMidboe/spotify-downloader.git
synced 2025-10-29 18:00:15 +00:00
Merge options -f and -ff into -f
This commit is contained in:
@@ -8,7 +8,10 @@ def match_arguments(arguments):
|
|||||||
for track in arguments.tracks:
|
for track in arguments.tracks:
|
||||||
if track == "-":
|
if track == "-":
|
||||||
for line in sys.stdin:
|
for line in sys.stdin:
|
||||||
command_line.helpers.download_track(line, arguments)
|
command_line.helpers.download_track(
|
||||||
|
line,
|
||||||
|
arguments
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
command_line.helpers.download_track(track, arguments)
|
command_line.helpers.download_track(track, arguments)
|
||||||
elif arguments.list:
|
elif arguments.list:
|
||||||
|
|||||||
@@ -136,12 +136,6 @@ def get_arguments(argv=None, to_merge=True):
|
|||||||
choices={"ffmpeg", "avconv", "null"},
|
choices={"ffmpeg", "avconv", "null"},
|
||||||
help="use this encoder for conversion",
|
help="use this encoder for conversion",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"-f",
|
|
||||||
"--directory",
|
|
||||||
default=os.path.abspath(config["directory"]),
|
|
||||||
help="path to directory where downloaded tracks will be stored in",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--overwrite",
|
"--overwrite",
|
||||||
default=config["overwrite"],
|
default=config["overwrite"],
|
||||||
@@ -175,11 +169,11 @@ def get_arguments(argv=None, to_merge=True):
|
|||||||
help="write tracks from Spotify playlist, album, etc. to this file",
|
help="write tracks from Spotify playlist, album, etc. to this file",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-ff",
|
"-f",
|
||||||
"--file-format",
|
"--output-file",
|
||||||
default=config["file-format"],
|
default=config["output-file"],
|
||||||
help="file format to save the downloaded track with, each tag "
|
help="path where to write the downloaded track to, special tags "
|
||||||
"is surrounded by curly braces. Possible formats: "
|
"are to be surrounded by curly braces. Possible tags: "
|
||||||
# "{}".format([spotdl.util.formats[x] for x in spotdl.util.formats]),
|
# "{}".format([spotdl.util.formats[x] for x in spotdl.util.formats]),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -192,8 +186,8 @@ def get_arguments(argv=None, to_merge=True):
|
|||||||
"-sf",
|
"-sf",
|
||||||
"--search-format",
|
"--search-format",
|
||||||
default=config["search-format"],
|
default=config["search-format"],
|
||||||
help="search format to search for on YouTube, each tag "
|
help="search format to search for on YouTube, special tags "
|
||||||
"is surrounded by curly braces. Possible formats: "
|
"are to be surrounded by curly braces. Possible tags: "
|
||||||
# "{}".format([spotdl.util.formats[x] for x in spotdl.util.formats]),
|
# "{}".format([spotdl.util.formats[x] for x in spotdl.util.formats]),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -289,10 +283,10 @@ def get_arguments(argv=None, to_merge=True):
|
|||||||
if parsed.config is not None and to_merge:
|
if parsed.config is not None and to_merge:
|
||||||
parsed = override_config(parsed.config, parser)
|
parsed = override_config(parsed.config, parser)
|
||||||
|
|
||||||
return run_errands(parser, parsed)
|
return run_errands(parser, parsed, config)
|
||||||
|
|
||||||
|
|
||||||
def run_errands(parser, parsed):
|
def run_errands(parser, parsed, config):
|
||||||
if (parsed.list
|
if (parsed.list
|
||||||
and not mimetypes.MimeTypes().guess_type(parsed.list)[0] == "text/plain"
|
and not mimetypes.MimeTypes().guess_type(parsed.list)[0] == "text/plain"
|
||||||
):
|
):
|
||||||
@@ -334,12 +328,23 @@ def run_errands(parser, parsed):
|
|||||||
setattr(parsed, "tracks", parsed.song)
|
setattr(parsed, "tracks", parsed.song)
|
||||||
del parsed.song
|
del parsed.song
|
||||||
|
|
||||||
if parsed.file_format == "-" and parsed.no_metadata is False:
|
if parsed.output_file == "-" and parsed.no_metadata is False:
|
||||||
# log.warn(
|
# log.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):
|
||||||
|
adjusted_output_file = os.path.join(
|
||||||
|
parsed.output_file,
|
||||||
|
config["output-file"]
|
||||||
|
)
|
||||||
|
# log.warn(
|
||||||
|
# "Specified output file is a directory. Will write the filename as in
|
||||||
|
# "default file format. Pass --output-file={} to hide this warning".format(
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from spotdl.metadata.providers import ProviderYouTube
|
|||||||
from spotdl.metadata.providers import YouTubeSearch
|
from spotdl.metadata.providers import YouTubeSearch
|
||||||
from spotdl.metadata.embedders import EmbedderDefault
|
from spotdl.metadata.embedders import EmbedderDefault
|
||||||
from spotdl.metadata.exceptions import SpotifyMetadataNotFoundError
|
from spotdl.metadata.exceptions import SpotifyMetadataNotFoundError
|
||||||
|
import spotdl.metadata
|
||||||
|
|
||||||
from spotdl.encode.encoders import EncoderFFmpeg
|
from spotdl.encode.encoders import EncoderFFmpeg
|
||||||
from spotdl.encode.encoders import EncoderAvconv
|
from spotdl.encode.encoders import EncoderAvconv
|
||||||
@@ -46,17 +47,16 @@ def search_metadata(track, search_format="{artist} - {track-name} lyrics", manua
|
|||||||
if spotdl.util.is_spotify(track):
|
if spotdl.util.is_spotify(track):
|
||||||
spotify = ProviderSpotify()
|
spotify = ProviderSpotify()
|
||||||
spotify_metadata = spotify.from_url(track)
|
spotify_metadata = spotify.from_url(track)
|
||||||
lyric_query = spotdl.util.format_string(
|
lyric_query = spotdl.metadata.format_string(
|
||||||
"{artist} - {track-name}",
|
"{artist} - {track-name}",
|
||||||
spotify_metadata,
|
spotify_metadata,
|
||||||
)
|
)
|
||||||
search_query = spotdl.util.format_string(search_format, spotify_metadata)
|
search_query = spotdl.metadata.format_string(search_format, spotify_metadata)
|
||||||
youtube_urls = youtube_searcher.search(search_query)
|
youtube_urls = youtube_searcher.search(search_query)
|
||||||
if not youtube_urls:
|
if not youtube_urls:
|
||||||
# raise NoYouTubeVideoError(
|
raise NoYouTubeVideoError(
|
||||||
# 'No videos found for the search query: "{}"'.format(search_query)
|
'No videos found for the search query: "{}"'.format(search_query)
|
||||||
# )
|
)
|
||||||
return
|
|
||||||
if manual:
|
if manual:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@@ -69,7 +69,7 @@ def search_metadata(track, search_format="{artist} - {track-name} lyrics", manua
|
|||||||
|
|
||||||
elif spotdl.util.is_youtube(track):
|
elif spotdl.util.is_youtube(track):
|
||||||
metadata = youtube.from_url(track)
|
metadata = youtube.from_url(track)
|
||||||
lyric_query = spotdl.util.format_string(
|
lyric_query = spotdl.metadata.format_string(
|
||||||
"{artist} - {track-name}",
|
"{artist} - {track-name}",
|
||||||
metadata,
|
metadata,
|
||||||
)
|
)
|
||||||
@@ -107,11 +107,14 @@ def search_metadata(track, search_format="{artist} - {track-name} lyrics", manua
|
|||||||
|
|
||||||
|
|
||||||
def download_track(track, arguments):
|
def download_track(track, arguments):
|
||||||
|
track_splits = track.split(":")
|
||||||
|
if len(track_splits) == 2:
|
||||||
|
youtube_track, spotify_track = track_splits
|
||||||
metadata = search_metadata(track, search_format=arguments.search_format)
|
metadata = search_metadata(track, search_format=arguments.search_format)
|
||||||
log_fmt = spotdl.util.format_string(
|
log_fmt = spotdl.metadata.format_string(
|
||||||
arguments.file_format,
|
arguments.output_file,
|
||||||
metadata,
|
metadata,
|
||||||
output_extension=arguments.output_ext
|
output_extension=arguments.output_ext,
|
||||||
)
|
)
|
||||||
# log.info(log_fmt)
|
# log.info(log_fmt)
|
||||||
download_track_from_metadata(metadata, arguments)
|
download_track_from_metadata(metadata, arguments)
|
||||||
@@ -119,7 +122,6 @@ def download_track(track, arguments):
|
|||||||
|
|
||||||
def download_track_from_metadata(metadata, arguments):
|
def download_track_from_metadata(metadata, arguments):
|
||||||
# TODO: Add `-m` flag
|
# TODO: Add `-m` flag
|
||||||
|
|
||||||
track = Track(metadata, cache_albumart=(not arguments.no_metadata))
|
track = Track(metadata, cache_albumart=(not arguments.no_metadata))
|
||||||
print(metadata["name"])
|
print(metadata["name"])
|
||||||
|
|
||||||
@@ -139,11 +141,15 @@ def download_track_from_metadata(metadata, arguments):
|
|||||||
else:
|
else:
|
||||||
output_extension = arguments.output_ext
|
output_extension = arguments.output_ext
|
||||||
|
|
||||||
filename = spotdl.util.format_string(
|
filename = spotdl.metadata.format_string(
|
||||||
arguments.file_format,
|
arguments.output_file,
|
||||||
metadata,
|
metadata,
|
||||||
output_extension=output_extension
|
output_extension=output_extension,
|
||||||
|
sanitizer=lambda s: spotdl.util.sanitize(
|
||||||
|
s, spaces_to_underscores=arguments.no_spaces
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
print(filename)
|
||||||
# log.info(filename)
|
# log.info(filename)
|
||||||
|
|
||||||
to_skip = arguments.dry_run
|
to_skip = arguments.dry_run
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ DEFAULT_CONFIGURATION = {
|
|||||||
"no-fallback-metadata": False,
|
"no-fallback-metadata": False,
|
||||||
"avconv": False,
|
"avconv": False,
|
||||||
"encoder": "ffmpeg",
|
"encoder": "ffmpeg",
|
||||||
"directory": spotdl.util.get_music_dir(),
|
|
||||||
"overwrite": "prompt",
|
"overwrite": "prompt",
|
||||||
"quality": "best",
|
"quality": "best",
|
||||||
"input-ext": "automatic",
|
"input-ext": "automatic",
|
||||||
@@ -23,7 +22,7 @@ DEFAULT_CONFIGURATION = {
|
|||||||
"music-videos-only": False,
|
"music-videos-only": False,
|
||||||
"no-spaces": False,
|
"no-spaces": False,
|
||||||
"processor": "synchronous",
|
"processor": "synchronous",
|
||||||
"file-format": "{artist} - {track-name}.{output-ext}",
|
"output-file": "{artist} - {track-name}.{output-ext}",
|
||||||
"search-format": "{artist} - {track-name} lyrics",
|
"search-format": "{artist} - {track-name} lyrics",
|
||||||
"youtube-api-key": None,
|
"youtube-api-key": None,
|
||||||
"skip": None,
|
"skip": None,
|
||||||
|
|||||||
@@ -7,3 +7,5 @@ from spotdl.metadata.exceptions import YouTubeMetadataNotFoundError
|
|||||||
|
|
||||||
from spotdl.metadata.embedder_base import EmbedderBase
|
from spotdl.metadata.embedder_base import EmbedderBase
|
||||||
|
|
||||||
|
from spotdl.metadata.formatter import format_string
|
||||||
|
|
||||||
|
|||||||
24
spotdl/metadata/formatter.py
Normal file
24
spotdl/metadata/formatter.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
def format_string(string, metadata, output_extension="", sanitizer=lambda s: s):
|
||||||
|
formats = {
|
||||||
|
"{track-name}" : metadata["name"],
|
||||||
|
"{artist}" : metadata["artists"][0]["name"],
|
||||||
|
"{album}" : metadata["album"]["name"],
|
||||||
|
"{album-artist}" : metadata["artists"][0]["name"],
|
||||||
|
"{genre}" : metadata["genre"],
|
||||||
|
"{disc-number}" : metadata["disc_number"],
|
||||||
|
"{duration}" : metadata["duration"],
|
||||||
|
"{year}" : metadata["year"],
|
||||||
|
"{original-date}": metadata["release_date"],
|
||||||
|
"{track-number}" : metadata["track_number"],
|
||||||
|
"{total-tracks}" : metadata["total_tracks"],
|
||||||
|
"{isrc}" : metadata["external_ids"]["isrc"],
|
||||||
|
# TODO: Call `str.zfill` fill on track-id
|
||||||
|
"{track-id}" : metadata.get("id", ""),
|
||||||
|
"{output-ext}" : output_extension,
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value in formats.items():
|
||||||
|
string = string.replace(key, sanitizer(str(value)))
|
||||||
|
|
||||||
|
return string
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ def test_default_music_directory():
|
|||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def directory_fixture(tmpdir_factory):
|
def directory_fixture(tmpdir_factory):
|
||||||
dir_path = os.path.join(str(tmpdir_factory.mktemp("tmpdir")), "filter_this_folder")
|
dir_path = os.path.join(str(tmpdir_factory.mktemp("tmpdir")), "filter_this_directory")
|
||||||
return dir_path
|
return dir_path
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -80,43 +80,15 @@ def is_youtube(raw_song):
|
|||||||
return status
|
return status
|
||||||
|
|
||||||
|
|
||||||
def format_string(string, metadata, output_extension=""):
|
def sanitize(string, ok="-_()[]{}", spaces_to_underscores=False):
|
||||||
formats = {
|
|
||||||
"{track-name}" : metadata["name"],
|
|
||||||
"{artist}" : metadata["artists"][0]["name"],
|
|
||||||
"{album}" : metadata["album"]["name"],
|
|
||||||
"{album-artist}" : metadata["artists"][0]["name"],
|
|
||||||
"{genre}" : metadata["genre"],
|
|
||||||
"{disc-number}" : metadata["disc_number"],
|
|
||||||
"{duration}" : metadata["duration"],
|
|
||||||
"{year}" : metadata["year"],
|
|
||||||
"{original-date}": metadata["release_date"],
|
|
||||||
"{track-number}" : metadata["track_number"],
|
|
||||||
"{total-tracks}" : metadata["total_tracks"],
|
|
||||||
"{isrc}" : metadata["external_ids"]["isrc"],
|
|
||||||
# TODO: Call `str.zfill` fill on track-id
|
|
||||||
"{track-id}" : metadata.get("id", ""),
|
|
||||||
"{output-ext}" : output_extension,
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value in formats.items():
|
|
||||||
string = string.replace(key, str(value))
|
|
||||||
|
|
||||||
return string
|
|
||||||
|
|
||||||
|
|
||||||
def sanitize_title(title, ok="-_()[]{}"):
|
|
||||||
""" Generate filename of the song to be downloaded. """
|
""" Generate filename of the song to be downloaded. """
|
||||||
|
if spaces_to_underscores:
|
||||||
if const.args.no_spaces:
|
string = string.replace(" ", "_")
|
||||||
title = title.replace(" ", "_")
|
# replace slashes with "-" to avoid directory creation errors
|
||||||
|
string = string.replace("/", "-").replace("\\", "-")
|
||||||
# replace slashes with "-" to avoid folder creation errors
|
|
||||||
title = title.replace("/", "-").replace("\\", "-")
|
|
||||||
|
|
||||||
# slugify removes any special characters
|
# slugify removes any special characters
|
||||||
title = slugify(title, ok=ok, lower=False, spaces=True)
|
string = slugify(string, ok=ok, lower=False, spaces=True)
|
||||||
return title
|
return string
|
||||||
|
|
||||||
|
|
||||||
def filter_path(path):
|
def filter_path(path):
|
||||||
@@ -162,7 +134,7 @@ def get_sec(time_str):
|
|||||||
def get_music_dir():
|
def get_music_dir():
|
||||||
home = os.path.expanduser("~")
|
home = os.path.expanduser("~")
|
||||||
|
|
||||||
# On Linux, the localized folder names are the actual ones.
|
# On Linux, the localized directory names are the actual ones.
|
||||||
# It's a freedesktop standard though.
|
# It's a freedesktop standard though.
|
||||||
if sys.platform.startswith("linux"):
|
if sys.platform.startswith("linux"):
|
||||||
for file_item in (".config/user-dirs.dirs", "user-dirs.dirs"):
|
for file_item in (".config/user-dirs.dirs", "user-dirs.dirs"):
|
||||||
@@ -176,7 +148,7 @@ def get_music_dir():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Windows / Cygwin
|
# Windows / Cygwin
|
||||||
# Queries registry for 'My Music' folder path (as this can be changed)
|
# Queries registry for 'My Music' directory path (as this can be changed)
|
||||||
if "win" in sys.platform:
|
if "win" in sys.platform:
|
||||||
try:
|
try:
|
||||||
key = winreg.OpenKey(
|
key = winreg.OpenKey(
|
||||||
@@ -189,7 +161,7 @@ def get_music_dir():
|
|||||||
except (FileNotFoundError, NameError):
|
except (FileNotFoundError, NameError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# On both Windows and macOS, the localized folder names you see in
|
# On both Windows and macOS, the localized directory names you see in
|
||||||
# Explorer and Finder are actually in English on the file system.
|
# Explorer and Finder are actually in English on the file system.
|
||||||
# So, defaulting to C:\Users\<user>\Music or /Users/<user>/Music
|
# So, defaulting to C:\Users\<user>\Music or /Users/<user>/Music
|
||||||
# respectively is sufficient.
|
# respectively is sufficient.
|
||||||
|
|||||||
Reference in New Issue
Block a user