grooveshark 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: