From 86dce02d256ea418aef2a28721fe0c0582eb5907 Mon Sep 17 00:00:00 2001 From: Christoph Rohrer Date: Fri, 9 Jun 2017 11:46:19 +0200 Subject: [PATCH] fixes problem with spotify search spotify search requires auth since ~30-05-2017 adds better error handling / error messages adds progress indicator for search requests on TTY increases wait time for search requests to reduce failed requests due to rate limits --- main.js | 10 +++++--- package.json | 2 +- spotifySearch.js | 62 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/main.js b/main.js index 3c8ca79..302956f 100644 --- a/main.js +++ b/main.js @@ -21,11 +21,15 @@ function start(){ spotifyPlaylist.getAllTracks(playlistName) .then(() => radioCrawler.getTracks(playlistName)) .then(radioTracks => radioCrawler.cleanTracks(radioTracks)) - .then(cleanedTracks => spotifySearch.searchTracks(cleanedTracks)) + .then(cleanedTracks => spotifySearch.searchTracks(cleanedTracks, playlistName)) .then(newTracks => spotifyPlaylist.addTracks(playlistName, newTracks)) .then(process.exit) - .catch(() => { - logger.log('exited due to error.', playlistName); + .catch((error) => { + let message = 'Unknown Error'; + if(typeof error === 'string'){ + message = error; + } + logger.log('exited due to error: ' + message, playlistName); process.exit(); }); } diff --git a/package.json b/package.json index aebd616..d3df1d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "spotifyRadioPlaylist", - "version": "2.5.3", + "version": "2.5.4", "license": "MIT", "repository": { "type": "git", diff --git a/spotifySearch.js b/spotifySearch.js index 1335ac9..2f95f9f 100644 --- a/spotifySearch.js +++ b/spotifySearch.js @@ -5,21 +5,29 @@ var https = require('https'); var logger = require('./logger'); var spotifyHelper = require('./spotifyHelper'); +var spotifyOAuth = require('./spotifyOAuth'); var Promise = require('bluebird'); /** * searchTracks * @param {Array} tracks + * @param {String} playlistName * @returns {Promise} */ -function searchTracks(tracks){ +function searchTracks(tracks, playlistName){ var searchRequests = []; console.log('Searching spotify for '+tracks.length+' tracks'); tracks.forEach(function(track, i){ - searchRequests.push(sendSearchRequest(track, i * 100)); // timeout so we don't run into limits that fast + searchRequests.push(sendSearchRequest(track, i * 150, playlistName)); // timeout so we don't run into limits that fast }); - return Promise.all(searchRequests).then(results => results.filter(result => typeof result === 'string')); + return Promise.all(searchRequests) + .then(results => { + if(process.stdout.isTTY){ + process.stdout.write('\n'); + } + return results.filter(result => typeof result === 'string'); + }); } /** @@ -28,24 +36,40 @@ function searchTracks(tracks){ * @param {string} track.artist in UPPERCASE * @param {string} track.title in UPPERCASE, * @param {int} [timeOut] waits for this time (ms) so we don't run into limits that fast + * @param {string} playlistName * @returns {Promise} */ -function sendSearchRequest(track, timeOut){ +function sendSearchRequest(track, timeOut, playlistName){ var time = timeOut || 0; - return new Promise((resolve) => { - setTimeout(() => makeRequest(resolve), time); + return new Promise((resolve, reject) => { + setTimeout(() => makeRequest(resolve, reject), time); }); /** * @param {function} resolve Callback to resolve promise + * @param {function} reject */ - function makeRequest(resolve){ + function makeRequest(resolve, reject){ + let accessToken = spotifyOAuth.getAccessToken(); + + if(accessToken === false || accessToken === ''){ + return reject('no access token found'); + } + var spotifySearchReq = https.request({ hostname: "api.spotify.com", - path: "/v1/search?type=track&q=artist:"+encodeURIComponent(track.artist+' ')+'track:'+encodeURIComponent(track.title) + path: "/v1/search?type=track&q=artist:"+encodeURIComponent(track.artist+' ')+'track:'+encodeURIComponent(track.title), + method: 'GET', + headers: { + 'Authorization': 'Bearer '+ accessToken, + 'Accept': 'application/json' + } }, function(res){ - spotifyHelper.checkForRateLimit(res, 'searching track', () => sendSearchRequest(track)) - .then(() => { + spotifyHelper.checkForRateLimit(res, 'searching track', () => sendSearchRequest(track, 0, playlistName)) + .then((rateLimitPromise) => { + if(rateLimitPromise){ + return rateLimitPromise; + } var jsonResponse = ''; res.on('data', function(chunk){ jsonResponse += chunk; @@ -54,10 +78,27 @@ function sendSearchRequest(track, timeOut){ var spotifyPlaylist = require('./spotifyPlaylist'), result = JSON.parse(jsonResponse); + if(result.error && result.error.status === 429){ + return resolve(); // rateLimit is exceeded - this is already handled above by spotifyHelper + } + + if(result.error){ + let message = result.error.message || 'Unknown error while searching'; + if(process.stdout.isTTY){ + process.stdout.write('\n'); + } + logger.log('Error searching for "'+ track.artist + ' - ' + track.title + '": ' + message, playlistName); + return resolve(); + } + if(!result.tracks || !result.tracks.items) { return resolve(); } + if(process.stdout.isTTY){ + process.stdout.write('.'); + } + result.tracks.items.some(function(item){ // iterate all items and break on success (return true) var titleMatches = cleanString(item.name.toUpperCase()) === cleanString(track.title), isAlreadyInPlaylist = spotifyPlaylist.tracks.indexOf(item.uri) > -1, @@ -75,6 +116,7 @@ function sendSearchRequest(track, timeOut){ } resolve(encodeURIComponent(item.uri)); + return true; }); resolve();