grooveshark 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,41 @@
1
+ !.gitignore
2
+ *.gem
3
+ *.rbc
4
+ *.sw[a-p]
5
+ *.tmproj
6
+ *.tmproject
7
+ *.un~
8
+ *~
9
+ .DS_Store
10
+ .Spotlight-V100
11
+ .Trashes
12
+ ._*
13
+ .bundle
14
+ .config
15
+ .directory
16
+ .elc
17
+ .redcar
18
+ .yardoc
19
+ /.emacs.desktop
20
+ /.emacs.desktop.lock
21
+ Desktop.ini
22
+ Gemfile.lock
23
+ Icon?
24
+ InstalledFiles
25
+ Session.vim
26
+ Thumbs.db
27
+ \#*\#
28
+ _yardoc
29
+ auto-save-list
30
+ coverage
31
+ doc/
32
+ lib/bundler/man
33
+ pkg
34
+ pkg/*
35
+ rdoc
36
+ spec/reports
37
+ test/tmp
38
+ test/version_tmp
39
+ tmp
40
+ tmtags
41
+ tramp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format=nested
3
+ --backtrace
data/README.rdoc ADDED
@@ -0,0 +1,159 @@
1
+ = Grooveshark API
2
+
3
+ Unofficial grooveshark API ruby library gives your ability to search and stream songs,
4
+ manage playlists, media library and favorites.
5
+ API was discovered using http proxy and does not pretend to be always valid due to website API changes.
6
+
7
+ = Installation
8
+
9
+ gem install grooveshark
10
+
11
+ = Getting Started
12
+
13
+ Lets first create a session.
14
+ Grooveshark session is a regular PHP session with expiration date of 7 days.
15
+
16
+ require 'rubygems'
17
+ require 'grooveshark'
18
+
19
+ client = Grooveshark::Client.new
20
+
21
+ To get session key just call
22
+
23
+ session = client.session
24
+
25
+ You can store this key for 7 days after creation and use it like this:
26
+
27
+ client = Grooveshark::Client.new(SESSION_KEY)
28
+
29
+ Now we can find some songs:
30
+
31
+ songs = client.search_songs('Nirvana')
32
+
33
+ songs.each do |s|
34
+ s.id # Song ID
35
+ s.name # Song name
36
+ s.artist # Song artist name
37
+ s.album # Song album name
38
+ s.duration # Song duration in seconds (not always present, 0 by default)
39
+ end
40
+
41
+ We got collection of songs. Check Song object for additional attributes.
42
+ In order to stream song we need to get the authorization
43
+
44
+ song = songs.first
45
+ url = client.get_song_url(song)
46
+
47
+ Given url is valid only for current session and cannot be shared or stored permanently.
48
+ Also, it probably violates terms of service.
49
+
50
+ = User Authentication
51
+
52
+ To get your user account you need to provide username and password.
53
+ If username or password is not valid InvalidAuthentication exception will be raised.
54
+
55
+ client = Grooveshark::Client.new
56
+
57
+ begin
58
+ user = client.login('username', 'password')
59
+ rescue InvalidAuthentication
60
+ puts "Oooops! Wrong username or password"
61
+ end
62
+
63
+ = Playlists and favorites
64
+
65
+ Get all user playlists
66
+
67
+ user.playlists.each do |p|
68
+ p.id # Playlist ID
69
+ p.name # Playlist name
70
+ p.about # Playlist description (empty by default)
71
+ end
72
+
73
+ Get user playlist
74
+
75
+ playlist = user.get_playlist(PLAYLIST_ID)
76
+
77
+ Get all playlist songs
78
+
79
+ playlist = user.get_playlist(ID)
80
+ playlist.load_songs
81
+ songs = playlist.songs
82
+
83
+ Rename existing playlist
84
+
85
+ playlist = user.get_playlist(ID)
86
+ playlist.rename('NEW NAME', 'NEW DESCRIPTION')
87
+
88
+ Delete existing user playlist
89
+
90
+ playlist = user.get_playlist(ID)
91
+ playlist.delete
92
+
93
+ Create a new playlist. First parameter is mandatory, description and songs are optional.
94
+ For songs you can provide array of Song objects or array of IDs.
95
+
96
+ songs = client.search_songs('Joe Satriani')
97
+ p = user.create_playlist('NAME', 'DESCRIPTION', songs)
98
+
99
+ Get user favorite songs
100
+
101
+ songs = user.favorites
102
+
103
+ Add song to favorites
104
+
105
+ user.add_favorite(song) # Song object or song ID
106
+
107
+ Remove song from favorites
108
+
109
+ user.remove_favorite(song) # Song object or song ID
110
+
111
+ = User library
112
+
113
+ Get all songs from library as a collection of Song objects
114
+
115
+ songs = user.library
116
+
117
+ Add songs to library
118
+
119
+ songs = client.search_songs('The Beatles')
120
+ user.library_add(songs)
121
+
122
+ Remove selected songs from library. Unfortunately mass-deletion is not supported by Grooveshark API. You will have to delete each song via separate method call.
123
+
124
+ song = user.library.first # Lest pick a first song in the library
125
+ user.library_remove(song)
126
+
127
+ = Explore community
128
+
129
+ Get all recently active users
130
+
131
+ client.recent_users
132
+
133
+ Find user by ID
134
+
135
+ client.get_user_by_id('ID')
136
+
137
+ Find user by username
138
+
139
+ client.get_user_by_username('username')
140
+
141
+ Fetch recent user activity
142
+
143
+ user = client.get_user_by_username('user')
144
+ user.feed
145
+
146
+ = Known issues
147
+
148
+ * Communication token gets rejected after some time. This timeframe is always different. Additional research didnt show any results.
149
+
150
+ = TODO
151
+
152
+ * Testing
153
+ * Library management coverage
154
+ * More methods
155
+
156
+ = Credits
157
+
158
+ * Dan Sosedoff - http://github.com/sosedoff
159
+ * Daniel Lamando - http://github.com/danopia
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "rspec/core/rake_task"
2
+
3
+ RSpec::Core::RakeTask.new(:test) do |spec|
4
+ spec.pattern = 'spec/*_spec.rb'
5
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/grooveshark/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "grooveshark"
6
+ s.version = Grooveshark::VERSION.dup
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
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
16
+ s.require_paths = ['lib']
17
+
18
+ s.add_development_dependency 'rspec', '~> 2.6'
19
+
20
+ s.add_runtime_dependency 'json', '>= 1.4.6'
21
+ s.add_runtime_dependency 'rest-client', '>= 1.5.1'
22
+ end
@@ -4,12 +4,20 @@ module Grooveshark
4
4
 
5
5
  attr_accessor :session, :comm_token
6
6
  attr_reader :user
7
+ attr_reader :comm_token_ttl
8
+
9
+ SALT = 'backToTheScienceLab'
10
+ METHOD_SALTS = {
11
+ 'getStreamKeyFromSongIDEx' => 'bewareOfBearsharktopus'
12
+ }
7
13
 
8
14
  def initialize(session=nil)
9
15
  @session = session || get_session
10
16
  get_comm_token
11
17
  end
12
18
 
19
+ protected
20
+
13
21
  # Obtain new session from Grooveshark
14
22
  def get_session
15
23
  resp = RestClient.get('http://listen.grooveshark.com')
@@ -18,18 +26,22 @@ module Grooveshark
18
26
 
19
27
  # Get communication token
20
28
  def get_comm_token
21
- @comm_token = nil # so that it doesn't send a token
29
+ @comm_token = nil
22
30
  @comm_token = request('getCommunicationToken', {:secretKey => Digest::MD5.hexdigest(@session)}, true)
31
+ @comm_token_ttl = Time.now.to_i
23
32
  end
24
33
 
25
34
  # Sign method
26
35
  def create_token(method)
27
36
  rnd = rand(256**3).to_s(16).rjust(6, '0')
28
- plain = [method, @comm_token, 'quitStealinMahShit', rnd].join(':')
37
+ salt = METHOD_SALTS.key?(method) ? METHOD_SALTS[method] : SALT
38
+ plain = [method, @comm_token, salt, rnd].join(':')
29
39
  hash = Digest::SHA1.hexdigest(plain)
30
40
  "#{rnd}#{hash}"
31
41
  end
32
42
 
43
+ public
44
+
33
45
  # Authenticate user
34
46
  def login(user, password)
35
47
  data = request('authenticateUser', {:username => user, :password => password}, true)
@@ -54,6 +66,13 @@ module Grooveshark
54
66
  def recent_users
55
67
  request('getRecentlyActiveUsers', {})['users'].map { |u| User.new(self, u) }
56
68
  end
69
+
70
+ # Get popular songs
71
+ # type => daily, monthly
72
+ def popular_songs(type='daily')
73
+ raise ArgumentError, 'Invalid type' unless ['daily', 'monthly'].include?(type)
74
+ request('popularGetSongs', {:type => type})['songs'].map { |s| Song.new(s) }
75
+ end
57
76
 
58
77
  # Perform search request for query
59
78
  def search(type, query)
@@ -2,4 +2,17 @@ module Grooveshark
2
2
  class InvalidAuthentication < Exception ; end
3
3
  class ReadOnlyAccess < Exception ; end
4
4
  class GeneralError < Exception ; end
5
+
6
+ class ApiError < Exception
7
+ attr_reader :code
8
+
9
+ def initialize(fault)
10
+ @code = fault['code']
11
+ @message = fault['message']
12
+ end
13
+
14
+ def to_s
15
+ "#{@code} - #{@message}"
16
+ end
17
+ end
5
18
  end
@@ -1,10 +1,11 @@
1
1
  module Grooveshark
2
2
  module Request
3
- API_BASE = 'cowbell.grooveshark.com'
4
- UUID = 'E2AB1A59-C6B7-480E-992A-55DE1699D7F8'
5
- CLIENT = 'htmlshark'
6
- CLIENT_REV = '20101012.37'
7
- COUNTRY = {"CC2" => "0","IPR" => "1","CC1" => "0","ID" => "1","CC4" => "0","CC3" => "0"}
3
+ API_BASE = 'cowbell.grooveshark.com'
4
+ UUID = 'A3B724BA-14F5-4932-98B8-8D375F85F266'
5
+ CLIENT = 'htmlshark'
6
+ CLIENT_REV = '20110606.04'
7
+ COUNTRY = {"CC2" => "0", "IPR" => "353", "CC4" => "1073741824", "CC3" => "0", "CC1" => "0", "ID" => "223"}
8
+ TOKEN_TTL = 120 # 2 minutes
8
9
 
9
10
  # Client overrides for different methods
10
11
  METHOD_CLIENTS = {
@@ -13,6 +14,8 @@ module Grooveshark
13
14
 
14
15
  # Perform API request
15
16
  def request(method, params={}, secure=false)
17
+ refresh_token if @comm_token
18
+
16
19
  agent = METHOD_CLIENTS.key?(method) ? METHOD_CLIENTS[method] : CLIENT
17
20
  url = "#{secure ? 'https' : 'http'}://#{API_BASE}/more.php?#{method}"
18
21
  body = {
@@ -41,7 +44,17 @@ module Grooveshark
41
44
 
42
45
  data = JSON.parse(data)
43
46
  data = data.normalize if data.kind_of?(Hash)
44
- return data['result'] unless data['fault']
47
+
48
+ if data.key?('fault')
49
+ raise ApiError.new(data['fault'])
50
+ else
51
+ data['result']
52
+ end
53
+ end
54
+
55
+ # Refresh communications token on ttl
56
+ def refresh_token
57
+ get_comm_token if Time.now.to_i - @comm_token_ttl > TOKEN_TTL
45
58
  end
46
59
  end
47
60
  end
@@ -2,8 +2,8 @@ module Grooveshark
2
2
  class Song
3
3
  attr_reader :data
4
4
  attr_reader :id, :artist_id, :album_id
5
- attr_reader :name, :artist, :album, :track
6
- attr_reader :duraion, :artwork, :playcount
5
+ attr_reader :name, :artist, :album, :track, :year
6
+ attr_reader :duration, :artwork, :playcount
7
7
 
8
8
  def initialize(data=nil)
9
9
  unless data.nil?
@@ -18,6 +18,7 @@ module Grooveshark
18
18
  @duration = data['estimate_duration']
19
19
  @artwork = data['cover_art_filename']
20
20
  @playcount = data['song_plays']
21
+ @year = data['year']
21
22
  end
22
23
  end
23
24
 
@@ -52,6 +52,11 @@ module Grooveshark
52
52
  @client.request('userRemoveSongFromLibrary', req)
53
53
  end
54
54
 
55
+ # Get library modification time
56
+ def library_ts_modified
57
+ @client.request('userGetLibraryTSModified', {:userID => @id})
58
+ end
59
+
55
60
  # --------------------------------------------------------------------------
56
61
  # User Playlists
57
62
  # --------------------------------------------------------------------------
@@ -69,6 +74,8 @@ module Grooveshark
69
74
  result.nil? ? nil : result.first
70
75
  end
71
76
 
77
+ alias :playlist :get_playlist
78
+
72
79
  # Create new user playlist
73
80
  def create_playlist(name, description='', songs=[])
74
81
  @client.request('createPlaylist', {
@@ -1,3 +1,3 @@
1
1
  module Grooveshark
2
- VERSION = '0.2.1'
2
+ VERSION = '0.2.2'
3
3
  end
@@ -0,0 +1,36 @@
1
+ require File.expand_path("./helper", File.dirname(__FILE__))
2
+
3
+ describe 'Client' do
4
+ context 'initialization' do
5
+ it 'should have a valid session' do
6
+ @gs = Grooveshark::Client.new
7
+ @gs.session.should_not == nil
8
+ @gs.session.should match /^[abcdef\d]{32}$/i
9
+ end
10
+ end
11
+
12
+ context 'authentication' do
13
+ it 'should raise InvalidAuthentication error for invalid credentials' do
14
+ @gs = Grooveshark::Client.new
15
+ lambda { @gs.login('invlid_user_name', 'invalid_password') }.should raise_error Grooveshark::InvalidAuthentication
16
+ end
17
+ end
18
+
19
+ context 'search' do
20
+ before(:all) do
21
+ @gs = Grooveshark::Client.new
22
+ end
23
+
24
+ it 'should return empty songs collection' do
25
+ songs = @gs.search_songs("@@@@@%%%%%%%@%@%%@")
26
+ songs.should be_a_kind_of Array
27
+ songs.size.should == 0
28
+ end
29
+
30
+ it 'should return songs collection' do
31
+ songs = @gs.search_songs('Nirvana')
32
+ songs.should be_a_kind_of Array
33
+ songs.size.should_not == 0
34
+ end
35
+ end
36
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,3 @@
1
+ $:.unshift File.expand_path("../..", __FILE__)
2
+
3
+ require 'grooveshark'
@@ -0,0 +1,23 @@
1
+ require File.expand_path("./helper", File.dirname(__FILE__))
2
+
3
+ describe 'Request' do
4
+ module Grooveshark
5
+ module Request
6
+ TOKEN_TTL = 1 # override default ttl for tests
7
+ end
8
+ end
9
+
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
@@ -0,0 +1,35 @@
1
+ require File.expand_path("./helper", File.dirname(__FILE__))
2
+
3
+ describe 'String' do
4
+ it 'should normalize attributes' do
5
+ vars = ['key_name', 'keyName', 'KeyName', 'KeyNAME']
6
+ target = 'key_name'
7
+ vars.each { |s| s.normalize_attribute.should == target }
8
+ end
9
+ end
10
+
11
+ describe 'Hash' do
12
+ it 'should normalize simple keys' do
13
+ h = {'KeyName' => 'Value'}.normalize
14
+
15
+ h.key?('KeyName').should == false
16
+ h.key?('key_name').should == true
17
+ end
18
+
19
+ it 'should normalize symbol keys' do
20
+ h = {:KeyName => 'Value'}.normalize
21
+ h.key?(:KeyName).should == false
22
+ h.key?('key_name').should == true
23
+ end
24
+
25
+ it 'should normalize nested data' do
26
+ h = {
27
+ 'keyA' => {'nestedKey' => 'Value'},
28
+ 'keyB' => [{'arrKey' => 'Value'}]
29
+ }.normalize
30
+
31
+ h['key_a'].key?('nested_key').should == true
32
+ h['key_b'].class.should == Array
33
+ h['key_b'].first.key?('arr_key').should == true
34
+ end
35
+ end
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grooveshark
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
5
4
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
5
+ version: 0.2.2
11
6
  platform: ruby
12
7
  authors:
13
8
  - Dan Sosedoff
@@ -15,41 +10,42 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-01-26 00:00:00 -06:00
13
+ date: 2011-08-01 00:00:00 -05:00
19
14
  default_executable:
20
15
  dependencies:
21
16
  - !ruby/object:Gem::Dependency
22
- name: json
17
+ name: rspec
23
18
  prerelease: false
24
19
  requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: "2.6"
25
+ type: :development
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
25
31
  none: false
26
32
  requirements:
27
33
  - - ">="
28
34
  - !ruby/object:Gem::Version
29
- hash: 11
30
- segments:
31
- - 1
32
- - 4
33
- - 6
34
35
  version: 1.4.6
35
36
  type: :runtime
36
- version_requirements: *id001
37
+ version_requirements: *id002
37
38
  - !ruby/object:Gem::Dependency
38
39
  name: rest-client
39
40
  prerelease: false
40
- requirement: &id002 !ruby/object:Gem::Requirement
41
+ requirement: &id003 !ruby/object:Gem::Requirement
41
42
  none: false
42
43
  requirements:
43
44
  - - ">="
44
45
  - !ruby/object:Gem::Version
45
- hash: 1
46
- segments:
47
- - 1
48
- - 5
49
- - 1
50
46
  version: 1.5.1
51
47
  type: :runtime
52
- version_requirements: *id002
48
+ version_requirements: *id003
53
49
  description: Unofficial ruby library for consuming the Grooveshark API.
54
50
  email: dan.sosedoff@gmail.com
55
51
  executables: []
@@ -59,15 +55,24 @@ extensions: []
59
55
  extra_rdoc_files: []
60
56
 
61
57
  files:
58
+ - .gitignore
59
+ - .rspec
60
+ - README.rdoc
61
+ - Rakefile
62
+ - grooveshark.gemspec
62
63
  - lib/grooveshark.rb
63
- - lib/grooveshark/version.rb
64
- - lib/grooveshark/utils.rb
65
64
  - lib/grooveshark/client.rb
66
65
  - lib/grooveshark/errors.rb
67
66
  - lib/grooveshark/playlist.rb
68
67
  - lib/grooveshark/request.rb
69
68
  - lib/grooveshark/song.rb
70
69
  - lib/grooveshark/user.rb
70
+ - lib/grooveshark/utils.rb
71
+ - lib/grooveshark/version.rb
72
+ - spec/client_spec.rb
73
+ - spec/helper.rb
74
+ - spec/request_spec.rb
75
+ - spec/utils_spec.rb
71
76
  has_rdoc: true
72
77
  homepage: http://github.com/sosedoff/grooveshark
73
78
  licenses: []
@@ -82,25 +87,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
87
  requirements:
83
88
  - - ">="
84
89
  - !ruby/object:Gem::Version
85
- hash: 3
86
- segments:
87
- - 0
88
90
  version: "0"
89
91
  required_rubygems_version: !ruby/object:Gem::Requirement
90
92
  none: false
91
93
  requirements:
92
94
  - - ">="
93
95
  - !ruby/object:Gem::Version
94
- hash: 3
95
- segments:
96
- - 0
97
96
  version: "0"
98
97
  requirements: []
99
98
 
100
99
  rubyforge_project:
101
- rubygems_version: 1.4.1
100
+ rubygems_version: 1.6.2
102
101
  signing_key:
103
102
  specification_version: 3
104
103
  summary: Grooveshark API
105
- test_files: []
106
-
104
+ test_files:
105
+ - spec/client_spec.rb
106
+ - spec/helper.rb
107
+ - spec/request_spec.rb
108
+ - spec/utils_spec.rb