diff --git a/README.md b/README.md index d905bb4..d9b9a88 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,21 @@ To run the script, [save it from here](https://raw.githubusercontent.com/caseych You can run the script from the command line: - python spotify-backup.py playlists.txt + python spotify-backup.py --file playlists.txt or, to get a JSON dump, use: - python spotify-backup.py playlists.json --format=json + python spotify-backup.py --file playlists.json --format json By default, it includes your playlists. To include your Liked Songs, you can use: - python spotify-backup.py playlists.txt --dump=liked,playlists + python spotify-backup.py --file liked_songs_and_playlists.txt --dump liked playlists +By default, it includes all your playlists. To include and/or exclude some, you can use: + + python spotify-backup.py --file no_autogenerated_playlists.txt --exclude-playlists "Discover Weekly" "Release Radar" + + python spotify-backup.py --file best_of_playlists_without_1980s.txt --include-playlists "Best of year *" --exclude-playlists "Best of year 198?" If for some reason the browser-based authorization flow doesn't work, you can also [generate an OAuth token](https://developer.spotify.com/web-api/console/get-playlists/) on the developer site (with the `playlist-read-private` permission) and pass it with the `--token` option. diff --git a/spotify-backup.py b/spotify-backup.py index ce7f7cc..d890d7f 100755 --- a/spotify-backup.py +++ b/spotify-backup.py @@ -2,6 +2,7 @@ import argparse import codecs +import fnmatch import http.client import http.server import json @@ -131,12 +132,16 @@ def main(): parser = argparse.ArgumentParser(description='Exports your Spotify playlists. By default, opens a browser window ' + 'to authorize the Spotify Web API, but you can also manually specify' + ' an OAuth token with the --token option.') - parser.add_argument('--token', metavar='OAUTH_TOKEN', help='use a Spotify OAuth token (requires the ' - + '`playlist-read-private` permission)') - parser.add_argument('--dump', default='playlists', choices=['liked,playlists', 'playlists,liked', 'playlists', 'liked'], + parser.add_argument('--token', metavar='OAUTH_TOKEN', + help='use a Spotify OAuth token (requires the `playlist-read-private` permission)') + parser.add_argument('--dump', default=['playlists'], nargs='+', choices=['liked', 'playlists'], help='dump playlists or liked songs, or both (default: playlists)') parser.add_argument('--format', default='txt', choices=['json', 'txt'], help='output format (default: txt)') - parser.add_argument('file', help='output filename', nargs='?') + parser.add_argument('--include-playlists', metavar='NAME_PATTERN', default=['*'], nargs='*', + help='playlist name(s) to be included (default: all) if dumping playlists; ? and * wildcards may be used') + parser.add_argument('--exclude-playlists', metavar='NAME_PATTERN', default=[], nargs='*', + help='playlist name(s) to be excluded (default: none) if dumping playlists; ? and * wildcards may be used') + parser.add_argument('--file', help='output filename') args = parser.parse_args() # If they didn't give a filename, then just prompt them. (They probably just double-clicked.) @@ -166,18 +171,24 @@ def main(): liked_albums = spotify.list('me/albums', {'limit': 50}) playlists += [{'name': 'Liked Songs', 'tracks': liked_tracks}] - # List all playlists and the tracks in each playlist + # List all playlists and the tracks in each requested playlist if 'playlists' in args.dump: logging.info('Loading playlists...') playlist_data = spotify.list('users/{user_id}/playlists'.format(user_id=me['id']), {'limit': 50}) logging.info(f'Found {len(playlist_data)} playlists') - # List all tracks in each playlist + # List all tracks in each requested playlist for playlist in playlist_data: - logging.info('Loading playlist: {name} ({tracks[total]} songs)'.format(**playlist)) - playlist['tracks'] = spotify.list(playlist['tracks']['href'], {'limit': 100}) - playlists += playlist_data - + is_included = any([fnmatch.fnmatch(playlist['name'], pattern) for pattern in args.include_playlists]) + is_excluded = any([fnmatch.fnmatch(playlist['name'], pattern) for pattern in args.exclude_playlists]) + + if is_included and not is_excluded: + logging.info('Loading playlist: {name} ({tracks[total]} songs)'.format(**playlist)) + playlist['tracks'] = spotify.list(playlist['tracks']['href'], {'limit': 100}) + playlists.append(playlist) + else: + logging.info('Skipped playlist: {name}'.format(**playlist)) + # Write the file. logging.info('Writing files...') with open(args.file, 'w', encoding='utf-8') as f: