mirror of
https://github.com/Wojtek242/qobuz-dl.git
synced 2024-11-22 11:05:25 +01:00
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:
parent
53eb898b0a
commit
2cd31e5a8a
@ -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
|
||||||
|
@ -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))
|
||||||
|
@ -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"]:
|
||||||
|
try:
|
||||||
parse = client.get_track_url(i["id"], quality)
|
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:
|
||||||
|
try:
|
||||||
parse = client.get_track_url(item_id, quality)
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user