React/Node: Spotify API Error: 404 - No Active Device Found

坚强是说给别人听的谎言 提交于 2019-12-13 00:26:44

问题


I've created an app which works for Spotify Premium users only (PUT methods don't work for non-premium users according to Spotify's documentation). It's a ten-question interactive quiz where a playlist generates in your Spotify account, plays it and you have to guess the name of each song. It's generated with a NodeJS Backend and displayed via ReactJS. The game can be demoed here: https://am-spotify-quiz.herokuapp.com/

Code can be reviewed below:

server.js

const express = require('express');
const request = require('request');
const cors = require('cors');
const querystring = require('querystring');
const cookieParser = require('cookie-parser');

const client_id = ''; // Hiding for now
const client_secret = ''; // Hiding
const redirect_uri =  'https://am-spotify-quiz-api.herokuapp.com/callback/'; 
const appUrl = 'https://am-spotify-quiz.herokuapp.com/#';

/**
 * Generates a random string containing numbers and letters
 * @param  {number} length The length of the string
 * @return {string} The generated string
 */
var generateRandomString = function(length) {
  var text = '';
  var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

  for (var i = 0; i < length; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }
  return text;
};

var stateKey = 'spotify_auth_state';

var app = express();

app.use(express.static(__dirname + '/public'))
   .use(cors())
   .use(cookieParser());

app.get('/login', function(req, res) {

  var state = generateRandomString(16);
  res.cookie(stateKey, state);

  // scopes needed to make required functions work
  var scope = 'user-read-private ' + 
              'user-read-email ' + 
              'user-read-playback-state ' + 
              'user-top-read ' +
              'playlist-modify-public ' +
              'playlist-modify-private ' +
              'user-modify-playback-state ' +
              'user-read-playback-state';
  res.redirect('https://accounts.spotify.com/authorize?' +
    querystring.stringify({
      response_type: 'code',
      client_id: client_id,
      scope: scope,
      redirect_uri: redirect_uri,
      state: state
    }));
});

app.get('/callback/', function(req, res) {

  // your application requests refresh and access tokens
  // after checking the state parameter

  var code = req.query.code || null;
  var state = req.query.state || null;
  var storedState = req.cookies ? req.cookies[stateKey] : null;

  if (state === null || state !== storedState) {
    res.redirect(appUrl +
      querystring.stringify({
        access_token: access_token,
        refresh_token: refresh_token
      }));
  } else {
    res.clearCookie(stateKey);
    var authOptions = {
      url: 'https://accounts.spotify.com/api/token',
      form: {
        code: code,
        redirect_uri: redirect_uri,
        grant_type: 'authorization_code'
      },
      headers: {
        'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')),
      },
      json: true
    };

    request.post(authOptions, function(error, response, body) {
      if (!error && response.statusCode === 200) {

        var access_token = body.access_token,
            refresh_token = body.refresh_token;

        var options = {
          url: 'https://api.spotify.com/v1/me',
          headers: { 
            'Authorization': 'Bearer ' + access_token,
            'Content-Type': 'application/json' // May not need
          },
          body: { // Likely don't need this anymore!
            'name': 'Test Playlist', 
            'public': false
          },
          json: true
        };

        // use the access token to access the Spotify Web API
        request.get(options, function(error, response, body) {
          console.log(body);
        });

        // we can also pass the token to the browser to make requests from there
        res.redirect(appUrl +
          querystring.stringify({
            access_token: access_token,
            refresh_token: refresh_token
          }));
      } else {
        res.redirect(appUrl +
          querystring.stringify({
            error: 'invalid_token'
          }));
      }
    });
  }
});

// AM - May not even need this anymore!
app.get('/refresh_token', function(req, res) {

  // requesting access token from refresh token
  var refresh_token = req.query.refresh_token;
  var authOptions = {
    url: 'https://accounts.spotify.com/api/token',
    headers: { 'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')) },
    form: {
      grant_type: 'refresh_token',
      refresh_token: refresh_token
    },
    json: true
  };

  request.post(authOptions, function(error, response, body) {
    if (!error && response.statusCode === 200) {
      var access_token = body.access_token;
      res.send({
        'access_token': access_token
      });
    }
  });
});

console.log('Listening on 8888');
app.listen(process.env.PORT || 8888);

I have a react component which displays as soon as the user is logged in, called premium.js. If you need all the code, you can see it here. Below are the two PUT methods that I need for my game; one to turn off the shuffle feature and the other one used to play the playlist:

 removeShuffle() {
    axios({
      url: 'https://api.spotify.com/v1/me/player/shuffle?state=false',
      method: "PUT",
      headers: {
        'Authorization': 'Bearer ' + this.state.accesstoken
      }
    })
      .then((response) => {
        console.log(response)
      })
      .catch((error) => {
        console.log(error)
      })

  }

  // Then... play the playlist to get started
  playPlaylist(contextUri) {
    axios({
      url: 'https://api.spotify.com/v1/me/player/play',
      method: "PUT",
      data: {
        context_uri: contextUri
      },
      headers: {
        'Authorization': 'Bearer ' + this.state.accesstoken
      }
    })
      .then((response) => {
        console.log(response)
      })
      .catch((error) => {
        console.log(error)
      })
  }

These work perfectly fine when I, the creator of the game, try it; however, I had another premium user try it and found this error:

This doesn't seem to make much sense as I've discovered this error happens with another user, regardless of whether they are using Windows or Mac. Does anyone know what this means, and how can I solve? Thanks in advance!


回答1:


I've also been using Spotify's API and I eventually got the same error when trying to PUT https://api.spotify.com/v1/me/player/play after an inactivity period, where no device was marked as active (I don't know exactly how long, but no more than a couple of hours).

Apparently one device must be set as active so that you can invoke the play endpoint successfully.

If you want to change the status of a device as active, according to their documentation, you can first try to GET https://api.spotify.com/v1/me/player/devices in order to obtain the list of available devices:

// Example response
{
  "devices" : [ {
    "id" : "5fbb3ba6aa454b5534c4ba43a8c7e8e45a63ad0e",
    "is_active" : false,
    "is_private_session": true,
    "is_restricted" : false,
    "name" : "My fridge",
    "type" : "Computer",
    "volume_percent" : 100
  } ]
}

and then select one of the available devices by invoking the player endpoint PUT https://api.spotify.com/v1/me/player, including:

  • device_ids Required. A JSON array containing the ID of the device on which playback should be started/transferred. For example: {device_ids:["74ASZWbe4lXaubB36ztrGX"]} Note: Although an array is accepted, only a single device_id is currently supported. Supplying more than one will return 400 Bad Request
  • play with value true if you want to start playing right away.

Most likely you didn't get that error yourself because one of your devices was already active when you tested it. If you have no activity during a couple of hours on your own account and then try to invoke the v1/me/player/play endpoint, I'd expect you to get the same error.

An easy workaround to make sure that this was in fact your problem would be to ask your test user to please start playing a song on the Spotify app (no matter which), then pause it, and then trigger the function on your app that invokes the v1/me/player/play endpoint. That shouldn't return the No active device found error anymore.




回答2:


The way I understand it is you are trying to play a playlist that does not belong to the current user (/me) Which could be the cause of the error.



来源:https://stackoverflow.com/questions/52268847/react-node-spotify-api-error-404-no-active-device-found

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!