mirror of
https://github.com/KevinMidboe/spotify-downloader.git
synced 2025-10-29 18:00:15 +00:00
Add support for .m3u playlists (#401)
* Add support for .m3u playlists * Run black code formatter on changes * Stay consistent with Spotify test track
This commit is contained in:
@@ -124,6 +124,12 @@ def get_arguments(raw_args=None, to_group=True, to_merge=True):
|
||||
"-V", "--version", help="show version and exit", action="store_true"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--write-m3u',
|
||||
help="generate an .m3u playlist file with youtube links given "
|
||||
"a text file containing tracks",
|
||||
action='store_true'
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--manual",
|
||||
@@ -257,6 +263,9 @@ def get_arguments(raw_args=None, to_group=True, to_merge=True):
|
||||
if parsed.config is not None and to_merge:
|
||||
parsed = override_config(parsed.config, parser)
|
||||
|
||||
if parsed.write_m3u and not parsed.list:
|
||||
parser.error('--write-m3u can only be used with --list')
|
||||
|
||||
parsed.log_level = log_leveller(parsed.log_level)
|
||||
|
||||
return parsed
|
||||
|
||||
@@ -205,7 +205,7 @@ def get_music_dir():
|
||||
return winreg.QueryValueEx(key, "My Music")[0]
|
||||
except (FileNotFoundError, NameError):
|
||||
pass
|
||||
|
||||
|
||||
# On both Windows and macOS, the localized folder names you see in
|
||||
# Explorer and Finder are actually in English on the file system.
|
||||
# So, defaulting to C:\Users\<user>\Music or /Users/<user>/Music
|
||||
|
||||
@@ -127,15 +127,7 @@ def download_list(tracks_file, skip_file=None, write_successful_file=None):
|
||||
|
||||
def download_single(raw_song, number=None):
|
||||
""" Logic behind downloading a song. """
|
||||
|
||||
if internals.is_youtube(raw_song):
|
||||
log.debug("Input song is a YouTube URL")
|
||||
content = youtube_tools.go_pafy(raw_song, meta_tags=None)
|
||||
raw_song = slugify(content.title).replace("-", " ")
|
||||
meta_tags = spotify_tools.generate_metadata(raw_song)
|
||||
else:
|
||||
meta_tags = spotify_tools.generate_metadata(raw_song)
|
||||
content = youtube_tools.go_pafy(raw_song, meta_tags)
|
||||
content, meta_tags = youtube_tools.match_video_and_metadata(raw_song)
|
||||
|
||||
if content is None:
|
||||
log.debug("Found no matching video")
|
||||
@@ -219,11 +211,14 @@ def main():
|
||||
if const.args.song:
|
||||
download_single(raw_song=const.args.song)
|
||||
elif const.args.list:
|
||||
download_list(
|
||||
tracks_file=const.args.list,
|
||||
skip_file=const.args.skip,
|
||||
write_successful_file=const.args.write_successful,
|
||||
)
|
||||
if const.args.write_m3u:
|
||||
youtube_tools.generate_m3u(track_file=const.args.list)
|
||||
else:
|
||||
download_list(
|
||||
tracks_file=const.args.list,
|
||||
skip_file=const.args.skip,
|
||||
write_successful_file=const.args.write_successful,
|
||||
)
|
||||
elif const.args.playlist:
|
||||
spotify_tools.write_playlist(playlist_url=const.args.playlist)
|
||||
elif const.args.album:
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from bs4 import BeautifulSoup
|
||||
import urllib
|
||||
import pafy
|
||||
from slugify import slugify
|
||||
from logzero import logger as log
|
||||
|
||||
from spotdl import spotify_tools
|
||||
from spotdl import internals
|
||||
from spotdl import const
|
||||
|
||||
@@ -38,6 +40,21 @@ def go_pafy(raw_song, meta_tags=None):
|
||||
return track_info
|
||||
|
||||
|
||||
def match_video_and_metadata(track, force_pafy=True):
|
||||
if internals.is_youtube(track):
|
||||
log.debug("Input song is a YouTube URL")
|
||||
content = go_pafy(track, meta_tags=None)
|
||||
track = slugify(content.title).replace("-", " ")
|
||||
meta_tags = spotify_tools.generate_metadata(track)
|
||||
else:
|
||||
meta_tags = spotify_tools.generate_metadata(track)
|
||||
if force_pafy:
|
||||
content = go_pafy(track, meta_tags)
|
||||
else:
|
||||
content = None
|
||||
return content, meta_tags
|
||||
|
||||
|
||||
def get_youtube_title(content, number=None):
|
||||
""" Get the YouTube video's title. """
|
||||
title = content.title
|
||||
@@ -47,6 +64,34 @@ def get_youtube_title(content, number=None):
|
||||
return title
|
||||
|
||||
|
||||
def generate_m3u(track_file):
|
||||
tracks = internals.get_unique_tracks(track_file)
|
||||
target_file = "{}.m3u".format(track_file.split(".")[0])
|
||||
total_tracks = len(tracks)
|
||||
log.info("Generating {0} from {1} YouTube URLs".format(target_file, total_tracks))
|
||||
with open(target_file, "w") as output_file:
|
||||
output_file.write("#EXTM3U\n\n")
|
||||
for n, track in enumerate(tracks, 1):
|
||||
content, _ = match_video_and_metadata(track)
|
||||
if content is None:
|
||||
log.warning("Skipping {}".format(track))
|
||||
else:
|
||||
log.info(
|
||||
"Matched track {0}/{1} ({2})".format(
|
||||
n, total_tracks, content.watchv_url
|
||||
)
|
||||
)
|
||||
log.debug(track)
|
||||
m3u_key = "#EXTINF:{duration},{title}\n{youtube_url}\n".format(
|
||||
duration=internals.get_sec(content.duration),
|
||||
title=content.title,
|
||||
youtube_url=content.watchv_url,
|
||||
)
|
||||
log.debug(m3u_key)
|
||||
with open(target_file, "a") as output_file:
|
||||
output_file.write(m3u_key)
|
||||
|
||||
|
||||
def download_song(file_name, content):
|
||||
""" Download the audio file from YouTube. """
|
||||
_, extension = os.path.splitext(file_name)
|
||||
@@ -164,7 +209,7 @@ class GenerateYouTubeURL:
|
||||
duration_tolerance += 1
|
||||
if duration_tolerance > max_duration_tolerance:
|
||||
log.error(
|
||||
"{0} by {1} was not found.\n".format(
|
||||
"{0} by {1} was not found.".format(
|
||||
self.meta_tags["name"],
|
||||
self.meta_tags["artists"][0]["name"],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user