Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6cccd52
feat: :sparkles: Add optional syncing of artists and albums
c0ball Nov 27, 2024
299e21e
chore: :lock: Expand spotify scopes
c0ball Nov 27, 2024
c0155a2
fix: :bug: Fix album syncing
c0ball Nov 27, 2024
967977a
docs: :memo: Update example config
c0ball Nov 27, 2024
6643889
fix: :bug: Fix CLI arguments
c0ball Nov 27, 2024
f686326
docs: :memo: Update docs
c0ball Nov 27, 2024
a7a6c9c
feat: :sparkles: Improve artist and album syncing by checking multipl…
c0ball Nov 29, 2024
f761bde
fix: :art: Clean up code
c0ball Dec 5, 2024
58394fd
refactor: :recycle: Simplify sync logic by removing redundant try-exc…
c0ball Dec 22, 2024
9363751
chore: 🧹 Update .gitignore to include logs and additional files
c0ball Aug 19, 2025
00f339f
feat(sync): ✨ Add ISRC validation and formatting function
c0ball Aug 19, 2025
01f8820
fix(sync): 🐛 Handle ISRC validation and object not found exceptions
c0ball Aug 19, 2025
717518e
fix(sync): 🐛 Correct ISRC validation regex and formatting
c0ball Sep 22, 2025
26791c7
fix(sync): 🐛 Improve ISRC search error handling and fallback logic
c0ball Sep 22, 2025
fd086fd
fix(sync): 🐛 Enhance artist matching logic with improved fallback and…
c0ball Sep 22, 2025
b416dd0
fix(sync): 🐛 Improve error handling on artist synchronization
c0ball Sep 22, 2025
863356c
fix(sync): 🐛 small fixes
c0ball Sep 22, 2025
3a22ddc
docs(sync): :memo: improve docs
c0ball Sep 22, 2025
b7c60fb
fix(sync): 🐛 Improve matching algorithm on album synchronization usin…
c0ball Sep 22, 2025
60f34bd
fix(sync): 🐛 Improve error handling on album synchronization; log fai…
c0ball Sep 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions example_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ spotify:
# - when true: favorites will be synced by default (overriden when any command line arg provided)
# - when false: favorites can only be synced manually via --sync-favorites argument
sync_favorites_default: true
sync_artists_default: true
sync_albums_default: true

# increasing these parameters should increase the search speed, while decreasing reduces likelihood of 429 errors
max_concurrency: 10 # max concurrent connections at any given time
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ requires-python = ">= 3.10"

dependencies = [
"spotipy~=2.24.0",
"tidalapi==0.7.6",
"tidalapi==0.8.2",
"pyyaml~=6.0",
"tqdm~=4.64",
"sqlalchemy~=2.0",
Expand Down
12 changes: 12 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ or sync just your 'Liked Songs' with:
spotify_to_tidal --sync-favorites
```

or sync just your saved albums with:

```bash
spotify_to_tidal --sync-albums
```

or sync just your saved artists with:

```bash
spotify_to_tidal --sync-artists
```

See example_config.yml for more configuration options, and `spotify_to_tidal --help` for more options.

---
Expand Down
25 changes: 25 additions & 0 deletions src/spotify_to_tidal/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ def main():
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('--sync-artists', action=argparse.BooleanOptionalAction, help='synchronize the artists')
parser.add_argument('--sync-albums', action=argparse.BooleanOptionalAction, help='synchronize the albums')
args = parser.parse_args()

sync_favorites = False
sync_artists = False
sync_albums = False

with open(args.config, 'r') as f:
config = yaml.safe_load(f)
print("Opening Spotify session")
Expand All @@ -27,20 +33,39 @@ def main():
tidal_playlist = _sync.pick_tidal_playlist_for_spotify_playlist(spotify_playlist, tidal_playlists)
_sync.sync_playlists_wrapper(spotify_session, tidal_session, [tidal_playlist], config)
sync_favorites = args.sync_favorites # only sync favorites if command line argument explicitly passed
sync_artists = args.sync_artists # only sync artists if command line argument explicitly passed
sync_albums = args.sync_albums # only sync albums if command line argument explicitly passed
elif args.sync_favorites:
sync_favorites = True # sync only the favorites
elif args.sync_artists:
sync_artists = True # sync only the artists
elif args.sync_albums:
sync_albums = True # sync only the albums
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)
sync_favorites = args.sync_favorites is None and config.get('sync_favorites_default', True)
sync_artists = args.sync_artists is None and config.get('sync_artists_default', False)
sync_albums = args.sync_albums is None and config.get('sync_albums_default', False)
else:
# otherwise sync all the user playlists in the Spotify account and favorites unless explicitly disabled
_sync.sync_playlists_wrapper(spotify_session, tidal_session, _sync.get_user_playlist_mappings(spotify_session, tidal_session, config), config)
sync_favorites = args.sync_favorites is None and config.get('sync_favorites_default', True)
sync_artists = args.sync_artists is None and config.get('sync_artists_default', False)
sync_albums = args.sync_albums is None and config.get('sync_albums_default', False)

# Sync favorites
if sync_favorites:
_sync.sync_favorites_wrapper(spotify_session, tidal_session, config)

# Sync artists
if sync_artists:
_sync.sync_artists_wrapper(spotify_session, tidal_session, config)

# Sync albums
if sync_albums:
_sync.sync_albums_wrapper(spotify_session, tidal_session, config)

if __name__ == '__main__':
main()
sys.exit(0)
2 changes: 1 addition & 1 deletion src/spotify_to_tidal/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
'open_tidal_session'
]

SPOTIFY_SCOPES = 'playlist-read-private, user-library-read'
SPOTIFY_SCOPES = 'playlist-read-private, user-library-read, user-follow-read'

def open_spotify_session(config) -> spotipy.Spotify:
credentials_manager = spotipy.SpotifyOAuth(username=config['username'],
Expand Down
Loading