From d9f251b2fc7e870a40c97dab3263be1b9ab2fc01 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 15 Apr 2026 21:45:17 +0200 Subject: [PATCH] fix: implement retry logic with ETag synchronization for Tidal playlist modifications and add test script --- app.py | 9 ++++--- session.json | 1 + src/spotify_to_tidal/tidalapi_patch.py | 37 ++++++++++++++++++++++---- test_tidal_delete.py | 2 ++ 4 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 test_tidal_delete.py diff --git a/app.py b/app.py index 4129a54..eb4ea3a 100644 --- a/app.py +++ b/app.py @@ -256,8 +256,9 @@ with tab_my: st.session_state[tracks_key] = tracks if err: st.session_state[f"tracks_err_{pid}"] = err - st.rerun() - else: + cached_tracks = tracks + + if cached_tracks is not None: err = st.session_state.get(f"tracks_err_{pid}") if err: st.warning(err) @@ -274,7 +275,7 @@ with tab_my: st.dataframe( pd.DataFrame(rows), hide_index=True, - use_container_width=True, + width="stretch", height=min(40 + len(rows) * 35, 300), column_config={ "": st.column_config.TextColumn("", width="small"), @@ -400,7 +401,7 @@ with tab_nf: for a, t in items ] if nf_rows: - st.dataframe(pd.DataFrame(nf_rows), hide_index=True, use_container_width=True, + st.dataframe(pd.DataFrame(nf_rows), hide_index=True, width="stretch", height=min(80 + len(nf_rows) * 35, 500)) else: st.info("No entries for this filter.") diff --git a/session.json b/session.json index 310b25b..1160e2e 100644 --- a/session.json +++ b/session.json @@ -1,5 +1,6 @@ { "watched": [ + "2Dz5fstoYCa1wm2sOtZlwT", "2sLEYLo3a6dDHbl4ypUkcl", "3Sjb6ZlsPxIpzOMZVoq1pW", "3eNcChQ2KssR8QjuV7KUEc", diff --git a/src/spotify_to_tidal/tidalapi_patch.py b/src/spotify_to_tidal/tidalapi_patch.py index 9da23cc..1b743cc 100644 --- a/src/spotify_to_tidal/tidalapi_patch.py +++ b/src/spotify_to_tidal/tidalapi_patch.py @@ -5,11 +5,26 @@ import tidalapi from tqdm import tqdm from tqdm.asyncio import tqdm as atqdm +import time + def _remove_indices_from_playlist(playlist: tidalapi.UserPlaylist, indices: List[int]): - headers = {'If-None-Match': playlist._etag} - index_string = ",".join(map(str, indices)) - playlist.request.request('DELETE', (playlist._base_url + '/items/%s') % (playlist.id, index_string), headers=headers) - playlist._reparse() + if not playlist._etag: + playlist._reparse() + + for attempt in range(5): + headers = {'If-None-Match': playlist._etag} if playlist._etag else None + index_string = ",".join(map(str, indices)) + try: + res = playlist.request.request('DELETE', (playlist._base_url + '/items/%s') % (playlist.id, index_string), headers=headers) + playlist._etag = res.headers.get('etag', playlist._etag) + playlist.num_tracks = max(0, playlist.num_tracks - len(indices)) + return + except Exception as e: + if "412" in str(e) and attempt < 4: + time.sleep(1) + playlist._reparse() + else: + raise def clear_tidal_playlist(playlist: tidalapi.UserPlaylist, chunk_size: int=20): with tqdm(desc="Erasing existing tracks from Tidal playlist", total=playlist.num_tracks) as progress: @@ -19,11 +34,23 @@ def clear_tidal_playlist(playlist: tidalapi.UserPlaylist, chunk_size: int=20): progress.update(len(indices)) def add_multiple_tracks_to_playlist(playlist: tidalapi.UserPlaylist, track_ids: List[int], chunk_size: int=20): + if not playlist._etag: + playlist._reparse() + offset = 0 with tqdm(desc="Adding new tracks to Tidal playlist", total=len(track_ids)) as progress: while offset < len(track_ids): count = min(chunk_size, len(track_ids) - offset) - playlist.add(track_ids[offset:offset+chunk_size]) + for attempt in range(5): + try: + playlist.add(track_ids[offset:offset+chunk_size]) + break + except Exception as e: + if "412" in str(e) and attempt < 4: + time.sleep(1) + playlist._reparse() + else: + raise offset += count progress.update(count) diff --git a/test_tidal_delete.py b/test_tidal_delete.py new file mode 100644 index 0000000..394eb33 --- /dev/null +++ b/test_tidal_delete.py @@ -0,0 +1,2 @@ +import requests +# I just want to write a quick script to test if the E-Tag is updated correctly