How to search for a user by name using Spotify Web

2019-06-10 04:59发布

问题:

I'm trying to create an Android application that contains a feature where you can search for Spotify users by name. When using Spotify, you can search for a user by their ID or by their name. However, currently, I see no way that the Spotify Web API supports searching users by name. If you have the User ID, you can access their profile using the Web API. But I was hoping there was a way that you could run a search for the User name and get their ID that way.

Spotify's Web API allows you to search for a "track" or "artist", where they will return all of the search results. So why can't you do the same with User names? Is there any way to make this work that I'm just not seeing?

回答1:

I think this is an interesting question, since it regards user data of a huge community, so I will try to figure out.

First, you can clearly see that the Spotify Web API endpoint search has a type parameter that admits only the following types: artist, playlist, and track. Let's try it out to run from here (note: see the Promise.all solution to fetch data I'm using here)

NOTE Recent Spotify search APIs will require an access token to be specified in the request headers:

-H "Authorization: Bearer {your access token}" 

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
      document.getElementById("console").innerHTML += s + "<br/>"
    }
  }
  // Simple XMLHttpRequest
  // based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
      var request;
      if (window.XMLHttpRequest) { // Mozilla, Safari, ...
        request = new XMLHttpRequest();
      } else if (window.ActiveXObject) { // IE
        try {
          request = new ActiveXObject('Msxml2.XMLHTTP');
        } catch (e) {
          try {
            request = new ActiveXObject('Microsoft.XMLHTTP');
          } catch (e) {}
        }
      }
      // state changes
      request.onreadystatechange = function() {
        if (request.readyState === 4) { // done
          if (request.status === 200) { // complete	
            response(request.responseText)
          } else response();
        }
      }
      request.open('GET', what, true);
      request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
      request.send(null);
    }
  }
  //PromiseAll
var promiseAll = function(items, block, done, fail) {
  var self = this;
  var promises = [],
    index = 0;
  items.forEach(function(item) {
    promises.push(function(item, i) {
      return new Promise(function(resolve, reject) {
        if (block) {
          block.apply(this, [item, index, resolve, reject]);
        }
      });
    }(item, ++index))
  });
  Promise.all(promises).then(function AcceptHandler(results) {
    if (done) done(results);
  }, function ErrorHandler(error) {
    if (fail) fail(error);
  });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
  var url="https://api.spotify.com/v1/"
  url+=item;
  SimpleRequest.call(url, function(result) {
    if (result) {
      console.log( JSON.stringify( result, null, 2)
                  .replace(/\\n/g,"<br>").replace(/\\"/g,'"'));
      resolve(result);
    } else {
      reject(new Error("call error"));
    }
  })
}

arr = [
  "search?type=playlist&q=%22doom%20metal%22",
  "search?type=playlist&q=Adele"]
promiseAll(arr, function(item, index, resolve, reject) {
  console.log("Making request [" + index + "]")
  ExecutionBlock(item, index, resolve, reject);
}, function(results) { // aggregated results
  console.log("All response received " + results.length);
  //console.log(JSON.stringify(results));
}, function(error) { // error
  console.log(error);
});
<div id="console" />

All those objects are self defined, but one: playlist. In fact we can see from this response that we have user objects like (example):

"owner" {
    "external_urls": {
   "spotify": "http://open.spotify.com/user/12345678"
 }

Nice!, the external_urls are user profiles url, while the href are playlists url (that require authentication btw) so that we can first grab them from this json response:

var console = {
        log: function(s) {
          document.getElementById("console").innerHTML += s + "<br/>"
        }
      }
      // Simple XMLHttpRequest
      // based on https://davidwalsh.name/xmlhttprequest
    SimpleRequest = {
        call: function(what, response) {
          var request;
          if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
          } else if (window.ActiveXObject) { // IE
            try {
              request = new ActiveXObject('Msxml2.XMLHTTP');
            } catch (e) {
              try {
                request = new ActiveXObject('Microsoft.XMLHTTP');
              } catch (e) {}
            }
          }
          // state changes
          request.onreadystatechange = function() {
            if (request.readyState === 4) { // done
              if (request.status === 200) { // complete	
                response(request.responseText)
              } else response();
            }
          }
          request.open('GET', what, true);
          request.send(null);
        }
      }
      //PromiseAll
    var promiseAll = function(items, block, done, fail) {
      var self = this;
      var promises = [],
        index = 0;
      items.forEach(function(item) {
        promises.push(function(item, i) {
          return new Promise(function(resolve, reject) {
            if (block) {
              block.apply(this, [item, index, resolve, reject]);
            }
          });
        }(item, ++index))
      });
      Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
      }, function ErrorHandler(error) {
        if (fail) fail(error);
      });
    }; //promiseAll

    // LP: deferred execution block
    var ExecutionBlock = function(item, index, resolve, reject) {
      var url="https://api.spotify.com/v1/"
      url+=item;
      SimpleRequest.call(url, function(result) {
        if (result) {
          
          JSON.parse(result).playlists.items.map(function(item,index) {
            var userProfileUrl = item.owner.href;
            console.log( JSON.stringify( userProfileUrl, null, 2)
                      .replace(/\\n/g,"<br>").replace(/\\"/g,'"') ); 
          })
          
          resolve(result);
        } else {
          reject(new Error("call error"));
        }
      })
    }

    arr = [
      "search?type=playlist&q=%22doom%20metal%22",
      "search?type=playlist&q=Adele"]
    promiseAll(arr, function(item, index, resolve, reject) {
      console.log("Making request [" + index + "]")
      ExecutionBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
      console.log("All response received " + results.length);
      //console.log(JSON.stringify(results));
    }, function(error) { // error
      console.log(error);
    });
<div id="console" />

At this point you will see a list of spotify user objects urls like (example url)

"https://api.spotify.com/v1/users/12345678"

So now we can fetch user profiles url (no auth required):

var console = {
    log: function(s) {
      document.getElementById("console").innerHTML += s + "<br/>"
    }
  }
  // Simple XMLHttpRequest
  // based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
      var request;
      if (window.XMLHttpRequest) { // Mozilla, Safari, ...
        request = new XMLHttpRequest();
      } else if (window.ActiveXObject) { // IE
        try {
          request = new ActiveXObject('Msxml2.XMLHTTP');
        } catch (e) {
          try {
            request = new ActiveXObject('Microsoft.XMLHTTP');
          } catch (e) {}
        }
      }
      // state changes
      request.onreadystatechange = function() {
        if (request.readyState === 4) { // done
          if (request.status === 200) { // complete	
            response(request.responseText)
          } else response();
        }
      }
      request.open('GET', what, true);
      request.send(null);
    }
  }
  //PromiseAll
