New feature and error handling with downloads

Playlist m3u for playlist download [Enhancement]. Close #31
Fix #32
Update README.md
This commit is contained in:
vitiko98 2020-12-16 14:21:15 -04:00
parent 53eb898b0a
commit 2cd31e5a8a
3 changed files with 36 additions and 12 deletions

View File

@ -9,6 +9,7 @@ Search, discover and download Lossless and Hi-Res music from [Qobuz](https://www
* Download music from last.fm playlists (Spotify, Apple Music and Youtube playlists are also supported through this method) * Download music from last.fm playlists (Spotify, Apple Music and Youtube playlists are also supported through this method)
* Queue support on **interactive** mode * Queue support on **interactive** mode
* Support for albums with multiple discs * Support for albums with multiple discs
* Support for M3U playlists
* Downloads URLs from text file * Downloads URLs from text file
* And more * And more
@ -67,7 +68,7 @@ qobuz-dl dl https://play.qobuz.com/artist/2528676 --albums-only
``` ```
#### Last.fm playlists #### Last.fm playlists
Last.fm has a new feature for creating playlists: you can create your own based on the music you listen to or you can import one from popular streaming services like Spotify, Apple Music and Youtube. Visit: `https://www.last.fm/user/<your profile>/playlists` (e.g. https://www.last.fm/user/vitiko98/playlists) to get started. > Last.fm has a new feature for creating playlists: you can create your own based on the music you listen to or you can import one from popular streaming services like Spotify, Apple Music and Youtube. Visit: `https://www.last.fm/user/<your profile>/playlists` (e.g. https://www.last.fm/user/vitiko98/playlists) to get started.
Download a last.fm playlist in the maximum quality Download a last.fm playlist in the maximum quality
``` ```
@ -157,7 +158,7 @@ qobuz.handle_url("https://play.qobuz.com/album/va4j3hdlwaubc")
Attributes, methods and parameters have been named as self-explanatory as possible. Attributes, methods and parameters have been named as self-explanatory as possible.
## A note about Qo-DL ## A note about Qo-DL
`qobuz-dl` is inspired in the discontinued Qo-DL-Reborn. This program uses two modules from Qo-DL: `qopy` and `spoofer`, both written by Sorrow446 and DashLt. `qobuz-dl` is inspired in the discontinued Qo-DL-Reborn. This tool uses two modules from Qo-DL: `qopy` and `spoofer`, both written by Sorrow446 and DashLt.
## Disclaimer ## Disclaimer
* This tool was written for educational purposes. I will not be responsible if you use this program in bad faith. By using it, you are accepting the [Qobuz API Terms of Use](https://static.qobuz.com/apps/api/QobuzAPI-TermsofUse.pdf). * This tool was written for educational purposes. I will not be responsible if you use this program in bad faith. By using it, you are accepting the [Qobuz API Terms of Use](https://static.qobuz.com/apps/api/QobuzAPI-TermsofUse.pdf).
* `qobuz-dl` is not affiliated with Qobuz * `qobuz-dl` is not affiliated with Qobuz

View File

@ -53,6 +53,7 @@ class QobuzDL:
interactive_limit=20, interactive_limit=20,
ignore_singles_eps=False, ignore_singles_eps=False,
no_m3u_for_playlists=False, no_m3u_for_playlists=False,
raise_request_exceptions=False,
): ):
self.directory = self.create_dir(directory) self.directory = self.create_dir(directory)
self.quality = quality self.quality = quality
@ -353,7 +354,7 @@ class QobuzDL:
pl_title = sanitize_filename(soup.select_one("h1").text) pl_title = sanitize_filename(soup.select_one("h1").text)
pl_directory = os.path.join(self.directory, pl_title) pl_directory = os.path.join(self.directory, pl_title)
print("Downloading playlist: " + pl_title) print("Downloading playlist: {} ({} tracks)".format(pl_title, len(track_list)))
for i in track_list: for i in track_list:
track_id = self.get_id(self.search_by_type(i, "track", 1, lucky=True)[0]) track_id = self.get_id(self.search_by_type(i, "track", 1, lucky=True)[0])
@ -365,30 +366,44 @@ class QobuzDL:
def make_m3u(self, pl_directory): def make_m3u(self, pl_directory):
if self.no_m3u_for_playlists: if self.no_m3u_for_playlists:
return return
track_list = ["#EXTM3U"] track_list = ["#EXTM3U"]
pl_name = os.path.basename(os.path.normpath(pl_directory)) + ".m3u" rel_folder = os.path.basename(os.path.normpath(pl_directory))
pl_name = rel_folder + ".m3u"
for local, dirs, files in os.walk(pl_directory): for local, dirs, files in os.walk(pl_directory):
dirs.sort() dirs.sort()
audio_rel_files = [
# os.path.abspath(os.path.join(local, file_))
# os.path.join(rel_folder, os.path.basename(os.path.normpath(local)), file_)
os.path.join(os.path.basename(os.path.normpath(local)), file_)
for file_ in files
if os.path.splitext(file_)[-1] in EXTENSIONS
]
audio_files = [ audio_files = [
os.path.abspath(os.path.join(local, file_)) os.path.abspath(os.path.join(local, file_))
for file_ in files for file_ in files
if os.path.splitext(file_)[-1] in EXTENSIONS if os.path.splitext(file_)[-1] in EXTENSIONS
] ]
if not audio_files: if not audio_files or len(audio_files) != len(audio_rel_files):
continue continue
for audio in audio_files:
for audio_rel_file, audio_file in zip(audio_rel_files, audio_files):
try: try:
pl_item = EasyMP3(audio) if ".mp3" in audio else FLAC(audio) pl_item = (
EasyMP3(audio_file)
if ".mp3" in audio_file
else FLAC(audio_file)
)
title = pl_item["TITLE"][0] title = pl_item["TITLE"][0]
artist = pl_item["ARTIST"][0] artist = pl_item["ARTIST"][0]
length = int(pl_item.info.length) length = int(pl_item.info.length)
index = "#EXTINF:{}, {} - {}\n{}".format( index = "#EXTINF:{}, {} - {}\n{}".format(
length, artist, title, audio length, artist, title, audio_rel_file
) )
except: # noqa except: # noqa
continue continue
track_list.append(index) track_list.append(index)
if len(track_list) > 1: if len(track_list) > 1:
with open(os.path.join(self.directory, pl_name), "w") as pl: with open(os.path.join(pl_directory, pl_name), "w") as pl:
pl.write("\n\n".join(track_list)) pl.write("\n\n".join(track_list))

View File

@ -46,7 +46,7 @@ def get_format(client, item_dict, quality, is_track_id=False):
): ):
return "FLAC" return "FLAC"
return "Hi-Res" return "Hi-Res"
except KeyError: except (KeyError, requests.exceptions.HTTPError):
return "Unknown" return "Unknown"
@ -196,7 +196,11 @@ def download_id_by_type(
media_numbers = [track["media_number"] for track in meta["tracks"]["items"]] media_numbers = [track["media_number"] for track in meta["tracks"]["items"]]
is_multiple = True if len([*{*media_numbers}]) > 1 else False is_multiple = True if len([*{*media_numbers}]) > 1 else False
for i in meta["tracks"]["items"]: for i in meta["tracks"]["items"]:
parse = client.get_track_url(i["id"], quality) try:
parse = client.get_track_url(i["id"], quality)
except requests.exceptions.HTTPError:
print("Nothing found")
continue
if "sample" not in parse and parse["sampling_rate"]: if "sample" not in parse and parse["sampling_rate"]:
is_mp3 = True if int(quality) == 5 else False is_mp3 = True if int(quality) == 5 else False
download_and_tag( download_and_tag(
@ -214,7 +218,11 @@ def download_id_by_type(
print("Demo. Skipping") print("Demo. Skipping")
count = count + 1 count = count + 1
else: else:
parse = client.get_track_url(item_id, quality) try:
parse = client.get_track_url(item_id, quality)
except requests.exceptions.HTTPError:
print("Nothing found")
return
if "sample" not in parse and parse["sampling_rate"]: if "sample" not in parse and parse["sampling_rate"]:
meta = client.get_track_meta(item_id) meta = client.get_track_meta(item_id)