From 9b7760fe73069293b226d2bb6f3d8420f13fdc15 Mon Sep 17 00:00:00 2001 From: Dniel97 <6324072+Dniel97@users.noreply.github.com> Date: Tue, 4 Oct 2022 15:14:06 +0200 Subject: [PATCH 1/5] Fixed mobile session switching to properly get Dolby Atmos?, close #22 --- interface.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface.py b/interface.py index 9ed4c00..644a645 100644 --- a/interface.py +++ b/interface.py @@ -398,10 +398,11 @@ class ModuleInterface: self.album_cache = {album_id: album_data} # check if album is only available in LOSSLESS and STEREO, so it switches to the MOBILE_DEFAULT which will - # get FLACs faster + # get FLACs faster, instead of using MPEG-DASH + # TODO: Can the MOBILE_DEFAULT" be deleted? if (self.settings['force_non_spatial'] or ( (quality_tier is QualityEnum.LOSSLESS or album_data.get('audioQuality') == 'LOSSLESS') - and 'STEREO' in album_data.get('audioModes'))) and SessionType.MOBILE_DEFAULT.name in self.available_sessions: + and album_data.get('audioModes') == ['STEREO'])) and SessionType.MOBILE_DEFAULT.name in self.available_sessions: self.session.default = SessionType.MOBILE_DEFAULT elif (track_data.get('audioModes') == ['SONY_360RA'] or ('DOLBY_ATMOS' in track_data.get('audioModes') and self.settings['prefer_ac4'])) \ From 5b84d3e0e6e98aa0b0f689e9777c3a2b30bd2b28 Mon Sep 17 00:00:00 2001 From: Dniel97 <6324072+Dniel97@users.noreply.github.com> Date: Mon, 10 Oct 2022 18:55:53 +0200 Subject: [PATCH 2/5] Added `duration` field and fixed playlist search --- interface.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/interface.py b/interface.py index 644a645..480a87c 100644 --- a/interface.py +++ b/interface.py @@ -185,6 +185,7 @@ class ModuleInterface: items = [] for i in results[query_type.name + 's'].get('items'): + duration = None if query_type is DownloadTypeEnum.artist: name = i.get('name') artists = None @@ -197,14 +198,17 @@ class ModuleInterface: else: artists = ['Unknown'] + duration = i.get('duration') # TODO: Use playlist creation date or lastUpdated? year = i.get('created')[:4] elif query_type is DownloadTypeEnum.track: artists = [j.get('name') for j in i.get('artists')] # Getting the year from the album? year = i.get('album').get('releaseDate')[:4] if i.get('album').get('releaseDate') else None + duration = i.get('duration') elif query_type is DownloadTypeEnum.album: artists = [j.get('name') for j in i.get('artists')] + duration = i.get('duration') year = i.get('releaseDate')[:4] else: raise Exception('Query type is invalid') @@ -214,7 +218,7 @@ class ModuleInterface: name += f' ({i.get("version")})' if i.get("version") else '' additional = None - if query_type is not DownloadTypeEnum.artist: + if query_type not in {DownloadTypeEnum.artist, DownloadTypeEnum.playlist}: if 'DOLBY_ATMOS' in i.get('audioModes'): additional = "Dolby Atmos" elif 'SONY_360RA' in i.get('audioModes'): @@ -230,6 +234,7 @@ class ModuleInterface: year=year, result_id=str(i.get('id')) if query_type is not DownloadTypeEnum.playlist else i.get('uuid'), explicit=i.get('explicit'), + duration=duration, additional=[additional] if additional else None ) @@ -255,6 +260,7 @@ class ModuleInterface: creator=creator_name, tracks=tracks, release_year=playlist_data.get('created')[:4], + duration=playlist_data.get('duration'), creator_id=playlist_data['creator'].get('id'), cover_url=self.generate_artwork_url(playlist_data['squareImage'], size=self.cover_size, max_size=1080) if playlist_data['squareImage'] else None, @@ -362,6 +368,7 @@ class ModuleInterface: explicit=album_data.get('explicit'), quality=quality, upc=album_data.get('upc'), + duration=album_data.get('duration'), cover_url=self.generate_artwork_url(album_data.get('cover'), size=self.cover_size) if album_data.get('cover') else None, animated_cover_url=self.generate_animated_artwork_url(album_data.get('videoCover')) if album_data.get( @@ -528,7 +535,7 @@ class ModuleInterface: return track_info @staticmethod - def download_temp_header(file_url: str, chunk_size: int = 16384) -> str: + def download_temp_header(file_url: str, chunk_size: int = 32768) -> str: # create flac temp_location temp_location = create_temp_filename() + '.flac' From 06238521eace4de526bd4b1ff1f3266ed27b5776 Mon Sep 17 00:00:00 2001 From: Dniel97 <6324072+Dniel97@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:48:12 +0200 Subject: [PATCH 3/5] Hotfix: Ignore inactive issues when "feature-request" is present --- .github/workflows/close_inactive_issues.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/close_inactive_issues.yml b/.github/workflows/close_inactive_issues.yml index d89c9c7..56101bc 100644 --- a/.github/workflows/close_inactive_issues.yml +++ b/.github/workflows/close_inactive_issues.yml @@ -19,4 +19,5 @@ jobs: close-issue-message: "This issue was automatically closed because it has been inactive for 7 days since being marked as stale." days-before-pr-stale: -1 days-before-pr-close: -1 + exempt-issue-labels: "feature-request" repo-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 263f220b0763797292b6eeb967bbe9416c847a87 Mon Sep 17 00:00:00 2001 From: Dniel97 <6324072+Dniel97@users.noreply.github.com> Date: Wed, 12 Oct 2022 20:29:35 +0200 Subject: [PATCH 4/5] Hotfix: Fixed lyrics for spatial audio tracks with multiple artists --- interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface.py b/interface.py index 480a87c..08d8e0a 100644 --- a/interface.py +++ b/interface.py @@ -698,7 +698,7 @@ class ModuleInterface: # search for title and artist to find a matching track (non Atmos) results = self.search( DownloadTypeEnum.track, - f'{track_data.get("title")} {"".join(a.get("name") for a in track_data.get("artists"))}', + f'{track_data.get("title")} {" ".join(a.get("name") for a in track_data.get("artists"))}', limit=10) # check every result to find a matching result From 7f34df71da11d500fe193980366d166492f13cd0 Mon Sep 17 00:00:00 2001 From: Dniel97 <6324072+Dniel97@users.noreply.github.com> Date: Thu, 13 Oct 2022 18:10:14 +0200 Subject: [PATCH 5/5] Added `cover_resize_flag` --- interface.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/interface.py b/interface.py index 08d8e0a..01a3137 100644 --- a/interface.py +++ b/interface.py @@ -30,6 +30,7 @@ module_information = ModuleInformation( 'prefer_ac4': False, 'fix_mqa': True }, + flags=ModuleFlags.needs_cover_resize, session_storage_variables=['sessions'], netlocation_constant='tidal', test_url='https://tidal.com/browse/track/92265335' @@ -166,7 +167,7 @@ class ModuleInterface: return True @staticmethod - def generate_artwork_url(cover_id: str, size: int, max_size: int = 1280): + def _generate_artwork_url(cover_id: str, size: int, max_size: int = 1280): # not the best idea, but it rounds the self.cover_size to the nearest number in supported_sizes, 1281 is needed # for the "uncompressed" cover supported_sizes = [80, 160, 320, 480, 640, 1080, 1280, 1281] @@ -177,7 +178,7 @@ class ModuleInterface: return f'https://resources.tidal.com/images/{cover_id.replace("-", "/")}/{image_name}' @staticmethod - def generate_animated_artwork_url(cover_id: str, size=1280): + def _generate_animated_artwork_url(cover_id: str, size=1280): return 'https://resources.tidal.com/videos/{0}/{1}x{1}.mp4'.format(cover_id.replace('-', '/'), size) def search(self, query_type: DownloadTypeEnum, query: str, track_info: TrackInfo = None, limit: int = 20): @@ -262,8 +263,8 @@ class ModuleInterface: release_year=playlist_data.get('created')[:4], duration=playlist_data.get('duration'), creator_id=playlist_data['creator'].get('id'), - cover_url=self.generate_artwork_url(playlist_data['squareImage'], size=self.cover_size, - max_size=1080) if playlist_data['squareImage'] else None, + cover_url=self._generate_artwork_url(playlist_data['squareImage'], size=self.cover_size, + max_size=1080) if playlist_data['squareImage'] else None, track_extra_kwargs={ 'data': {track.get('item').get('id'): track.get('item') for track in playlist_tracks.get('items')} } @@ -369,9 +370,9 @@ class ModuleInterface: quality=quality, upc=album_data.get('upc'), duration=album_data.get('duration'), - cover_url=self.generate_artwork_url(album_data.get('cover'), - size=self.cover_size) if album_data.get('cover') else None, - animated_cover_url=self.generate_animated_artwork_url(album_data.get('videoCover')) if album_data.get( + cover_url=self._generate_artwork_url(album_data.get('cover'), + size=self.cover_size) if album_data.get('cover') else None, + animated_cover_url=self._generate_animated_artwork_url(album_data.get('videoCover')) if album_data.get( 'videoCover') else None, artist=album_data.get('artist').get('name'), artist_id=album_data.get('artist').get('id'), @@ -514,8 +515,8 @@ class ModuleInterface: sample_rate=sample_rate, bitrate=bitrate, duration=track_data.get('duration'), - cover_url=self.generate_artwork_url(track_data['album'].get('cover'), - size=self.cover_size) if track_data['album'].get('cover') else None, + cover_url=self._generate_artwork_url(track_data['album'].get('cover'), + size=self.cover_size) if track_data['album'].get('cover') else None, explicit=track_data.get('explicit'), tags=self.convert_tags(track_data, album_data, mqa_file), codec=track_codec, @@ -684,7 +685,7 @@ class ModuleInterface: cover_id = track_data['album'].get('cover') # Tidal don't support PNG, so it will always get JPG - cover_url = self.generate_artwork_url(cover_id, size=cover_options.resolution) + cover_url = self._generate_artwork_url(cover_id, size=cover_options.resolution) return CoverInfo(url=cover_url, file_type=ImageFileTypeEnum.jpg) def get_track_lyrics(self, track_id: str, track_data: dict = None) -> LyricsInfo: