add mdule grooveshark

Signed-off-by: Bezleputh <carton_ben@yahoo.fr>
Signed-off-by: Romain Bignon <romain@symlink.me>
This commit is contained in:
Bezleputh 2013-04-22 19:34:26 +02:00 committed by Romain Bignon
commit 6eedb3a396
5 changed files with 339 additions and 0 deletions

View file

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2013 Bezleputh
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from .backend import GroovesharkBackend
__all__ = ['GroovesharkBackend']

View file

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2013 Bezleputh
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.tools.backend import BaseBackend
from weboob.capabilities.radio import ICapRadio, Radio, Stream, Emission
from weboob.capabilities.video import ICapVideo, BaseVideo
from weboob.capabilities.collection import ICapCollection
from .browser import GroovesharkBrowser
__all__ = ['GroovesharkBackend']
class GroovesharkBackend(BaseBackend, ICapVideo, ICapCollection):
NAME = 'grooveshark'
DESCRIPTION = u'grooveshark website'
MAINTAINER = u'Bezleputh'
EMAIL = 'carton_ben@yahoo.fr'
VERSION = '0.g'
LICENSE = 'AGPLv3+'
BROWSER = GroovesharkBrowser
def fill_video(self, video, fields):
if 'url' in fields:
with self.browser:
video.url = unicode(self.browser.get_stream_url_from_song_id(video.id))
if 'thumbnail' in fields and video.thumbnail:
with self.browser:
video.thumbnail.data = self.browser.readurl(video.thumbnail.url)
def search_videos(self, pattern, sortby=ICapVideo.SEARCH_RELEVANCE, nsfw=False, max_results=10):
with self.browser:
for video in self.browser.search_videos(pattern, max_results):
yield video
def get_video(self, video):
with self.browser:
return self.browser.fill_stream_list(video)
OBJECTS = {BaseVideo: fill_video}

View file

