How do I scrobble to Last.fm's API in Python without getting Invalid method signature supplied?

305 Views Asked by At

I have exchanged a token for a session key that looks like this "UserName 94ag345Sd3452C2ffa3aT 0"

I have the following code: session_key = 'UserName 94ag345Sd3452C2ffa3aT 0'

method = "track.scrobble"
artist = "Muse"
track = "Time Is Running Out"
timestamp = str(time.time())
api_sig = hashlib.md5(
f"api_key{API_KEY}artist{artist}method{method}sk{session_key}timestamp{timestamp}track{track}{API_SECRET}".encode('utf-8')).hexdigest()

header = {"user-agent" : "mediascrobbler/0.1",
      "Content-type": "application/x-www-form-urlencoded"}

# API URL
url = "https://ws.audioscrobbler.com/2.0/"

# Parameters for the POST request
params = {
    "api_key": API_KEY,
    "artist[0]": artist,
    "method": method,
    "sk": session_key,
    "timestamp[0]": timestamp,
    "track[0]": track,
    "api_sig": api_sig,
}

params['api_sig'] = api_sig


# Sending the POST request
try:
    response = requests.post(url, params=params, headers=header)
except requests.exceptions.RequestException as e:
    print(e)
    sys.exit(1)

return response.text

Why does it keep returning Invalid Method Signature? I have utf encoded my params. All required params are there. I've exchanged a token for a session key.

Update: If I set the api sig to look like this:

api_sig = hashlib.md5(
f"api_key{API_KEY}artist[0]{artist}method{method}sk{session_key}timestamp[0]{timestamp}track[0]{track}{API_SECRET}".encode('utf-8')

).hexdigest()

but this is returning Invalid session key - Please re-authenticate with a brand newly generated session key I know to be valid. If I'm not mistaken, those sessions don't expire.

4

There are 4 best solutions below

3
elebur On BEST ANSWER

You forgot to hexdigest your md5 signature in this line:

api_sig = hashlib.md5(
    f"api_key{API_KEY}artist[0]{artist}method{method}sk{session_key}timestamp[0]{timestamp}track[0]{track}{API_SECRET}".encode('utf-8')
)

This should work:

import time, requests, hashlib

# API method and parameters
API_KEY = 'API_KEY'
session_key = 'session_key'
API_SECRET = 'API_SECRET'
method = "track.scrobble"
artist = "Muse"
track = "Time Is Running Out"
timestamp = int(time.time())

api_sig = hashlib.md5(
    f"api_key{API_KEY}artist[0]{artist}method{method}sk{session_key}timestamp[0]{timestamp}track[0]{track}{API_SECRET}".encode('utf-8')
).hexdigest()
# API URL
url = "http://ws.audioscrobbler.com/2.0/"

# Parameters for the POST request
params = {
    "method": method,
    "api_key": API_KEY,
    "api_sig": api_sig,
    "sk": session_key,
    "artist[0]": artist,
    "track[0]": track,
    "timestamp[0]": timestamp,
}

# Sending the POST request
try:
    response = requests.post(url, data=params)
except requests.exceptions.RequestException as e:
    print(e)
    sys.exit(1)

print(response.text)

Response:

<?xml version="1.0" encoding="UTF-8"?>
<lfm status="ok">
  <scrobbles ignored="0" accepted="1">
    <scrobble>
      <track corrected="0">Time Is Running Out</track>
      <artist corrected="0">Muse</artist>
      <album corrected="0" />
      <albumArtist corrected="0"></albumArtist>
      <timestamp>1692987267</timestamp>
      <ignoredMessage code="0"></ignoredMessage>
    </scrobble>
  </scrobbles>
</lfm>
1
Tranbi On

I suggest using pylast. You'll be able to create a connection (code from the doc):

import pylast

# You have to have your own unique two values for API_KEY and API_SECRET
# Obtain yours from https://www.last.fm/api/account/create for Last.fm
API_KEY = "b25b959554ed76058ac220b7b2e0a026"  # this is a sample key
API_SECRET = "425b55975eed76058ac220b7b4e8a054"

# In order to perform a write operation you need to authenticate yourself
username = "your_user_name"
password_hash = pylast.md5("your_password")

network = pylast.LastFMNetwork(
    api_key=API_KEY,
    api_secret=API_SECRET,
    username=username,
    password_hash=password_hash,
)

Then provide artist, track name and timestamp to the scrobble method:

network.scrobble(artist, track, timestamp)
1
a.akbarnejad On

the order of parameters in params should be the same as they are in md5 hash. and also make sure your system time is ok

1
HeavenHM On

The issue you are facing with the Last.fm API is related to the generation of the API signature. The API signature is used to ensure the integrity and authenticity of the request.

Looking at your code, i found that you are missing a few steps checkout these below i've mentioned.

  1. Ensure that you have the correct values for API_KEY and API_SECRET. Make sure they are valid and correspond to your Last.fm API credentials.

  2. Update the generation of the api_sig variable. The API signature should be an MD5 hash of the concatenated string of all the parameters, sorted alphabetically by parameter name. The parameter values should be URL-encoded. Here's the updated code for generating the api_sig:

api_sig = hashlib.md5(
    f"api_key{API_KEY}artist{artist}method{method}sk{session_key}timestamp{timestamp}track{track}{API_SECRET}".encode('utf-8')
).hexdigest()
  1. Update the params dictionary to include the updated api_sig:
params = {
    "method": method,
    "api_key": API_KEY,
    "api_sig": api_sig,
    "sk": session_key,
    "artist": artist,
    "track": track,
    "timestamp": timestamp,
}
  1. Make sure to use the urlencode from the urllib.parse module to encode the parameters when sending the POST request:
import urllib.parse

# ...

try:
    response = requests.post(url, data=urllib.parse.urlencode(params))
    response.raise_for_status()  # Check for any HTTP errors
except requests.exceptions.RequestException as e:
    print(e)
    sys.exit(1)

Hope that solves your issue there.