var promiseAll = function(items, block, done, fail) {
  var self = this;
  var promises = [],
    index = 0;
  items.forEach(function(item) {
    promises.push(function(item, i) {
      return new Promise(function(resolve, reject) {
        if (block) {
          block.apply(this, [item, index, resolve, reject]);
        }
      });
    }(item, ++index))
  });
  Promise.all(promises).then(function AcceptHandler(results) {
    if (done) done(results);
  }, function ErrorHandler(error) {
    if (fail) fail(error);
  });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
  var url = "https://api.spotify.com/v1/"
  url += item;
  SimpleRequest.call(url, function(result) {
    if (result) {

      var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
        return item.owner.href;
      })
      resolve(profileUrls);
    } else {
      reject(new Error("call error"));
    }
  })
}

arr = [
  "search?type=playlist&q=%22doom%20metal%22",
  "search?type=playlist&q=Adele"
]
promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
  }, function(results) { // aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
      SimpleRequest.call(item, function(result) {
        if (result) {
          var obj = JSON.parse(result);
          resolve({
            name: obj.display_name ,
            followers: obj.followers.total,
            url : obj.href
          });
        }//result
      })
    }//ExecutionProfileBlock

promiseAll(results[0], function(item, index, resolve, reject) {
              //console.log("Making request [" + index + "] " + item)
              ExecutionProfileBlock(item, index, resolve, reject);
            }, function(results) { // aggregated results
               console.log("All response received " + results.length);
              console.log( JSON.stringify(results, null, 2) );
            }

            ,function(error) { // error
              console.log(error);
            })



    /////


  },
  function(error) { // error
    console.log(error);
  });
<div id="console" />

Get it out! At this point we can see user objects with display_name and followers count like (example json):

{
    "name": "Joe Deere",
    "followers": 666,
    "url": "https://api.spotify.com/v1/users/12345678"
}

So, to recap, the starting point is playlist search type in the search api. Given that, you can get user profiles with no authentication. Assumed that you obtain valid oauth tokens you can fetch top charts to do further queries.



标签: spotify