44from .cache import failure_cache , track_match_cache
55import datetime
66from difflib import SequenceMatcher
7- from functools import partial
87from typing import Callable , List , Sequence , Set , Mapping
98import math
109import requests
1716from tqdm import tqdm
1817import traceback
1918import unicodedata
20- import math
2119
2220from .type import spotify as t_spotify
2321
@@ -28,8 +26,11 @@ def simple(input_string: str) -> str:
2826 # only take the first part of a string before any hyphens or brackets to account for different versions
2927 return input_string .split ('-' )[0 ].strip ().split ('(' )[0 ].strip ().split ('[' )[0 ].strip ()
3028
31- def isrc_match (tidal_track : tidalapi .Track , spotify_track ) -> bool :
32- if "isrc" in spotify_track ["external_ids" ]:
29+ def deprecated_isrc_match (tidal_track : tidalapi .Track , spotify_track ) -> bool :
30+ """Spotify has deprecated the external_ids attribute and it will not be
31+ returned to clients past 03/09/2026. This method can be removed past this
32+ date."""
33+ if "isrc" in spotify_track .get ("external_ids" , {}):
3334 return tidal_track .isrc == spotify_track ["external_ids" ]["isrc" ]
3435 return False
3536
@@ -89,7 +90,7 @@ def get_spotify_artists(spotify, do_normalize=False) -> Set[str]:
8990
9091def match (tidal_track , spotify_track ) -> bool :
9192 if not spotify_track ['id' ]: return False
92- return isrc_match (tidal_track , spotify_track ) or (
93+ return deprecated_isrc_match (tidal_track , spotify_track ) or (
9394 duration_match (tidal_track , spotify_track )
9495 and name_match (tidal_track , spotify_track )
9596 and artist_match (tidal_track , spotify_track )
@@ -161,7 +162,7 @@ async def repeat_on_request_error(function, *args, remaining=5, **kwargs):
161162async def _fetch_all_from_spotify_in_chunks (fetch_function : Callable ) -> List [dict ]:
162163 output = []
163164 results = fetch_function (0 )
164- output .extend ([item ['track ' ] for item in results ['items' ] if item ['track' ] is not None ])
165+ output .extend ([item ['item ' ] for item in results ['items' ] if item ['item' ][ 'type' ] == 'track' ])
165166
166167 # Get all the remaining tracks in parallel
167168 if results ['next' ]:
@@ -171,15 +172,15 @@ async def _fetch_all_from_spotify_in_chunks(fetch_function: Callable) -> List[di
171172 desc = "Fetching additional data chunks"
172173 )
173174 for extra_result in extra_results :
174- output .extend ([item ['track ' ] for item in extra_result ['items' ] if item ['track' ] is not None ])
175+ output .extend ([item ['item ' ] for item in extra_result ['items' ] if item ['item' ][ 'type' ] == 'track' ])
175176
176177 return output
177178
178179
179180async def get_tracks_from_spotify_playlist (spotify_session : spotipy .Spotify , spotify_playlist ):
180181 def _get_tracks_from_spotify_playlist (offset : int , playlist_id : str ):
181- fields = "next,total,limit,items(track (name,album(name,artists),artists,track_number,duration_ms,id,external_ids(isrc))),type"
182- return spotify_session .playlist_tracks (playlist_id = playlist_id , fields = fields , offset = offset )
182+ fields = "next,total,limit,items(item (name,album(name,artists),artists,track_number,duration_ms,id,external_ids(isrc))),type"
183+ return spotify_session .playlist_items (playlist_id = playlist_id , fields = fields , offset = offset )
183184
184185 print (f"Loading tracks from Spotify playlist '{ spotify_playlist ['name' ]} '" )
185186 items = await repeat_on_request_error ( _fetch_all_from_spotify_in_chunks , lambda offset : _get_tracks_from_spotify_playlist (offset = offset , playlist_id = spotify_playlist ["id" ]))
@@ -239,7 +240,7 @@ def get_tracks_for_new_tidal_playlist(spotify_tracks: Sequence[t_spotify.Spotify
239240 if tidal_id in seen_tracks :
240241 track_name = spotify_track ['name' ]
241242 artist_names = ', ' .join ([artist ['name' ] for artist in spotify_track ['artists' ]])
242- print (f'Duplicate found: Track "{ track_name } " by { artist_names } will be ignored' )
243+ print (f'Duplicate found: Track "{ track_name } " by { artist_names } will be ignored' )
243244 else :
244245 output .append (tidal_id )
245246 seen_tracks .add (tidal_id )
@@ -287,7 +288,7 @@ async def _run_rate_limiter(semaphore):
287288 for song in song404 :
288289 file .write (f"{ song } \n " )
289290
290-
291+
291292async def sync_playlist (spotify_session : spotipy .Spotify , tidal_session : tidalapi .Session , spotify_playlist , tidal_playlist : tidalapi .Playlist | None , config : dict ):
292293 """ sync given playlist to tidal """
293294 # Get the tracks from both Spotify and Tidal, creating a new Tidal playlist if necessary
@@ -321,7 +322,7 @@ async def sync_playlist(spotify_session: spotipy.Spotify, tidal_session: tidalap
321322async def sync_favorites (spotify_session : spotipy .Spotify , tidal_session : tidalapi .Session , config : dict ):
322323 """ sync user favorites to tidal """
323324 async def get_tracks_from_spotify_favorites () -> List [dict ]:
324- _get_favorite_tracks = lambda offset : spotify_session .current_user_saved_tracks (offset = offset )
325+ _get_favorite_tracks = lambda offset : spotify_session .current_user_saved_tracks (offset = offset )
325326 tracks = await repeat_on_request_error ( _fetch_all_from_spotify_in_chunks , _get_favorite_tracks )
326327 tracks .reverse ()
327328 return tracks
0 commit comments