Adding Genius as a full authenticator

This commit is contained in:
James Barnsley
2018-08-15 16:52:24 +12:00
parent abc08d72d3
commit dbc6cc65b9
9 changed files with 1312 additions and 762 deletions

View File

@ -58,7 +58,7 @@ class IrisCore(object):
except socket.error, e:
logger.error("Iris could not connect to Snapcast: %s" % e)
raise Exception(e);
return snapcast
@ -106,7 +106,7 @@ class IrisCore(object):
self.broadcast(data)
##
##
# Send a request to Snapcast
#
# We create a connection for this request, and then drop it once completed. This is because
@ -130,7 +130,7 @@ class IrisCore(object):
# Create our connection
try:
snapcast_socket = self.new_snapcast_socket()
except Exception, e:
except Exception, e:
callback(response=None, error={
'message': "Could not connect to Snapcast",
'data': str(e)
@ -158,7 +158,7 @@ class IrisCore(object):
'message': "Failed to receive Snapcast response",
'data': str(e)
})
snapcast_socket.close()
if not len(response):
@ -169,7 +169,7 @@ class IrisCore(object):
if 'result' in response:
callback(response=response['result'])
else:
callback(error=response['error'])
callback(error=response['error'])
except:
logger.error("Iris received malformed Snapcast response: "+response)
callback(response=None, error={
@ -192,7 +192,7 @@ class IrisCore(object):
def generateGuid(self):
length = 12
return ''.join(random.choice(string.lowercase) for i in range(length))
##
# Digest a protocol header into it's id/name parts
@ -200,28 +200,28 @@ class IrisCore(object):
# @return dict
##
def digest_protocol(self, protocol):
# if we're a string, split into list
# this handles the different ways we get this passed (select_subprotocols gives string, headers.get gives list)
if isinstance(protocol, basestring):
# make sure we strip any spaces (IE gives "element,element", proper browsers give "element, element")
protocol = [i.strip() for i in protocol.split(',')]
# if we've been given a valid array
try:
client_id = protocol[0]
connection_id = protocol[1]
username = protocol[2]
generated = False
# invalid, so just create a default connection, and auto-generate an ID
except:
client_id = self.generateGuid()
connection_id = self.generateGuid()
username = 'Anonymous'
generated = True
# construct our protocol object, and return
return {
"client_id": client_id,
@ -231,8 +231,8 @@ class IrisCore(object):
}
def send_message(self, *args, **kwargs):
callback = kwargs.get('callback', None)
def send_message(self, *args, **kwargs):
callback = kwargs.get('callback', None)
data = kwargs.get('data', None)
logger.debug(data)
@ -281,7 +281,7 @@ class IrisCore(object):
if (callback):
callback(response)
else:
return response
return response
except:
error = 'Failed to send message to '+ data['recipient']
logger.error(error)
@ -331,9 +331,9 @@ class IrisCore(object):
if (callback):
callback(response)
else:
return response
return response
##
# Connections
#
@ -342,20 +342,20 @@ class IrisCore(object):
# to all current connections
##
def get_connections(self, *args, **kwargs):
def get_connections(self, *args, **kwargs):
callback = kwargs.get('callback', None)
connections = []
for connection in self.connections.itervalues():
connections.append(connection['client'])
response = {
'connections': connections
}
if (callback):
callback(response)
else:
return response
return response
def add_connection(self, *args, **kwargs):
connection_id = kwargs.get('connection_id', None)
@ -388,11 +388,11 @@ class IrisCore(object):
'connection': client
}
})
def remove_connection(self, connection_id):
if connection_id in self.connections:
try:
client = self.connections[connection_id]['client']
client = self.connections[connection_id]['client']
del self.connections[connection_id]
self.broadcast(data={
'method': "connection_removed",
@ -423,7 +423,7 @@ class IrisCore(object):
if (callback):
callback(response)
else:
return response
return response
else:
error = 'Connection "'+data['connection_id']+'" not found'
@ -436,7 +436,7 @@ class IrisCore(object):
callback(False, error)
else:
return error
@ -444,7 +444,7 @@ class IrisCore(object):
# System controls
#
# Faciitates upgrades and configuration fetching
##
##
def get_config(self, *args, **kwargs):
callback = kwargs.get('callback', False)
@ -453,7 +453,7 @@ class IrisCore(object):
# Iris won't work properly anyway, but at least we won't get server errors
if 'spotify' in self.config and 'username' in self.config['spotify']:
spotify_username = self.config['spotify']['username']
else:
else:
spotify_username = False
response = {
@ -464,7 +464,7 @@ class IrisCore(object):
"locale": self.config['iris']['locale'],
"spotify_authorization_url": self.config['iris']['spotify_authorization_url'],
"lastfm_authorization_url": self.config['iris']['lastfm_authorization_url'],
"genius_provider_url": self.config['iris']['genius_provider_url'],
"genius_authorization_url": self.config['iris']['genius_authorization_url'],
"snapcast_enabled": self.config['iris']['snapcast_enabled']
}
}
@ -479,12 +479,12 @@ class IrisCore(object):
callback = kwargs.get('callback', False)
url = 'https://pypi.python.org/pypi/Mopidy-Iris/json'
req = urllib2.Request(url)
try:
response = urllib2.urlopen(req, timeout=30).read()
response = json.loads(response)
latest_version = response['info']['version']
# compare our versions, and convert result to boolean
upgrade_available = cmp( parse_version( latest_version ), parse_version( self.version ) )
upgrade_available = ( upgrade_available == 1 )
@ -492,7 +492,7 @@ class IrisCore(object):
except urllib2.HTTPError as e:
latest_version = '0.0.0'
upgrade_available = False
response = {
'version': {
'current': self.version,
@ -564,7 +564,7 @@ class IrisCore(object):
});
output = result.decode();
response = {
'message': "Restart required to complete upgrade",
'data': {
@ -577,7 +577,7 @@ class IrisCore(object):
else:
return response
def restart(self, *args, **kwargs):
logger.info("Restarting")
callback = kwargs.get('callback', False)
@ -646,7 +646,7 @@ class IrisCore(object):
self.initial_consume = self.core.tracklist.get_consume().get()
else:
starting = False
# fetch more tracks from Mopidy-Spotify
self.radio = {
'seed_artists': data['seed_artists'],
@ -701,7 +701,7 @@ class IrisCore(object):
self.get_radio(callback=callback)
return
# Failed fetching/adding tracks, so no-go
else:
logger.error("No recommendations returned by Spotify")
@ -732,7 +732,7 @@ class IrisCore(object):
# restore initial consume state
self.core.tracklist.set_consume(self.initial_consume)
self.core.playback.stop()
self.core.playback.stop()
self.broadcast(data={
'method': "radio_stopped",
@ -740,7 +740,7 @@ class IrisCore(object):
'radio': self.radio
}
})
response = {
'message': 'Stopped radio'
}
@ -751,7 +751,7 @@ class IrisCore(object):
def load_more_tracks(self, *args, **kwargs):
try:
self.get_spotify_token()
spotify_token = self.spotify_token
@ -760,7 +760,7 @@ class IrisCore(object):
error = 'IrisFrontend: access_token missing or invalid'
logger.error(error)
return False
try:
url = 'https://api.spotify.com/v1/recommendations/'
url = url+'?seed_artists='+(",".join(self.radio['seed_artists'])).replace('spotify:artist:','')
@ -773,7 +773,7 @@ class IrisCore(object):
response = urllib2.urlopen(req, timeout=30).read()
response_dict = json.loads(response)
uris = []
for track in response_dict['tracks']:
uris.append( track['uri'] )
@ -786,9 +786,9 @@ class IrisCore(object):
def check_for_radio_update( self ):
tracklistLength = self.core.tracklist.length.get()
tracklistLength = self.core.tracklist.length.get()
if (tracklistLength < 3 and self.radio['enabled'] == 1):
# Grab our loaded tracks
uris = self.radio['results']
@ -803,7 +803,7 @@ class IrisCore(object):
uris = uris[0:3]
self.core.tracklist.add(uris = uris)
##
@ -842,7 +842,7 @@ class IrisCore(object):
'queue_metadata': self.queue_metadata
}
})
response = {
'message': 'Added queue metadata'
}
@ -890,7 +890,7 @@ class IrisCore(object):
def refresh_spotify_token(self, *args, **kwargs):
callback = kwargs.get('callback', None)
# Use client_id and client_secret from config
# This was introduced in Mopidy-Spotify 3.1.0
url = 'https://auth.mopidy.com/spotify/token'
@ -937,7 +937,7 @@ class IrisCore(object):
##
# Detect if we're running as root
##
def is_root(self):
def is_root(self):
if sys.platform == 'win32':
return ctypes.windll.shell32.IsUserAnAdmin() != 0
else: