grooveshark 0.2.11 → 0.2.12
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.rspec +2 -2
- data/.rubocop.yml +6 -0
- data/.travis.yml +11 -0
- data/Gemfile +2 -1
- data/README.md +5 -3
- data/Rakefile +11 -4
- data/grooveshark.gemspec +24 -17
- data/lib/grooveshark/broadcast.rb +5 -3
- data/lib/grooveshark/client.rb +67 -46
- data/lib/grooveshark/errors.rb +11 -4
- data/lib/grooveshark/playlist.rb +24 -20
- data/lib/grooveshark/song.rb +17 -15
- data/lib/grooveshark/user.rb +43 -27
- data/lib/grooveshark/utils.rb +14 -12
- data/lib/grooveshark/version.rb +2 -1
- data/spec/grooveshark/broadcast_spec.rb +41 -0
- data/spec/grooveshark/client_spec.rb +80 -0
- data/spec/grooveshark/errors_spec.rb +15 -0
- data/spec/grooveshark/playlist_spec.rb +98 -0
- data/spec/grooveshark/song_spec.rb +43 -0
- data/spec/grooveshark/user_spec.rb +233 -0
- data/spec/grooveshark/utils_spec.rb +38 -0
- data/spec/helper.rb +7 -2
- metadata +105 -39
- data/spec/broadcast_spec.rb +0 -39
- data/spec/client_spec.rb +0 -85
- data/spec/utils_spec.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 265e469716c245b3c4421d9b1f4a97e9808c3371
|
4
|
+
data.tar.gz: 4e1d82a8c9b0c9e3c12f40d200052ad4d7398c43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a9732cce0fdaf0edafd248efae89676f7003c1b4c6fa02fabbaadcb19d78cde55bb2fc69ab488abe124f5a3417f66cbec617b531782ca4177f8ce730b3a0a9f
|
7
|
+
data.tar.gz: b7d86f7a0ae7cc1170b3e62ad4bbadbaad2014472eca7c054abbcc56e5cb3db9f181e7e9d8b27ce1c9c66b230cf83243e96802ec2edf69b38bd44cf8e47f5448
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
--color
|
2
|
-
--format=
|
3
|
-
--backtrace
|
2
|
+
--format=documentation
|
3
|
+
--backtrace
|
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
# Grooveshark API
|
1
|
+
# Grooveshark API [](https://travis-ci.org/sosedoff/grooveshark)
|
2
2
|
|
3
3
|
Unofficial grooveshark API ruby library gives your ability to search and stream songs,
|
4
4
|
manage playlists, media library and favorites.
|
5
5
|
API was discovered using http proxy and does not pretend to be always valid due to website API changes.
|
6
6
|
|
7
|
+
**Looking for maintainer. Submit a new issue if interested.**
|
8
|
+
|
7
9
|
## Installation
|
8
10
|
|
9
11
|
Install gem from rubygems:
|
@@ -242,14 +244,14 @@ user.feed
|
|
242
244
|
Run test suite:
|
243
245
|
|
244
246
|
```
|
245
|
-
rake
|
247
|
+
bundle exec rake
|
246
248
|
```
|
247
249
|
|
248
250
|
## Contact
|
249
251
|
|
250
252
|
- Dan Sosedoff
|
251
253
|
- dan.sosedoff@gmail.com
|
252
|
-
- http://twitter.com/
|
254
|
+
- http://twitter.com/sosedoff
|
253
255
|
|
254
256
|
## License
|
255
257
|
|
data/Rakefile
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
require 'bundler/gem_tasks'
|
3
|
-
require
|
3
|
+
require 'rspec/core/rake_task'
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
begin
|
6
|
+
require 'rubocop/rake_task'
|
7
|
+
|
8
|
+
RuboCop::RakeTask.new(:rubocop)
|
9
|
+
rescue LoadError
|
10
|
+
puts 'Rubocop is needed to run this task.'
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
7
14
|
t.verbose = false
|
8
15
|
end
|
9
16
|
|
10
|
-
task :
|
17
|
+
task default: [:rubocop, :spec]
|
data/grooveshark.gemspec
CHANGED
@@ -1,25 +1,32 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
require File.expand_path(
|
2
|
+
require File.expand_path('../lib/grooveshark/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
|
-
s.name =
|
5
|
+
s.name = 'grooveshark'
|
6
6
|
s.version = Grooveshark::VERSION
|
7
|
-
s.description =
|
8
|
-
s.summary =
|
9
|
-
s.authors = [
|
10
|
-
s.email =
|
11
|
-
s.homepage =
|
12
|
-
s.license =
|
7
|
+
s.description = 'Unofficial ruby library for consuming the Grooveshark API.'
|
8
|
+
s.summary = 'Grooveshark API'
|
9
|
+
s.authors = ['Dan Sosedoff']
|
10
|
+
s.email = 'dan.sosedoff@gmail.com'
|
11
|
+
s.homepage = 'http://github.com/sosedoff/grooveshark'
|
12
|
+
s.license = 'MIT'
|
13
13
|
|
14
14
|
s.files = `git ls-files`.split("\n")
|
15
15
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map
|
17
|
-
|
18
|
-
|
19
|
-
s.
|
20
|
-
|
21
|
-
|
22
|
-
s.add_runtime_dependency
|
23
|
-
s.add_runtime_dependency
|
24
|
-
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map do |f|
|
17
|
+
File.basename(f)
|
18
|
+
end
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
|
21
|
+
s.add_runtime_dependency 'json', '>= 1.4.6'
|
22
|
+
s.add_runtime_dependency 'rest-client', '>= 1.5.1'
|
23
|
+
s.add_runtime_dependency 'uuid', '~> 2.0'
|
24
|
+
|
25
|
+
s.add_development_dependency 'rake', '~>10.0'
|
26
|
+
s.add_development_dependency 'rack-test', '~>0.6'
|
27
|
+
s.add_development_dependency 'rspec', '~>3.0'
|
28
|
+
s.add_development_dependency 'simplecov', '~>0.9'
|
29
|
+
s.add_development_dependency 'fakefs', '~>0.5'
|
30
|
+
|
31
|
+
s.add_development_dependency 'rubocop', '~>0.25' if RUBY_VERSION != '1.9.2'
|
25
32
|
end
|
@@ -1,16 +1,18 @@
|
|
1
|
+
# Grooveshark module
|
1
2
|
module Grooveshark
|
3
|
+
# Broadcast class
|
2
4
|
class Broadcast
|
3
5
|
attr_reader :id, :user_ids
|
4
6
|
attr_reader :is_active, :is_playing
|
5
7
|
attr_reader :name, :usernames
|
6
8
|
attr_reader :active_song, :next_song
|
7
9
|
|
8
|
-
def initialize(client, broadcast_id=nil, data=nil)
|
10
|
+
def initialize(client, broadcast_id = nil, data = nil)
|
9
11
|
@client = client
|
10
12
|
|
11
13
|
if broadcast_id
|
12
14
|
@id = broadcast_id
|
13
|
-
reload_status
|
15
|
+
reload_status
|
14
16
|
elsif data
|
15
17
|
@id = data['broadcast_id'] || broadcast_id
|
16
18
|
@name = data['name']
|
@@ -28,7 +30,7 @@ module Grooveshark
|
|
28
30
|
def reload_status
|
29
31
|
initialize(
|
30
32
|
@client, nil,
|
31
|
-
@client.request('broadcastStatusPoll',
|
33
|
+
@client.request('broadcastStatusPoll', broadcastID: @id)
|
32
34
|
)
|
33
35
|
true
|
34
36
|
rescue
|
data/lib/grooveshark/client.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
# Grooveshark module
|
1
2
|
module Grooveshark
|
3
|
+
# Client class
|
2
4
|
class Client
|
3
5
|
attr_accessor :session, :comm_token
|
4
6
|
attr_reader :user, :comm_token_ttl, :country
|
@@ -6,60 +8,71 @@ module Grooveshark
|
|
6
8
|
def initialize(params = {})
|
7
9
|
@ttl = params[:ttl] || 120 # 2 minutes
|
8
10
|
@uuid = UUID.new.generate.upcase
|
9
|
-
|
11
|
+
token_data
|
10
12
|
end
|
11
13
|
|
12
14
|
# Authenticate user
|
13
15
|
def login(user, password)
|
14
|
-
data = request('authenticateUser',
|
16
|
+
data = request('authenticateUser',
|
17
|
+
{ username: user, password: password },
|
18
|
+
true)
|
15
19
|
@user = User.new(self, data)
|
16
|
-
|
17
|
-
|
20
|
+
fail InvalidAuthentication, 'Wrong username or password!' if @user.id == 0
|
21
|
+
|
22
|
+
@user
|
18
23
|
end
|
19
24
|
|
20
25
|
# Find user by ID
|
21
26
|
def get_user_by_id(id)
|
22
|
-
resp = request('getUserByID',
|
23
|
-
resp['
|
27
|
+
resp = request('getUserByID', userID: id)['user']
|
28
|
+
resp['user_id'].nil? ? nil : User.new(self, resp)
|
24
29
|
end
|
25
30
|
|
26
31
|
# Find user by username
|
27
32
|
def get_user_by_username(name)
|
28
|
-
resp = request('getUserByUsername',
|
29
|
-
resp['
|
33
|
+
resp = request('getUserByUsername', username: name)['user']
|
34
|
+
resp['user_id'].nil? ? nil : User.new(self, resp)
|
30
35
|
end
|
31
36
|
|
32
37
|
# Get recently active users
|
33
38
|
def recent_users
|
34
|
-
request('getRecentlyActiveUsers', {})['users']
|
39
|
+
request('getRecentlyActiveUsers', {})['users']
|
40
|
+
.map do |u|
|
41
|
+
User.new(self, u)
|
42
|
+
end
|
35
43
|
end
|
36
44
|
|
37
45
|
# Get popular songs
|
38
46
|
# type => daily, monthly
|
39
|
-
def popular_songs(type='daily')
|
40
|
-
|
41
|
-
request('popularGetSongs',
|
47
|
+
def popular_songs(type = 'daily')
|
48
|
+
fail ArgumentError, 'Invalid type' unless %w(daily monthly).include?(type)
|
49
|
+
request('popularGetSongs', type: type)['songs'].map { |s| Song.new(s) }
|
42
50
|
end
|
43
51
|
|
44
52
|
# Get top broadcasts
|
45
53
|
# count => specifies how many broadcasts to get
|
46
|
-
def top_broadcasts(count=10)
|
54
|
+
def top_broadcasts(count = 10)
|
47
55
|
top_broadcasts = []
|
48
|
-
request('getTopBroadcastsCombined').each do |key,
|
56
|
+
request('getTopBroadcastsCombined').each do |key, _val|
|
49
57
|
broadcast_id = key.split(':')[1]
|
50
58
|
top_broadcasts.push(Broadcast.new(self, broadcast_id))
|
51
59
|
count -= 1
|
52
|
-
if count == 0
|
53
|
-
break
|
54
|
-
end
|
60
|
+
break if count == 0
|
55
61
|
end
|
56
|
-
|
62
|
+
|
63
|
+
top_broadcasts
|
57
64
|
end
|
58
65
|
|
59
66
|
# Perform search request for query
|
60
67
|
def search(type, query)
|
61
|
-
results =
|
62
|
-
|
68
|
+
results = []
|
69
|
+
search = request('getResultsFromSearch', type: type, query: query)
|
70
|
+
results = search['result'].map do |data|
|
71
|
+
next Song.new data if type == 'Songs'
|
72
|
+
next Playlist.new data if type == 'Playlists'
|
73
|
+
data
|
74
|
+
end if search.key?('result')
|
75
|
+
results
|
63
76
|
end
|
64
77
|
|
65
78
|
# Perform songs search request for query
|
@@ -69,20 +82,20 @@ module Grooveshark
|
|
69
82
|
|
70
83
|
# Return raw response for songs search request
|
71
84
|
def search_songs_pure(query)
|
72
|
-
request('getSearchResultsEx',
|
85
|
+
request('getSearchResultsEx', type: 'Songs', query: query)
|
73
86
|
end
|
74
87
|
|
75
88
|
# Get stream authentication by song ID
|
76
89
|
def get_stream_auth_by_songid(song_id)
|
77
|
-
result = request('getStreamKeyFromSongIDEx',
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
90
|
+
result = request('getStreamKeyFromSongIDEx',
|
91
|
+
'type' => 0,
|
92
|
+
'prefetch' => false,
|
93
|
+
'songID' => song_id,
|
94
|
+
'country' => @country,
|
95
|
+
'mobile' => false)
|
96
|
+
if result == []
|
97
|
+
fail GeneralError, 'No data for this song. ' \
|
98
|
+
'Maybe Grooveshark banned your IP.'
|
86
99
|
end
|
87
100
|
result
|
88
101
|
end
|
@@ -103,16 +116,18 @@ module Grooveshark
|
|
103
116
|
get_song_url_by_id(song.id)
|
104
117
|
end
|
105
118
|
|
106
|
-
def
|
119
|
+
def token_data
|
107
120
|
response = RestClient.get('http://grooveshark.com')
|
108
121
|
|
109
|
-
preload_regex = /gsPreloadAjax\(\{url: '\/preload.php\?(.*)&hash=' \+ clientPage\}\)/
|
122
|
+
preload_regex = /gsPreloadAjax\(\{url: '\/preload.php\?(.*)&hash=' \+ clientPage\}\)/ # rubocop:disable Metrics/LineLength
|
110
123
|
preload_id = response.to_s.scan(preload_regex).flatten.first
|
111
|
-
preload_url = "http://grooveshark.com/preload.php?#{preload_id}
|
124
|
+
preload_url = "http://grooveshark.com/preload.php?#{preload_id}" \
|
125
|
+
'&getCommunicationToken=1&hash=%2F'
|
112
126
|
preload_response = RestClient.get(preload_url)
|
113
127
|
|
114
|
-
token_data_json = preload_response.to_s
|
115
|
-
|
128
|
+
token_data_json = preload_response.to_s
|
129
|
+
.scan(/window.tokenData = (.*);/).flatten.first
|
130
|
+
fail GeneralError, 'token data not found' unless token_data_json
|
116
131
|
token_data = JSON.parse(token_data_json)
|
117
132
|
@comm_token = token_data['getCommunicationToken']
|
118
133
|
@comm_token_ttl = Time.now.to_i
|
@@ -124,7 +139,7 @@ module Grooveshark
|
|
124
139
|
# Sign method
|
125
140
|
def create_token(method)
|
126
141
|
rnd = get_random_hex_chars(6)
|
127
|
-
salt =
|
142
|
+
salt = 'gooeyFlubber'
|
128
143
|
plain = [method, @comm_token, salt, rnd].join(':')
|
129
144
|
hash = Digest::SHA1.hexdigest(plain)
|
130
145
|
"#{rnd}#{hash}"
|
@@ -135,11 +150,7 @@ module Grooveshark
|
|
135
150
|
(0...length).map { chars[rand(chars.length)] }.join
|
136
151
|
end
|
137
152
|
|
138
|
-
|
139
|
-
def request(method, params={}, secure=false)
|
140
|
-
refresh_token if @comm_token
|
141
|
-
|
142
|
-
url = "#{secure ? 'https' : 'http'}://grooveshark.com/more.php?#{method}"
|
153
|
+
def body(method, params)
|
143
154
|
body = {
|
144
155
|
'header' => {
|
145
156
|
'client' => 'mobileshark',
|
@@ -153,17 +164,27 @@ module Grooveshark
|
|
153
164
|
'parameters' => params
|
154
165
|
}
|
155
166
|
body['header']['token'] = create_token(method) if @comm_token
|
167
|
+
body
|
168
|
+
end
|
169
|
+
|
170
|
+
# Perform API request
|
171
|
+
def request(method, params = {}, secure = false)
|
172
|
+
refresh_token if @comm_token
|
173
|
+
|
174
|
+
url = "#{secure ? 'https' : 'http'}://grooveshark.com/more.php?#{method}"
|
156
175
|
begin
|
157
|
-
data = RestClient.post(url,
|
158
|
-
|
176
|
+
data = RestClient.post(url,
|
177
|
+
body(method, params).to_json,
|
178
|
+
'Content-Type' => 'application/json')
|
179
|
+
rescue StandardError => ex
|
159
180
|
raise GeneralError, ex.message
|
160
181
|
end
|
161
182
|
|
162
183
|
data = JSON.parse(data)
|
163
|
-
data = data.normalize if data.
|
184
|
+
data = data.normalize if data.is_a?(Hash)
|
164
185
|
|
165
186
|
if data.key?('fault')
|
166
|
-
|
187
|
+
fail ApiError, data['fault']
|
167
188
|
else
|
168
189
|
data['result']
|
169
190
|
end
|
@@ -171,7 +192,7 @@ module Grooveshark
|
|
171
192
|
|
172
193
|
# Refresh communications token on ttl
|
173
194
|
def refresh_token
|
174
|
-
|
195
|
+
token_data if Time.now.to_i - @comm_token_ttl > @ttl
|
175
196
|
end
|
176
197
|
end
|
177
198
|
end
|
data/lib/grooveshark/errors.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
|
+
# Grooveshark module
|
1
2
|
module Grooveshark
|
2
|
-
class InvalidAuthentication < Exception
|
3
|
-
|
4
|
-
|
3
|
+
class InvalidAuthentication < Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
class ReadOnlyAccess < Exception
|
7
|
+
end
|
8
|
+
|
9
|
+
class GeneralError < Exception
|
10
|
+
end
|
5
11
|
|
12
|
+
# Api error
|
6
13
|
class ApiError < Exception
|
7
14
|
attr_reader :code
|
8
15
|
|
@@ -15,4 +22,4 @@ module Grooveshark
|
|
15
22
|
"#{@code} - #{@message}"
|
16
23
|
end
|
17
24
|
end
|
18
|
-
end
|
25
|
+
end
|
data/lib/grooveshark/playlist.rb
CHANGED
@@ -1,44 +1,48 @@
|
|
1
|
+
# Grooveshark module
|
1
2
|
module Grooveshark
|
3
|
+
# Playlist class
|
2
4
|
class Playlist
|
3
5
|
attr_reader :id, :user_id
|
4
6
|
attr_reader :name, :about, :picture, :username
|
5
7
|
attr_reader :songs
|
6
8
|
|
7
|
-
def initialize(client, data=nil, user_id=nil)
|
9
|
+
def initialize(client, data = nil, user_id = nil)
|
8
10
|
@client = client
|
9
11
|
@songs = []
|
10
12
|
|
11
|
-
if data
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
13
|
+
return if data.nil?
|
14
|
+
@id = data['playlist_id']
|
15
|
+
@name = data['name']
|
16
|
+
@about = data['about']
|
17
|
+
@picture = data['picture']
|
18
|
+
@user_id = data['user_id'] || user_id
|
19
|
+
@username = data['user_name']
|
19
20
|
end
|
20
21
|
|
21
22
|
# Fetch playlist songs
|
22
23
|
def load_songs
|
23
|
-
@songs =
|
24
|
-
@
|
24
|
+
@songs = []
|
25
|
+
playlist = @client.request('getPlaylistByID', playlistID: @id)
|
26
|
+
@songs = playlist['songs'].map! do |s|
|
27
|
+
Song.new(s)
|
28
|
+
end if playlist.key?('songs')
|
29
|
+
@songs
|
25
30
|
end
|
26
31
|
|
27
32
|
# Rename playlist
|
28
33
|
def rename(name, description)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
34
|
+
@client.request('renamePlaylist', playlistID: @id, playlistName: name)
|
35
|
+
@client.request('setPlaylistAbout', playlistID: @id, about: description)
|
36
|
+
@name = name
|
37
|
+
@about = description
|
38
|
+
true
|
39
|
+
rescue
|
40
|
+
false
|
37
41
|
end
|
38
42
|
|
39
43
|
# Delete existing playlist
|
40
44
|
def delete
|
41
|
-
@client.request('deletePlaylist',
|
45
|
+
@client.request('deletePlaylist', playlistID: @id, name: @name)
|
42
46
|
end
|
43
47
|
end
|
44
48
|
end
|