rscrobbler 0.0.2 → 0.0.3
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/bin/generate_lastfm_auth_token +16 -0
- data/lib/rscrobbler.rb +161 -161
- metadata +6 -4
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rscrobbler'
|
4
|
+
|
5
|
+
begin
|
6
|
+
print "Last.fm username: "
|
7
|
+
LastFM.username = STDIN.gets.strip
|
8
|
+
|
9
|
+
print "Last.fm password: "
|
10
|
+
system "stty -echo"
|
11
|
+
puts "\nYour Last.fm auth token: #{LastFM.generate_auth_token(STDIN.gets.strip)}"
|
12
|
+
system "stty echo"
|
13
|
+
rescue NoMethodError, Interrupt
|
14
|
+
system "stty echo"
|
15
|
+
exit
|
16
|
+
end
|
data/lib/rscrobbler.rb
CHANGED
@@ -1,161 +1,161 @@
|
|
1
|
-
require 'digest/md5'
|
2
|
-
require 'libxml'
|
3
|
-
require 'net/http'
|
4
|
-
require 'uri'
|
5
|
-
|
6
|
-
$:.unshift(File.dirname(__FILE__))
|
7
|
-
|
8
|
-
require 'lastfm/album'
|
9
|
-
require 'lastfm/artist'
|
10
|
-
require 'lastfm/auth'
|
11
|
-
require 'lastfm/chart'
|
12
|
-
require 'lastfm/event'
|
13
|
-
require 'lastfm/geo'
|
14
|
-
require 'lastfm/group'
|
15
|
-
require 'lastfm/library'
|
16
|
-
require 'lastfm/playlist'
|
17
|
-
require 'lastfm/radio'
|
18
|
-
require 'lastfm/tag'
|
19
|
-
require 'lastfm/tasteometer'
|
20
|
-
require 'lastfm/track'
|
21
|
-
require 'lastfm/user'
|
22
|
-
require 'lastfm/venue'
|
23
|
-
|
24
|
-
module LastFM
|
25
|
-
VERSION = '0.0.
|
26
|
-
|
27
|
-
HOST = 'ws.audioscrobbler.com'
|
28
|
-
POST_URI = URI.parse("http://#{HOST}/2.0/")
|
29
|
-
|
30
|
-
class RequestError < StandardError; end
|
31
|
-
class AuthenticationError < StandardError; end
|
32
|
-
|
33
|
-
class << self
|
34
|
-
|
35
|
-
attr_accessor :api_key, :api_secret, :username, :auth_token
|
36
|
-
attr_reader :session_key, :logger
|
37
|
-
|
38
|
-
# Authenticate the service with provided login credentials. Use mobile
|
39
|
-
# authentication to avoid redirecting to the website to log in.
|
40
|
-
#
|
41
|
-
# @see http://www.last.fm/api/authspec last.fm auth spec
|
42
|
-
# @return [String] session key provided from authentication
|
43
|
-
def authenticate!
|
44
|
-
|
45
|
-
raise AuthenticationError, "Missing credential: #{cred}" unless LastFM.send(cred)
|
46
|
-
end
|
47
|
-
@session_key = Auth.get_mobile_session( username, auth_token ).find_first('session/key').content
|
48
|
-
end
|
49
|
-
|
50
|
-
# Has the service been authenticated?
|
51
|
-
#
|
52
|
-
# @return [Boolean] whether the service has been authenticated
|
53
|
-
def authenticated?
|
54
|
-
!!session_key
|
55
|
-
end
|
56
|
-
|
57
|
-
# Ensure the service has been authenticated; raise an error if it hasn't.
|
58
|
-
#
|
59
|
-
# @raise [AuthenticationError] if the service hasn't been authenticated.
|
60
|
-
def requires_authentication
|
61
|
-
raise AuthenticationError, 'LastFM Authentication Required' unless authenticated?
|
62
|
-
end
|
63
|
-
|
64
|
-
# Generate auth token from username and given password.
|
65
|
-
#
|
66
|
-
# @param [String] password password to use
|
67
|
-
# @return [String] md5 digest of the username and password
|
68
|
-
def generate_auth_token( password )
|
69
|
-
self.auth_token = Digest::MD5.hexdigest( username.dup << Digest::MD5.hexdigest(password) )
|
70
|
-
end
|
71
|
-
|
72
|
-
# Construct an HTTP GET call from params, and load the response into a LibXML Document.
|
73
|
-
#
|
74
|
-
# @param [String] method last.fm api method to call
|
75
|
-
# @param [optional, Boolean] secure whether sign the request with a method signature and session key
|
76
|
-
# (one exception being auth methods, which require a method signature but no session key)
|
77
|
-
# @param [Hash] params parameters to send, excluding method, api_key, api_sig, and sk
|
78
|
-
# @return [LibXML::XML::Document] xml document of the data contained in the response
|
79
|
-
# @raise [LastFMError] if the request fails
|
80
|
-
def get( method, secure = false, params = {} )
|
81
|
-
path = generate_path(method, secure, params)
|
82
|
-
logger.debug( "Last.fm HTTP GET: #{HOST+path}" ) if logger
|
83
|
-
response = Net::HTTP.get_response( HOST, path )
|
84
|
-
check_status( LibXML::XML::Parser.string( response.body ).parse )
|
85
|
-
end
|
86
|
-
|
87
|
-
# Construct an HTTP POST call from params, and check the response status.
|
88
|
-
#
|
89
|
-
# @param [String] method last.fm api method to call
|
90
|
-
# @param [Hash] params parameters to send, excluding method, api_key, api_sig, and sk
|
91
|
-
# @return [LibXML::XML::Document] xml document of the data contained in the response
|
92
|
-
# @raise [LastFMError] if the request fails
|
93
|
-
def post( method, params )
|
94
|
-
params = construct_params( method, :secure, params )
|
95
|
-
logger.debug( "Last.fm HTTP POST: #{POST_URI}, #{params.to_s}" ) if logger
|
96
|
-
response = Net::HTTP.post_form( POST_URI, params )
|
97
|
-
check_status( LibXML::XML::Parser.string( response.body ).parse )
|
98
|
-
end
|
99
|
-
|
100
|
-
private
|
101
|
-
|
102
|
-
# Check an XML document for status = failed, and throw a descriptive error if it's found.
|
103
|
-
#
|
104
|
-
# @param [LibXML::XML::Document] xml xml document to check for errors
|
105
|
-
# @return [LibXML::XML::Document] the xml document if no errors were found
|
106
|
-
# @raise [LastFMError] if an error is found
|
107
|
-
# @private
|
108
|
-
def check_status( xml )
|
109
|
-
raise RequestError, xml.find_first('error').content if xml.root.attributes['status'] == 'failed'
|
110
|
-
xml
|
111
|
-
end
|
112
|
-
|
113
|
-
# Normalize the parameter list by converting values to a string and removing any nils. Add method,
|
114
|
-
# api key, session key, and api signature parameters where necessary.
|
115
|
-
#
|
116
|
-
# @param [String] method last.fm api method
|
117
|
-
# @param [Boolean] secure whether to include session key and api signature in the parameters
|
118
|
-
# @param [Hash] params parameters to normalize and add to
|
119
|
-
# @return [Hash] complete, normalized parameters
|
120
|
-
def construct_params( method, secure, params )
|
121
|
-
params.delete_if{|k,v| v.nil? }
|
122
|
-
params.each{|k,v| params[k] = params[k].to_s }
|
123
|
-
params['method'] = method
|
124
|
-
params['api_key'] = api_key
|
125
|
-
params['sk'] = session_key if authenticated? && secure
|
126
|
-
params['api_sig'] = generate_method_signature( params ) if secure
|
127
|
-
params
|
128
|
-
end
|
129
|
-
|
130
|
-
# Generate the path for a particular method call given params.
|
131
|
-
#
|
132
|
-
# @param [String] method last.fm method to call
|
133
|
-
# @param [Boolean] secure whether to include session key and api signature in the call
|
134
|
-
# @param [optional, Hash] params parameters to include in the api call
|
135
|
-
# @return [String] path for the api call
|
136
|
-
# @private
|
137
|
-
def generate_path( method, secure, params={} )
|
138
|
-
params = construct_params( method, secure, params )
|
139
|
-
url = "/2.0/?method=#{params.delete('method')}"
|
140
|
-
params.keys.each do |key|
|
141
|
-
url << "&#{key}=#{params[key]}"
|
142
|
-
end
|
143
|
-
URI.encode(url)
|
144
|
-
end
|
145
|
-
|
146
|
-
# Generate a method signature based on given parameters.
|
147
|
-
#
|
148
|
-
# @param [Hash] parameters to combine into a method signature
|
149
|
-
# @return [String] method signature based on all the parameters
|
150
|
-
# @see http://www.last.fm/api/authspec#8
|
151
|
-
# @private
|
152
|
-
def generate_method_signature( params )
|
153
|
-
str = ''
|
154
|
-
params.keys.sort.each do |key|
|
155
|
-
str << key << params[key]
|
156
|
-
end
|
157
|
-
Digest::MD5.hexdigest( str << api_secret )
|
158
|
-
end
|
159
|
-
|
160
|
-
end
|
161
|
-
end
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'libxml'
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
$:.unshift(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
require 'lastfm/album'
|
9
|
+
require 'lastfm/artist'
|
10
|
+
require 'lastfm/auth'
|
11
|
+
require 'lastfm/chart'
|
12
|
+
require 'lastfm/event'
|
13
|
+
require 'lastfm/geo'
|
14
|
+
require 'lastfm/group'
|
15
|
+
require 'lastfm/library'
|
16
|
+
require 'lastfm/playlist'
|
17
|
+
require 'lastfm/radio'
|
18
|
+
require 'lastfm/tag'
|
19
|
+
require 'lastfm/tasteometer'
|
20
|
+
require 'lastfm/track'
|
21
|
+
require 'lastfm/user'
|
22
|
+
require 'lastfm/venue'
|
23
|
+
|
24
|
+
module LastFM
|
25
|
+
VERSION = '0.0.3'
|
26
|
+
|
27
|
+
HOST = 'ws.audioscrobbler.com'
|
28
|
+
POST_URI = URI.parse("http://#{HOST}/2.0/")
|
29
|
+
|
30
|
+
class RequestError < StandardError; end
|
31
|
+
class AuthenticationError < StandardError; end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
|
35
|
+
attr_accessor :api_key, :api_secret, :username, :auth_token
|
36
|
+
attr_reader :session_key, :logger
|
37
|
+
|
38
|
+
# Authenticate the service with provided login credentials. Use mobile
|
39
|
+
# authentication to avoid redirecting to the website to log in.
|
40
|
+
#
|
41
|
+
# @see http://www.last.fm/api/authspec last.fm auth spec
|
42
|
+
# @return [String] session key provided from authentication
|
43
|
+
def authenticate!
|
44
|
+
[:api_key, :api_secret, :username, :auth_token].each do |cred|
|
45
|
+
raise AuthenticationError, "Missing credential: #{cred}" unless LastFM.send(cred)
|
46
|
+
end
|
47
|
+
@session_key = Auth.get_mobile_session( username, auth_token ).find_first('session/key').content
|
48
|
+
end
|
49
|
+
|
50
|
+
# Has the service been authenticated?
|
51
|
+
#
|
52
|
+
# @return [Boolean] whether the service has been authenticated
|
53
|
+
def authenticated?
|
54
|
+
!!session_key
|
55
|
+
end
|
56
|
+
|
57
|
+
# Ensure the service has been authenticated; raise an error if it hasn't.
|
58
|
+
#
|
59
|
+
# @raise [AuthenticationError] if the service hasn't been authenticated.
|
60
|
+
def requires_authentication
|
61
|
+
raise AuthenticationError, 'LastFM Authentication Required' unless authenticated?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Generate auth token from username and given password.
|
65
|
+
#
|
66
|
+
# @param [String] password password to use
|
67
|
+
# @return [String] md5 digest of the username and password
|
68
|
+
def generate_auth_token( password )
|
69
|
+
self.auth_token = Digest::MD5.hexdigest( username.dup << Digest::MD5.hexdigest(password) )
|
70
|
+
end
|
71
|
+
|
72
|
+
# Construct an HTTP GET call from params, and load the response into a LibXML Document.
|
73
|
+
#
|
74
|
+
# @param [String] method last.fm api method to call
|
75
|
+
# @param [optional, Boolean] secure whether sign the request with a method signature and session key
|
76
|
+
# (one exception being auth methods, which require a method signature but no session key)
|
77
|
+
# @param [Hash] params parameters to send, excluding method, api_key, api_sig, and sk
|
78
|
+
# @return [LibXML::XML::Document] xml document of the data contained in the response
|
79
|
+
# @raise [LastFMError] if the request fails
|
80
|
+
def get( method, secure = false, params = {} )
|
81
|
+
path = generate_path(method, secure, params)
|
82
|
+
logger.debug( "Last.fm HTTP GET: #{HOST+path}" ) if logger
|
83
|
+
response = Net::HTTP.get_response( HOST, path )
|
84
|
+
check_status( LibXML::XML::Parser.string( response.body ).parse )
|
85
|
+
end
|
86
|
+
|
87
|
+
# Construct an HTTP POST call from params, and check the response status.
|
88
|
+
#
|
89
|
+
# @param [String] method last.fm api method to call
|
90
|
+
# @param [Hash] params parameters to send, excluding method, api_key, api_sig, and sk
|
91
|
+
# @return [LibXML::XML::Document] xml document of the data contained in the response
|
92
|
+
# @raise [LastFMError] if the request fails
|
93
|
+
def post( method, params )
|
94
|
+
params = construct_params( method, :secure, params )
|
95
|
+
logger.debug( "Last.fm HTTP POST: #{POST_URI}, #{params.to_s}" ) if logger
|
96
|
+
response = Net::HTTP.post_form( POST_URI, params )
|
97
|
+
check_status( LibXML::XML::Parser.string( response.body ).parse )
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Check an XML document for status = failed, and throw a descriptive error if it's found.
|
103
|
+
#
|
104
|
+
# @param [LibXML::XML::Document] xml xml document to check for errors
|
105
|
+
# @return [LibXML::XML::Document] the xml document if no errors were found
|
106
|
+
# @raise [LastFMError] if an error is found
|
107
|
+
# @private
|
108
|
+
def check_status( xml )
|
109
|
+
raise RequestError, xml.find_first('error').content if xml.root.attributes['status'] == 'failed'
|
110
|
+
xml
|
111
|
+
end
|
112
|
+
|
113
|
+
# Normalize the parameter list by converting values to a string and removing any nils. Add method,
|
114
|
+
# api key, session key, and api signature parameters where necessary.
|
115
|
+
#
|
116
|
+
# @param [String] method last.fm api method
|
117
|
+
# @param [Boolean] secure whether to include session key and api signature in the parameters
|
118
|
+
# @param [Hash] params parameters to normalize and add to
|
119
|
+
# @return [Hash] complete, normalized parameters
|
120
|
+
def construct_params( method, secure, params )
|
121
|
+
params.delete_if{|k,v| v.nil? }
|
122
|
+
params.each{|k,v| params[k] = params[k].to_s }
|
123
|
+
params['method'] = method
|
124
|
+
params['api_key'] = api_key
|
125
|
+
params['sk'] = session_key if authenticated? && secure
|
126
|
+
params['api_sig'] = generate_method_signature( params ) if secure
|
127
|
+
params
|
128
|
+
end
|
129
|
+
|
130
|
+
# Generate the path for a particular method call given params.
|
131
|
+
#
|
132
|
+
# @param [String] method last.fm method to call
|
133
|
+
# @param [Boolean] secure whether to include session key and api signature in the call
|
134
|
+
# @param [optional, Hash] params parameters to include in the api call
|
135
|
+
# @return [String] path for the api call
|
136
|
+
# @private
|
137
|
+
def generate_path( method, secure, params={} )
|
138
|
+
params = construct_params( method, secure, params )
|
139
|
+
url = "/2.0/?method=#{params.delete('method')}"
|
140
|
+
params.keys.each do |key|
|
141
|
+
url << "&#{key}=#{params[key]}"
|
142
|
+
end
|
143
|
+
URI.encode(url)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Generate a method signature based on given parameters.
|
147
|
+
#
|
148
|
+
# @param [Hash] parameters to combine into a method signature
|
149
|
+
# @return [String] method signature based on all the parameters
|
150
|
+
# @see http://www.last.fm/api/authspec#8
|
151
|
+
# @private
|
152
|
+
def generate_method_signature( params )
|
153
|
+
str = ''
|
154
|
+
params.keys.sort.each do |key|
|
155
|
+
str << key << params[key]
|
156
|
+
end
|
157
|
+
Digest::MD5.hexdigest( str << api_secret )
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rscrobbler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2011-10-25 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: libxml-ruby
|
16
|
-
requirement: &
|
16
|
+
requirement: &2153707780 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,11 +21,12 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2153707780
|
25
25
|
description: rscrobbler is a Ruby gem for accessing Last.fm's API (http://www.last.fm/api).
|
26
26
|
email:
|
27
27
|
- sgt.floydpepper@gmail.com
|
28
|
-
executables:
|
28
|
+
executables:
|
29
|
+
- generate_lastfm_auth_token
|
29
30
|
extensions: []
|
30
31
|
extra_rdoc_files: []
|
31
32
|
files:
|
@@ -45,6 +46,7 @@ files:
|
|
45
46
|
- lib/lastfm/user.rb
|
46
47
|
- lib/lastfm/venue.rb
|
47
48
|
- lib/rscrobbler.rb
|
49
|
+
- bin/generate_lastfm_auth_token
|
48
50
|
homepage: https://github.com/sgtFloyd/rscrobbler
|
49
51
|
licenses: []
|
50
52
|
post_install_message:
|