Skip to content

Instantly share code, notes, and snippets.

@marsam
Last active December 25, 2015 15:59
Show Gist options
  • Select an option

  • Save marsam/7002860 to your computer and use it in GitHub Desktop.

Select an option

Save marsam/7002860 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from __future__ import print_function
import binascii
import hashlib
import json
import logging
import os
import random
import uuid
try:
import http.client as httplib
from urllib.parse import urlencode
from urllib.request import urlretrieve
except ImportError:
import httplib
from urllib import urlretrieve, urlencode
try:
input = raw_input
except NameError:
pass
USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5"
class Groove(object):
host = 'grooveshark.com'
secret = 'nuggetsOfBaller'
client = 'htmlshark'
client_revision = '20130520'
_token = None
_queueid = None
def __init__(self, session=None):
self.session = session or binascii.hexlify(os.urandom(16)).decode('utf-8')
self._secretkey = hashlib.md5(self.session.encode('utf-8')).hexdigest()
self._conn = httplib.HTTPSConnection(self.host)
self._uuid = str(uuid.uuid4()).upper()
self._country = {
'CC1': '72057594037927940',
'CC2': '0',
'CC3': '0',
'CC4': '0',
'ID': '57',
'IPR': '0',
}
def _hashtoken(self, method, secret):
rand = binascii.hexlify(os.urandom(3)).decode('utf-8')
string = '{0}:{1}:{2}:{3}'.format(method, self.token, secret, rand)
hash = hashlib.sha1(string.encode('utf-8')).hexdigest()
return '{0}{1}'.format(rand, hash)
def _request(self, method, url, body, headers={}):
body = json.dumps(body)
headers.update({
'User-Agent': USER_AGENT,
'Content-Type': 'application/json',
})
logging.debug('Sending: %s', body)
self._conn.request(method, url=url, body=body, headers=headers)
response = self._conn.getresponse().read()
logging.debug('Received: %s', response)
return json.loads(response.decode('utf-8'))
@property
def queueid(self):
if self._queueid is None:
self._queueid = str(random.randrange(1e+23, 1e+24))
return self._queueid
@property
def basebody(self):
return {
'header': {
'uuid': self._uuid,
'client': self.client,
'session': self.session,
'clientRevision': self.client_revision,
'privacy': '0',
'country': self._country,
},
'parameters': {
'secretKey': self._secretkey,
}
}
@property
def token(self):
if self._token is None:
body = self.basebody
body['method'] = 'getCommunicationToken'
response = self._request('POST', '/more.php', body)
self._token = response['result']
return self._token
def search(self, query, type='Songs'):
method = 'getResultsFromSearch'
body = self.basebody
body['method'] = method
body['header'].update({
'token': self._hashtoken(method, 'nuggetsOfBaller'),
})
body['parameters'].update({
'type': type,
'query': query,
})
return self._request('POST', '/more.php?{0}'.format(method), body)
def add_songs(self, songs):
"""Add song to queue."""
method = 'addSongsToQueue'
body = self.basebody
queue = [{'source': 'user',
'songID': song['SongID'],
'artistID': song['ArtistID'],
'songQueueSongID': 1} for song in songs]
body['method'] = method
body['header'].update({
'client': 'jsqueue',
'clientRevision': '20130520',
'token': self._hashtoken(method, 'chickenFingers'),
})
body['parameters'].update({
'songQueueID': self.queueid,
'songIDsArtistIDs': queue,
})
return self._request('POST', '/more.php?{0}'.format(method), body)
def get_streamkeys(self, songs):
method = 'getStreamKeysFromSongIDs'
body = self.basebody
body['method'] = method
body['header'].update({
'client': 'jsqueue',
'clientRevision': '20130520',
'token': self._hashtoken(method, 'chickenFingers'),
})
body['parameters'].update({
'type': 8,
'mobile': False,
'prefetch': False,
'songIDs': [song['SongID'] for song in songs],
'country': self._country,
})
headers = {
'Referer': 'http://{0}/JSQueue.swf?jsqueue'.format(self.host),
}
return self._request('POST', '/more.php?{0}'.format(method), body, headers=headers)
def main():
import argparse
parser = argparse.ArgumentParser(description='Download songs from grooveshark') # nopep8
parser.add_argument('term', metavar='term', type=str, help='Search term (song or atist name).')
parser.add_argument('-l', '--loglevel', dest='loglevel', default='info', type=str, help='Logging level')
parser.add_argument('-r', '--results', dest='results', default=30, type=int, help='Number of results to show.')
args = parser.parse_args()
level = getattr(logging, args.loglevel.upper(), logging.INFO)
logging.basicConfig(level=level)
g = Groove()
response = g.search(args.term)
songs = response['result']['result']
for number, song in enumerate(songs[:args.results]):
print(u'[{number}] {artist} — {songname}'.format(number=number, artist=song['ArtistName'], songname=song['SongName']))
idxs = input(u'Enter the songs numbers separated by commas: ')
queue = map(lambda x: songs[x], {int(idx) for idx in idxs.split(',') if idx})
g.add_songs(queue)
streamkeys = g.get_streamkeys(queue)
if not streamkeys['result']:
logging.info("I'm sorry, there were no results")
return
for key, value in streamkeys['result'].items():
logging.info('Downloading song %s', key)
url = 'http://{ip}/stream.php'.format(ip=value['ip'])
filename = '{0}.mp3'.format(value['SongID'])
data = urlencode({'streamKey': value['streamKey']})
if os.path.exists(filename):
logging.info('File with name %s already exists... skipping', filename)
continue
urlretrieve(url=url, filename=filename, data=data)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment