From fdfe0ac3d12ec5cb1b0e1c5306435013589c740e Mon Sep 17 00:00:00 2001 From: Rum <40669776+rumzie@users.noreply.github.com> Date: Tue, 19 May 2026 00:59:17 -0400 Subject: [PATCH] rum implementation download counts album artists from album level attr. including performers when collecgin artists respect original artwork cover size comment and source tags --- orpheus/core.py | 31 +++++++++++++++++++++++++++++++ orpheus/music_downloader.py | 37 ++++++++++++++++++++++++++++++++++++- orpheus/tagging.py | 15 +++++++++++++++ utils/models.py | 1 + 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/orpheus/core.py b/orpheus/core.py index 977ae2e..d147814 100644 --- a/orpheus/core.py +++ b/orpheus/core.py @@ -561,5 +561,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') \ No newline at end of file diff --git a/orpheus/music_downloader.py b/orpheus/music_downloader.py index 219986e..b16448b 100644 --- a/orpheus/music_downloader.py +++ b/orpheus/music_downloader.py @@ -284,6 +284,17 @@ 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 + def _fetch_metadata(self, track_info: TrackInfo): """Fetches lyrics and credits using either the main service or third-party modules.""" # 1. Fetch Lyrics @@ -938,6 +949,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 @@ -1684,6 +1699,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 = [] @@ -2636,6 +2657,18 @@ def return_with_blank_line(value): safe_extra_kwargs = extra_kwargs if extra_kwargs is not None else {} track_info = self.service.get_track_info(track_id, quality_tier, codec_options, **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 @@ -2846,6 +2879,7 @@ def return_with_blank_line(value): 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") @@ -3201,6 +3235,7 @@ def return_with_blank_line(value): 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): @@ -3434,7 +3469,7 @@ def _get_artwork_settings(self, module_name = None, is_external = False): if not module_name: module_name = self.service_name return { - 'should_resize': True, # Always attempt resizing to respect the user's resolution setting + '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' diff --git a/orpheus/tagging.py b/orpheus/tagging.py index f7d760b..6bbe2e9 100644 --- a/orpheus/tagging.py +++ b/orpheus/tagging.py @@ -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 * @@ -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: @@ -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] @@ -184,6 +191,11 @@ 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 + if container == ContainerEnum.m4a or container == ContainerEnum.mp4: # MP4 uses tuple format: [(track_number, total_tracks)] tn = track_info.tags.track_number or 0 @@ -300,6 +312,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: diff --git a/utils/models.py b/utils/models.py index f8dfa36..f027804 100644 --- a/utils/models.py +++ b/utils/models.py @@ -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