pythonner
pythonner

Reputation: 53

Last.fm API invalid method signature but valid when getting session key

I wanna make python client for Last.fm API. I wanna build kind of library.

I managed to get and set a session by getting a session key. Afterwards, I try to call a POST method that requires API_key, api_signature and session key. So I use the APi key I have, same api_signature I used to get the session key and the session key itself.

But I get an "invalid method signature" even though I use the same api_signature for the POST calls.

import json
import webbrowser
from hashlib import md5
import urllib3

class PyLast():

def __init__(self, API_KEY, SECRET, SESSION_KEY=None):
    self.__API_KEY__ = API_KEY
    self.__SECRET__ = SECRET
    self.__SESSION_KEY__ = SESSION_KEY
    self.__api_signature__ = None
    if SESSION_KEY is None:
        self.__is_authorized__ = False
    else:
        self.__is_authorized__ = True

    self.__http__ = urllib3.PoolManager()

def request_token(self):
    print("Getting the token...")
    url = 'http://ws.audioscrobbler.com/2.0/?method=auth.gettoken&api_key={}&format=json'.format(self.__API_KEY__)
    req_response = self.__http__.request('GET', url, headers={'User-Agent' : 'Mozilla/5.0'})
    if req_response.status == 200:
        json_data = json.loads(req_response.data.decode('utf-8'))
        TOKEN = json_data['token']
        self.__TOKEN__ = TOKEN
        return TOKEN
    else:
        print("Error with code " + req_response.status)

def authorize(self):
    if not self.__is_authorized__:
        url = 'http://www.last.fm/api/auth/?api_key={}&token={}'.format(self.__API_KEY__, self.__TOKEN__)
        # open browser to authorize app
        webbrowser.open(url, new=0, autoraise=True)
        # Make sure authorized
        self.__is_authorized__ = True


def start_session(self):
    if self.__is_authorized__:
        data = "api_key{}methodauth.getSessiontoken{}{}" \
                                     .format(self.__API_KEY__, self.__TOKEN__, self.__SECRET__).encode(
            encoding='utf-8')
        self.__api_signature__ = md5(data).hexdigest()
        url = 'http://ws.audioscrobbler.com/2.0/?method=auth.getSession&api_key={}&token={}&api_sig={}&format=json'.format(
            self.__API_KEY__, self.__TOKEN__, self.__api_signature__)
        req_response = self.__http__.request('GET', url)

        if req_response.status == 200:
            json_data = json.loads(req_response.data.decode('utf-8'))
            session_key = json_data['session']['key']
            self.__SESSION_KEY__ = session_key

            url = 'http://ws.audioscrobbler.com/2.0/?method=track.love&api_key={}&api_sig={}&sk={}&artist=cher&track=believe&format=json'.format(
                self.__API_KEY__, self.__api_signature__, self.__SESSION_KEY__)
            req_response = self.__http__.request('POST', url)

            return self.__SESSION_KEY__
        else:
            print("Error with code " + str(req_response.status))




    else:
        print("Not authorized!")

Upvotes: 0

Views: 1370

Answers (1)

pythonner
pythonner

Reputation: 53

I found a solution. The problem was that I was using the same parameters used to generate session key to make a POST call. The right way to sign a method for Last.fm API is to build the api_sig from the POST method we want to use. for example, to generate api_sig for track.love we use these parameters:

 data = {"api_key": API_KEY,
                    "method": "track.love",
                    "track" : "yellow",
                    "artist" :"coldplay",
                    "sk" : SESSION_KEY
                    }
 keys = sorted(data.keys())
 param = [k+data[k] for k in keys]
 param = "".join(param) + SECRET
 api_sig = md5(param.encode()).hexdigest() # this api_sig used to sign track.love call.

Upvotes: 3

Related Questions