debloating
This commit is contained in:
@@ -33,6 +33,8 @@ from deezspot.libutils.utils import (
|
||||
trasform_sync_lyric,
|
||||
create_zip,
|
||||
sanitize_name,
|
||||
save_cover_image,
|
||||
__get_dir as get_album_directory,
|
||||
)
|
||||
from mutagen.flac import FLAC
|
||||
from mutagen.mp3 import MP3
|
||||
@@ -192,7 +194,6 @@ class EASY_DW:
|
||||
self.__ids = preferences.ids
|
||||
self.__link = preferences.link
|
||||
self.__output_dir = preferences.output_dir
|
||||
self.__method_save = preferences.method_save
|
||||
self.__not_interface = preferences.not_interface
|
||||
self.__quality_download = preferences.quality_download
|
||||
self.__recursive_quality = preferences.recursive_quality
|
||||
@@ -277,7 +278,6 @@ class EASY_DW:
|
||||
self.__output_dir,
|
||||
self.__song_quality,
|
||||
self.__file_format,
|
||||
self.__method_save,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks
|
||||
@@ -292,7 +292,6 @@ class EASY_DW:
|
||||
self.__output_dir,
|
||||
self.__song_quality,
|
||||
self.__file_format,
|
||||
self.__method_save,
|
||||
is_episode=True,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
@@ -915,10 +914,9 @@ class DW_ALBUM:
|
||||
self.__ids = self.__preferences.ids
|
||||
self.__make_zip = self.__preferences.make_zip
|
||||
self.__output_dir = self.__preferences.output_dir
|
||||
self.__method_save = self.__preferences.method_save
|
||||
self.__song_metadata = self.__preferences.song_metadata
|
||||
self.__not_interface = self.__preferences.not_interface
|
||||
self.__quality_download = self.__preferences.quality_download
|
||||
self.__recursive_quality = self.__preferences.recursive_quality
|
||||
|
||||
self.__song_metadata_items = self.__song_metadata.items()
|
||||
|
||||
@@ -966,11 +964,11 @@ class DW_ALBUM:
|
||||
infos_dw = API_GW.get_album_data(self.__ids)['data']
|
||||
|
||||
md5_image = infos_dw[0]['ALB_PICTURE']
|
||||
image = API.choose_img(md5_image)
|
||||
self.__song_metadata['image'] = image
|
||||
image_bytes = API.choose_img(md5_image, size="1400x1400") # Fetch highest quality
|
||||
self.__song_metadata['image'] = image_bytes # Store for tagging if needed, already bytes
|
||||
|
||||
album = Album(self.__ids)
|
||||
album.image = image
|
||||
album.image = image_bytes # Store raw image bytes
|
||||
album.md5_image = md5_image
|
||||
album.nb_tracks = self.__song_metadata['nb_tracks']
|
||||
album.album_name = self.__song_metadata['album']
|
||||
@@ -983,7 +981,13 @@ class DW_ALBUM:
|
||||
infos_dw, self.__quality_download
|
||||
)
|
||||
|
||||
# The album_artist for tagging individual tracks will be derived_album_artist_from_contributors
|
||||
# Determine album base directory once
|
||||
album_base_directory = get_album_directory(
|
||||
self.__song_metadata, # Album level metadata
|
||||
self.__output_dir,
|
||||
custom_dir_format=self.__preferences.custom_dir_format,
|
||||
pad_tracks=self.__preferences.pad_tracks
|
||||
)
|
||||
|
||||
total_tracks = len(infos_dw)
|
||||
for a in range(total_tracks):
|
||||
@@ -1074,6 +1078,10 @@ class DW_ALBUM:
|
||||
logger.warning(f"Track not found: {song} :( Details: {track.error_message}. URL: {c_preferences.link if c_preferences else 'N/A'}")
|
||||
tracks.append(track)
|
||||
|
||||
# Save album cover image
|
||||
if album.image and album_base_directory:
|
||||
save_cover_image(album.image, album_base_directory, "cover.jpg")
|
||||
|
||||
if self.__make_zip:
|
||||
song_quality = tracks[0].quality if tracks else 'Unknown'
|
||||
# Pass along custom directory format if set
|
||||
@@ -1083,7 +1091,6 @@ class DW_ALBUM:
|
||||
output_dir=self.__output_dir,
|
||||
song_metadata=self.__song_metadata,
|
||||
song_quality=song_quality,
|
||||
method_save=self.__method_save,
|
||||
custom_dir_format=custom_dir_format
|
||||
)
|
||||
album.zip_path = zip_name
|
||||
@@ -1226,7 +1233,6 @@ class DW_EPISODE:
|
||||
self.__preferences = preferences
|
||||
self.__ids = preferences.ids
|
||||
self.__output_dir = preferences.output_dir
|
||||
self.__method_save = preferences.method_save
|
||||
self.__not_interface = preferences.not_interface
|
||||
self.__quality_download = preferences.quality_download
|
||||
|
||||
@@ -1335,6 +1341,16 @@ class DW_EPISODE:
|
||||
}
|
||||
Download_JOB.report_progress(progress_data)
|
||||
|
||||
# Save cover image for the episode
|
||||
episode_image_md5 = infos_dw.get('EPISODE_IMAGE_MD5', '')
|
||||
episode_image_data = None
|
||||
if episode_image_md5:
|
||||
episode_image_data = API.choose_img(episode_image_md5, size="1200x1200")
|
||||
|
||||
if episode_image_data:
|
||||
episode_directory = os.path.dirname(output_path)
|
||||
save_cover_image(episode_image_data, episode_directory, "cover.jpg")
|
||||
|
||||
return episode
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -39,7 +39,7 @@ from deezspot.libutils.others_settings import (
|
||||
stock_recursive_download,
|
||||
stock_not_interface,
|
||||
stock_zip,
|
||||
method_save,
|
||||
stock_save_cover,
|
||||
)
|
||||
from deezspot.libutils.logging_utils import ProgressReporter, logger
|
||||
|
||||
@@ -97,7 +97,6 @@ class DeeLogin:
|
||||
recursive_quality=stock_recursive_quality,
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
@@ -130,7 +129,6 @@ class DeeLogin:
|
||||
preferences.recursive_quality = recursive_quality
|
||||
preferences.recursive_download = recursive_download
|
||||
preferences.not_interface = not_interface
|
||||
preferences.method_save = method_save
|
||||
# New custom formatting preferences:
|
||||
preferences.custom_dir_format = custom_dir_format
|
||||
preferences.custom_track_format = custom_track_format
|
||||
@@ -155,14 +153,14 @@ class DeeLogin:
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
make_zip=stock_zip,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
initial_retry_delay=30,
|
||||
retry_delay_increase=30,
|
||||
max_retries=5,
|
||||
convert_to=None
|
||||
convert_to=None,
|
||||
save_cover=stock_save_cover
|
||||
) -> Album:
|
||||
|
||||
link_is_valid(link_album)
|
||||
@@ -185,7 +183,6 @@ class DeeLogin:
|
||||
preferences.recursive_quality = recursive_quality
|
||||
preferences.recursive_download = recursive_download
|
||||
preferences.not_interface = not_interface
|
||||
preferences.method_save = method_save
|
||||
preferences.make_zip = make_zip
|
||||
# New custom formatting preferences:
|
||||
preferences.custom_dir_format = custom_dir_format
|
||||
@@ -198,6 +195,7 @@ class DeeLogin:
|
||||
preferences.max_retries = max_retries
|
||||
# Audio conversion parameter
|
||||
preferences.convert_to = convert_to
|
||||
preferences.save_cover = save_cover
|
||||
|
||||
album = DW_ALBUM(preferences).dw()
|
||||
|
||||
@@ -211,7 +209,6 @@ class DeeLogin:
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
make_zip=stock_zip,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
@@ -251,7 +248,6 @@ class DeeLogin:
|
||||
preferences.recursive_quality = recursive_quality
|
||||
preferences.recursive_download = recursive_download
|
||||
preferences.not_interface = not_interface
|
||||
preferences.method_save = method_save
|
||||
preferences.make_zip = make_zip
|
||||
# New custom formatting preferences:
|
||||
preferences.custom_dir_format = custom_dir_format
|
||||
@@ -276,7 +272,6 @@ class DeeLogin:
|
||||
recursive_quality=stock_recursive_quality,
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
@@ -293,7 +288,6 @@ class DeeLogin:
|
||||
track['link'], output_dir,
|
||||
quality_download, recursive_quality,
|
||||
recursive_download, not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
@@ -332,7 +326,6 @@ class DeeLogin:
|
||||
recursive_quality=stock_recursive_quality,
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
@@ -351,7 +344,6 @@ class DeeLogin:
|
||||
recursive_quality=recursive_quality,
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
@@ -450,7 +442,6 @@ class DeeLogin:
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
make_zip=stock_zip,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
@@ -466,7 +457,7 @@ class DeeLogin:
|
||||
link_dee, output_dir,
|
||||
quality_download, recursive_quality,
|
||||
recursive_download, not_interface,
|
||||
make_zip, method_save,
|
||||
make_zip,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
@@ -486,7 +477,6 @@ class DeeLogin:
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
make_zip=stock_zip,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
@@ -563,7 +553,6 @@ class DeeLogin:
|
||||
recursive_quality=recursive_quality,
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
@@ -618,7 +607,6 @@ class DeeLogin:
|
||||
recursive_quality=stock_recursive_quality,
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
@@ -648,7 +636,6 @@ class DeeLogin:
|
||||
recursive_quality=recursive_quality,
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
@@ -665,14 +652,14 @@ class DeeLogin:
|
||||
recursive_quality=stock_recursive_quality,
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
initial_retry_delay=30,
|
||||
retry_delay_increase=30,
|
||||
max_retries=5,
|
||||
convert_to=None
|
||||
convert_to=None,
|
||||
save_cover=stock_save_cover
|
||||
) -> Episode:
|
||||
|
||||
link_is_valid(link_episode)
|
||||
@@ -707,16 +694,8 @@ class DeeLogin:
|
||||
preferences.recursive_quality = recursive_quality
|
||||
preferences.recursive_download = recursive_download
|
||||
preferences.not_interface = not_interface
|
||||
preferences.method_save = method_save
|
||||
# New custom formatting preferences:
|
||||
preferences.custom_dir_format = custom_dir_format
|
||||
preferences.custom_track_format = custom_track_format
|
||||
# Track number padding option
|
||||
preferences.pad_tracks = pad_tracks
|
||||
# Retry parameters
|
||||
preferences.initial_retry_delay = initial_retry_delay
|
||||
preferences.retry_delay_increase = retry_delay_increase
|
||||
preferences.max_retries = max_retries
|
||||
# No convert_to for episode download
|
||||
preferences.save_cover = save_cover
|
||||
|
||||
episode = DW_EPISODE(preferences).dw()
|
||||
|
||||
@@ -730,7 +709,6 @@ class DeeLogin:
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
make_zip=stock_zip,
|
||||
method_save=method_save,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
pad_tracks=True,
|
||||
@@ -773,7 +751,6 @@ class DeeLogin:
|
||||
recursive_quality=recursive_quality,
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
method_save=method_save,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
@@ -800,7 +777,6 @@ class DeeLogin:
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
make_zip=make_zip,
|
||||
method_save=method_save,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
@@ -827,7 +803,6 @@ class DeeLogin:
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
make_zip=make_zip,
|
||||
method_save=method_save,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
|
||||
@@ -115,45 +115,6 @@ def check_track_md5(infos_dw):
|
||||
logger.error(f"Failed to check track MD5: {str(e)}")
|
||||
raise
|
||||
|
||||
def set_path(song_metadata, output_dir, method_save):
|
||||
"""
|
||||
Set the output path for a track based on metadata and save method.
|
||||
|
||||
Args:
|
||||
song_metadata: Track metadata
|
||||
output_dir: Base output directory
|
||||
method_save: Save method (e.g., 'artist/album/track')
|
||||
|
||||
Returns:
|
||||
str: Full output path
|
||||
"""
|
||||
try:
|
||||
# Create base directory if it doesn't exist
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Build path based on method
|
||||
if method_save == 'artist/album/track':
|
||||
path = os.path.join(
|
||||
output_dir,
|
||||
song_metadata['artist'],
|
||||
song_metadata['album'],
|
||||
f"{song_metadata['music']}.mp3"
|
||||
)
|
||||
else:
|
||||
path = os.path.join(
|
||||
output_dir,
|
||||
f"{song_metadata['artist']} - {song_metadata['music']}.mp3"
|
||||
)
|
||||
|
||||
# Create parent directories
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
|
||||
return path
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to set path: {str(e)}")
|
||||
raise
|
||||
|
||||
def trasform_sync_lyric(lyrics):
|
||||
"""
|
||||
Transform synchronized lyrics into a standard format.
|
||||
|
||||
@@ -21,5 +21,5 @@ stock_recursive_quality = False
|
||||
stock_recursive_download = False
|
||||
stock_not_interface = False
|
||||
stock_zip = False
|
||||
is_thread = False # WARNING FOR TRUE, LOOP ON DEFAULT
|
||||
stock_real_time_dl = True
|
||||
stock_save_cover = False # Default for saving cover image
|
||||
|
||||
@@ -203,7 +203,6 @@ def set_path(
|
||||
pad_tracks=True
|
||||
):
|
||||
# Determine the directory for the song
|
||||
# method_save is removed, __get_dir now only relies on custom_dir_format
|
||||
directory = __get_dir(
|
||||
song_metadata,
|
||||
output_dir,
|
||||
@@ -306,3 +305,27 @@ def trasform_sync_lyric(lyric):
|
||||
arr = (a['line'], int(a['milliseconds']))
|
||||
sync_array.append(arr)
|
||||
return sync_array
|
||||
|
||||
def save_cover_image(image_data: bytes, directory_path: str, cover_filename: str = "cover.jpg"):
|
||||
if not image_data:
|
||||
logger.warning(f"No image data provided to save cover in {directory_path}.")
|
||||
return
|
||||
|
||||
if not isdir(directory_path):
|
||||
# This case should ideally be handled by prior directory creation (e.g., __get_dir)
|
||||
# but as a fallback, we can try to create it or log a warning.
|
||||
logger.warning(f"Directory {directory_path} does not exist. Attempting to create it for cover image.")
|
||||
try:
|
||||
makedirs(directory_path, exist_ok=True)
|
||||
logger.info(f"Created directory {directory_path} for cover image.")
|
||||
except OSError as e:
|
||||
logger.error(f"Failed to create directory {directory_path} for cover: {e}")
|
||||
return
|
||||
|
||||
cover_path = join(directory_path, cover_filename)
|
||||
try:
|
||||
with open(cover_path, "wb") as f:
|
||||
f.write(image_data)
|
||||
logger.info(f"Successfully saved cover image to {cover_path}")
|
||||
except OSError as e:
|
||||
logger.error(f"Failed to save cover image to {cover_path}: {e}")
|
||||
|
||||
@@ -18,4 +18,5 @@ class Preferences:
|
||||
self.pad_tracks = True # Default to padded track numbers (01, 02, etc.)
|
||||
self.initial_retry_delay = 30 # Default initial retry delay in seconds
|
||||
self.retry_delay_increase = 30 # Default increase in delay between retries in seconds
|
||||
self.max_retries = 5 # Default maximum number of retries per track
|
||||
self.max_retries = 5 # Default maximum number of retries per track
|
||||
self.save_cover: bool = False # Option to save a cover.jpg image
|
||||
@@ -32,6 +32,8 @@ from deezspot.libutils.utils import (
|
||||
create_zip,
|
||||
request,
|
||||
sanitize_name,
|
||||
save_cover_image,
|
||||
__get_dir as get_album_directory,
|
||||
)
|
||||
from mutagen import File
|
||||
from mutagen.easyid3 import EasyID3
|
||||
@@ -136,7 +138,6 @@ class EASY_DW:
|
||||
self.__ids = preferences.ids
|
||||
self.__link = preferences.link
|
||||
self.__output_dir = preferences.output_dir
|
||||
self.__method_save = preferences.method_save
|
||||
self.__song_metadata = preferences.song_metadata
|
||||
self.__not_interface = preferences.not_interface
|
||||
self.__quality_download = preferences.quality_download or "NORMAL"
|
||||
@@ -169,7 +170,6 @@ class EASY_DW:
|
||||
self.__output_dir,
|
||||
self.__song_quality,
|
||||
self.__file_format,
|
||||
self.__method_save,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks
|
||||
@@ -184,7 +184,6 @@ class EASY_DW:
|
||||
self.__output_dir,
|
||||
self.__song_quality,
|
||||
self.__file_format,
|
||||
self.__method_save,
|
||||
is_episode=True,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
@@ -1004,29 +1003,37 @@ class EASY_DW:
|
||||
self.__write_episode()
|
||||
# Write metadata tags so subsequent skips work
|
||||
write_tags(self.__c_episode)
|
||||
|
||||
# Save episode cover image if download was successful
|
||||
if self.__c_episode.success and hasattr(self.__c_episode, 'episode_path') and self.__c_episode.episode_path:
|
||||
episode_directory = dirname(self.__c_episode.episode_path)
|
||||
image_url = self.__song_metadata.get('image')
|
||||
image_bytes = None
|
||||
if image_url:
|
||||
try:
|
||||
image_bytes = request(image_url).content
|
||||
except Exception as e_img:
|
||||
logger.warning(f"Failed to fetch cover image for episode {self.__c_episode.tags.get('name', '')}: {e_img}")
|
||||
if image_bytes:
|
||||
save_cover_image(image_bytes, episode_directory, "cover.jpg")
|
||||
|
||||
return self.__c_episode
|
||||
|
||||
def download_cli(preferences: Preferences) -> None:
|
||||
__link = preferences.link
|
||||
__output_dir = preferences.output_dir
|
||||
__method_save = preferences.method_save
|
||||
__not_interface = preferences.not_interface
|
||||
__quality_download = preferences.quality_download
|
||||
__recursive_download = preferences.recursive_download
|
||||
__recursive_quality = preferences.recursive_quality
|
||||
cmd = f"deez-dw.py -so spo -l \"{__link}\" "
|
||||
if __output_dir:
|
||||
cmd += f"-o {__output_dir} "
|
||||
if __method_save:
|
||||
cmd += f"-sa {__method_save} "
|
||||
if __not_interface:
|
||||
cmd += f"-g "
|
||||
if __quality_download:
|
||||
cmd += f"-q {__quality_download} "
|
||||
if __recursive_download:
|
||||
cmd += f"-rd "
|
||||
if __recursive_quality:
|
||||
cmd += f"-rq"
|
||||
system(cmd)
|
||||
|
||||
class DW_TRACK:
|
||||
@@ -1042,11 +1049,6 @@ class DW_TRACK:
|
||||
# it's an intentional skip, not an error
|
||||
return track
|
||||
|
||||
def dw2(self) -> Track:
|
||||
track = EASY_DW(self.__preferences).get_no_dw_track()
|
||||
download_cli(self.__preferences)
|
||||
return track
|
||||
|
||||
class DW_ALBUM:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -1056,7 +1058,6 @@ class DW_ALBUM:
|
||||
self.__ids = self.__preferences.ids
|
||||
self.__make_zip = self.__preferences.make_zip
|
||||
self.__output_dir = self.__preferences.output_dir
|
||||
self.__method_save = self.__preferences.method_save
|
||||
self.__song_metadata = self.__preferences.song_metadata
|
||||
self.__not_interface = self.__preferences.not_interface
|
||||
self.__song_metadata_items = self.__song_metadata.items()
|
||||
@@ -1098,11 +1099,12 @@ class DW_ALBUM:
|
||||
"url": f"https://open.spotify.com/album/{album_id}"
|
||||
})
|
||||
|
||||
pic = self.__song_metadata['image']
|
||||
image = request(pic).content
|
||||
self.__song_metadata['image'] = image
|
||||
pic_url = self.__song_metadata['image'] # This is URL for spotify
|
||||
image_bytes = request(pic_url).content
|
||||
self.__song_metadata['image'] = image_bytes # Keep bytes for tagging
|
||||
|
||||
album = Album(self.__ids)
|
||||
album.image = image
|
||||
album.image = image_bytes # Store raw image bytes for cover saving
|
||||
album.nb_tracks = self.__song_metadata['nb_tracks']
|
||||
album.album_name = self.__song_metadata['album']
|
||||
album.upc = self.__song_metadata['upc']
|
||||
@@ -1110,6 +1112,14 @@ class DW_ALBUM:
|
||||
album.md5_image = self.__ids
|
||||
album.tags = self.__song_metadata
|
||||
|
||||
# Determine album base directory once
|
||||
album_base_directory = get_album_directory(
|
||||
self.__song_metadata, # Album level metadata
|
||||
self.__output_dir,
|
||||
custom_dir_format=self.__preferences.custom_dir_format,
|
||||
pad_tracks=self.__preferences.pad_tracks
|
||||
)
|
||||
|
||||
c_song_metadata = {}
|
||||
for key, item in self.__song_metadata_items:
|
||||
if type(item) is not list:
|
||||
@@ -1147,6 +1157,11 @@ class DW_ALBUM:
|
||||
track.error_message = f"An unexpected error occurred: {str(e_generic)}"
|
||||
logger.error(f"Unexpected error downloading track '{song_name}' by '{artist_name}' from album '{album.album_name}'. Reason: {track.error_message}")
|
||||
tracks.append(track)
|
||||
|
||||
# Save album cover image
|
||||
if album.image and album_base_directory:
|
||||
save_cover_image(album.image, album_base_directory, "cover.jpg")
|
||||
|
||||
if self.__make_zip:
|
||||
song_quality = tracks[0].quality
|
||||
custom_dir_format = getattr(self.__preferences, 'custom_dir_format', None)
|
||||
@@ -1155,7 +1170,6 @@ class DW_ALBUM:
|
||||
output_dir=self.__output_dir,
|
||||
song_metadata=self.__song_metadata,
|
||||
song_quality=song_quality,
|
||||
method_save=self.__method_save,
|
||||
custom_dir_format=custom_dir_format
|
||||
)
|
||||
album.zip_path = zip_name
|
||||
@@ -1185,11 +1199,6 @@ class DW_ALBUM:
|
||||
|
||||
return album
|
||||
|
||||
def dw2(self) -> Album:
|
||||
track = EASY_DW(self.__preferences).get_no_dw_track()
|
||||
download_cli(self.__preferences)
|
||||
return track
|
||||
|
||||
class DW_PLAYLIST:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -1284,86 +1293,6 @@ class DW_PLAYLIST:
|
||||
|
||||
return playlist
|
||||
|
||||
def dw2(self) -> Playlist:
|
||||
# Extract playlist metadata for reporting
|
||||
playlist_name = self.__json_data.get('name', 'Unknown Playlist')
|
||||
playlist_owner = self.__json_data.get('owner', {}).get('display_name', 'Unknown Owner')
|
||||
total_tracks = self.__json_data.get('tracks', {}).get('total', 'unknown')
|
||||
playlist_id = self.__ids
|
||||
|
||||
# Report playlist initializing status
|
||||
Download_JOB.report_progress({
|
||||
"type": "playlist",
|
||||
"owner": playlist_owner,
|
||||
"status": "initializing",
|
||||
"total_tracks": total_tracks,
|
||||
"name": playlist_name,
|
||||
"url": f"https://open.spotify.com/playlist/{playlist_id}"
|
||||
})
|
||||
|
||||
playlist = Playlist()
|
||||
tracks = playlist.tracks
|
||||
for i, c_song_metadata in enumerate(self.__song_metadata):
|
||||
if type(c_song_metadata) is str:
|
||||
logger.warning(f"Track not found {c_song_metadata}")
|
||||
continue
|
||||
c_preferences = deepcopy(self.__preferences)
|
||||
c_preferences.ids = c_song_metadata['ids']
|
||||
c_preferences.song_metadata = c_song_metadata
|
||||
c_preferences.json_data = self.__json_data # Pass playlist data for reporting
|
||||
c_preferences.track_number = i + 1 # Track number in the playlist
|
||||
|
||||
# Even though we're not downloading directly, we still need to set up the track object
|
||||
track = EASY_DW(c_preferences, parent='playlist').get_no_dw_track()
|
||||
if not track.success:
|
||||
song = f"{c_song_metadata['music']} - {c_song_metadata['artist']}"
|
||||
error_detail = getattr(track, 'error_message', 'Download failed for unspecified reason.')
|
||||
logger.warning(f"Cannot download '{song}' (CLI mode). Reason: {error_detail} (Link: {track.link or c_preferences.link})")
|
||||
tracks.append(track)
|
||||
|
||||
# Track-level progress reporting using the standardized format
|
||||
progress_data = {
|
||||
"type": "track",
|
||||
"song": c_song_metadata.get("music", ""),
|
||||
"artist": c_song_metadata.get("artist", ""),
|
||||
"status": "progress",
|
||||
"current_track": i + 1,
|
||||
"total_tracks": total_tracks,
|
||||
"parent": {
|
||||
"type": "playlist",
|
||||
"name": playlist_name,
|
||||
"owner": self.__json_data.get('owner', {}).get('display_name', 'unknown'),
|
||||
"total_tracks": total_tracks,
|
||||
"url": f"https://open.spotify.com/playlist/{self.__json_data.get('id', '')}"
|
||||
},
|
||||
"url": f"https://open.spotify.com/track/{c_song_metadata['ids']}"
|
||||
}
|
||||
Download_JOB.report_progress(progress_data)
|
||||
download_cli(self.__preferences)
|
||||
|
||||
if self.__make_zip:
|
||||
playlist_title = self.__json_data['name']
|
||||
zip_name = f"{self.__output_dir}/{playlist_title} [playlist {self.__ids}]"
|
||||
create_zip(tracks, zip_name=zip_name)
|
||||
playlist.zip_path = zip_name
|
||||
|
||||
# Report playlist done status
|
||||
playlist_name = self.__json_data.get('name', 'Unknown Playlist')
|
||||
playlist_owner = self.__json_data.get('owner', {}).get('display_name', 'Unknown Owner')
|
||||
total_tracks = self.__json_data.get('tracks', {}).get('total', 0)
|
||||
playlist_id = self.__ids
|
||||
|
||||
Download_JOB.report_progress({
|
||||
"type": "playlist",
|
||||
"owner": playlist_owner,
|
||||
"status": "done",
|
||||
"total_tracks": total_tracks,
|
||||
"name": playlist_name,
|
||||
"url": f"https://open.spotify.com/playlist/{playlist_id}"
|
||||
})
|
||||
|
||||
return playlist
|
||||
|
||||
class DW_EPISODE:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -1389,42 +1318,19 @@ class DW_EPISODE:
|
||||
|
||||
episode = EASY_DW(self.__preferences).download_eps()
|
||||
|
||||
# Using standardized episode progress format
|
||||
progress_data = {
|
||||
"type": "episode",
|
||||
"song": self.__preferences.song_metadata.get('name', 'Unknown Episode'),
|
||||
"artist": self.__preferences.song_metadata.get('show', 'Unknown Show'),
|
||||
"status": "done"
|
||||
}
|
||||
|
||||
# Set URL if available
|
||||
episode_id = self.__preferences.ids
|
||||
if episode_id:
|
||||
progress_data["url"] = f"https://open.spotify.com/episode/{episode_id}"
|
||||
|
||||
Download_JOB.report_progress(progress_data)
|
||||
|
||||
return episode
|
||||
# Save episode cover image if download was successful
|
||||
if episode.success and hasattr(episode, 'episode_path') and episode.episode_path:
|
||||
episode_directory = dirname(episode.episode_path)
|
||||
image_url = self.__preferences.song_metadata.get('image')
|
||||
image_bytes = None
|
||||
if image_url:
|
||||
try:
|
||||
image_bytes = request(image_url).content
|
||||
except Exception as e_img:
|
||||
logger.warning(f"Failed to fetch cover image for episode {episode.tags.get('name', '')}: {e_img}")
|
||||
if image_bytes:
|
||||
save_cover_image(image_bytes, episode_directory, "cover.jpg")
|
||||
|
||||
def dw2(self) -> Episode:
|
||||
# Using standardized episode progress format
|
||||
progress_data = {
|
||||
"type": "episode",
|
||||
"song": self.__preferences.song_metadata.get('name', 'Unknown Episode'),
|
||||
"artist": self.__preferences.song_metadata.get('show', 'Unknown Show'),
|
||||
"status": "initializing"
|
||||
}
|
||||
|
||||
# Set URL if available
|
||||
episode_id = self.__preferences.ids
|
||||
if episode_id:
|
||||
progress_data["url"] = f"https://open.spotify.com/episode/{episode_id}"
|
||||
|
||||
Download_JOB.report_progress(progress_data)
|
||||
|
||||
episode = EASY_DW(self.__preferences).get_no_dw_track()
|
||||
download_cli(self.__preferences)
|
||||
|
||||
# Using standardized episode progress format
|
||||
progress_data = {
|
||||
"type": "episode",
|
||||
|
||||
@@ -32,8 +32,7 @@ from deezspot.libutils.others_settings import (
|
||||
stock_recursive_download,
|
||||
stock_not_interface,
|
||||
stock_zip,
|
||||
method_save,
|
||||
is_thread,
|
||||
stock_save_cover,
|
||||
stock_real_time_dl
|
||||
)
|
||||
from deezspot.libutils.logging_utils import logger, ProgressReporter
|
||||
@@ -90,8 +89,6 @@ class SpoLogin:
|
||||
recursive_quality=stock_recursive_quality,
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
method_save=method_save,
|
||||
is_thread=is_thread,
|
||||
real_time_dl=stock_real_time_dl,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
@@ -118,7 +115,6 @@ class SpoLogin:
|
||||
preferences.recursive_quality = recursive_quality
|
||||
preferences.recursive_download = recursive_download
|
||||
preferences.not_interface = not_interface
|
||||
preferences.method_save = method_save
|
||||
preferences.is_episode = False
|
||||
preferences.custom_dir_format = custom_dir_format
|
||||
preferences.custom_track_format = custom_track_format
|
||||
@@ -128,10 +124,7 @@ class SpoLogin:
|
||||
preferences.max_retries = max_retries
|
||||
preferences.convert_to = convert_to
|
||||
|
||||
if not is_thread:
|
||||
track = DW_TRACK(preferences).dw()
|
||||
else:
|
||||
track = DW_TRACK(preferences).dw2()
|
||||
track = DW_TRACK(preferences).dw()
|
||||
|
||||
return track
|
||||
except Exception as e:
|
||||
@@ -147,8 +140,6 @@ class SpoLogin:
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
make_zip=stock_zip,
|
||||
method_save=method_save,
|
||||
is_thread=is_thread,
|
||||
real_time_dl=stock_real_time_dl,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
@@ -156,7 +147,8 @@ class SpoLogin:
|
||||
initial_retry_delay=30,
|
||||
retry_delay_increase=30,
|
||||
max_retries=5,
|
||||
convert_to=None
|
||||
convert_to=None,
|
||||
save_cover=stock_save_cover
|
||||
) -> Album:
|
||||
try:
|
||||
link_is_valid(link_album)
|
||||
@@ -178,7 +170,6 @@ class SpoLogin:
|
||||
preferences.recursive_quality = recursive_quality
|
||||
preferences.recursive_download = recursive_download
|
||||
preferences.not_interface = not_interface
|
||||
preferences.method_save = method_save
|
||||
preferences.make_zip = make_zip
|
||||
preferences.is_episode = False
|
||||
preferences.custom_dir_format = custom_dir_format
|
||||
@@ -188,11 +179,9 @@ class SpoLogin:
|
||||
preferences.retry_delay_increase = retry_delay_increase
|
||||
preferences.max_retries = max_retries
|
||||
preferences.convert_to = convert_to
|
||||
preferences.save_cover = save_cover
|
||||
|
||||
if not is_thread:
|
||||
album = DW_ALBUM(preferences).dw()
|
||||
else:
|
||||
album = DW_ALBUM(preferences).dw2()
|
||||
album = DW_ALBUM(preferences).dw()
|
||||
|
||||
return album
|
||||
except Exception as e:
|
||||
@@ -208,8 +197,6 @@ class SpoLogin:
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
make_zip=stock_zip,
|
||||
method_save=method_save,
|
||||
is_thread=is_thread,
|
||||
real_time_dl=stock_real_time_dl,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
@@ -217,7 +204,8 @@ class SpoLogin:
|
||||
initial_retry_delay=30,
|
||||
retry_delay_increase=30,
|
||||
max_retries=5,
|
||||
convert_to=None
|
||||
convert_to=None,
|
||||
save_cover=stock_save_cover
|
||||
) -> Playlist:
|
||||
try:
|
||||
link_is_valid(link_playlist)
|
||||
@@ -253,7 +241,6 @@ class SpoLogin:
|
||||
preferences.recursive_quality = recursive_quality
|
||||
preferences.recursive_download = recursive_download
|
||||
preferences.not_interface = not_interface
|
||||
preferences.method_save = method_save
|
||||
preferences.make_zip = make_zip
|
||||
preferences.is_episode = False
|
||||
preferences.custom_dir_format = custom_dir_format
|
||||
@@ -263,11 +250,9 @@ class SpoLogin:
|
||||
preferences.retry_delay_increase = retry_delay_increase
|
||||
preferences.max_retries = max_retries
|
||||
preferences.convert_to = convert_to
|
||||
preferences.save_cover = save_cover
|
||||
|
||||
if not is_thread:
|
||||
playlist = DW_PLAYLIST(preferences).dw()
|
||||
else:
|
||||
playlist = DW_PLAYLIST(preferences).dw2()
|
||||
playlist = DW_PLAYLIST(preferences).dw()
|
||||
|
||||
return playlist
|
||||
except Exception as e:
|
||||
@@ -282,8 +267,6 @@ class SpoLogin:
|
||||
recursive_quality=stock_recursive_quality,
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
method_save=method_save,
|
||||
is_thread=is_thread,
|
||||
real_time_dl=stock_real_time_dl,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
@@ -291,7 +274,8 @@ class SpoLogin:
|
||||
initial_retry_delay=30,
|
||||
retry_delay_increase=30,
|
||||
max_retries=5,
|
||||
convert_to=None
|
||||
convert_to=None,
|
||||
save_cover=stock_save_cover
|
||||
) -> Episode:
|
||||
try:
|
||||
link_is_valid(link_episode)
|
||||
@@ -312,7 +296,6 @@ class SpoLogin:
|
||||
preferences.recursive_quality = recursive_quality
|
||||
preferences.recursive_download = recursive_download
|
||||
preferences.not_interface = not_interface
|
||||
preferences.method_save = method_save
|
||||
preferences.is_episode = True
|
||||
preferences.custom_dir_format = custom_dir_format
|
||||
preferences.custom_track_format = custom_track_format
|
||||
@@ -321,11 +304,9 @@ class SpoLogin:
|
||||
preferences.retry_delay_increase = retry_delay_increase
|
||||
preferences.max_retries = max_retries
|
||||
preferences.convert_to = convert_to
|
||||
preferences.save_cover = save_cover
|
||||
|
||||
if not is_thread:
|
||||
episode = DW_EPISODE(preferences).dw()
|
||||
else:
|
||||
episode = DW_EPISODE(preferences).dw2()
|
||||
episode = DW_EPISODE(preferences).dw()
|
||||
|
||||
return episode
|
||||
except Exception as e:
|
||||
@@ -343,8 +324,6 @@ class SpoLogin:
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
make_zip=stock_zip,
|
||||
method_save=method_save,
|
||||
is_thread=is_thread,
|
||||
real_time_dl=stock_real_time_dl,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
@@ -382,8 +361,6 @@ class SpoLogin:
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
make_zip=make_zip,
|
||||
method_save=method_save,
|
||||
is_thread=is_thread,
|
||||
real_time_dl=real_time_dl,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
@@ -408,7 +385,6 @@ class SpoLogin:
|
||||
recursive_download=stock_recursive_download,
|
||||
not_interface=stock_not_interface,
|
||||
make_zip=stock_zip,
|
||||
method_save=method_save,
|
||||
real_time_dl=stock_real_time_dl,
|
||||
custom_dir_format=None,
|
||||
custom_track_format=None,
|
||||
@@ -416,7 +392,8 @@ class SpoLogin:
|
||||
initial_retry_delay=30,
|
||||
retry_delay_increase=30,
|
||||
max_retries=5,
|
||||
convert_to=None
|
||||
convert_to=None,
|
||||
save_cover=stock_save_cover
|
||||
) -> Smart:
|
||||
try:
|
||||
link_is_valid(link)
|
||||
@@ -439,7 +416,6 @@ class SpoLogin:
|
||||
recursive_quality=recursive_quality,
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
method_save=method_save,
|
||||
real_time_dl=real_time_dl,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
@@ -462,14 +438,15 @@ class SpoLogin:
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
make_zip=make_zip,
|
||||
method_save=method_save,
|
||||
real_time_dl=real_time_dl,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
initial_retry_delay=initial_retry_delay,
|
||||
retry_delay_increase=retry_delay_increase,
|
||||
max_retries=max_retries
|
||||
max_retries=max_retries,
|
||||
convert_to=convert_to,
|
||||
save_cover=save_cover
|
||||
)
|
||||
smart.type = "album"
|
||||
smart.album = album
|
||||
@@ -485,14 +462,15 @@ class SpoLogin:
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
make_zip=make_zip,
|
||||
method_save=method_save,
|
||||
real_time_dl=real_time_dl,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
initial_retry_delay=initial_retry_delay,
|
||||
retry_delay_increase=retry_delay_increase,
|
||||
max_retries=max_retries
|
||||
max_retries=max_retries,
|
||||
convert_to=convert_to,
|
||||
save_cover=save_cover
|
||||
)
|
||||
smart.type = "playlist"
|
||||
smart.playlist = playlist
|
||||
@@ -507,14 +485,15 @@ class SpoLogin:
|
||||
recursive_quality=recursive_quality,
|
||||
recursive_download=recursive_download,
|
||||
not_interface=not_interface,
|
||||
method_save=method_save,
|
||||
real_time_dl=real_time_dl,
|
||||
custom_dir_format=custom_dir_format,
|
||||
custom_track_format=custom_track_format,
|
||||
pad_tracks=pad_tracks,
|
||||
initial_retry_delay=initial_retry_delay,
|
||||
retry_delay_increase=retry_delay_increase,
|
||||
max_retries=max_retries
|
||||
max_retries=max_retries,
|
||||
convert_to=convert_to,
|
||||
save_cover=save_cover
|
||||
)
|
||||
smart.type = "episode"
|
||||
smart.episode = episode
|
||||
|
||||
Reference in New Issue
Block a user