Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions orpheus/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,5 +629,36 @@ def orpheus_core_download(orpheus_session: Orpheus, media_to_download, third_par
# Show "no tracks deferred" message only for multiple track downloads
downloader.print('No tracks were deferred due to rate limiting.', drop_level=0)
print() # Add blank line after message


minutes = int(downloader.total_download_time // 60)
seconds = downloader.total_download_time % 60
if downloader.total_download_time > 0:
time_str = f"{minutes}m {seconds:.1f}s"
else:
time_str = f"{seconds:.1f}s"
print()
print("=== TOTAL COUNTS ===")
print(f"=== Download time: {time_str}")
print(f'=== {downloader.track_download_count} tracks downloaded ===')
print(f'=== {downloader.track_skipped_count} tracks skipped ===')
print()

if (downloader.track_not_streamable_count > 0) or (downloader.track_download_failed_count):
albumsSet = set(downloader.albums_with_failed_tracks)
print("=== ERRORS ===")
# if(downloader.track_not_streamable_count > 0):
# print(f'=== {downloader.track_not_streamable_count} tracks not streamable ===')
# for album in set(downloader.tracks_not_streamable):
# print(f' https://play.qobuz.com/album/{album}')
if(downloader.track_download_failed_count > 0):
print(f'=== {downloader.track_download_failed_count} tracks failed on {len(albumsSet)} albums ===')
for album in albumsSet:
print(f' https://play.qobuz.com/album/{album}')
print()
else:
print("=== NO ERRORS ===")
print()


if os.path.exists('temp'): shutil.rmtree('temp')
38 changes: 36 additions & 2 deletions orpheus/music_downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,16 @@ def __init__(self, settings, module_controls, oprinter, path, third_party_module
self.print = self.oprinter.oprint
self.set_indent_number = self.oprinter.set_indent_number

self.track_download_count = 0
self.track_skipped_count = 0

self.track_not_streamable_count = 0
self.tracks_not_streamable = []

self.track_download_failed_count = 0
self.albums_with_failed_tracks = []

self.total_download_time: float = 0
self._download_error_log_path = None
self._download_error_log_context = None
self._download_error_count = 0
Expand Down Expand Up @@ -1694,6 +1704,10 @@ async def bounded_download(index, args):
actual_already_existed = sum(1 for r in results if r and r[2] is None and r[1] is None) # Already existed
actual_failed = sum(1 for r in results if r and r[2] is not None) # Failed with error

self.track_download_count += actual_downloaded
self.track_skipped_count += actual_already_existed
self.track_download_failed_count += actual_failed

# Show final summary only when there are failures
if actual_failed > 0:
# Check if most failures are SoundCloud FFmpeg-related
Expand Down Expand Up @@ -2715,6 +2729,12 @@ def download_album(self, album_id, artist_name='', path=None, indent_level=1, ex
# Download tracks concurrently
results = self._concurrent_download_tracks(album_info.tracks, download_args_list, concurrent_downloads, performance_summary_indent=0)

for result in results:
if(isinstance(result[2], Exception)):
# self.albums_with_failed_tracks.append(album_info.album_id)
# self.albums_with_failed_tracks.append(album_info.album_id)
self.albums_with_failed_tracks.append(list(album_info.track_extra_kwargs['data'].values())[result[0]]['album']['id'])

# Process results and collect rate-limited tracks
# (Errors are already reported by concurrent download progress monitor)
rate_limited_tracks = []
Expand Down Expand Up @@ -3722,6 +3742,18 @@ def return_with_blank_line(value, failure_reason=None):
track_info = self._ensure_track_info_id(track_info, track_id)
self._apply_album_context_to_track(track_info, safe_extra_kwargs)

if track_info.error:
if "is not streamable" in track_info.error:
print(f'NOT STREAMABLE: {track_info}')
self.track_not_streamable_count +=1
self.tracks_not_streamable.append(track_info.album_id)
else:
self.track_download_failed_count +=1
self.albums_with_failed_tracks.append(track_info.album_id)

self.print(track_info.error)
self.print(f'=== Track {track_id} failed ===', drop_level=1)

# If we got track info, break out of retry loop
if track_info is not None:
break
Expand Down Expand Up @@ -3931,6 +3963,7 @@ def return_with_blank_line(value, failure_reason=None):

symbols = self._get_status_symbols()
d_print(f'=== {symbols["skip"]} Track skipped ===', drop_level=header_drop_level)
self.track_skipped_count +=1

return return_with_blank_line("SKIPPED")

Expand Down Expand Up @@ -4290,6 +4323,7 @@ def return_with_blank_line(value, failure_reason=None):

symbols = self._get_status_symbols()
d_print(f'=== {symbols["success"]} Track completed ===', drop_level=header_drop_level)
self.track_download_count+=1

# Clean up temporary artwork file
if artwork_path and os.path.exists(artwork_path):
Expand Down Expand Up @@ -4525,8 +4559,8 @@ def _get_artwork_settings(self, module_name = None, is_external = False):
covers = self.global_settings.get('covers', {})
save_original = bool(covers.get('save_original_cover_size', False))
return {
'should_resize': not save_original,
'resolution': covers.get('external_resolution', 3000) if is_external else covers.get('main_resolution', 1400),
'should_resize': ModuleFlags.needs_cover_resize in self.module_settings[module_name].flags,
'resolution': self.global_settings['covers']['external_resolution'] if is_external else self.global_settings['covers']['main_resolution'],
'compression': self.global_settings['covers']['external_compression'] if is_external else self.global_settings['covers']['main_compression'],
'format': self.global_settings['covers']['external_format'] if is_external else 'jpg'
}
14 changes: 14 additions & 0 deletions orpheus/tagging.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from mutagen.oggopus import OggOpus
from mutagen.oggvorbis import OggVorbis
from mutagen.oggvorbis import OggVorbisHeaderError
from datetime import date
import mutagen

from utils.exceptions import *
Expand All @@ -24,6 +25,7 @@

# Needed for Windows tagging support
MP4Tags._padding = 0
today = date.today()


def _resize_image_if_needed(image_path: str, max_size_bytes: int = 16 * 1024 * 1024, target_resolution: tuple = (3000, 3000)) -> str:
Expand Down Expand Up @@ -156,6 +158,11 @@ def tag_file(file_path: str, image_path: str, track_info: TrackInfo, credits_lis
if 'encoder' in tagger.tags:
del tagger.tags['encoder']

tagger['title'] = track_info.name
if track_info.album: tagger['album'] = track_info.album
if track_info.tags.album_artists: tagger['albumartist'] = track_info.tags.album_artists
tagger['artist'] = track_info.artists

if container == ContainerEnum.m4a or container == ContainerEnum.mp4:
# Raw MP4 atom names for standard tags
tagger['\xa9nam'] = [track_info.name]
Expand Down Expand Up @@ -185,6 +192,10 @@ def tag_file(file_path: str, image_path: str, track_info: TrackInfo, credits_lis
else:
tagger['artist'] = metadata_separator.join(track_info.artists) if isinstance(track_info.artists, list) else track_info.artists

tagger['title'] = track_info.name
if track_info.album: tagger['album'] = track_info.album
if track_info.tags.album_artists: tagger['albumartist'] = track_info.tags.album_artists
tagger['artist'] = track_info.artists
tn = track_info.tags.track_number
tt = track_info.tags.total_tracks
dn = track_info.tags.disc_number
Expand Down Expand Up @@ -320,6 +331,9 @@ def tag_file(file_path: str, image_path: str, track_info: TrackInfo, credits_lis
if track_info.tags.description and (container == ContainerEnum.m4a or container == ContainerEnum.mp4):
tagger['desc'] = [track_info.tags.description]

tagger['comment'] = 'Qobuz OrpheusDL ' + today.strftime("%m/%d/%y")
tagger['source'] = 'Qobuz'

# add comment tag
if track_info.tags.comment:
if container == ContainerEnum.m4a or container == ContainerEnum.mp4:
Expand Down
1 change: 1 addition & 0 deletions utils/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ def get_gui_handler(self, handler_name: str):
@dataclass
class Tags:
album_artist: Optional[str] = None
album_artists: Optional[list] = None
composer: Optional[str] = None
track_number: Optional[int] = None
total_tracks: Optional[int] = None
Expand Down