@ -0,0 +1,204 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2013 Bezleputh
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.tools.browser import BaseBrowser
from weboob.tools.json import json as simplejson
from weboob.capabilities.video import BaseVideo
from weboob.tools.capabilities.thumbnail import Thumbnail
import urllib2
import hashlib
import uuid
import string
import random
import datetime
from .pages import IndexPage
__all__ = ['GroovesharkBrowser']
class APIError(Exception):
pass
class GroovesharkBrowser(BaseBrowser):
PROTOCOL = 'http'
DOMAIN = 'html5.grooveshark.com'
#SAVE_RESPONSE = True
#DEBUG_HTTP = True
#DEBUG_MECHANIZE = True
API_URL = 'https://html5.grooveshark.com/more.php'
#Setting the static header (country, session and uuid)
HEADER = {}
HEADER['country'] = {}
HEADER['country']['CC1'] = 0
HEADER['country']['CC2'] = 0
HEADER['country']['CC3'] = 0
HEADER['country']['CC4'] = 0
HEADER['country']['ID'] = 1
HEADER['country']['IPR'] = 1
HEADER['privacy'] = 0
HEADER['session'] = (''.join(random.choice(string.digits + string.letters[:6]) for x in range(32))).lower()
HEADER["uuid"] = str.upper(str(uuid.uuid4()))
#those values depends on a grooveshark version and may change
GROOVESHARK_CONSTANTS = ('mobileshark', '20120830', 'gooeyFlubber')
COMMUNICATION_TOKEN = None
PAGES = {
'http://html5.grooveshark.com/.*': IndexPage,
}
def home(self):
self.location('/');
assert self.is_on_page(IndexPage)
self.get_communication_token()
def search_videos(self, pattern, max_results):
method = 'getResultsFromSearch'
parameters = {}
parameters['query'] = pattern.encode(self.ENCODING)
parameters['type'] = ['Songs']#['Songs','Playlists','Albums']
parameters['guts'] = 0
parameters['ppOverr'] = ''
response = self.API_post(method, parameters, self.create_token(method))
songs = self.create_video_from_songs_result(response['result']['result']['Songs'], max_results)
#playlists = self.create_video_from_playlist_result(response['result']['result']['Playlists'])
#albums = self.create_video_from_albums_result(response['result']['result']['Albums'])
return songs
def create_video_from_songs_result(self, songs, max_results):
videos = []
if max_results:
songs = songs[0:max_results]
for song in songs:
video = BaseVideo(song['SongID'])
video.title = u'Song - %s' % song['SongName'].encode('ascii', 'replace')
video.author = u'%s' % song['ArtistName'].encode('ascii', 'replace')
video.description = u'%s - %s - %s'%(video.author, song['AlbumName'].encode('ascii', 'replace'), song['Year'].encode('ascii', 'replace'))
video.thumbnail = Thumbnail(u'http://images.gs-cdn.net/static/albums/40_' + song['CoverArtFilename'])
video.duration = datetime.timedelta(seconds=int(float(song['EstimateDuration'])))
video.rating = float(song['AvgRating'])
video.date = datetime.date(year=int(song['Year']), month=1, day=1)
videos.append(video)
return videos
def create_video_from_playlist_result(self, playlists):
videos = []
for playlist in playlists:
video = BaseVideo(playlist['PlaylistID'])
video.title = u'Playlist - %s' %(playlist['Name'])
video.description = playlist['Artists']
videos.append(video)
return videos
def create_video_from_albums_result(self, albums):
videos = []
for album in albums:
video = BaseVideo(album['AlbumID'])
video.title = u'Album - %s' %(album['Name'])
video.description = album['Year']
videos.append(video)
return videos
def get_communication_token(self):
parameters = {'secretKey': hashlib.md5(self.HEADER["session"]).hexdigest()}
result = self.API_post('getCommunicationToken', parameters)
self.COMMUNICATION_TOKEN = result['result']
def create_token(self, method):
if self.COMMUNICATION_TOKEN is None:
self.get_communication_token()
rnd = (''.join(random.choice(string.hexdigits) for x in range(6)))
return rnd + hashlib.sha1('%s:%s:%s:%s' % (method, self.COMMUNICATION_TOKEN, self.GROOVESHARK_CONSTANTS[2], rnd)).hexdigest()
def fill_stream_list(self, video):
if isinstance(video, BaseVideo):
video.url = self.get_stream_url_from_song_id(video.id)
def get_stream_url_from_song_id(self, song_id):
method = 'getStreamKeyFromSongIDEx'
parameters = {}
parameters['prefetch'] = False
parameters['mobile'] = True
parameters['songID'] = int(song_id)
parameters['country'] = self.HEADER['country']
response = self.API_post(method, parameters, self.create_token(method))
self.mark_song_downloaded_ex(response['result'])
return u'http://%s/stream.php?streamKey=%s' %(response['result']['ip'], response['result']['streamKey'])
# in order to simulate a real browser
def mark_song_downloaded_ex(self, response):
method = 'markSongDownloadedEx'
parameters = {}
parameters['streamKey'] = response['streamKey']
parameters['streamServerID'] = response['streamServerID']
parameters['songID'] = response['SongID']
response = self.API_post(method, parameters, self.create_token(method))
def check_result(self, result):
if 'fault' in result:
raise APIError('%s' % result['fault']['message'])
if not result['result']:
raise APIError('%s' % "No response found")
def API_post(self, method, parameters, token=None):
"""
Submit a POST request to the website
The JSON data is parsed and returned as a dictionary
"""
data = self.create_json_data(method, parameters, token)
req = self.create_request(method)
response = urllib2.urlopen(req, data)
return self.parse_response(response)
def create_json_data(self, method, parameters, token):
data = {}
data['header'] = self.HEADER
data['header']['client'] = self.GROOVESHARK_CONSTANTS[0]
data['header']['clientRevision'] = self.GROOVESHARK_CONSTANTS[1]
if(token is not None):
data['header']['token'] = token
data['method'] = method
data['parameters'] = parameters
return simplejson.dumps(data)
def create_request(self, method):
req = urllib2.Request('%s?%s' %(self.API_URL, method))
req.add_header('Content-Type', 'application/json')
return req
def parse_response(self, response):
result = simplejson.loads(response.read(), self.ENCODING)
self.check_result(result)
return result

View file

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2013 Bezleputh
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.tools.browser import BasePage
__all__ = ['IndexPage']
class IndexPage(BasePage):
pass

View file

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2013 Bezleputh
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.tools.test import BackendTest
class GroovesharkTest(BackendTest):
BACKEND = 'grooveshark'
def test_grooveshark_video_search(self):
result = list(self.backend.search_videos("Loic Lantoine"))
self.assertTrue(len(result)>0)