rdio 0.0.96 → 0.0.98b
Sign up to get free protection for your applications and to get access to all the features.
- data/README +10 -2
- data/lib/rdio.rb +18 -1
- data/lib/rdio/base.rb +24 -4
- data/lib/rdio/oauth.rb +3 -3
- data/lib/rdio/simple_om.rb +151 -0
- data/lib/rdio/simple_rdio.rb +87 -0
- data/lib/rdio/types.rb +1 -1
- metadata +15 -10
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
-*-
|
1
|
+
-*- org -*-
|
2
2
|
|
3
3
|
* ABOUT
|
4
4
|
|
@@ -35,7 +35,7 @@ variables to the appropriate values:
|
|
35
35
|
RDIO_KEY
|
36
36
|
RDIO_SECRET
|
37
37
|
|
38
|
-
You must also serialize an
|
38
|
+
There is a sample .rc file in dot-rdio. You must also serialize an authorized access token to the file
|
39
39
|
.rdio_access_token. This can be done with:
|
40
40
|
|
41
41
|
make .rdio_access_token
|
@@ -44,3 +44,11 @@ You must also serialize an authrozied access token to the file
|
|
44
44
|
|
45
45
|
./create_access_token
|
46
46
|
|
47
|
+
There is a sample in dot-rdio_access_token.
|
48
|
+
|
49
|
+
* CONTRIBUTORS
|
50
|
+
|
51
|
+
Thanks for the pull requests that have been merged to the following:
|
52
|
+
|
53
|
+
- Scott Hyndman ( https://github.com/shyndman )
|
54
|
+
- Rick Fletcher ( https://github.com/rfletcher )
|
data/lib/rdio.rb
CHANGED
@@ -20,6 +20,17 @@ module Rdio
|
|
20
20
|
attr_accessor :log_methods
|
21
21
|
attr_accessor :log_symbols
|
22
22
|
attr_accessor :log_posts
|
23
|
+
attr_accessor :log_fill
|
24
|
+
#
|
25
|
+
# Specific setting to turn off warnings like the following:
|
26
|
+
#
|
27
|
+
# Couldn't find symbol: radio_key => rr31531 for type: Rdio::Artist
|
28
|
+
#
|
29
|
+
# during tests, because we see it constantly and isn't really a
|
30
|
+
# problem. By default it is true, but it is turned off for all
|
31
|
+
# tests.
|
32
|
+
attr_accessor :log_couldnt_find_symbols
|
33
|
+
|
23
34
|
def log(str)
|
24
35
|
logger.debug { str }
|
25
36
|
end
|
@@ -29,6 +40,8 @@ module Rdio
|
|
29
40
|
self.log_methods = false
|
30
41
|
self.log_symbols = false
|
31
42
|
self.log_posts = false
|
43
|
+
self.log_couldnt_find_symbols = false
|
44
|
+
self.log_fill = false
|
32
45
|
|
33
46
|
@logger ||= ::Logger.new(STDERR)
|
34
47
|
@api = nil
|
@@ -89,7 +102,11 @@ require 'rdio/datatypes'
|
|
89
102
|
require 'rdio/types'
|
90
103
|
require 'rdio/call'
|
91
104
|
|
92
|
-
#
|
105
|
+
# simple additions
|
106
|
+
require 'rdio/simple_om'
|
107
|
+
require 'rdio/simple_rdio'
|
108
|
+
|
109
|
+
# Silly syntax so you can say Rd::io.<method>
|
93
110
|
module Rd
|
94
111
|
|
95
112
|
# Returns the shared Rdio::Api instance from Rdio::api
|
data/lib/rdio/base.rb
CHANGED
@@ -148,7 +148,17 @@ module Rdio
|
|
148
148
|
|
149
149
|
def fill(x)
|
150
150
|
return self if not x
|
151
|
+
if Rdio::log_fill
|
152
|
+
Rdio::log "#{self.class}.fill #{x.class} : #{x}"
|
153
|
+
end
|
151
154
|
syms_to_types = Rdio::symbols_to_types || {}
|
155
|
+
#
|
156
|
+
# https://github.com/spudtrooper/rdiorb/issues/9: Certain rubys
|
157
|
+
# don't declare 'each' on String. In this case and others where
|
158
|
+
# the key is a mapped type and the resulting body isn't a Hash,
|
159
|
+
# make x a Hash
|
160
|
+
#
|
161
|
+
x = {x => nil} if not x.is_a? Hash
|
152
162
|
x.each do |k,v|
|
153
163
|
sym = Rdio::camel2underscores(k).to_sym
|
154
164
|
#
|
@@ -158,9 +168,9 @@ module Rdio
|
|
158
168
|
#
|
159
169
|
type = syms_to_types[sym]
|
160
170
|
if Rdio::log_symbols
|
161
|
-
Rdio::log "#{self.class}.#{sym} => #{type}"
|
171
|
+
Rdio::log "#{self.class}.#{sym} => #{type} v=#{v.class}"
|
162
172
|
end
|
163
|
-
if type
|
173
|
+
if type and v.is_a? Enumerable
|
164
174
|
#
|
165
175
|
# Allow simple types that are used for arrays
|
166
176
|
#
|
@@ -177,8 +187,10 @@ module Rdio
|
|
177
187
|
sym_eq = (Rdio::camel2underscores(k)+'=').to_sym
|
178
188
|
self.send sym_eq,o
|
179
189
|
rescue Exception => e
|
180
|
-
Rdio::
|
181
|
-
"
|
190
|
+
if Rdio::log_couldnt_find_symbols
|
191
|
+
Rdio::logger.warn "Couldn't find symbol: " +
|
192
|
+
"#{sym} => #{o} for type: #{self.class}"
|
193
|
+
end
|
182
194
|
end
|
183
195
|
end
|
184
196
|
self
|
@@ -297,7 +309,15 @@ module Rdio
|
|
297
309
|
if Rdio::log_posts
|
298
310
|
Rdio::log "Post to url=#{url} method=#{method} args=#{args}"
|
299
311
|
end
|
312
|
+
#
|
313
|
+
# For backwards compatibility the response may have a 'body'
|
314
|
+
# attribute or a tuple could be returned. Handle both cases.
|
315
|
+
#
|
300
316
|
resp,data = access_token(requires_auth).post url,new_args
|
317
|
+
begin
|
318
|
+
return resp.body
|
319
|
+
rescue
|
320
|
+
end
|
301
321
|
return data
|
302
322
|
end
|
303
323
|
|
data/lib/rdio/oauth.rb
CHANGED
@@ -32,9 +32,9 @@ module Rdio
|
|
32
32
|
require 'launchy'
|
33
33
|
Launchy.open url
|
34
34
|
rescue Exception => e
|
35
|
-
Rdio::
|
36
|
-
Rdio::
|
37
|
-
Rdio::
|
35
|
+
Rdio::logger.error e
|
36
|
+
Rdio::logger.info 'Install the \'launchy\' gem to avoid this error'
|
37
|
+
Rdio::logger.info 'Trying system \'open\''
|
38
38
|
system 'open',url
|
39
39
|
end
|
40
40
|
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# om is oauth-mini - a simple implementation of a useful subset of OAuth.
|
2
|
+
# It's designed to be useful and reusable but not general purpose.
|
3
|
+
#
|
4
|
+
# (c) 2011 Rdio Inc
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
# A simple OAuth client implementation. Do less better.
|
24
|
+
# Here are the restrictions:
|
25
|
+
# - only HMAC-SHA1 is supported
|
26
|
+
# - only WWW-Authentiate form signatures are generated
|
27
|
+
#
|
28
|
+
# To sign a request:
|
29
|
+
# auth = om([consumer_key,consumer_secret], url, params)
|
30
|
+
# send Authorization: <auth>
|
31
|
+
# when POSTing <params> to <url>
|
32
|
+
# Optional additional arguments are:
|
33
|
+
# token = [oauth_token, oauth_token_secret]
|
34
|
+
# method = "POST"
|
35
|
+
# realm = "Realm-for-authorization-header"
|
36
|
+
#
|
37
|
+
# Changes by jeff@jeffpalm.com:
|
38
|
+
#
|
39
|
+
# - added module Rdio
|
40
|
+
#
|
41
|
+
|
42
|
+
require 'uri'
|
43
|
+
require 'cgi'
|
44
|
+
require 'digest'
|
45
|
+
require 'digest/sha1'
|
46
|
+
|
47
|
+
module Rdio
|
48
|
+
|
49
|
+
class OM
|
50
|
+
|
51
|
+
def self.om(consumer, url, post_params, token=nil, method='POST', realm=nil)
|
52
|
+
# A one-shot simple OAuth signature generator
|
53
|
+
|
54
|
+
# the method must be upper-case
|
55
|
+
method.upcase!
|
56
|
+
|
57
|
+
# we want params as an Array of name / value pairs
|
58
|
+
if post_params.is_a?(Array)
|
59
|
+
params = post_params
|
60
|
+
else
|
61
|
+
params = post_params.collect
|
62
|
+
end
|
63
|
+
|
64
|
+
# normalize the URL
|
65
|
+
url = URI.parse(url)
|
66
|
+
# scheme is lower-case
|
67
|
+
url.scheme = url.scheme.downcase
|
68
|
+
# remove username & password
|
69
|
+
url.user = url.password = nil
|
70
|
+
# host is lowercase
|
71
|
+
url.host.downcase!
|
72
|
+
|
73
|
+
# add URL params to the params
|
74
|
+
if url.query
|
75
|
+
CGI.parse(url.query).each { |k,vs| vs.each { |v| params.push([k,v]) } }
|
76
|
+
end
|
77
|
+
|
78
|
+
# remove the params and fragment
|
79
|
+
url.query = nil
|
80
|
+
url.fragment = nil
|
81
|
+
|
82
|
+
# add OAuth params
|
83
|
+
params = params + [
|
84
|
+
['oauth_version', '1.0'],
|
85
|
+
['oauth_timestamp', Time.now.to_i.to_s],
|
86
|
+
['oauth_nonce', rand(1000000).to_s],
|
87
|
+
['oauth_signature_method', 'HMAC-SHA1'],
|
88
|
+
['oauth_consumer_key', consumer[0]],
|
89
|
+
]
|
90
|
+
|
91
|
+
# the consumer secret is the first half of the HMAC-SHA1 key
|
92
|
+
hmac_key = consumer[1] + '&'
|
93
|
+
|
94
|
+
if token != nil
|
95
|
+
# include a token in params
|
96
|
+
params.push ['oauth_token', token[0]]
|
97
|
+
# and the token secret in the HMAC-SHA1 key
|
98
|
+
hmac_key += token[1]
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sort lexicographically, first after key, then after value.
|
102
|
+
params.sort!
|
103
|
+
# escape the key/value pairs and combine them into a string
|
104
|
+
normalized_params = (params.collect {|p| percent_encode(p[0])+'='+percent_encode(p[1])}).join '&'
|
105
|
+
|
106
|
+
# build the signature base string
|
107
|
+
signature_base_string = (percent_encode(method) +
|
108
|
+
'&' + percent_encode(url.to_s) +
|
109
|
+
'&' + percent_encode(normalized_params))
|
110
|
+
|
111
|
+
# HMAC-SHA1
|
112
|
+
hmac = Digest::HMAC.new(hmac_key, Digest::SHA1)
|
113
|
+
hmac.update(signature_base_string)
|
114
|
+
|
115
|
+
# Calculate the digest base 64. Drop the trailing \n
|
116
|
+
oauth_signature = [hmac.digest].pack('m0').strip
|
117
|
+
|
118
|
+
# Build the Authorization header
|
119
|
+
if realm
|
120
|
+
authorization_params = [['realm', realm]]
|
121
|
+
else
|
122
|
+
authorization_params = []
|
123
|
+
end
|
124
|
+
authorization_params.push(['oauth_signature', oauth_signature])
|
125
|
+
|
126
|
+
# we only want certain params in the auth header
|
127
|
+
oauth_params = ['oauth_version', 'oauth_timestamp', 'oauth_nonce',
|
128
|
+
'oauth_signature_method', 'oauth_signature',
|
129
|
+
'oauth_consumer_key', 'oauth_token']
|
130
|
+
authorization_params.concat(params.select { |param| nil != oauth_params.index(param[0]) })
|
131
|
+
|
132
|
+
return 'OAuth ' + (authorization_params.collect {|param| '%s="%s"' % param}).join(', ')
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.percent_encode(s)
|
136
|
+
chars = s.chars.map do |c|
|
137
|
+
if ((c >= '0' and c <= '9') or
|
138
|
+
(c >= 'A' and c <= 'Z') or
|
139
|
+
(c >= 'a' and c <= 'z') or
|
140
|
+
c == '-' or c == '.' or c == '_' or c == '~')
|
141
|
+
c
|
142
|
+
else
|
143
|
+
'%%%02X' % c[0]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
chars.join
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# (c) 2011 Rdio Inc
|
2
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
3
|
+
# of this software and associated documentation files (the "Software"), to deal
|
4
|
+
# in the Software without restriction, including without limitation the rights
|
5
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
6
|
+
# copies of the Software, and to permit persons to whom the Software is
|
7
|
+
# furnished to do so, subject to the following conditions:
|
8
|
+
#
|
9
|
+
# The above copyright notice and this permission notice shall be included in
|
10
|
+
# all copies or substantial portions of the Software.
|
11
|
+
#
|
12
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
13
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
14
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
15
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
16
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
17
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
18
|
+
# THE SOFTWARE.
|
19
|
+
#
|
20
|
+
# Changes by jeff@jeffpalm.com:
|
21
|
+
#
|
22
|
+
# - added module Rdio
|
23
|
+
# - changed class named from Rdio to SimpleRdio
|
24
|
+
#
|
25
|
+
|
26
|
+
require 'uri'
|
27
|
+
require 'net/http'
|
28
|
+
require 'cgi'
|
29
|
+
require 'json'
|
30
|
+
|
31
|
+
module Rdio
|
32
|
+
|
33
|
+
class SimpleRdio
|
34
|
+
# the consumer and token can be accessed
|
35
|
+
attr_accessor :consumer, :token
|
36
|
+
|
37
|
+
def initialize(consumer, token=nil)
|
38
|
+
@consumer = consumer
|
39
|
+
@token = token
|
40
|
+
end
|
41
|
+
|
42
|
+
def begin_authentication(callback_url)
|
43
|
+
# request a request token from the server
|
44
|
+
response = signed_post('http://api.rdio.com/oauth/request_token',
|
45
|
+
{'oauth_callback' => callback_url})
|
46
|
+
# parse the response
|
47
|
+
parsed = CGI.parse(response)
|
48
|
+
# save the token
|
49
|
+
@token = [parsed['oauth_token'][0], parsed['oauth_token_secret'][0]]
|
50
|
+
# return an URL that the user can use to authorize this application
|
51
|
+
return parsed['login_url'][0] + '?oauth_token=' + parsed['oauth_token'][0]
|
52
|
+
end
|
53
|
+
|
54
|
+
def complete_authentication(verifier)
|
55
|
+
# request an access token
|
56
|
+
response = signed_post('http://api.rdio.com/oauth/access_token',
|
57
|
+
{'oauth_verifier' => verifier})
|
58
|
+
# parse the response
|
59
|
+
parsed = CGI.parse(response)
|
60
|
+
# save the token
|
61
|
+
@token = [parsed['oauth_token'][0], parsed['oauth_token_secret'][0]]
|
62
|
+
end
|
63
|
+
|
64
|
+
def call(method, params={})
|
65
|
+
# make a copy of the dict
|
66
|
+
params = params.clone
|
67
|
+
# put the method in the dict
|
68
|
+
params['method'] = method
|
69
|
+
# call to the server and parse the response
|
70
|
+
return JSON.load(signed_post('http://api.rdio.com/1/', params))
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def signed_post(url, params)
|
76
|
+
auth = OM::om(@consumer, url, params, @token)
|
77
|
+
url = URI.parse(url)
|
78
|
+
http = Net::HTTP.new(url.host, url.port)
|
79
|
+
req = Net::HTTP::Post.new(url.path, {'Authorization' => auth})
|
80
|
+
req.set_form_data(params)
|
81
|
+
res = http.request(req)
|
82
|
+
return res.body
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
data/lib/rdio/types.rb
CHANGED
@@ -30,7 +30,7 @@ module Rdio
|
|
30
30
|
|
31
31
|
attr_accessor :album_keys
|
32
32
|
|
33
|
-
# Returns an array of
|
33
|
+
# Returns an array of Artist for the query and other params
|
34
34
|
def self.search(query,never_or=nil,extras=nil,start=nil,count=nil)
|
35
35
|
extras = Rdio::add_to_array extras,'artists'
|
36
36
|
Search.search query,Artist,never_or,extras,start,count
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 419
|
5
|
+
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
|
9
|
+
- 98
|
10
|
+
- b
|
11
|
+
version: 0.0.98b
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- Jeffrey Palm
|
@@ -15,7 +16,7 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date:
|
19
|
+
date: 2012-03-03 00:00:00 Z
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: oauth
|
@@ -52,6 +53,8 @@ files:
|
|
52
53
|
- lib/rdio/datatypes.rb
|
53
54
|
- lib/rdio/types.rb
|
54
55
|
- lib/rdio/call.rb
|
56
|
+
- lib/rdio/simple_om.rb
|
57
|
+
- lib/rdio/simple_rdio.rb
|
55
58
|
homepage: http://github.com/spudtrooper/rdiorb
|
56
59
|
licenses: []
|
57
60
|
|
@@ -72,15 +75,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
76
|
none: false
|
74
77
|
requirements:
|
75
|
-
- - "
|
78
|
+
- - ">"
|
76
79
|
- !ruby/object:Gem::Version
|
77
|
-
hash:
|
80
|
+
hash: 25
|
78
81
|
segments:
|
79
|
-
-
|
80
|
-
|
82
|
+
- 1
|
83
|
+
- 3
|
84
|
+
- 1
|
85
|
+
version: 1.3.1
|
81
86
|
requirements:
|
82
87
|
- launchy gem to use authorized calls
|
83
|
-
rubyforge_project:
|
88
|
+
rubyforge_project: rdio
|
84
89
|
rubygems_version: 1.8.6
|
85
90
|
signing_key:
|
86
91
|
specification_version: 2
|