0

Ok this is really frustrating. I'm using my API key perfectly fine. Authentication does just fine, token is generated fine, session key is also OK.

But when I run the playlist.create, the playlist is not created.

I started from almost scratch today, and redid most of my code. I created 16 playlists successfully, no errors. But today, the same code's not working. I didn't touch it at all and yet the error.

private static void buildPlaylist() {

    String mood = "Happy";
    System.out.println("\nMood is " + mood + "\n\n");

    String title = URLEncoder.encode(mood + " " + new Date().getTime(), "UTF-8");
    String description = URLEncoder.encode("For when you are " + mood + ". Created by MoodicPlayer.", "UTF-8");

    MessageDigest md = MessageDigest.getInstance("MD5");

    String apiSig = "api_key" + key + "description" + description + "methodplaylist.createsk" + sessionKey + "title" + title + secret;
    md.update(apiSig.getBytes());
    byte byteData[] = md.digest();
    //convert the byte to hex format
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < byteData.length; i++) {
        sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
    }
    String hashedSig = sb.toString();

    // FOR DEBUGGING
    System.out.println("api_key = " + key);
    System.out.println("api_sig = " + hashedSig);
    System.out.println("session key = " + sessionKey);
    // FOR DEBUGGING

    String urlParameters = "method=playlist.create&api_key="+ key + "&api_sig=" + hashedSig + "&description=" + description + "&sk=" + sessionKey + "&title=" + title;
    String request = "http://ws.audioscrobbler.com/2.0/";

    URL url = new URL(request);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();     
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setInstanceFollowRedirects(false); 
    connection.setRequestMethod("POST"); 
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 
    connection.setRequestProperty("charset", "utf-8");
    connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
    connection.setUseCaches(false);

    DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
    wr.writeBytes(urlParameters);
    wr.flush();
    wr.close();

    InputStream is = null;
    Scanner s = null;
    try {
        if (connection.getResponseCode() != 200) {
            s = new Scanner(connection.getErrorStream());
        } else {
            is = connection.getInputStream();
            s = new Scanner(is);
        }
        s.useDelimiter("\\Z");
        String response = s.next();
        System.out.println("\nResponse: " + response + "\n\n");
        BufferedWriter out = new BufferedWriter(new FileWriter("requestCreate.xml"));
        out.write(response);
        out.close();
    } catch (IOException e2) {
        e2.printStackTrace();
    }

    // FOR DEBUGGING
    try {
        System.out.println("Response Code: " + connection.getResponseCode());
        System.out.println("Response Message: " + connection.getResponseMessage()); 
    } catch (IOException e) {
        e.printStackTrace();
    }
    // FOR DEBUGGING
    connection.disconnect();
}

And the output:

Starting test for creating playlist...

Mood is Happy
api_key = xxxxx
api_sig = xxxxxx
session key = xxxxx

Response: <?xml version="1.0" encoding="utf-8"?>
<lfm status="failed">
<error code="13">
    Invalid method signature supplied
</error>
</lfm>


Response Code: 403
Response Message: Forbidden

I have checked the code, but cannot find any error. I mean the code was fine 16 hours ago, and suddenly it's not working!


For security purposes, I cannot show you my security keys. However, I did write a quick program to find the last.fm session key: http://github.com/thekarangoel/LastFMSessionKeyFinder Simply register at last.fm/api, enter your API key and secret in the program and you'll have everything you need to test the API call.

Karan Goel
  • 1,117
  • 1
  • 12
  • 24

1 Answers1

2

When generating your MD5 hash, you are using the URL encoded versions of your title and description values. The documentation does not say to do that. Try using the non-URL-encoded values instead (they do need to be UTF-8 encoded, though). URL encoding should only be used in actual URL data (hense its name).

Try this:

private static void buildPlaylist() {

    String mood = "Happy";
    System.out.println("\nMood is " + mood + "\n\n");

    String title = mood + " " + new Date().getTime();
    String description = "For when you are " + mood + ". Created by MoodicPlayer.";

    MessageDigest md = MessageDigest.getInstance("MD5");

    String apiSig = "api_key" + key + "description" + description + "methodplaylist.createsk" + sessionKey + "title" + title + secret;
    md.update(apiSig.getBytes("UTF-8"));
    byte byteData[] = md.digest();
    //convert the byte to hex format
    StringBuffer sb = new StringBuffer(byteData.length*2);
    for (int i = 0; i < byteData.length; i++) {
        sb.append(String.format("%02X", byteData[i]));    
    }
    String hashedSig = sb.toString();

    // FOR DEBUGGING
    System.out.println("api_key = " + key);
    System.out.println("api_sig = " + hashedSig);
    System.out.println("session key = " + sessionKey);
    // FOR DEBUGGING

    String urlParameters = "method=playlist.create&api_key="+ key + "&api_sig=" + hashedSig + "&description=" + URLEncoder.encode(description, "UTF-8") + "&sk=" + sessionKey + "&title=" + URLEncoder.encode(title, "UTF-8");
    String request = "http://ws.audioscrobbler.com/2.0/";

    URL url = new URL(request);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();     
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setInstanceFollowRedirects(false); 
    connection.setRequestMethod("POST"); 
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 
    connection.setRequestProperty("charset", "utf-8");
    connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
    connection.setUseCaches(false);

    DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
    wr.writeBytes(urlParameters);
    wr.flush();
    wr.close();

    ...
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I tried this, doesn't work either. And the API actually says to encode the parameters when generating the signature: `Ensure your parameters are utf8 encoded.` – Karan Goel Apr 09 '13 at 19:46
  • @user2059238 utf-8 encoding does not equal URL encoding. This answer is correct and works for me (save for the missing parenthesis in the code) – jkovacs Apr 09 '13 at 20:34
  • I copied your answer but still get a 403 Forbidden error. Not sure why/how it's working for you. – Karan Goel Apr 09 '13 at 22:39
  • I tried everything possible, even with new API keys, yet not working for me. This is so damn random. – Karan Goel Apr 10 '13 at 23:40
  • Without seeing a working HTTP request that uses the same keys as your code, it is very hard to diagnose what is actually failing. – Remy Lebeau Apr 10 '13 at 23:55
  • @user2059238 Please update your original post with your current source code or create a new question if there's still a problem. – jkovacs Apr 11 '13 at 02:50
  • @RemyLebeau Can you post a screenshot showing that the code is working? Unfortunately sharing last.fm API keys and secrets is not allowed. – Karan Goel Apr 11 '13 at 15:41
  • @user2059238: I cannot provide screenshots as I do not use AudioScrobbler or last.fm. All I did was point out some things that were wrong with your code that do not match what the API docs say to do. If you are still having problems, then you need to talk to AudioScrobbler about it since you can't provide your actual keys for testing. – Remy Lebeau Apr 11 '13 at 17:28