Never erase tracks from Tidal favorites
This commit is contained in:
@@ -194,15 +194,6 @@ async def get_tracks_from_spotify_playlist(spotify_session: spotipy.Spotify, spo
|
|||||||
return await _fetch_all_from_spotify_in_chunks(lambda offset, session=spotify_session: _get_tracks_from_spotify_playlist(offset=offset, spotify_session=session, playlist_id=spotify_playlist["id"]))
|
return await _fetch_all_from_spotify_in_chunks(lambda offset, session=spotify_session: _get_tracks_from_spotify_playlist(offset=offset, spotify_session=session, playlist_id=spotify_playlist["id"]))
|
||||||
|
|
||||||
|
|
||||||
async def get_tracks_from_spotify_favorites(spotify_session: spotipy.Spotify) -> List[dict]:
|
|
||||||
def _get_favorite_tracks(offset: int):
|
|
||||||
return spotify_session.current_user_saved_tracks(offset=offset)
|
|
||||||
|
|
||||||
print("Loading favorite tracks from Spotify")
|
|
||||||
tracks = await _fetch_all_from_spotify_in_chunks(_get_favorite_tracks)
|
|
||||||
tracks.reverse()
|
|
||||||
return tracks
|
|
||||||
|
|
||||||
def populate_track_match_cache(spotify_tracks_: Sequence[t_spotify.SpotifyTrack], tidal_tracks_: Sequence[tidalapi.Track]):
|
def populate_track_match_cache(spotify_tracks_: Sequence[t_spotify.SpotifyTrack], tidal_tracks_: Sequence[tidalapi.Track]):
|
||||||
""" Populate the track match cache with all the existing tracks in Tidal playlist corresponding to Spotify playlist """
|
""" Populate the track match cache with all the existing tracks in Tidal playlist corresponding to Spotify playlist """
|
||||||
def _populate_one_track_from_spotify(spotify_track: t_spotify.SpotifyTrack):
|
def _populate_one_track_from_spotify(spotify_track: t_spotify.SpotifyTrack):
|
||||||
@@ -257,8 +248,8 @@ def get_tracks_for_new_tidal_playlist(spotify_tracks: Sequence[t_spotify.Spotify
|
|||||||
seen_tracks.add(tidal_id)
|
seen_tracks.add(tidal_id)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
async def search_new_tracks_on_tidal(tidal_session: tidalapi.Session, spotify_tracks: Sequence[t_spotify.SpotifyTrack], playlist_name: str, config: dict) -> List[t_spotify.SpotifyTrack]:
|
async def search_new_tracks_on_tidal(tidal_session: tidalapi.Session, spotify_tracks: Sequence[t_spotify.SpotifyTrack], playlist_name: str, config: dict):
|
||||||
""" Generic function for searching for each item in a list of Spotify tracks which have not already been seen """
|
""" Generic function for searching for each item in a list of Spotify tracks which have not already been seen and adding them to the cache """
|
||||||
async def _run_rate_limiter(semaphore):
|
async def _run_rate_limiter(semaphore):
|
||||||
''' Leaky bucket algorithm for rate limiting. Periodically releases items from semaphore at rate_limit'''
|
''' Leaky bucket algorithm for rate limiting. Periodically releases items from semaphore at rate_limit'''
|
||||||
_sleep_time = config.get('max_concurrency', 10)/config.get('rate_limit', 10)/4 # aim to sleep approx time to drain 1/4 of 'bucket'
|
_sleep_time = config.get('max_concurrency', 10)/config.get('rate_limit', 10)/4 # aim to sleep approx time to drain 1/4 of 'bucket'
|
||||||
@@ -274,7 +265,7 @@ async def search_new_tracks_on_tidal(tidal_session: tidalapi.Session, spotify_tr
|
|||||||
# Extract the new tracks that do not already exist in the old tidal tracklist
|
# Extract the new tracks that do not already exist in the old tidal tracklist
|
||||||
tracks_to_search = get_new_spotify_tracks(spotify_tracks)
|
tracks_to_search = get_new_spotify_tracks(spotify_tracks)
|
||||||
if not tracks_to_search:
|
if not tracks_to_search:
|
||||||
return get_tracks_for_new_tidal_playlist(spotify_tracks)
|
return
|
||||||
|
|
||||||
# Search for each of the tracks on Tidal concurrently
|
# Search for each of the tracks on Tidal concurrently
|
||||||
task_description = "Searching Tidal for {}/{} tracks in Spotify playlist '{}'".format(len(tracks_to_search), len(spotify_tracks), playlist_name)
|
task_description = "Searching Tidal for {}/{} tracks in Spotify playlist '{}'".format(len(tracks_to_search), len(spotify_tracks), playlist_name)
|
||||||
@@ -291,8 +282,6 @@ async def search_new_tracks_on_tidal(tidal_session: tidalapi.Session, spotify_tr
|
|||||||
color = ('\033[91m', '\033[0m')
|
color = ('\033[91m', '\033[0m')
|
||||||
print(color[0] + f"Could not find track {spotify_track['id']}: {','.join([a['name'] for a in spotify_track['artists']])} - {spotify_track['name']}" + color[1])
|
print(color[0] + f"Could not find track {spotify_track['id']}: {','.join([a['name'] for a in spotify_track['artists']])} - {spotify_track['name']}" + color[1])
|
||||||
|
|
||||||
return get_tracks_for_new_tidal_playlist(spotify_tracks)
|
|
||||||
|
|
||||||
async def sync_playlist(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, spotify_playlist, tidal_playlist: tidalapi.Playlist | None, config: dict):
|
async def sync_playlist(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, spotify_playlist, tidal_playlist: tidalapi.Playlist | None, config: dict):
|
||||||
""" sync given playlist to tidal """
|
""" sync given playlist to tidal """
|
||||||
# Create a new Tidal playlist if required
|
# Create a new Tidal playlist if required
|
||||||
@@ -304,7 +293,8 @@ async def sync_playlist(spotify_session: spotipy.Spotify, tidal_session: tidalap
|
|||||||
spotify_tracks = await get_tracks_from_spotify_playlist(spotify_session, spotify_playlist)
|
spotify_tracks = await get_tracks_from_spotify_playlist(spotify_session, spotify_playlist)
|
||||||
old_tidal_tracks = tidal_playlist.tracks()
|
old_tidal_tracks = tidal_playlist.tracks()
|
||||||
populate_track_match_cache(spotify_tracks, old_tidal_tracks)
|
populate_track_match_cache(spotify_tracks, old_tidal_tracks)
|
||||||
new_tidal_track_ids = await search_new_tracks_on_tidal(tidal_session, spotify_tracks, spotify_playlist['name'], config)
|
await search_new_tracks_on_tidal(tidal_session, spotify_tracks, spotify_playlist['name'], config)
|
||||||
|
new_tidal_track_ids = get_tracks_for_new_tidal_playlist(spotify_tracks)
|
||||||
|
|
||||||
# Update the Tidal playlist if there are changes
|
# Update the Tidal playlist if there are changes
|
||||||
old_tidal_track_ids = [t.id for t in old_tidal_tracks]
|
old_tidal_track_ids = [t.id for t in old_tidal_tracks]
|
||||||
@@ -320,30 +310,33 @@ async def sync_playlist(spotify_session: spotipy.Spotify, tidal_session: tidalap
|
|||||||
|
|
||||||
async def sync_favorites(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, config: dict):
|
async def sync_favorites(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, config: dict):
|
||||||
""" sync user favorites to tidal """
|
""" sync user favorites to tidal """
|
||||||
def _clear_favorites(session: tidalapi.Session, old_tidal_track_ids: List[tidalapi.Track]):
|
async def get_tracks_from_spotify_favorites() -> List[dict]:
|
||||||
for track_id in tqdm(old_tidal_track_ids, desc="Erasing existing Tidal favorites"):
|
_get_favorite_tracks = lambda offset: spotify_session.current_user_saved_tracks(offset=offset)
|
||||||
session.user.favorites.remove_track(track_id)
|
tracks = await _fetch_all_from_spotify_in_chunks(_get_favorite_tracks)
|
||||||
def _add_to_favorites(track_ids: Sequence[int], session: tidalapi.Session):
|
tracks.reverse()
|
||||||
for track_id in tqdm(track_ids, desc="Adding tracks to Tidal favorites"):
|
return tracks
|
||||||
session.user.favorites.add_track(track_id)
|
|
||||||
|
|
||||||
spotify_tracks = await get_tracks_from_spotify_favorites(spotify_session=spotify_session)
|
def get_new_tidal_favorites() -> List[int]:
|
||||||
print("Loading favorite tracks from Tidal")
|
existing_favorite_ids = set([track.id for track in old_tidal_tracks])
|
||||||
|
new_ids = []
|
||||||
|
for spotify_track in spotify_tracks:
|
||||||
|
match_id = track_match_cache.get(spotify_track['id'])
|
||||||
|
if match_id and not match_id in existing_favorite_ids:
|
||||||
|
new_ids.append(match_id)
|
||||||
|
return new_ids
|
||||||
|
|
||||||
|
print("Loading favorite tracks from Spotify")
|
||||||
|
spotify_tracks = await get_tracks_from_spotify_favorites()
|
||||||
|
print("Loading existing favorite tracks from Tidal")
|
||||||
old_tidal_tracks = await get_all_favorites(tidal_session.user.favorites, order='DATE')
|
old_tidal_tracks = await get_all_favorites(tidal_session.user.favorites, order='DATE')
|
||||||
populate_track_match_cache(spotify_tracks, old_tidal_tracks)
|
populate_track_match_cache(spotify_tracks, old_tidal_tracks)
|
||||||
new_tidal_track_ids = await search_new_tracks_on_tidal(tidal_session, spotify_tracks, "Favorites", config)
|
await search_new_tracks_on_tidal(tidal_session, spotify_tracks, "Favorites", config)
|
||||||
|
new_tidal_favorite_ids = get_new_tidal_favorites()
|
||||||
# Update the Tidal playlist if there are changes
|
if new_tidal_favorite_ids:
|
||||||
old_tidal_track_ids = [t.id for t in old_tidal_tracks]
|
for tidal_id in tqdm(new_tidal_favorite_ids, desc="Adding new tracks to Tidal favorites"):
|
||||||
if new_tidal_track_ids == old_tidal_track_ids:
|
tidal_session.user.favorites.add_track(tidal_id)
|
||||||
print("No changes to write to Tidal favorites")
|
|
||||||
elif new_tidal_track_ids[:len(old_tidal_track_ids)] == old_tidal_track_ids:
|
|
||||||
# Append new tracks to the existing playlist if possible
|
|
||||||
_add_to_favorites(new_tidal_track_ids[len(old_tidal_track_ids):], tidal_session)
|
|
||||||
else:
|
else:
|
||||||
# Erase old playlist and add new tracks from scratch if any reordering occured
|
print("No new tracks to add to Tidal favorites")
|
||||||
_clear_favorites(tidal_session, old_tidal_track_ids)
|
|
||||||
_add_to_favorites(new_tidal_track_ids, tidal_session)
|
|
||||||
|
|
||||||
def sync_playlists_wrapper(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, playlists, config: dict):
|
def sync_playlists_wrapper(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, playlists, config: dict):
|
||||||
for spotify_playlist, tidal_playlist in playlists:
|
for spotify_playlist, tidal_playlist in playlists:
|
||||||
|
|||||||
Reference in New Issue
Block a user