mirror of
https://github.com/KevinMidboe/spotify-downloader.git
synced 2025-10-29 18:00:15 +00:00
Implement --manual
This commit is contained in:
@@ -4,8 +4,8 @@ from spotdl import command_line
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
def match_arguments(arguments):
|
def match_arguments(arguments):
|
||||||
if arguments.tracks:
|
if arguments["song"]:
|
||||||
for track in arguments.tracks:
|
for track in arguments["song"]:
|
||||||
if track == "-":
|
if track == "-":
|
||||||
for line in sys.stdin:
|
for line in sys.stdin:
|
||||||
command_line.helpers.download_track(
|
command_line.helpers.download_track(
|
||||||
@@ -14,36 +14,36 @@ def match_arguments(arguments):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
command_line.helpers.download_track(track, arguments)
|
command_line.helpers.download_track(track, arguments)
|
||||||
elif arguments.list:
|
elif arguments["list"]:
|
||||||
if arguments.write_m3u:
|
if arguments["write_m3u"]:
|
||||||
youtube_tools.generate_m3u(
|
youtube_tools.generate_m3u(
|
||||||
track_file=arguments.list
|
track_file=arguments["list"]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
list_download = {
|
list_download = {
|
||||||
"sequential": command_line.helpers.download_tracks_from_file,
|
"sequential": command_line.helpers.download_tracks_from_file,
|
||||||
"threaded" : command_line.helpers.download_tracks_from_file_threaded,
|
"threaded" : command_line.helpers.download_tracks_from_file_threaded,
|
||||||
}[arguments.processor]
|
}[arguments["processor"]]
|
||||||
|
|
||||||
list_download(
|
list_download(
|
||||||
arguments.list,
|
arguments["list"],
|
||||||
arguments,
|
arguments,
|
||||||
)
|
)
|
||||||
elif arguments.playlist:
|
elif arguments["playlist"]:
|
||||||
spotify_tools.write_playlist(
|
spotify_tools.write_playlist(
|
||||||
playlist_url=arguments.playlist, text_file=arguments.write_to
|
playlist_url=arguments["playlist"], text_file=arguments["write_to"]
|
||||||
)
|
)
|
||||||
elif arguments.album:
|
elif arguments["album"]:
|
||||||
spotify_tools.write_album(
|
spotify_tools.write_album(
|
||||||
album_url=arguments.album, text_file=arguments.write_to
|
album_url=arguments["album"], text_file=arguments["write_to"]
|
||||||
)
|
)
|
||||||
elif arguments.all_albums:
|
elif arguments.all_albums:
|
||||||
spotify_tools.write_all_albums_from_artist(
|
spotify_tools.write_all_albums_from_artist(
|
||||||
artist_url=arguments.all_albums, text_file=arguments.write_to
|
artist_url=arguments["all_albums"], text_file=arguments["write_to"]
|
||||||
)
|
)
|
||||||
elif arguments.username:
|
elif arguments.username:
|
||||||
spotify_tools.write_user_playlist(
|
spotify_tools.write_user_playlist(
|
||||||
username=arguments.username, text_file=arguments.write_to
|
username=arguments["username"], text_file=arguments["write_to"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ def main():
|
|||||||
# logzero.setup_default_logger(formatter=const._formatter, level=const.args.log_level)
|
# logzero.setup_default_logger(formatter=const._formatter, level=const.args.log_level)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
match_arguments(arguments)
|
match_arguments(arguments.__dict__)
|
||||||
except KeyboardInterrupt as e:
|
except KeyboardInterrupt as e:
|
||||||
# log.exception(e)
|
# log.exception(e)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|||||||
@@ -51,18 +51,10 @@ def get_arguments(argv=None, to_merge=True):
|
|||||||
|
|
||||||
group = parser.add_mutually_exclusive_group(required=True)
|
group = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
|
||||||
# TODO: --song is deprecated. Remove in future versions.
|
|
||||||
# Use --tracks instead.
|
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"-s",
|
"-s",
|
||||||
"--song",
|
"--song",
|
||||||
nargs="+",
|
nargs="+",
|
||||||
help=argparse.SUPPRESS
|
|
||||||
)
|
|
||||||
group.add_argument(
|
|
||||||
"-t",
|
|
||||||
"--tracks",
|
|
||||||
nargs="+",
|
|
||||||
help="download track(s) by spotify link or name"
|
help="download track(s) by spotify link or name"
|
||||||
)
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
@@ -115,13 +107,6 @@ def get_arguments(argv=None, to_merge=True):
|
|||||||
help="do not embed metadata in tracks",
|
help="do not embed metadata in tracks",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"-nf",
|
|
||||||
"--no-fallback-metadata",
|
|
||||||
default=config["no-fallback-metadata"],
|
|
||||||
help="do not use YouTube as fallback for metadata if track not found on Spotify",
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-a",
|
"-a",
|
||||||
"--avconv",
|
"--avconv",
|
||||||
@@ -190,13 +175,6 @@ def get_arguments(argv=None, to_merge=True):
|
|||||||
"are to be surrounded by curly braces. Possible tags: "
|
"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(
|
|
||||||
"-dm",
|
|
||||||
"--download-only-metadata",
|
|
||||||
default=config["download-only-metadata"],
|
|
||||||
help="download tracks only whose metadata is found",
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-d",
|
"-d",
|
||||||
"--dry-run",
|
"--dry-run",
|
||||||
@@ -210,15 +188,17 @@ def get_arguments(argv=None, to_merge=True):
|
|||||||
"--music-videos-only",
|
"--music-videos-only",
|
||||||
default=config["music-videos-only"],
|
default=config["music-videos-only"],
|
||||||
help="search only for music videos on Youtube (works only "
|
help="search only for music videos on Youtube (works only "
|
||||||
"when YouTube API key is set",
|
"when YouTube API key is set)",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--processor",
|
"--processor",
|
||||||
default=config["processor"],
|
default=config["processor"],
|
||||||
|
choices={"synchronous", "threaded"},
|
||||||
help='list downloading strategy: - "synchronous" downloads '
|
help='list downloading strategy: - "synchronous" downloads '
|
||||||
'tracks one-by-one. - "threaded" (highly experimental!) pre-fetches '
|
'tracks one-by-one. - "threaded" (highly experimental at the '
|
||||||
'the next track\'s metadata for more efficient downloading'
|
'moment! expect it to slash & burn) pre-fetches the next '
|
||||||
|
'track\'s metadata for more efficient downloading'
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-ns",
|
"-ns",
|
||||||
@@ -321,13 +301,6 @@ def run_errands(parser, parsed, config):
|
|||||||
# "output format".format(parsed.encoder))
|
# "output format".format(parsed.encoder))
|
||||||
parsed.encoder = "null"
|
parsed.encoder = "null"
|
||||||
|
|
||||||
song_parameter_passed = parsed.song is not None and parsed.tracks is None
|
|
||||||
if song_parameter_passed:
|
|
||||||
# log.warn("-s / --song is deprecated and will be removed in future versions. "
|
|
||||||
# "Use -t / --tracks instead.")
|
|
||||||
setattr(parsed, "tracks", parsed.song)
|
|
||||||
del parsed.song
|
|
||||||
|
|
||||||
if parsed.output_file == "-" 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 "
|
||||||
|
|||||||
@@ -40,7 +40,23 @@ def search_metadata_on_spotify(query):
|
|||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_for_youtube_search_result(videos):
|
||||||
|
urls = []
|
||||||
|
print("0. Skip downloading this track")
|
||||||
|
for index, video in enumerate(videos, 1):
|
||||||
|
video_repr = "{index}. {title} [{duration}] ({url})".format(
|
||||||
|
index=index,
|
||||||
|
title=video["title"],
|
||||||
|
duration=video["duration"],
|
||||||
|
url=video["url"]
|
||||||
|
)
|
||||||
|
print(video_repr)
|
||||||
|
urls.append(video["url"])
|
||||||
|
return spotdl.util.prompt_user_for_selection(urls)
|
||||||
|
|
||||||
|
|
||||||
def search_metadata(track, search_format="{artist} - {track-name} lyrics", manual=False):
|
def search_metadata(track, search_format="{artist} - {track-name} lyrics", manual=False):
|
||||||
|
# TODO: Clean this function
|
||||||
youtube = ProviderYouTube()
|
youtube = ProviderYouTube()
|
||||||
youtube_searcher = YouTubeSearch()
|
youtube_searcher = YouTubeSearch()
|
||||||
|
|
||||||
@@ -52,16 +68,16 @@ def search_metadata(track, search_format="{artist} - {track-name} lyrics", manua
|
|||||||
spotify_metadata,
|
spotify_metadata,
|
||||||
)
|
)
|
||||||
search_query = spotdl.metadata.format_string(search_format, spotify_metadata)
|
search_query = spotdl.metadata.format_string(search_format, spotify_metadata)
|
||||||
youtube_urls = youtube_searcher.search(search_query)
|
youtube_videos = youtube_searcher.search(search_query)
|
||||||
if not youtube_urls:
|
if not youtube_videos:
|
||||||
raise NoYouTubeVideoError(
|
raise NoYouTubeVideoError(
|
||||||
'No videos found for the search query: "{}"'.format(search_query)
|
'No videos found for the search query: "{}"'.format(search_query)
|
||||||
)
|
)
|
||||||
if manual:
|
if manual:
|
||||||
pass
|
youtube_video = prompt_for_youtube_search_result(youtube_videos)
|
||||||
else:
|
else:
|
||||||
youtube_url = youtube_urls.bestmatch()["url"]
|
youtube_video = youtube_videos.bestmatch()["url"]
|
||||||
youtube_metadata = youtube.from_url(youtube_url)
|
youtube_metadata = youtube.from_url(youtube_video)
|
||||||
metadata = spotdl.util.merge(
|
metadata = spotdl.util.merge(
|
||||||
youtube_metadata,
|
youtube_metadata,
|
||||||
spotify_metadata
|
spotify_metadata
|
||||||
@@ -81,17 +97,17 @@ def search_metadata(track, search_format="{artist} - {track-name} lyrics", manua
|
|||||||
args=(track,)
|
args=(track,)
|
||||||
)
|
)
|
||||||
spotify_metadata.start()
|
spotify_metadata.start()
|
||||||
youtube_urls = youtube_searcher.search(track)
|
youtube_videos = youtube_searcher.search(track)
|
||||||
if not youtube_urls:
|
if not youtube_videos:
|
||||||
# raise NoYouTubeVideoError(
|
raise NoYouTubeVideoError(
|
||||||
# 'No videos found for the search query: "{}"'.format(track)
|
'No videos found for the search query: "{}"'.format(track)
|
||||||
# )
|
)
|
||||||
return
|
return
|
||||||
if manual:
|
if manual:
|
||||||
pass
|
youtube_video = prompt_for_youtube_search_result(youtube_videos)
|
||||||
else:
|
else:
|
||||||
youtube_url = youtube_urls.bestmatch()["url"]
|
youtube_video = youtube_videos.bestmatch()["url"]
|
||||||
youtube_metadata = youtube.from_url(youtube_url)
|
youtube_metadata = youtube.from_url(youtube_video)
|
||||||
metadata = spotdl.util.merge(
|
metadata = spotdl.util.merge(
|
||||||
youtube_metadata,
|
youtube_metadata,
|
||||||
spotify_metadata.join()
|
spotify_metadata.join()
|
||||||
@@ -110,11 +126,15 @@ def download_track(track, arguments):
|
|||||||
track_splits = track.split(":")
|
track_splits = track.split(":")
|
||||||
if len(track_splits) == 2:
|
if len(track_splits) == 2:
|
||||||
youtube_track, spotify_track = track_splits
|
youtube_track, spotify_track = track_splits
|
||||||
metadata = search_metadata(track, search_format=arguments.search_format)
|
metadata = search_metadata(
|
||||||
|
track,
|
||||||
|
search_format=arguments["search_format"],
|
||||||
|
manual=arguments["manual"],
|
||||||
|
)
|
||||||
log_fmt = spotdl.metadata.format_string(
|
log_fmt = spotdl.metadata.format_string(
|
||||||
arguments.output_file,
|
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)
|
||||||
@@ -122,41 +142,41 @@ 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"])
|
||||||
|
|
||||||
stream = metadata["streams"].get(
|
stream = metadata["streams"].get(
|
||||||
quality=arguments.quality,
|
quality=arguments["quality"],
|
||||||
preftype=arguments.input_ext,
|
preftype=arguments["input_ext"],
|
||||||
)
|
)
|
||||||
# log.info(stream)
|
# log.info(stream)
|
||||||
|
|
||||||
Encoder = {
|
Encoder = {
|
||||||
"ffmpeg": EncoderFFmpeg,
|
"ffmpeg": EncoderFFmpeg,
|
||||||
"avconv": EncoderAvconv,
|
"avconv": EncoderAvconv,
|
||||||
}.get(arguments.encoder)
|
}.get(arguments["encoder"])
|
||||||
|
|
||||||
if Encoder is None:
|
if Encoder is None:
|
||||||
output_extension = stream["encoding"]
|
output_extension = stream["encoding"]
|
||||||
else:
|
else:
|
||||||
output_extension = arguments.output_ext
|
output_extension = arguments["output_ext"]
|
||||||
|
|
||||||
filename = spotdl.metadata.format_string(
|
filename = spotdl.metadata.format_string(
|
||||||
arguments.output_file,
|
arguments["output_file"],
|
||||||
metadata,
|
metadata,
|
||||||
output_extension=output_extension,
|
output_extension=output_extension,
|
||||||
sanitizer=lambda s: spotdl.util.sanitize(
|
sanitizer=lambda s: spotdl.util.sanitize(
|
||||||
s, spaces_to_underscores=arguments.no_spaces
|
s, spaces_to_underscores=arguments["no_spaces"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(filename)
|
print(filename)
|
||||||
# log.info(filename)
|
# log.info(filename)
|
||||||
|
|
||||||
to_skip = arguments.dry_run
|
to_skip = arguments["dry_run"]
|
||||||
if not to_skip and os.path.isfile(filename):
|
if not to_skip and os.path.isfile(filename):
|
||||||
if arguments.overwrite == "force":
|
if arguments["overwrite"] == "force":
|
||||||
to_skip = False
|
to_skip = False
|
||||||
elif arguments.overwrite == "prompt":
|
elif arguments["overwrite"] == "prompt":
|
||||||
to_skip = not input("overwrite? (y/N)").lower() == "y"
|
to_skip = not input("overwrite? (y/N)").lower() == "y"
|
||||||
else:
|
else:
|
||||||
to_skip = True
|
to_skip = True
|
||||||
@@ -174,7 +194,7 @@ def download_track_from_metadata(metadata, arguments):
|
|||||||
encoder=Encoder()
|
encoder=Encoder()
|
||||||
)
|
)
|
||||||
|
|
||||||
if not arguments.no_metadata:
|
if not arguments["no_metadata"]:
|
||||||
track.metadata["lyrics"] = track.metadata["lyrics"].join()
|
track.metadata["lyrics"] = track.metadata["lyrics"].join()
|
||||||
try:
|
try:
|
||||||
track.apply_metadata(filename, encoding=output_extension)
|
track.apply_metadata(filename, encoding=output_extension)
|
||||||
@@ -206,7 +226,7 @@ def download_tracks_from_file(path, arguments):
|
|||||||
|
|
||||||
for number, track in enumerate(tracks, 1):
|
for number, track in enumerate(tracks, 1):
|
||||||
try:
|
try:
|
||||||
metadata = search_metadata(next_track, arguments.search_format)
|
metadata = search_metadata(next_track, arguments["search_format"])
|
||||||
log_fmt=(str(number) + ". {artist} - {track-name}")
|
log_fmt=(str(number) + ". {artist} - {track-name}")
|
||||||
# log.info(log_fmt)
|
# log.info(log_fmt)
|
||||||
download_track_from_metadata(metadata, arguments)
|
download_track_from_metadata(metadata, arguments)
|
||||||
@@ -215,8 +235,8 @@ def download_tracks_from_file(path, arguments):
|
|||||||
# log.warning("Failed. Will retry after other songs\n")
|
# log.warning("Failed. Will retry after other songs\n")
|
||||||
tracks.append(track)
|
tracks.append(track)
|
||||||
else:
|
else:
|
||||||
if arguments.write_sucessful:
|
if arguments["write_sucessful"]:
|
||||||
with open(arguments.write_successful, "a") as fout:
|
with open(arguments["write_successful"], "a") as fout:
|
||||||
fout.write(track)
|
fout.write(track)
|
||||||
finally:
|
finally:
|
||||||
with open(path, "w") as fout:
|
with open(path, "w") as fout:
|
||||||
@@ -254,7 +274,7 @@ def download_tracks_from_file_threaded(path, arguments):
|
|||||||
"current_track": None,
|
"current_track": None,
|
||||||
"next_track": spotdl.util.ThreadWithReturnValue(
|
"next_track": spotdl.util.ThreadWithReturnValue(
|
||||||
target=search_metadata,
|
target=search_metadata,
|
||||||
args=(next_track, arguments.search_format)
|
args=(next_track, arguments["search_format"])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
metadata["next_track"].start()
|
metadata["next_track"].start()
|
||||||
@@ -269,7 +289,7 @@ def download_tracks_from_file_threaded(path, arguments):
|
|||||||
next_track = tracks.pop(0)
|
next_track = tracks.pop(0)
|
||||||
metadata["next_track"] = spotdl.util.ThreadWithReturnValue(
|
metadata["next_track"] = spotdl.util.ThreadWithReturnValue(
|
||||||
target=search_metadata,
|
target=search_metadata,
|
||||||
args=(next_track, arguments.search_format)
|
args=(next_track, arguments["search_format"])
|
||||||
)
|
)
|
||||||
metadata["next_track"].start()
|
metadata["next_track"].start()
|
||||||
|
|
||||||
@@ -289,8 +309,8 @@ def download_tracks_from_file_threaded(path, arguments):
|
|||||||
tracks.append(current_track)
|
tracks.append(current_track)
|
||||||
else:
|
else:
|
||||||
tracks_count -= 1
|
tracks_count -= 1
|
||||||
if arguments.write_successful:
|
if arguments["write_successful"]:
|
||||||
with open(arguments.write_successful, "a") as fout:
|
with open(arguments["write_successful"], "a") as fout:
|
||||||
fout.write(current_track)
|
fout.write(current_track)
|
||||||
finally:
|
finally:
|
||||||
current_iteration += 1
|
current_iteration += 1
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ class YouTubeVideos(Sequence):
|
|||||||
self.videos = videos
|
self.videos = videos
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "YouTubeVideos({})".format(self.videos)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.videos)
|
return len(self.videos)
|
||||||
|
|
||||||
|
|||||||
@@ -49,20 +49,23 @@ def merge(base, overrider):
|
|||||||
return merger
|
return merger
|
||||||
|
|
||||||
|
|
||||||
def input_link(links):
|
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:")
|
# log.info("Choose your number:")
|
||||||
|
print("Choose your number:")
|
||||||
the_chosen_one = int(input("> "))
|
the_chosen_one = int(input("> "))
|
||||||
if 1 <= the_chosen_one <= len(links):
|
if 1 <= the_chosen_one <= len(items):
|
||||||
return links[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!")
|
# log.warning("Choose a valid number!")
|
||||||
|
print("Chose a valid number!")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
log.warning("Choose a valid number!")
|
# log.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