diff --git a/HISTORY.rst b/HISTORY.rst index 308875a..00be614 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,6 +4,7 @@ History ======= v0.7.7 ------ +* BREAKING: MQA, Sony 360 audio no longer supported July 24th 2024; removed references to these formats. - tehkillerbee_ * Bugfix: Logical loop of is_encrypted / encryption_key - TooYoungTooSimp_ * Tests: Added additional playlist, folder tests. - tehkillerbee_ * Feature: Add support for playlist merging. - tehkillerbee_ diff --git a/examples/pkce_example.py b/examples/pkce_example.py index d6f2a8d..dbe0475 100644 --- a/examples/pkce_example.py +++ b/examples/pkce_example.py @@ -33,13 +33,10 @@ # Low: Quality.low_96k (m4a 96k) # Normal: Quality.low_320k (m4a 320k) # HiFi: Quality.high_lossless (FLAC) -# HiFi+ Quality.hi_res (FLAC MQA) # HiFi+ Quality.hi_res_lossless (FLAC HI_RES) session.audio_quality = Quality.hi_res_lossless -# album_id = "249593867" # Alice In Chains / We Die Young (Max quality: HI_RES MHA1 SONY360) -# album_id = "77640617" # U2 / Achtung Baby (Max quality: HI_RES MQA, 16bit/44100Hz) # album_id = "110827651" # The Black Keys / Let's Rock (Max quality: LOSSLESS FLAC, 24bit/48000Hz) -album_id = "77646169" # Beck / Sea Change (Max quality: HI_RES_LOSSLESS FLAC, 24bit/192000Hz) +album_id = "77646169" # Beck / Sea Change (Max quality: HI_RES_LOSSLESS FLAC, 24bit/192000Hz) album = session.album(album_id) res = album.get_audio_resolution() tracks = album.tracks() diff --git a/examples/simple.py b/examples/simple.py index 3e167ab..708d927 100644 --- a/examples/simple.py +++ b/examples/simple.py @@ -17,9 +17,10 @@ # """simple.py: A simple example script that describes how to get started using tidalapi""" +from pathlib import Path + import tidalapi from tidalapi import Quality -from pathlib import Path session_file1 = Path("tidal-session-oauth.json") @@ -32,13 +33,12 @@ # Low: Quality.low_96k (m4a 96k) # Normal: Quality.low_320k (m4a 320k) # HiFi: Quality.high_lossless (FLAC) -# HiFi+ Quality.hi_res (FLAC MQA) # HiFi+ Quality.hi_res_lossless (FLAC HI_RES) session.audio_quality = Quality.hi_res_lossless # album_id = "77640617" # U2 / Achtung Baby (Max quality: HI_RES MQA, 16bit/44100Hz) # album_id = "110827651" # The Black Keys / Let's Rock (Max quality: LOSSLESS FLAC, 24bit/48000Hz) -album_id = "77646169" # Beck / Sea Change (Max quality: HI_RES_LOSSLESS FLAC, 24bit/192000Hz) +album_id = "77646169" # Beck / Sea Change (Max quality: HI_RES_LOSSLESS FLAC, 24bit/192000Hz) album = session.album(album_id) tracks = album.tracks() # list album tracks diff --git a/tests/test_media.py b/tests/test_media.py index 5e0a61e..02a9a6b 100644 --- a/tests/test_media.py +++ b/tests/test_media.py @@ -120,6 +120,10 @@ def test_track_streaming(session): @pytest.mark.skip(reason="SONY360 support has been removed") def test_track_quality_sony360(session): + # TIDAL: For 360 Reality Audio: If you had a 360 Reality Audio track or album in your Collection – + # including in a playlist or downloaded for offline access – + # the track or album will be grayed out and unavailable for streaming if you try to select it. + # Ref: https://support.tidal.com/hc/en-us/articles/25876825185425-Audio-Format-Updates # Session should allow highest possible quality (but will fallback to highest available album quality) session.audio_quality = Quality.hi_res_lossless # Alice In Chains / We Die Young (Max quality: HI_RES MHA1 SONY360; Album has now been removed) @@ -149,6 +153,10 @@ def test_track_quality_atmos(session): @pytest.mark.skip(reason="MQA albums appears to fallback to LOSSLESS") def test_track_quality_mqa(session): + # TIDAL: + # For MQA: If you had an MQA track or album in your Collection – including in playlists – + # the track was automatically replaced by the highest quality FLAC version that has been distributed to TIDAL. + # Ref: https://support.tidal.com/hc/en-us/articles/25876825185425-Audio-Format-Updates # Session should allow highest possible quality (but will fallback to highest available album quality) session.audio_quality = Quality.hi_res_lossless # U2 / Achtung Baby (Max quality: HI_RES MQA, 16bit/44100Hz) diff --git a/tidalapi/media.py b/tidalapi/media.py index 461cd8b..b4ea04d 100644 --- a/tidalapi/media.py +++ b/tidalapi/media.py @@ -79,7 +79,6 @@ def __str__(self) -> str: class AudioMode(str, Enum): stereo: str = "STEREO" - sony_360: str = "SONY_360RA" dolby_atmos: str = "DOLBY_ATMOS" def __str__(self) -> str: @@ -87,10 +86,8 @@ def __str__(self) -> str: class MediaMetadataTags(str, Enum): - mqa: str = "MQA" hires_lossless: str = "HIRES_LOSSLESS" lossless: str = "LOSSLESS" - sony_360: str = "SONY_360RA" dolby_atmos: str = "DOLBY_ATMOS" def __str__(self) -> str: @@ -129,12 +126,10 @@ class Codec(str, Enum): AAC: str = "AAC" MP4A: str = "MP4A" FLAC: str = "FLAC" - MQA: str = "MQA" Atmos: str = "EAC3" AC4: str = "AC4" - SONY360RA: str = "MHA1" LowResCodecs: [str] = [MP3, AAC, MP4A] - PremiumCodecs: [str] = [MQA, Atmos, AC4] + PremiumCodecs: [str] = [Atmos, AC4] HQCodecs: [str] = PremiumCodecs + [FLAC] def __str__(self) -> str: @@ -158,7 +153,6 @@ class MimeType(str, Enum): Codec.AAC: audio_m4a, Codec.MP4A: audio_m4a, Codec.FLAC: audio_xflac, - Codec.MQA: audio_xflac, Codec.Atmos: audio_eac3, Codec.AC4: audio_ac4, } @@ -171,7 +165,7 @@ def from_audio_codec(codec): return MimeType.audio_map.get(codec, MimeType.audio_m4a) @staticmethod - def is_FLAC(mime_type): + def is_flac(mime_type): return ( True if mime_type in [MimeType.audio_flac, MimeType.audio_xflac] else False ) @@ -443,26 +437,6 @@ def get_stream(self) -> "Stream": assert not isinstance(stream, list) return cast("Stream", stream) - @property - def is_mqa(self) -> bool: - try: - if self.media_metadata_tags: - return ( - True - if MediaMetadataTags.mqa in self.media_metadata_tags - and not self.is_sony360 - and not self.is_dolby_atmos - else False - ) - except: - pass - # Fallback to old method - return True if self.audio_quality == Quality.hi_res else False - - @property - def is_hi_res_mqa(self) -> bool: - return self.is_mqa - @property def is_hi_res_lossless(self) -> bool: try: @@ -494,13 +468,6 @@ def is_dolby_atmos(self) -> bool: except: return False - @property - def is_sony360(self) -> bool: - try: - return True if AudioMode.sony_360 in self.audio_modes else False - except: - return False - class Stream: """An object that stores the audio file properties and parameters needed for @@ -510,7 +477,7 @@ class Stream: """ track_id: int = -1 - audio_mode: str = AudioMode.stereo # STEREO, SONY_360RA, DOLBY_ATMOS + audio_mode: str = AudioMode.stereo # STEREO, DOLBY_ATMOS audio_quality: str = ( Quality.low_320k ) # LOW, HIGH, LOSSLESS, HI_RES, HI_RES_LOSSLESS @@ -659,13 +626,15 @@ def get_file_extension(stream_url: str, stream_codec: Optional[str] = None) -> s if AudioExtensions.FLAC in stream_url: result: str = AudioExtensions.FLAC elif AudioExtensions.MP4 in stream_url: - if "ac4" in stream_codec or "mha1" in stream_codec: - result = ".mp4" - elif "flac" in stream_codec: - result = ".flac" + if stream_codec: + if Codec.AC4 is stream_codec: + result: str = AudioExtensions.MP4 + elif Codec.FLAC is stream_codec: + result: str = AudioExtensions.FLAC + else: + result: str = AudioExtensions.M4A else: - result = ".m4a" - result: str = AudioExtensions.MP4 + result: str = AudioExtensions.MP4 elif VideoExtensions.TS in stream_url: result: str = VideoExtensions.TS else: