grooveshark 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ require 'bundler'
2
+ require 'bundler/gem_tasks'
1
3
  require "rspec/core/rake_task"
2
4
 
3
5
  RSpec::Core::RakeTask.new(:test) do |spec|
data/lib/grooveshark.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'digest'
2
2
  require 'json'
3
3
  require 'rest-client'
4
+ require 'uuid'
4
5
 
5
6
  require 'grooveshark/utils'
6
7
  require 'grooveshark/errors'
@@ -8,4 +9,4 @@ require 'grooveshark/request'
8
9
  require 'grooveshark/client'
9
10
  require 'grooveshark/user'
10
11
  require 'grooveshark/playlist'
11
- require 'grooveshark/song'
12
+ require 'grooveshark/song'
@@ -3,42 +3,45 @@ module Grooveshark
3
3
  include Grooveshark::Request
4
4
 
5
5
  attr_accessor :session, :comm_token
6
- attr_reader :user
7
- attr_reader :comm_token_ttl
8
-
9
- SALT = 'imOnAHorse'
10
- METHOD_SALTS = {
11
- 'getStreamKeyFromSongIDEx' => 'theTicketsAreNowDiamonds'
12
- }
6
+ attr_reader :user, :comm_token_ttl, :country
13
7
 
14
8
  def initialize(session=nil)
15
- @session = session || get_session
9
+ @session, @country = get_session_and_country
10
+ @uuid = UUID.new.generate.upcase
16
11
  get_comm_token
17
12
  end
18
13
 
19
14
  protected
20
-
21
- # Obtain new session from Grooveshark
22
- def get_session
23
- resp = RestClient.get('http://listen.grooveshark.com')
24
- resp.headers[:set_cookie].to_s.scan(/PHPSESSID=([a-z\d]{32});/i).flatten.first
15
+
16
+ def get_session_and_country
17
+ response = RestClient.get('http://grooveshark.com')
18
+ session = response.headers[:set_cookie].to_s.scan(/PHPSESSID=([a-z\d]{32});/i).flatten.first
19
+ config_json = response.to_s.scan(/window.gsConfig = ({.*?});/).flatten.first
20
+ raise GeneralError, "gsConfig not found" if not config_json
21
+ config = JSON.parse(config_json)
22
+ [session, config['country']]
25
23
  end
26
24
 
27
25
  # Get communication token
28
26
  def get_comm_token
29
- @comm_token = nil
27
+ @comm_token = nil # request() uses it
30
28
  @comm_token = request('getCommunicationToken', {:secretKey => Digest::MD5.hexdigest(@session)}, true)
31
29
  @comm_token_ttl = Time.now.to_i
32
30
  end
33
31
 
34
32
  # Sign method
35
33
  def create_token(method)
36
- rnd = rand(256**3).to_s(16).rjust(6, '0')
37
- salt = METHOD_SALTS.key?(method) ? METHOD_SALTS[method] : SALT
34
+ rnd = get_random_hex_chars(6)
35
+ salt = get_method_salt(method)
38
36
  plain = [method, @comm_token, salt, rnd].join(':')
39
37
  hash = Digest::SHA1.hexdigest(plain)
40
38
  "#{rnd}#{hash}"
41
39
  end
40
+
41
+ def get_random_hex_chars(length)
42
+ chars = ('a'..'f').to_a | (0..9).to_a
43
+ (0...length).map { chars[rand(chars.length)] }.join
44
+ end
42
45
 
43
46
  public
44
47
 
@@ -76,7 +79,7 @@ module Grooveshark
76
79
 
77
80
  # Perform search request for query
78
81
  def search(type, query)
79
- results = request('getSearchResults', {:type => type, :query => query})['songs']
82
+ results = request('getResultsFromSearch', {:type => type, :query => query})['result']
80
83
  results.map { |song| Song.new song }
81
84
  end
82
85
 
@@ -92,19 +95,24 @@ module Grooveshark
92
95
 
93
96
  # Get stream authentication by song ID
94
97
  def get_stream_auth_by_songid(song_id)
