-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstreaming_album_playlist_maker.py
executable file
·156 lines (133 loc) · 5.33 KB
/
streaming_album_playlist_maker.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env python
import click
import spotipy
import spotipy.util as util
class Context:
"""
Class used to keep CLI context and pass arguments from main group to
child commands
"""
def __init__(self, input_file):
self.input_file = input_file
# Decorator for child commands to access main group
pass_context = click.make_pass_decorator(Context)
@click.group()
@click.argument('input_file', type=click.Path(exists=True))
@click.pass_context
def cli(ctx, input_file):
"""
This script will take a list of album names from INPUT_FILE and create a
playlist of all their tracks
"""
ctx.obj = Context(input_file)
@cli.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option(
'--username',
help='Spotify username to create the playlist with',
required=True)
@click.option(
'--client_id',
help='Client ID from your appplication, can be set with env var SPOTIPY_CLIENT_ID',
envvar='SPOTIPY_CLIENT_ID',
required=True)
@click.option(
'--client_secret',
help='Client Secret from your application, can be set with env var SPOTIPY_CLIENT_SECRET',
envvar='SPOTIPY_CLIENT_SECRET',
required=True)
@click.option(
'--redirect_uri',
help='A whitelisted Redirect URI for your application, can be set with env var SPOTIPY_REDIRECT_URI',
envvar='SPOTIPY_REDIRECT_URI',
default='https://example.com/callback/',
show_default=True)
@click.option(
'--playlist_name', help='Name of playlist to create', required=True)
@pass_context
def spotify(context, username, client_id, client_secret, redirect_uri,
playlist_name):
"""
Create playlist for Spotify
See https://developer.spotify.com/web-api/tutorial/ for how to register
an application to get a client_id, client_secret, and redirect_uri
"""
token = util.prompt_for_user_token(
username=username,
scope='playlist-modify-public',
client_id=client_id,
client_secret=client_secret,
redirect_uri=redirect_uri)
sp = spotipy.Spotify(auth=token)
user_id = sp.current_user()['id']
with open(context.input_file) as f:
albums = f.read().splitlines()
track_ids = []
found_albums = []
not_found_albums = []
for album in albums:
search_resp = sp.search(album, type="album", limit=1)
if not search_resp['albums']['total']:
not_found_albums.append(album)
print('No result found: for "{}"'.format(album))
continue
first_result = search_resp['albums']['items'][0]
# 'artists' is a list of dicts, so assume there can be more than one
# and join them
album_artists = ', '.join(
[artist['name'] for artist in first_result['artists']])
album_name = first_result['name']
album_id = first_result['id']
print('Found album: "{} - {}"'.format(album_artists, album_name))
found_albums.append(album_id)
# Instead of querying 1 album at a time, batch lookup 20 at a time
for chunk in chunks(found_albums, 20):
batch_album_res = sp.albums(chunk)
for album in batch_album_res['albums']:
tracks = album['tracks']['items']
print('Found {} tracks for "{}"'.format(len(tracks), album['name']))
track_ids += [track['id'] for track in tracks]
print('\nFound {} albums for {} search terms'.format(
len(found_albums), len(albums)))
if not_found_albums:
quoted_not_found_albums = [
'"' + album + '"' for album in not_found_albums
]
print('No results found for:\n{}\n'.format('\n'.join(
quoted_not_found_albums)))
while True:
user_input = input(
'Create playlist of {} tracks? [Y/N]: '.format(len(track_ids)))
if user_input in ['Y', 'N']:
break
else:
print('Invalid option')
if user_input != 'Y':
raise SystemExit
print('Creating playlist: "{}"'.format(playlist_name))
create_resp = sp.user_playlist_create(user_id, playlist_name)
playlist_uri = create_resp['uri']
playlist_public_url = create_resp['external_urls']['spotify']
print('Created playlist at {} (external url: {} )'.format(
playlist_uri, playlist_public_url))
# Spotify limits adding 100 tracks at a time to a playlist
for chunk in chunks(track_ids, 100):
print('Adding {} tracks to playlist...'.format(len(chunk)))
add_track_res = sp.user_playlist_add_tracks(user_id, create_resp['id'],
chunk)
# Don't get much back from this method, but the response should at
# least contain 'snapshot_id'
assert 'snapshot_id' in add_track_res, "Did not receive a 'snapshot_id' after adding tracks"
print('All tracks added successfully!')
def chunks(l, n):
"""
Yield successive n-sized chunks from l
"""
for i in range(0, len(l), n):
yield l[i:i + n]
# This is discouraged with click apps, in favor of using setuptools,
# (see http://click.pocoo.org/5/quickstart/#switching-to-setuptools), however
# currently util.prompt_for_user_token() does not accept a cache dir as a
# parameter so it would need to re-authenticate in every dir the script is ran
# in. Can change when https://github.com/plamere/spotipy/issues/167 is implemented
if __name__ == '__main__':
cli()