diff --git a/src/spotify_to_tidal/__main__.py b/src/spotify_to_tidal/__main__.py index 8a95fc6..2c18d66 100644 --- a/src/spotify_to_tidal/__main__.py +++ b/src/spotify_to_tidal/__main__.py @@ -6,10 +6,11 @@ from . import auth as _auth def main(): - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(prog="spotify_to_tidal") parser.add_argument('--config', default='config.yml', help='location of the config file') parser.add_argument('--uri', help='synchronize a specific URI instead of the one in the config') parser.add_argument('--sync-favorites', action=argparse.BooleanOptionalAction, help='synchronize the favorites') + parser.add_argument('--favs-to-playlist', metavar='PLAYLIST_NAME', help='synchronize Spotify favorites to specific Tidal playlist') args = parser.parse_args() with open(args.config, 'r') as f: @@ -29,6 +30,14 @@ def main(): sync_favorites = args.sync_favorites # only sync favorites if command line argument explicitly passed elif args.sync_favorites: sync_favorites = True # sync only the favorites + elif args.favs_to_playlist: + # get tidal playlist by name if it exists + tidal_playlist = None + tidal_playlists = _sync.get_tidal_playlists_wrapper(tidal_session) + if args.favs_to_playlist in tidal_playlists: + tidal_playlist = tidal_playlists[args.favs_to_playlist] + _sync.sync_favorites_to_playlist_wrapper(spotify_session, tidal_session, tidal_playlist, args.favs_to_playlist, config) + sync_favorites = False # favs are already being synced elif config.get('sync_playlists', None): # if the config contains a sync_playlists list of mappings then use that _sync.sync_playlists_wrapper(spotify_session, tidal_session, _sync.get_playlists_from_config(spotify_session, tidal_session, config), config) diff --git a/src/spotify_to_tidal/sync.py b/src/spotify_to_tidal/sync.py index 4d0c482..2ab0675 100755 --- a/src/spotify_to_tidal/sync.py +++ b/src/spotify_to_tidal/sync.py @@ -318,6 +318,40 @@ async def sync_playlist(spotify_session: spotipy.Spotify, tidal_session: tidalap clear_tidal_playlist(tidal_playlist) add_multiple_tracks_to_playlist(tidal_playlist, new_tidal_track_ids) +async def sync_favorites_to_playlist(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, tidal_playlist: tidalapi.Playlist | None, tidal_playlist_name: str, config: dict): + """ sync user favorites to tidal playlist """ + async def get_tracks_from_spotify_favorites() -> List[dict]: + _get_favorite_tracks = lambda offset: spotify_session.current_user_saved_tracks(offset=offset) + tracks = await repeat_on_request_error( _fetch_all_from_spotify_in_chunks, _get_favorite_tracks) + tracks.reverse() + return tracks + + print("Loading favorite tracks from Spotify") + spotify_tracks = await get_tracks_from_spotify_favorites() + + if tidal_playlist: + old_tidal_tracks = await get_all_playlist_tracks(tidal_playlist) + else: + print(f"No playlist found on Tidal with provided name: '{tidal_playlist_name}', creating new playlist") + tidal_playlist = tidal_session.user.create_playlist(tidal_playlist_name, "") + old_tidal_tracks = [] + + populate_track_match_cache(spotify_tracks, old_tidal_tracks) + await search_new_tracks_on_tidal(tidal_session, spotify_tracks, "Favorites", config) + new_tidal_track_ids = get_tracks_for_new_tidal_playlist(spotify_tracks) + + # Update the Tidal playlist if there are changes + old_tidal_track_ids = [t.id for t in old_tidal_tracks] + if new_tidal_track_ids == old_tidal_track_ids: + print("No changes to write to Tidal playlist") + elif new_tidal_track_ids[:len(old_tidal_track_ids)] == old_tidal_track_ids: + # Append new tracks to the existing playlist if possible + add_multiple_tracks_to_playlist(tidal_playlist, new_tidal_track_ids[len(old_tidal_track_ids):]) + else: + # Erase old playlist and add new tracks from scratch if any reordering occured + clear_tidal_playlist(tidal_playlist) + add_multiple_tracks_to_playlist(tidal_playlist, new_tidal_track_ids) + async def sync_favorites(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, config: dict): """ sync user favorites to tidal """ async def get_tracks_from_spotify_favorites() -> List[dict]: @@ -356,6 +390,9 @@ def sync_playlists_wrapper(spotify_session: spotipy.Spotify, tidal_session: tida def sync_favorites_wrapper(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, config): asyncio.run(main=sync_favorites(spotify_session=spotify_session, tidal_session=tidal_session, config=config)) +def sync_favorites_to_playlist_wrapper(spotify_session: spotipy.Spotify, tidal_session: tidalapi.Session, tidal_playlist: tidalapi.Playlist | None, tidal_playlist_name: str, config): + asyncio.run(main=sync_favorites_to_playlist(spotify_session=spotify_session, tidal_session=tidal_session, tidal_playlist=tidal_playlist, tidal_playlist_name=tidal_playlist_name, config=config)) + def get_tidal_playlists_wrapper(tidal_session: tidalapi.Session) -> Mapping[str, tidalapi.Playlist]: tidal_playlists = asyncio.run(get_all_playlists(tidal_session.user)) return {playlist.name: playlist for playlist in tidal_playlists}