hardcorE_0x1A4 Posted January 29, 2010 Share Posted January 29, 2010 Hey what's up guys, Great work so far Zimmer. I've been looking for an app like this (open source) or specifically for the palm pre so I can access Grooveshark on the go. (Grooveshark has been working on one for a while with no release date in sight...) Since the pre has an underlying Linux os I can implement this python program on it with a few modifications. I'm not that familiar with python but it isn't hard to pick up. With that said, I was wondering if you would be willing to help me with an error I'm having with getting your source to work. I've downloaded both the new and old source, and I receive the error in both versions. The function getToken's return gives me an error... Traceback (most recent call last): File "myGrooveshark.py", line 11, in <module> g.sessionData() File "/home/myUsrname/Downloads/Grooveshark - python/distrobution/Grooveshark.py", line 61, in sessionData self.token = self.getToken() File "/home/myUsrname/Downloads/Grooveshark - python/distrobution/Grooveshark.py", line 69, in getToken return json.loads(reply)['result'] KeyError: 'result' Have you or anyone else expirenced this error? PS: I'm running Ubuntu 9.10 and installed python, wx, httplib2, & the JSON packages from my repositories. Thanks! Quote Link to comment Share on other sites More sharing options...
Zimmer Posted January 31, 2010 Author Share Posted January 31, 2010 That would be a dictionary You see the json.loads creates a dictionary (yes like a dictionary) basically a dictionary is well like a dictionary (so key - value or apple - a sweet fruit) the syntax is {key: value, second_key: second_value} or {'apple': 'a fruit', 'orange': 'another fruit'} so say you have x = {'apple': 'a fruit', 'orange': 'another fruit'} then you do print x['apple'] you get a fruit as a result but if you do print x['non existent value'] or x['peach'] in our example you get the key error Quote Link to comment Share on other sites More sharing options...
hardcorE_0x1A4 Posted January 31, 2010 Share Posted January 31, 2010 Thx, that clears it up. I still can't get the source to work though and I realize why now. The reply I get back from getToken after calling sessionData says invalid client. reply = {"header":{"session":"c599864a8dd58b66c74464aa3c838f10"},"fault":{"code":1024,"message":" invalid client"}} Have you found a workaround for this? Thanks again for the help! Quote Link to comment Share on other sites More sharing options...
Zimmer Posted January 31, 2010 Author Share Posted January 31, 2010 Well it points to a problem with the client so I am guessing the client rev # or the user agent Quote Link to comment Share on other sites More sharing options...
Macuyiko Posted January 31, 2010 Share Posted January 31, 2010 After changing the client revision, the program still does not work anymore. The response when searching for a song is "Bad Token". After taking a look with Tamper Data it seems that the communication token is not used "as is" anymore when sending a request to Grooveshark, but is now longer and different for every request. The new token probably hashes one or more of the following: time, the communication token, the song title, other, but it's pretty hard to figure out how it is formed exactly. Do you have any idea? Quote Link to comment Share on other sites More sharing options...
facelessphalanx Posted January 31, 2010 Share Posted January 31, 2010 I've been looking into this recently and the problem I have come across is that the token they are using is 46 charactors. This means it would have to be an 184-bit hash function which as far as I can tell doesn't exist. I agree that it has to be based on the current time since they are no constant characters. I'm trying to see if maybe it is the current time encrypted using the token as the key. If anyone else has any insight on this it would be great. EDIT: I found the solution: generate a 6 random leters and numbers such as: d298c5 Then find the name of the command you are going to run: logoutUser get your communicationToken: 4b6706fb94216 generate the sha1 hash of param+":"+communicationToken+":theColorIsRed:"+theRandom: "logoutUser:4b6706fb94216:theColorIsRed:d298c5" gives you e36c21d4a8585d5144643a1477a7d7dd9fb95fdb append the random onto the front giving: d298c5e36c21d4a8585d5144643a1477a7d7dd9fb95fdb and there's your Token. After changing the client revision, the program still does not work anymore. The response when searching for a song is "Bad Token". After taking a look with Tamper Data it seems that the communication token is not used "as is" anymore when sending a request to Grooveshark, but is now longer and different for every request. The new token probably hashes one or more of the following: time, the communication token, the song title, other, but it's pretty hard to figure out how it is formed exactly. Do you have any idea? Quote Link to comment Share on other sites More sharing options...
Tim Smart Posted February 4, 2010 Share Posted February 4, 2010 Someone has been looking at my codes, haven't they :P I've been looking into this recently and the problem I have come across is that the token they are using is 46 charactors. This means it would have to be an 184-bit hash function which as far as I can tell doesn't exist. I agree that it has to be based on the current time since they are no constant characters. I'm trying to see if maybe it is the current time encrypted using the token as the key. If anyone else has any insight on this it would be great. EDIT: I found the solution: Quote Link to comment Share on other sites More sharing options...
Netshroud Posted February 4, 2010 Share Posted February 4, 2010 Wireshark + edit out the HTTP headers seems to work still. Quote Link to comment Share on other sites More sharing options...
Zimmer Posted February 4, 2010 Author Share Posted February 4, 2010 So they have the literal string "theColorIsRed". that is weird. Quote Link to comment Share on other sites More sharing options...
Tim Smart Posted February 5, 2010 Share Posted February 5, 2010 So they have the literal string "theColorIsRed". that is weird. It changes everytime they release a new client (well almost). The previous one was "theHumansAreDead" Quote Link to comment Share on other sites More sharing options...
facelessphalanx Posted February 5, 2010 Share Posted February 5, 2010 No, just doing probably exactly what you did; decompile the main.swf and read through the action script. Someone has been looking at my codes, haven't they :P Quote Link to comment Share on other sites More sharing options...
gnounc Posted February 19, 2010 Share Posted February 19, 2010 Alright forum is back up to date, sweet! How's the next release lookin? Quote Link to comment Share on other sites More sharing options...
Zimmer Posted February 27, 2010 Author Share Posted February 27, 2010 It is almost done, still working on mplayer interaction, but the gui is almost finished and the grooveshark backend is done (except for streaming support, it is still being worked on). Quote Link to comment Share on other sites More sharing options...
Xqtftqx Posted March 4, 2010 Share Posted March 4, 2010 Getting Session Data Traceback (most recent call last):   File "Command Line App Grooveshark.py", line 293, in <module>     m = Menu(path)   File "Command Line App Grooveshark.py", line 26, in __init__     g.sessionData()   File "/home/xqtftqx/Downloads/Grooveshark/Grooveshark.py", line 48, in sessionData     self.token = self.getToken()   File "/home/xqtftqx/Downloads/Grooveshark/Grooveshark.py", line 56, in getToken     return json.loads(reply)['result'] KeyError: 'result' All libraries installed, im a python n00b so any explanation would be sweet. I look forward to using this Quote Link to comment Share on other sites More sharing options...
Zimmer Posted March 4, 2010 Author Share Posted March 4, 2010 Ya, Grooveshark broke that app a while ago, they changed how to get the token. Quote Link to comment Share on other sites More sharing options...
Xqtftqx Posted March 6, 2010 Share Posted March 6, 2010 Thats a shame, any motivation to find out how to get a new ticket? Quote Link to comment Share on other sites More sharing options...
Zimmer Posted March 6, 2010 Author Share Posted March 6, 2010 Ya I am working on a new version, but it also is the time that I converted the code to use twisted (http://www.twistedmatrix.com) so it also involved a lot of code rewrite and learning about asynchronous programing. On the upside, almost done :) Quote Link to comment Share on other sites More sharing options...
Zimmer Posted March 9, 2010 Author Share Posted March 9, 2010 I am still working on it, but some problems with the network framework (twistedmatrix.com) are causing playback problems :(. Quote Link to comment Share on other sites More sharing options...
kleemajo Posted March 11, 2010 Share Posted March 11, 2010 I came across this semi-accidentally and I think that it is a really cool project. I can't wait to see what else you have going! In the meantime, the script on the front page doesn't work, so here is a modified version that goes through the new grooveshark api calls that a few people were discussing above. I switched the functions around a bit so it won't work with the gui or command line and it won't actually play, but you can at least see how the api works from a python shell and get an mp3 file spewed out in hex. Just open a shell and type in the commands listed in Usage. ''' Main class for handling of grooveshark one grooveshark instance represents one session (so usually one per application instance) ''' import time import sys import uuid import hashlib import urllib import os import random import subprocess from mutagen.mp3 import MP3 try: import json except ImportError: import simplejson as json#If this also errors allows the gui/user/whatever to handle the failed import #Third Pary Libaries import httplib2#Need version 5 or higher for python 2.6 compatability - No version info in httplib2 so I can not check this :( #This is a default header that can be used if you just need a user agent change (this way not every function/method has to define this) header = {} header['user-agent'] = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)' ''' Intialize Grooveshark -- Get Session Data No Return -- set ID3 tags No Return -- Work methods - search, return json search results - songKeyfromID return songKey - Download - return song Usage: from Grooveshark import Grooveshark g = Grooveshark() g.sessionData() -Searching g.search('some search string') -Get Song Key and Stream server g.songKeyfromID(songID) -Download g.download(songKey, streamServer) -Popular Songs g.Popular() ''' class Grooveshark(object): def __init__(self): self.search_url = 'http://cowbell.grooveshark.com/more.php?getSearchResults' self.count = 0 def save(self, content, path): file_ = open(path, 'wb') file_.write(content) file_.close() def sessionData(self): self.session = self.getSessionID() self.uuid = self.getUID() self.token = self.getToken() def getToken(self): http = httplib2.Http() url = 'https://cowbell.grooveshark.com/service.php' self.secretKey = hashlib.md5(self.session).hexdigest() tokenPOSTdata = ('''{"header":{"session":"%s","uuid":"%s","client":"gslite","clientRevision":"20100211.13"},''' '''"parameters":{"secretKey":"%s"},"method":"getCommunicationToken"}''' % (self.session, self.uuid, self.secretKey)) request, reply = http.request(url, 'POST', headers = header, body = tokenPOSTdata) return json.loads(reply)['result'] def getUID(self): return uuid.uuid4() def getSessionID(self): http = httplib2.Http() url = 'http://listen.grooveshark.com' response, src = http.request(url, 'GET', headers = header) src = src.lower().replace(' ', '') start = src.find('session') end = src[start:].find("',") startSession = src[start:end+start].find("'") +1 return src[startSession+start:end+start] def getRequestToken(self, method): randomStr = ''.join([random.choice('0123456789abcdef') for x in xrange(6)]) return randomStr + hashlib.sha1(method + ":" + self.token + ":theColorIsRed:" + randomStr).hexdigest() #work Methods def search(self, search_string): http = httplib2.Http() data = ('''{"header":{"session":"%s","uuid":"%s","client":"gslite","clientRevision":"20100211.13","token":"%s"},''' '''"parameters":{"type":"Songs","query":"%s"},"method":"getSearchResults"}''' % (self.session, self.uuid, self.getRequestToken("getSearchResults"), search_string.lower())) self.response, self.result = http.request(self.search_url, 'POST', headers = header, body = data) self.result = self.result self.searchResults = json.loads(self.result)['result'] return self.searchResults def songKeyfromID(self, id): http = httplib2.Http() self.songID = id songKeyURL = ' http://cowbell.grooveshark.com/more.php?ge...FromSongID' songKeyPOSTdata = ('''{"header":{"token":"%s","session":"%s","uuid":"%s","client":"gslite","clientRevision":"20100211.13"},''' '''"parameters":{"songID":%s,"prefetch":false},"method":"getStreamKeyFromSongID"}''') % (self.getRequestToken("getStreamKeyFromSongID"), self.session, self.uuid, self.songID) request, reply = http.request(songKeyURL, 'POST', headers = header, body = songKeyPOSTdata) self.reply = json.loads(reply)['result'] self.songKey = self.reply['result']['streamKey'] return (self.songKey, self.reply['result']['streamServer']) def download(self, songKey, streamServer): http = httplib2.Http() self.mp3URL = 'http://'+streamServer+'/stream.php'#use self. so that any outer program can access it (not local) data = {} data['streamKey'] = songKey songHeader = dict(header) songHeader['content-length'] = str(len(urllib.urlencode(data))) songHeader['content-type'] = 'application/x-www-form-urlencoded' self.response, self.song = http.request(self.mp3URL, 'POST', headers = songHeader, body = urllib.urlencode(data)) if self.response['status'] == '302': self.response, self.song = http.request(self.response['location'], 'GET', headers = header) if self.response['status'] == '400': return '400 Error' return self.song def stream(self, streamKeys, songServers, file): '''A list of streamkeys and base file''' self.stream_process = subprocess.Popen('python stream.py %s %s %s' % (json.dumps(songServers), json.dumps(streamKeys), file)) def popular(self): http = httplib2.Http() url = 'http://cowbell.grooveshark.com/more.php?popularGetSongs' popularPOSTdata = ('''{"header":{"token":"%s","session":"%s","uuid":"%s","client":"gslite","clientRevision":"20100211.13"},''' '''"parameters":{},"method":"popularGetSongs"}''' % (self.getRequestToken("popularGetSongs"), self.session, self.uuid)) self.request, self.reply = http.request(url, 'POST', headers = header, body = popularPOSTdata) return json.loads(self.reply)['result']['Songs'] # def favorites(self): # http = httplib2.Http() # url = 'http://cowbell.grooveshark.com/more.php?getFavorites' # songKeyPOSTdata = ('''{"header":{"token":"%s","session":"%s","uuid":"%s","client":"gslite","clientRevision":"20091027.09"},''' # '''"parameters":{"prefetch":false},"method":"getFavorites"}''' % (self.token, self.session, self.uuid)) # request, reply = http.request(url, 'POST', headers = header, body = songKeyPOSTdata) # print request # def playlist(self): # http = httplib2.Http() # url = 'http://cowbell.grooveshark.com/more.php?playlistGetSongs' # songKeyPOSTdata = ('''{"header":{"token":"%s","session":"%s","uuid":"%s","client":"gslite","clientRevision":"20091027.09"},''' # '''"parameters":{"prefetch":false},"method":"getPaylist"}''' % (self.token, self.session, self.uuid)) # request, reply = http.request(url, 'POST', headers = header, body = songKeyPOSTdata) # print request def setID3tags(self, file, title = None, artist = None, album = None, albumArt = None, genre = None, composer = None): '''Does NOT WORK DUE TO UNKOWN UNICODE PROBLEM when saving''' if not os.path.exists(file): raise IOError('MP3 File does not exist.') print file tag_lookup = {'album':'TALB', 'comment':"COMM::'eng'", 'description':'TIT3', 'artist':'TPE1', 'title':'TIT2', 'track':'TRCK', 'composer':'TCOM', 'genre':'TCON'} http = httplib2.Http() request, albumArt = http.request('http://beta.grooveshark.com/static/amazonart/'+albumArt) if request['status'] == '404': albumArt = u'' tag_lookup['album_art'] = 'TART' tags = {} #tags['title'] = title #tags['artist'] = artist #tags['album'] = album #tags['album_art'] = albumArt tags['genre'] = genre #tags['composer'] = composer #if tags['composer'] == None: # tags['composer'] = tags['artist'] for i in tags: if tags[i] == None: tags[i] = '' audio = MP3(file) #Delete Existing Songs audio.delete() #Set the tags #Iterate through tags for i in tags: print i print type(tags[i]) print tags[i] audio[tag_lookup[i]] = tags[i] print audio[tag_lookup[i]] audio.tags.save() #This is for streaming support class Stream(urllib.FancyURLopener): version = header['user-agent'] Great project! Also, I have a semi-working obj-c/iPhone port if anyone is interested. Quote Link to comment Share on other sites More sharing options...
Zimmer Posted March 11, 2010 Author Share Posted March 11, 2010 (edited) Nice I have a version that is working pretty well but still rather unstable. Also I probably should mark that it is broken on the front page :) Oh by the way. the new version will have an update checker it will have a improved player improved backend code base no more lock ups, uses twisted now (so no more httplib2 :)) oh sorry no pandora support YET :( :( !!!!! I learned a lot (wait what, you don't think that is a plus ;)) Edited March 12, 2010 by Zimmer Quote Link to comment Share on other sites More sharing options...
gnounc Posted March 15, 2010 Share Posted March 15, 2010 update checker....now thats badass. Quote Link to comment Share on other sites More sharing options...
Bobbie Posted March 19, 2010 Share Posted March 19, 2010 with method = "getSearchResults", the result is [{u'SongClicks': 0, u'ArtistVerified': u'0', u'DSName': u'p', u'AlbumName': u'Track Of Time EP', u'ArtistPlays': 0, u'ArtistName': u'Anna Von Hausswolff', u'Score': 29204.446014486999, u'CoverArtFilename': None, u'QueryArtistClicks': u'0', u'QuerySongClicks': u'0', u'SongName': u'Pills', u'SphinxWeight': 1500689, u'DAName': u'', u'EstimateDuration': u'368', u'Popularity': u'0', u'Name': u'Anna Von Hausswolff', u'AlbumClicks': 0, u'Year': u'2010', u'AlbumVerified': u'0', u'AlbumID': u'4129726', u'AvgDuration': None, u'Flags': u'0', u'SongPlays': 0, u'QueryAlbumClicks': u'0', u'IsVerified': u'0', u'SongVerified': u'0', u'ArtistClicks': 0, u'ArtistID': u'1350648', u'TrackNum': u'2', u'AvgRating': None, u'IsLowBitrateAvailable': u'0', u'DALName': u'p'}] the key songID is not in the result How to get songID ? Quote Link to comment Share on other sites More sharing options...
Bobbie Posted March 19, 2010 Share Posted March 19, 2010 I found the explanation: The research was done with "parameters":{"type":"Albums","query":"%s"} and not with "parameters":{"type":"Songs","query":"%s"} With "type":"Albums", I think we should use after the method albumGetSongs for the songIDs of the Album Quote Link to comment Share on other sites More sharing options...
Zimmer Posted March 19, 2010 Author Share Posted March 19, 2010 Well I was about to have some sad news, but I right before I was about to post I had an epiphany :). My laptop's power was dead (I'm still not going to buy from Dell ever again) and it seemed that it was the motherboard. Well I still have no clue but I can boot (if I have a charged battery) and then run on AC power from the plug (it won't charge (dell has an id chip that if the tiny wire is broken the BIOS refuses to charge (it still receives power though)). Also I got my files to another computer. Anyways an indefinite suspension from this app moving forward was averted. and now I am back getting the app almost done :). Note to self: I need a blog :P Quote Link to comment Share on other sites More sharing options...
gnounc Posted March 20, 2010 Share Posted March 20, 2010 and an svn! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.