95
- request('getStreamKeyFromSongIDEx', {
96
- 'songID' => song_id,
97
- 'prefetch' => false,
98
- 'mobile' => false,
99
- 'country' => COUNTRY
98
+ result = request('getStreamKeyFromSongIDEx', {
99
+ 'type' => 0,
100
+ 'prefetch' => false,
101
+ 'songID' => song_id,
102
+ 'country' => @country,
103
+ 'mobile' => false,
100
104
  })
105
+ if result == [] then
106
+ raise GeneralError, "No data for this song. Maybe Grooveshark banned your IP."
107
+ end
108
+ result
101
109
  end
102
110
 
103
111
  # Get stream authentication for song object
104
112
  def get_stream_auth(song)
105
113
  get_stream_auth_by_songid(song.id)
106
114
  end
107
-
115
+
108
116
  # Get song stream url by ID
109
117
  def get_song_url_by_id(id)
110
118
  resp = get_stream_auth_by_songid(id)
@@ -115,5 +123,11 @@ module Grooveshark
115
123
  def get_song_url(song)
116
124
  get_song_url_by_id(song.id)
117
125
  end
126
+
127
+ private
128
+
129
+ def get_method_salt(method)
130
+ get_method_client(method) == 'jsqueue' ? 'spiralLightbulbs' : 'riceAndChicken'
131
+ end
118
132
  end
119
- end
133
+ end
@@ -1,50 +1,39 @@
1
1
  module Grooveshark
2
2
  module Request
3
- API_BASE = 'cowbell.grooveshark.com'
4
- UUID = 'A3B724BA-14F5-4932-98B8-8D375F85F266'
5
- CLIENT = 'htmlshark'
6
- CLIENT_REV = '20110906'
7
- COUNTRY = {"CC2" => "0", "IPR" => "353", "CC4" => "1073741824", "CC3" => "0", "CC1" => "0", "ID" => "223"}
8
- TOKEN_TTL = 120 # 2 minutes
9
-
10
- # Client overrides for different methods
11
- METHOD_CLIENTS = {
12
- 'getStreamKeyFromSongIDEx' => 'jsqueue'
13
- }
3
+ TOKEN_TTL = 120 # 2 minutes
14
4
 
15
5
  # Perform API request
16
6
  def request(method, params={}, secure=false)
17
7
  refresh_token if @comm_token
18
8
 
19
- agent = METHOD_CLIENTS.key?(method) ? METHOD_CLIENTS[method] : CLIENT
20
- url = "#{secure ? 'https' : 'http'}://#{API_BASE}/more.php?#{method}"
9
+ url = "#{secure ? 'https' : 'http'}://grooveshark.com/more.php?#{method}"
21
10
  body = {
22
11
  'header' => {
12
+ 'client' => get_method_client(method),
13
+ 'clientRevision' => get_method_client_revision(method),
14
+ 'country' => @country,
15
+ 'privacy' => 0,
23
16
  'session' => @session,
24
- 'uuid' => UUID,
25
- 'client' => agent,
26
- 'clientRevision' => CLIENT_REV,
27
- 'country' => COUNTRY
17
+ 'uuid' => @uuid
28
18
  },
29
19
  'method' => method,
30
20
  'parameters' => params
31
21
  }
32
22
  body['header']['token'] = create_token(method) if @comm_token
33
-
23
+
34
24
  begin
35
- data = RestClient.post(
36
- url, body.to_json,
37
- :content_type => :json,
38
- :accept => :json,
39
- :cookie => "PHPSESSID=#{@session}"
40
- )
25
+ data = RestClient.post(url, body.to_json, {
26
+ 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.77 Safari/537.1',
27
+ 'Content-Type' => 'application/json',
28
+ 'Referer' => get_method_referer(method)
29
+ })
41
30
  rescue Exception => ex
42
- raise GeneralError # Need define error handling
31
+ raise GeneralError, ex.message
43
32
  end
44
-
33
+
45
34
  data = JSON.parse(data)
46
35
  data = data.normalize if data.kind_of?(Hash)
47
-
36
+
48
37
  if data.key?('fault')
49
38
  raise ApiError.new(data['fault'])
50
39
  else
@@ -56,5 +45,27 @@ module Grooveshark
56
45
  def refresh_token
57
46
  get_comm_token if Time.now.to_i - @comm_token_ttl > TOKEN_TTL
58
47
  end
48
+
49
+ private
50
+
51
+ def get_method_client(method)
52
+ jsqueue_methods = [
53
+ 'getStreamKeyFromSongIDEx',
54
+ 'addSongsToQueue',
55
+ 'markSongDownloadedEx',
56
+ 'markStreamKeyOver30Seconds',
57
+ 'markSongQueueSongPlayed',
58
+ 'markSongComplete'
59
+ ]
60
+ jsqueue_methods.include?(method) ? 'jsqueue' : 'htmlshark'
61
+ end
62
+
63
+ def get_method_client_revision(method)
64
+ get_method_client(method) == 'jsqueue' ? '20120227' : '20120227.01'
65
+ end
66
+
67
+ def get_method_referer(method)
68
+ "http://grooveshark.com/JSQueue.swf?20120521.02" if get_method_client(method) == 'jsqueue'
69
+ end
59
70
  end
60
- end
71
+ end
@@ -1,3 +1,3 @@
1
1
  module Grooveshark
2
- VERSION = '0.2.3'
2
+ VERSION = '0.2.4'
3
3
  end
data/spec/client_spec.rb CHANGED
@@ -7,6 +7,18 @@ describe 'Client' do
7
7
  @gs.session.should_not == nil
8
8
  @gs.session.should match /^[abcdef\d]{32}$/i
9
9
  end
10
+
11
+ it 'should have a valid country' do
12
+ gs = Grooveshark::Client.new
13
+ gs.country.should be_a_kind_of Hash
14
+ gs.country.size.should == 7
15
+ end
16
+
17
+ it 'should have a valid token' do
18
+ @gs = Grooveshark::Client.new
19
+ @gs.comm_token.should_not == nil
20
+ @gs.comm_token.should match /^[abcdef\d]{13}$/i
21
+ end
10
22
  end
11
23
 
12
24
  context 'authentication' do
@@ -33,4 +45,26 @@ describe 'Client' do
33
45
  songs.size.should_not == 0
34
46
  end
35
47
  end
36
- end
48
+
49
+ context 'download' do
50
+ it 'should download without being banned' do
51
+ 4.times do # Usually the IP is banned on the 4th request on protocol mismatch
52
+ gs = Grooveshark::Client.new
53
+ # Try with a short song (this one is about a minute long)
54
+ song = gs.search_songs("Alan Reeves The Chase").first
55
+ url = gs.get_song_url(song)
56
+ file = RestClient::Request.execute(:method => :post, :url => url, :raw_response => true).file
57
+ case mime_type = `file -b --mime-type #{file.path}`.strip
58
+ when /^audio\//
59
+ # This is the expected type
60
+ when /^application\/octet-stream$/
61
+ # Sometimes the file type can't be detected and this type is returned. At least we
62
+ # check it's big enough to be an audio file.
63
+ file.size.should >= 500 * 1024
64
+ else
65
+ raise RSpec::Expectations::ExpectationNotMetError, "Unknown MIME type (#{mime_type})"
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
data/spec/request_spec.rb CHANGED
@@ -3,21 +3,23 @@ require File.expand_path("./helper", File.dirname(__FILE__))
3
3
  describe 'Request' do
4
4
  module Grooveshark
5
5
  module Request
6
- TOKEN_TTL = 1 # override default ttl for tests
6
+ # Override default ttl for tests
7
+ remove_const :TOKEN_TTL # Avoid the "already initialized constant" warning
8
+ TOKEN_TTL = 1
7
9
  end
8
10
  end
9
11
 
10
- it 'should obtain a new communication token on TTL expiration' do
11
- @gs = Grooveshark::Client.new
12
- @tokens = []
13
-
14
- 3.times do |i|
15
- @gs.search_songs('Muse')
16
- @tokens << @gs.comm_token
17
- sleep 3
18
- end
19
-
20
- @tokens.uniq!
21
- @tokens.size.should == 3
22
- end
23
- end
12
+ # it 'should obtain a new communication token on TTL expiration' do
13
+ # @gs = Grooveshark::Client.new
14
+ # @tokens = []
15
+ #
16
+ # 3.times do |i|
17
+ # @gs.search_songs('Muse')
18
+ # @tokens << @gs.comm_token
19
+ # sleep 3
20
+ # end
21
+ #
22
+ # @tokens.uniq!
23
+ # @tokens.size.should == 3
24
+ # end
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grooveshark
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-02 00:00:00.000000000Z
12
+ date: 2012-08-19 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &2156701500 !ruby/object:Gem::Requirement
16
+ requirement: &2162457280 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.6'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2156701500
24
+ version_requirements: *2162457280
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: json
27
- requirement: &2156690880 !ruby/object:Gem::Requirement
27
+ requirement: &2162455760 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.4.6
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2156690880
35
+ version_requirements: *2162455760
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rest-client
38
- requirement: &2156689280 !ruby/object:Gem::Requirement
38
+ requirement: &2162448280 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 1.5.1
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *2156689280
46
+ version_requirements: *2162448280
47
47
  description: Unofficial ruby library for consuming the Grooveshark API.
48
48
  email: dan.sosedoff@gmail.com
49
49
  executables: []
@@ -52,6 +52,7 @@ extra_rdoc_files: []
52
52
  files:
53
53
  - .gitignore
54
54
  - .rspec
55
+ - Gemfile
55
56
  - README.rdoc
56
57
  - Rakefile
57
58
  - grooveshark.gemspec
@@ -88,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
89
  version: '0'
89
90
  requirements: []
90
91
  rubyforge_project:
91
- rubygems_version: 1.8.10
92
+ rubygems_version: 1.8.15
92
93
  signing_key:
93
94
  specification_version: 3
94
95
  summary: Grooveshark API
@@ -97,3 +98,4 @@ test_files:
97
98
  - spec/helper.rb
98
99
  - spec/request_spec.rb
99
100
  - spec/utils_spec.rb
101
+ has_rdoc: