diff --git a/spotify-backup-favourite-songs.py b/spotify-backup-favourite-songs.py index c40817e..ac43518 100644 --- a/spotify-backup-favourite-songs.py +++ b/spotify-backup-favourite-songs.py @@ -13,114 +13,10 @@ import urllib.parse import urllib.request import webbrowser -class SpotifyAPI: - - # Requires an OAuth token. - def __init__(self, auth): - self._auth = auth - - # Gets a resource from the Spotify API and returns the object. - def get(self, url, params={}, tries=3): - # Construct the correct URL. - if not url.startswith('https://api.spotify.com/v1/'): - url = 'https://api.spotify.com/v1/' + url - if params: - url += ('&' if '?' in url else '?') + urllib.parse.urlencode(params) - - # Try the sending off the request a specified number of times before giving up. - for _ in range(tries): - try: - req = urllib.request.Request(url) - req.add_header('Authorization', 'Bearer ' + self._auth) - res = urllib.request.urlopen(req) - reader = codecs.getreader('utf-8') - return json.load(reader(res)) - except Exception as err: - log('Couldn\'t load URL: {} ({})'.format(url, err)) - time.sleep(2) - log('Trying again...') - sys.exit(1) - - # The Spotify API breaks long lists into multiple pages. This method automatically - # fetches all pages and joins them, returning in a single list of objects. - def list(self, url, params={}): - response = self.get(url, params) - requests = int(response['total'] / params['limit']) - if requests > 10: - timeEstimate = int(0.27*requests); # in seconds, 0.28 was the average speed of 130 requests - log('{} items, {} items per request, {} requests, ~{} seconds'.format(response['total'], params['limit'], requests, timeEstimate)) - items = response['items'] - while response['next']: - response = self.get(response['next']) - items += response['items'] - return items - - # Pops open a browser window for a user to log in and authorize API access. - @staticmethod - def authorize(client_id, scope): - webbrowser.open('https://accounts.spotify.com/authorize?' + urllib.parse.urlencode({ - 'response_type': 'token', - 'client_id': client_id, - 'scope': scope, - 'redirect_uri': 'http://127.0.0.1:{}/redirect'.format(SpotifyAPI._SERVER_PORT) - })) - - # Start a simple, local HTTP server to listen for the authorization token... (i.e. a hack). - server = SpotifyAPI._AuthorizationServer('127.0.0.1', SpotifyAPI._SERVER_PORT) - try: - while True: - server.handle_request() - except SpotifyAPI._Authorization as auth: - return SpotifyAPI(auth.access_token) - - # The port that the local server listens on. Don't change this, - # as Spotify only will redirect to certain predefined URLs. - _SERVER_PORT = 43019 - - class _AuthorizationServer(http.server.HTTPServer): - def __init__(self, host, port): - http.server.HTTPServer.__init__(self, (host, port), SpotifyAPI._AuthorizationHandler) - - # Disable the default error handling. - def handle_error(self, request, client_address): - raise - - class _AuthorizationHandler(http.server.BaseHTTPRequestHandler): - def do_GET(self): - # The Spotify API has redirected here, but access_token is hidden in the URL fragment. - # Read it using JavaScript and send it to /token as an actual query string... - if self.path.startswith('/redirect'): - self.send_response(200) - self.send_header('Content-Type', 'text/html') - self.end_headers() - self.wfile.write(b'') - - # Read access_token and use an exception to kill the server listening... - elif self.path.startswith('/token?'): - self.send_response(200) - self.send_header('Content-Type', 'text/html') - self.end_headers() - self.wfile.write(b'Thanks! You may now close this window.') - raise SpotifyAPI._Authorization(re.search('access_token=([^&]*)', self.path).group(1)) - - else: - self.send_error(404) - - # Disable the default logging. - def log_message(self, format, *args): - pass - - class _Authorization(Exception): - def __init__(self, access_token): - self.access_token = access_token - -def log(str): - fileOutput = False #replace with the opposite value if errors occur - if not fileOutput: - print('[{}] {}'.format(time.strftime('%I:%M:%S'), str).encode(sys.stdout.encoding, errors='replace')) - else: - sys.stdout.buffer.write('[{}] {}\n'.format(time.strftime('%I:%M:%S'), str).encode(sys.stdout.encoding, errors='replace')) - sys.stdout.flush() +import importlib +spotifyBackup = importlib.import_module('spotify-backup') +SpotifyAPI = spotifyBackup.SpotifyAPI +log = spotifyBackup.log def main(): # Parse arguments.