Improve recursive_quality behaviour
This commit is contained in:
@@ -187,15 +187,15 @@ class Download_JOB:
|
|||||||
# Post-process each returned media in the chunk
|
# Post-process each returned media in the chunk
|
||||||
for idx in range(len(chunk_medias)):
|
for idx in range(len(chunk_medias)):
|
||||||
if "errors" in chunk_medias[idx]:
|
if "errors" in chunk_medias[idx]:
|
||||||
c_media_json = cls.__get_url(non_episode_tracks[len(non_episode_medias) + idx], quality_download)
|
# Do not fallback to legacy URL; mark as unavailable for this quality
|
||||||
chunk_medias[idx] = c_media_json
|
chunk_medias[idx] = {"media": []}
|
||||||
else:
|
else:
|
||||||
if not chunk_medias[idx]['media']:
|
if not chunk_medias[idx]['media']:
|
||||||
c_media_json = cls.__get_url(non_episode_tracks[len(non_episode_medias) + idx], quality_download)
|
# Do not fallback to legacy URL; mark as unavailable
|
||||||
chunk_medias[idx] = c_media_json
|
chunk_medias[idx] = {"media": []}
|
||||||
elif len(chunk_medias[idx]['media'][0]['sources']) == 1:
|
elif len(chunk_medias[idx]['media'][0]['sources']) == 1:
|
||||||
c_media_json = cls.__get_url(non_episode_tracks[len(non_episode_medias) + idx], quality_download)
|
# Keep single-source media as-is; do not fallback
|
||||||
chunk_medias[idx] = c_media_json
|
pass
|
||||||
non_episode_medias.extend(chunk_medias)
|
non_episode_medias.extend(chunk_medias)
|
||||||
except NoRightOnMedia:
|
except NoRightOnMedia:
|
||||||
for c_track in tokens_chunk:
|
for c_track in tokens_chunk:
|
||||||
@@ -585,7 +585,9 @@ class EASY_DW:
|
|||||||
if filesize == 0:
|
if filesize == 0:
|
||||||
song = self.__song_metadata['music']
|
song = self.__song_metadata['music']
|
||||||
artist = self.__song_metadata['artist']
|
artist = self.__song_metadata['artist']
|
||||||
# Switch quality settings to MP3_320.
|
if not self.__recursive_quality:
|
||||||
|
raise QualityNotFound(f"FLAC not available for {song} - {artist} and recursive quality search is disabled.")
|
||||||
|
# Fallback to MP3_320 if recursive_quality is enabled
|
||||||
self.__quality_download = 'MP3_320'
|
self.__quality_download = 'MP3_320'
|
||||||
self.__file_format = '.mp3'
|
self.__file_format = '.mp3'
|
||||||
self.__song_path = self.__song_path.rsplit('.', 1)[0] + '.mp3'
|
self.__song_path = self.__song_path.rsplit('.', 1)[0] + '.mp3'
|
||||||
@@ -598,51 +600,97 @@ class EASY_DW:
|
|||||||
# Continue with the normal download process.
|
# Continue with the normal download process.
|
||||||
try:
|
try:
|
||||||
media_list = self.__infos_dw['media_url']['media']
|
media_list = self.__infos_dw['media_url']['media']
|
||||||
song_link = media_list[0]['sources'][0]['url']
|
|
||||||
|
|
||||||
try:
|
# Try all sources for the requested quality before attempting any fallback
|
||||||
crypted_audio = API_GW.song_exist(song_link)
|
crypted_audio = None
|
||||||
except TrackNotFound:
|
last_error = None
|
||||||
|
for media_entry in media_list:
|
||||||
|
sources = media_entry.get('sources') or []
|
||||||
|
for src in sources:
|
||||||
|
song_link = src.get('url')
|
||||||
|
if not song_link:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
crypted_audio = API_GW.song_exist(song_link)
|
||||||
|
if crypted_audio:
|
||||||
|
last_error = None
|
||||||
|
break
|
||||||
|
except Exception as e_try_src:
|
||||||
|
last_error = e_try_src
|
||||||
|
continue
|
||||||
|
if crypted_audio:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not crypted_audio:
|
||||||
song = self.__song_metadata['music']
|
song = self.__song_metadata['music']
|
||||||
artist = self.__song_metadata['artist']
|
artist = self.__song_metadata['artist']
|
||||||
|
|
||||||
if self.__file_format == '.flac':
|
if self.__file_format == '.flac':
|
||||||
|
if not self.__recursive_quality:
|
||||||
|
raise QualityNotFound(f"FLAC not available for {song} - {artist} and recursive quality search is disabled.")
|
||||||
logger.warning(f"\n⚠ {song} - {artist} is not available in FLAC format. Trying MP3...")
|
logger.warning(f"\n⚠ {song} - {artist} is not available in FLAC format. Trying MP3...")
|
||||||
self.__quality_download = 'MP3_320'
|
self.__quality_download = 'MP3_320'
|
||||||
self.__file_format = '.mp3'
|
self.__file_format = '.mp3'
|
||||||
self.__song_path = self.__song_path.rsplit('.', 1)[0] + '.mp3'
|
self.__song_path = self.__song_path.rsplit('.', 1)[0] + '.mp3'
|
||||||
|
|
||||||
media = Download_JOB.check_sources(
|
media = Download_JOB.check_sources([self.__infos_dw], 'MP3_320')
|
||||||
[self.__infos_dw], 'MP3_320'
|
|
||||||
)
|
|
||||||
if media:
|
if media:
|
||||||
self.__infos_dw['media_url'] = media[0]
|
self.__infos_dw['media_url'] = media[0]
|
||||||
song_link = media[0]['media'][0]['sources'][0]['url']
|
media_list = self.__infos_dw['media_url']['media']
|
||||||
crypted_audio = API_GW.song_exist(song_link)
|
# Try all sources for fallback quality
|
||||||
|
for media_entry in media_list:
|
||||||
|
sources = media_entry.get('sources') or []
|
||||||
|
for src in sources:
|
||||||
|
song_link = src.get('url')
|
||||||
|
if not song_link:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
crypted_audio = API_GW.song_exist(song_link)
|
||||||
|
if crypted_audio:
|
||||||
|
last_error = None
|
||||||
|
break
|
||||||
|
except Exception as e_try_src2:
|
||||||
|
last_error = e_try_src2
|
||||||
|
continue
|
||||||
|
if crypted_audio:
|
||||||
|
break
|
||||||
|
if not crypted_audio:
|
||||||
|
raise TrackNotFound(f"Track {song} - {artist} not available in MP3 after FLAC attempt failed (all sources unreachable). Last error: {last_error}")
|
||||||
else:
|
else:
|
||||||
raise TrackNotFound(f"Track {song} - {artist} not available in MP3 after FLAC attempt failed (media not found for MP3).")
|
raise TrackNotFound(f"Track {song} - {artist} not available in MP3 after FLAC attempt failed (media not found for MP3).")
|
||||||
else:
|
else:
|
||||||
if not self.__recursive_quality:
|
if not self.__recursive_quality:
|
||||||
# msg was not defined, provide a more specific message
|
|
||||||
raise QualityNotFound(f"Quality {self.__quality_download} not found for {song} - {artist} and recursive quality search is disabled.")
|
raise QualityNotFound(f"Quality {self.__quality_download} not found for {song} - {artist} and recursive quality search is disabled.")
|
||||||
for c_quality in qualities:
|
for c_quality in qualities:
|
||||||
if self.__quality_download == c_quality:
|
if self.__quality_download == c_quality:
|
||||||
continue
|
continue
|
||||||
media = Download_JOB.check_sources(
|
media = Download_JOB.check_sources([self.__infos_dw], c_quality)
|
||||||
[self.__infos_dw], c_quality
|
|
||||||
)
|
|
||||||
if media:
|
if media:
|
||||||
self.__infos_dw['media_url'] = media[0]
|
self.__infos_dw['media_url'] = media[0]
|
||||||
song_link = media[0]['media'][0]['sources'][0]['url']
|
media_list = self.__infos_dw['media_url']['media']
|
||||||
try:
|
# Try all sources for alternative quality
|
||||||
crypted_audio = API_GW.song_exist(song_link)
|
for media_entry in media_list:
|
||||||
self.__c_quality = qualities[c_quality]
|
sources = media_entry.get('sources') or []
|
||||||
self.__set_quality()
|
for src in sources:
|
||||||
break
|
song_link = src.get('url')
|
||||||
except TrackNotFound:
|
if not song_link:
|
||||||
if c_quality == "MP3_128":
|
continue
|
||||||
raise TrackNotFound(f"Error with {song} - {artist}. All available qualities failed, last attempt was {c_quality}. Link: {self.__link}")
|
try:
|
||||||
continue
|
crypted_audio = API_GW.song_exist(song_link)
|
||||||
|
if crypted_audio:
|
||||||
|
self.__c_quality = qualities[c_quality]
|
||||||
|
self.__set_quality()
|
||||||
|
last_error = None
|
||||||
|
break
|
||||||
|
except Exception as e_try_src3:
|
||||||
|
last_error = e_try_src3
|
||||||
|
continue
|
||||||
|
if crypted_audio:
|
||||||
|
break
|
||||||
|
if crypted_audio:
|
||||||
|
break
|
||||||
|
if not crypted_audio:
|
||||||
|
raise TrackNotFound(f"Error with {song} - {artist}. All available qualities failed. Last error: {last_error}. Link: {self.__link}")
|
||||||
|
|
||||||
c_crypted_audio = crypted_audio.iter_content(2048)
|
c_crypted_audio = crypted_audio.iter_content(2048)
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ from requests import (
|
|||||||
post as req_post,
|
post as req_post,
|
||||||
)
|
)
|
||||||
from deezspot.libutils.logging_utils import logger
|
from deezspot.libutils.logging_utils import logger
|
||||||
|
import re
|
||||||
|
from urllib.parse import urlparse, urlunparse
|
||||||
|
|
||||||
class API_GW:
|
class API_GW:
|
||||||
|
|
||||||
@@ -271,28 +273,39 @@ class API_GW:
|
|||||||
if song_link and 'spreaker.com' in song_link:
|
if song_link and 'spreaker.com' in song_link:
|
||||||
return req_get(song_link, stream=True)
|
return req_get(song_link, stream=True)
|
||||||
|
|
||||||
crypted_audio = req_get(song_link)
|
try:
|
||||||
|
crypted_audio = req_get(song_link, stream=True, timeout=15)
|
||||||
if len(crypted_audio.content) == 0:
|
if len(crypted_audio.content) == 0:
|
||||||
raise TrackNotFound
|
raise TrackNotFound
|
||||||
|
return crypted_audio
|
||||||
return crypted_audio
|
except Exception as e:
|
||||||
|
# DNS fallback across dzcdn proxy hosts (e-cdns-proxy-0..7)
|
||||||
|
try:
|
||||||
|
parsed = urlparse(song_link)
|
||||||
|
host = parsed.netloc
|
||||||
|
if re.search(r"e-cdns-proxy-\d+\.dzcdn\.net", host):
|
||||||
|
m = re.search(r"e-cdns-proxy-(\d+)\.dzcdn\.net", host)
|
||||||
|
original_idx = int(m.group(1)) if m else -1
|
||||||
|
for i in range(0, 8):
|
||||||
|
if i == original_idx:
|
||||||
|
continue
|
||||||
|
new_host = re.sub(r"e-cdns-proxy-\d+\.dzcdn\.net", f"e-cdns-proxy-{i}.dzcdn.net", host)
|
||||||
|
new_url = urlunparse((parsed.scheme, new_host, parsed.path, parsed.params, parsed.query, parsed.fragment))
|
||||||
|
try:
|
||||||
|
alt_resp = req_get(new_url, stream=True, timeout=15)
|
||||||
|
if len(alt_resp.content) == 0:
|
||||||
|
continue
|
||||||
|
return alt_resp
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# If all fallbacks failed, re-raise as TrackNotFound/Connection error
|
||||||
|
raise
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_medias_url(cls, tracks_token, quality):
|
def get_medias_url(cls, tracks_token, quality):
|
||||||
others_qualities = []
|
# Only request the specific desired quality to avoid unexpected fallbacks
|
||||||
|
|
||||||
for c_quality in qualities:
|
|
||||||
if c_quality == quality:
|
|
||||||
continue
|
|
||||||
|
|
||||||
c_quality_set = {
|
|
||||||
"cipher": "BF_CBC_STRIPE",
|
|
||||||
"format": c_quality
|
|
||||||
}
|
|
||||||
|
|
||||||
others_qualities.append(c_quality_set)
|
|
||||||
|
|
||||||
json_data = {
|
json_data = {
|
||||||
"license_token": cls.__license_token,
|
"license_token": cls.__license_token,
|
||||||
"media": [
|
"media": [
|
||||||
@@ -303,7 +316,7 @@ class API_GW:
|
|||||||
"cipher": "BF_CBC_STRIPE",
|
"cipher": "BF_CBC_STRIPE",
|
||||||
"format": quality
|
"format": quality
|
||||||
}
|
}
|
||||||
] + others_qualities
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"track_tokens": tracks_token
|
"track_tokens": tracks_token
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class QualityNotFound(Exception):
|
|||||||
if not msg:
|
if not msg:
|
||||||
self.msg = (
|
self.msg = (
|
||||||
f"The {quality} quality doesn't exist :)\
|
f"The {quality} quality doesn't exist :)\
|
||||||
\nThe qualities have to be FLAC or MP3_320 or MP3_256 or MP3_128"
|
\nThe qualities have to be FLAC, MP3_320 or MP3_128"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|||||||
Reference in New Issue
Block a user