-
Notifications
You must be signed in to change notification settings - Fork 3
/
randomsong.py
212 lines (171 loc) · 6.68 KB
/
randomsong.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#!/usr/bin/env python3
"""
Plays random songs on Spotify.
"""
import random # for choosing random songs
import sys # for verbose mode
import string # for random queries
import pickle # for storing the username
import pathlib # for storing the username
from errors import AuthenticationError
import os
### Spotify-specific ###
import spotipy # Spotify API
import spotipy.util as util # for authorized requests
class RandomPlayer:
def __init__(self, random_song_schema='word'):
"""Creates an instance of the RandomPlayer class.
:random_song_schema: Either 'word' or 'char'. If 'word',
the class will generate random songs by randomly querying
Spotify for one of the 5000 most commonly used words. If
'char', it will search for a random three-character string.
"""
self.AUTH_SCOPE = 'user-read-currently-playing user-modify-playback-state user-read-playback-state streaming'
self.REDIRECT_URI = 'http://localhost/randomsong/authenticate'
self.CLIENT_ID = 'f2063b1303a240e5a5a1757ab364332e'
self.CLIENT_SECRET = 'ef9b4fccdeb34f67b703febcf1670d1b'
self.random_song_schema = random_song_schema
self.authenticated = False
def _getUsername(self):
"""Gets the Spotify username for the user and stores
it as a cached file.
"""
usernameFile = pathlib.Path(".cache-username")
if usernameFile.is_file():
with usernameFile.open('rb') as f:
return pickle.load(f)
else:
username = input("Spotify username? ")
with usernameFile.open('wb') as f:
pickle.dump(username, f)
return username
def authenticate(self, verbose = False):
"""Authenticates with the Spotify API by prompting the user for
information that we need.
:verbose: Enables verbose mode.
:returns: The authentication token, if it was successful.
"""
# Get the username
self.username = self._getUsername()
# Authenticate with the web API if it's not already provided.
token = util.prompt_for_user_token(self.username, self.AUTH_SCOPE, client_id = self.CLIENT_ID, client_secret = self.CLIENT_SECRET, redirect_uri = self.REDIRECT_URI)
if token:
if verbose: print("Authenticated successfully.")
self.token = token
self.sp = spotipy.Spotify(auth=token)
self.authenticated = True
else:
raise AuthenticationError("Authentication unsuccessful")
def checkAuthentication(self):
"""Raises an error if the instance is not authenticated.
"""
if not self.authenticated:
raise AuthenticationError("The instance is not authenticated.")
def playRandomSong(self, numSongs=1, verbose=False):
"""Plays random songs.
:numSongs: The number of random songs to play.
:verbose: Enables verbose mode.
"""
self.checkAuthentication()
if self.random_song_schema == 'word':
songs = [ self.getRandomSongFromWord(verbose) for i in range(numSongs) ]
elif self.random_song_schema == 'char':
songs = [ self.getRandomSongFromChars(verbose) for i in range(numSongs) ]
else:
raise AttributeError("random_song_schema must be either 'word' or 'char'.")
uris = [ song['uri'] for song in songs ]
device_id = self._checkActiveDevices()
print("Started playback...")
self.sp.start_playback(device_id = device_id, uris = uris)
def _checkActiveDevices(self):
"""Checks to see if there are active devices which the Spotify
API can bind to and waits until the user provides one to return.
"""
self.checkAuthentication()
while True:
activeDevices = self.sp.devices()['devices']
if activeDevices:
return activeDevices[0]['id']
print("There are no active devices available. The script can only execute once you open Spotify on a device.")
input("Press 'Enter' to check for active devices again. ")
def getRandomSongFromWord(self, verbose=False):
"""Returns a random song by searching for a random word
from the 5000 most commonly used words and picking a random
song from the top 30 songs returned for that query.
:verbose: Enables verbose mode.
"""
self.checkAuthentication()
# Get list of most commonly used words
with open("common-words.txt") as f:
words = f.read().split('\n')
while True:
randSearchString = random.choice(words)
if verbose: print("Retrieving songs for query '{}'...".format(randSearchString))
randSongs = self.sp.search(randSearchString, limit=30)
if verbose: print("Retrieved {} songs.".format(len(randSongs['tracks']['items'])))
if randSongs['tracks']['items']:
output = random.choice(randSongs['tracks']['items'])
if verbose:
artists = [ artist['name'] for artist in output['artists'] ]
print("There are valid songs. Choosing {} by {}.".format(output['name'], ', '.join(artists)))
return output
elif verbose:
print("There are no valid songs. Trying again.")
def getRandomSongFromChars(self, verbose=False):
"""Returns a random song by searching for a random three
character string and picking a random song from the top
30 songs.
:verbose: Enables verbose mode.
"""
self.checkAuthentication()
alphabet = string.ascii_lowercase + string.digits
while True:
randSearchString = random.choice(alphabet) + random.choice(alphabet) + random.choice(alphabet)
if verbose: print("Retrieving songs for query '{}'...".format(randSearchString))
randSongs = self.sp.search(randSearchString, limit=30)
if verbose: print("Retrieved {} songs.".format(len(randSongs['tracks']['items'])))
if randSongs['tracks']['items']:
output = random.choice(randSongs['tracks']['items'])
if verbose:
artists = [ artist['name'] for artist in output['artists'] ]
print("There are valid songs. Choosing {} by {}.".format(output['name'], ', '.join(artists)))
return output
elif verbose:
print("There are no valid songs. Trying again.")
def screen_clear():
# for mac and linux(here, os.name is 'posix')
if os.name == 'posix':
_ = os.system('clear')
else:
# for windows platfrom
_ = os.system('cls')
return
if __name__ == '__main__':
screen_clear()
print("--- This is a program to play random songs from spotify. Read README.md for more information ---")
if '--num-songs' in sys.argv:
index = sys.argv.index('--num-songs')
numSongs = int(sys.argv[index+1])
else:
numSongs = int(input("Number of songs to play: "))
if '-char' in sys.argv:
schema = "char"
elif "-word" in sys.argv:
schema = "word"
else:
schema_input = input("Use 'char' schema instead of 'word'? (Y/N): ")
if schema_input == "Y":
schema = "char"
elif schema_input == "N":
schema = "word"
else:
print("You didn't type Y or N, using 'word' schema by default...")
schema = "word"
if "-q" in sys.argv:
verbose = False
else:
verbose = True
player = RandomPlayer(random_song_schema=schema)
player.authenticate(verbose=verbose)
player.playRandomSong(numSongs=numSongs, verbose=verbose)
input("Press enter to exit")