rdio 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README +26 -0
- data/lib/rdio.rb +87 -0
- data/lib/rdio/api.rb +339 -0
- data/lib/rdio/base.rb +314 -0
- data/lib/rdio/datatypes.rb +287 -0
- data/lib/rdio/oauth.rb +54 -0
- data/lib/rdio/types.rb +527 -0
- metadata +75 -0
data/lib/rdio/base.rb
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class Object
|
5
|
+
def to_k
|
6
|
+
return self
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Rdio
|
11
|
+
|
12
|
+
# string -> string
|
13
|
+
#
|
14
|
+
# Converts camel-case string to underscore-delimited one.
|
15
|
+
#
|
16
|
+
def camel2underscores(s)
|
17
|
+
return s if not s
|
18
|
+
return s if s == ''
|
19
|
+
def decapitilize(s)
|
20
|
+
s[0,1].downcase + s[1,s.length-1].to_s
|
21
|
+
end
|
22
|
+
s = decapitilize s
|
23
|
+
while s.match /([A-Z]+)/
|
24
|
+
s = s.gsub /#{$1}/,'_'+ decapitilize($1)
|
25
|
+
end
|
26
|
+
s
|
27
|
+
end
|
28
|
+
|
29
|
+
def convert_args(args)
|
30
|
+
res = {}
|
31
|
+
args.each do |k,v|
|
32
|
+
if v.is_a? Array
|
33
|
+
v = keys v
|
34
|
+
else
|
35
|
+
v = v.to_k
|
36
|
+
end
|
37
|
+
res[k] = v
|
38
|
+
end
|
39
|
+
return res
|
40
|
+
end
|
41
|
+
|
42
|
+
def keys(objs)
|
43
|
+
objs.map {|x| x.to_k}
|
44
|
+
end
|
45
|
+
|
46
|
+
# object -> value
|
47
|
+
#
|
48
|
+
# Converts v which is probably a string, to a primitive value, so we
|
49
|
+
# can have primitives other than strings as attributes of BaseObjs.
|
50
|
+
#
|
51
|
+
def to_o(v)
|
52
|
+
if not v
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
s = v.to_s
|
56
|
+
if not s
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
if s == 'nil'
|
60
|
+
return nil
|
61
|
+
end
|
62
|
+
if s =~ /^\d+/
|
63
|
+
return s.to_i
|
64
|
+
end
|
65
|
+
if s =~ /^\d+\.?\d*$/
|
66
|
+
return s.to_f
|
67
|
+
end
|
68
|
+
if s == 'true'
|
69
|
+
return true
|
70
|
+
end
|
71
|
+
if s == 'false'
|
72
|
+
return false
|
73
|
+
end
|
74
|
+
if s =~ /^\[.*\]$/
|
75
|
+
s = s.gsub /^\[/,''
|
76
|
+
s = s.gsub /\]$/,''
|
77
|
+
return s.split(',').map {|x| to_o x}
|
78
|
+
end
|
79
|
+
return s
|
80
|
+
end
|
81
|
+
|
82
|
+
# Override this to declare how certain attributes are constructed.
|
83
|
+
# This is done at the end of types.rb.
|
84
|
+
class << self
|
85
|
+
attr_accessor :symbols_to_types
|
86
|
+
end
|
87
|
+
self.symbols_to_types = {}
|
88
|
+
|
89
|
+
# ----------------------------------------------------------------------
|
90
|
+
# Base class for remote objects. They contain an api instance, and
|
91
|
+
# also have a fill method that will set the values to the
|
92
|
+
# appropriate values
|
93
|
+
# ----------------------------------------------------------------------
|
94
|
+
class ApiObj
|
95
|
+
attr_reader :api
|
96
|
+
|
97
|
+
def initialize(api)
|
98
|
+
@api = api
|
99
|
+
end
|
100
|
+
|
101
|
+
def fill(x)
|
102
|
+
syms_to_types = Rdio::symbols_to_types || {}
|
103
|
+
x.each do |k,v|
|
104
|
+
sym = camel2underscores(k).to_sym
|
105
|
+
#
|
106
|
+
# If we have an actual type for this symbol, then use that
|
107
|
+
# type to construct this value. Otherwise, it's just a
|
108
|
+
# primitive type
|
109
|
+
#
|
110
|
+
type = syms_to_types[sym]
|
111
|
+
if Rdio::log_symbols
|
112
|
+
Rdio::log "#{self.class}.#{sym} => #{type}"
|
113
|
+
end
|
114
|
+
if type
|
115
|
+
#
|
116
|
+
# Allow simple types that are used for arrays
|
117
|
+
#
|
118
|
+
if v.is_a? Array
|
119
|
+
o = v.map do |x|
|
120
|
+
obj = type.new api
|
121
|
+
obj.fill x
|
122
|
+
obj
|
123
|
+
end
|
124
|
+
else
|
125
|
+
o = type.new api
|
126
|
+
o.fill v
|
127
|
+
end
|
128
|
+
else
|
129
|
+
o = to_o v
|
130
|
+
end
|
131
|
+
begin
|
132
|
+
sym_eq = (camel2underscores(k)+'=').to_sym
|
133
|
+
self.send sym_eq,o
|
134
|
+
rescue Exception => e
|
135
|
+
STDERR.puts "Couldn't find symbol: " +
|
136
|
+
"#{sym} => #{o} for type: #{self.class}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
# ----------------------------------------------------------------------
|
144
|
+
# An Object that is based on json with simple types
|
145
|
+
# ----------------------------------------------------------------------
|
146
|
+
class JSONObj
|
147
|
+
|
148
|
+
attr_reader :json
|
149
|
+
|
150
|
+
def initialize(json)
|
151
|
+
@json = json
|
152
|
+
end
|
153
|
+
|
154
|
+
def method_missing(method,args={})
|
155
|
+
meth = method.to_s
|
156
|
+
res = @json[meth]
|
157
|
+
#
|
158
|
+
# Maybe this should be a number
|
159
|
+
#
|
160
|
+
if meth =~ /_count$/ or meth =~ /^num_/
|
161
|
+
begin
|
162
|
+
return res.to_i
|
163
|
+
rescue Exception => e
|
164
|
+
STDERR.puts "#{meth} (err) #{e}"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
return res
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# ----------------------------------------------------------------------
|
172
|
+
# An ApiObj with a 'key'
|
173
|
+
# ----------------------------------------------------------------------
|
174
|
+
class BaseObj < ApiObj
|
175
|
+
|
176
|
+
attr_accessor :key
|
177
|
+
|
178
|
+
def initialize(api)
|
179
|
+
super api
|
180
|
+
end
|
181
|
+
|
182
|
+
# Compares only by key
|
183
|
+
def eql?(that)
|
184
|
+
self.class.equal?(that.class) and
|
185
|
+
self.key.equal?(that.key)
|
186
|
+
end
|
187
|
+
|
188
|
+
def to_k
|
189
|
+
key
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
# ----------------------------------------------------------------------
|
195
|
+
# Basis for making web-service calls and constructing the values.
|
196
|
+
# Subclasses should declare the api by calling 'call',
|
197
|
+
# 'return_object', and 'create_object'
|
198
|
+
# ----------------------------------------------------------------------
|
199
|
+
class BaseApi
|
200
|
+
|
201
|
+
def initialize(key,secret)
|
202
|
+
@oauth = RdioOAuth.new key,secret
|
203
|
+
@access_token_auth = nil
|
204
|
+
@access_token_no_auth = nil
|
205
|
+
end
|
206
|
+
|
207
|
+
def call(method,args,requires_auth=false)
|
208
|
+
#
|
209
|
+
# Convert object with keys just to use their keys
|
210
|
+
#
|
211
|
+
args = convert_args args
|
212
|
+
if Rdio::log_methods
|
213
|
+
Rdio::log "Called method: #{method}(#{args}) : auth=#{requires_auth}"
|
214
|
+
end
|
215
|
+
new_args = {}
|
216
|
+
new_args['method'] = method
|
217
|
+
args.each do |k,v|
|
218
|
+
new_args[k] = v.to_k.to_s
|
219
|
+
end
|
220
|
+
url = '/1/'
|
221
|
+
if Rdio::log_posts
|
222
|
+
Rdio::log "Post to url=#{url} method=#{method} args=#{args}"
|
223
|
+
end
|
224
|
+
resp,data = access_token(requires_auth).post url,new_args
|
225
|
+
return data
|
226
|
+
end
|
227
|
+
|
228
|
+
def return_object(type,method,args,requires_auth=false)
|
229
|
+
json = call method,args,requires_auth
|
230
|
+
if Rdio::log_json
|
231
|
+
Rdio::log json
|
232
|
+
end
|
233
|
+
create_object type,json
|
234
|
+
end
|
235
|
+
|
236
|
+
private
|
237
|
+
|
238
|
+
def fill_obj(type,x)
|
239
|
+
res = type.new self
|
240
|
+
res.fill x
|
241
|
+
return res
|
242
|
+
end
|
243
|
+
|
244
|
+
def create_object(type,json_str,keys_to_objects=false)
|
245
|
+
begin
|
246
|
+
_create_object(type,json_str,keys_to_objects)
|
247
|
+
rescue Exception => e
|
248
|
+
STDERR.puts "create_json (err): #{e}"
|
249
|
+
STDERR.puts "create_json (str): #{json_str}"
|
250
|
+
raise e
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def unwrap_json(json_str)
|
255
|
+
obj = JSON.parse json_str
|
256
|
+
status = obj['status']
|
257
|
+
if status == 'error'
|
258
|
+
raise Exception.new obj['message']
|
259
|
+
end
|
260
|
+
if status == 'ok'
|
261
|
+
return obj['result']
|
262
|
+
end
|
263
|
+
raise Exception.new status
|
264
|
+
end
|
265
|
+
|
266
|
+
def _create_object(type,json_str,keys_to_objects=false)
|
267
|
+
if type == true
|
268
|
+
return true
|
269
|
+
end
|
270
|
+
if type == false
|
271
|
+
return false
|
272
|
+
end
|
273
|
+
result = unwrap_json(json_str)
|
274
|
+
if type == Boolean or type == String or
|
275
|
+
type == Fixnum or type == Float
|
276
|
+
return false if not result
|
277
|
+
return to_o result
|
278
|
+
end
|
279
|
+
#
|
280
|
+
# A mild hack, but for get the result is a hash of keys to
|
281
|
+
# objects, in this case return an array of those objects
|
282
|
+
#
|
283
|
+
if keys_to_objects
|
284
|
+
result = result.values
|
285
|
+
end
|
286
|
+
#
|
287
|
+
# This could be an array (TODO: could not be general enough)
|
288
|
+
#
|
289
|
+
if result.is_a? Array
|
290
|
+
res = result.map {|x| fill_obj type,x}
|
291
|
+
else
|
292
|
+
res = fill_obj type,result
|
293
|
+
end
|
294
|
+
return res
|
295
|
+
end
|
296
|
+
|
297
|
+
def access_token(requires_auth)
|
298
|
+
if requires_auth
|
299
|
+
if not @access_token_auth
|
300
|
+
@access_token_auth = @oauth.access_token requires_auth
|
301
|
+
end
|
302
|
+
res = @access_token_auth
|
303
|
+
else
|
304
|
+
if not @access_token_no_auth
|
305
|
+
@access_token_no_auth = @oauth.access_token requires_auth
|
306
|
+
end
|
307
|
+
res = @access_token_no_auth
|
308
|
+
end
|
309
|
+
res
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
@@ -0,0 +1,287 @@
|
|
1
|
+
module Rdio
|
2
|
+
|
3
|
+
class ArtistData < BaseObj
|
4
|
+
def initialize(api)
|
5
|
+
super api
|
6
|
+
end
|
7
|
+
|
8
|
+
# the name of the artist
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
# the object type, always "r"
|
12
|
+
attr_accessor :type
|
13
|
+
|
14
|
+
# the URL of the artist on the Rdio web site
|
15
|
+
attr_accessor :url
|
16
|
+
|
17
|
+
# the number of tracks that the artist has on Rdio
|
18
|
+
attr_accessor :length
|
19
|
+
|
20
|
+
# an image for the artist
|
21
|
+
attr_accessor :icon
|
22
|
+
|
23
|
+
# an image for the artist, partial URL
|
24
|
+
attr_accessor :base_icon
|
25
|
+
|
26
|
+
# is an Rdio Station available for the artist?
|
27
|
+
attr_accessor :has_radio
|
28
|
+
|
29
|
+
# a short URL for the artist page
|
30
|
+
attr_accessor :short_url
|
31
|
+
|
32
|
+
# the number of albums that the artist has on Rdio
|
33
|
+
attr_accessor :album_count
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class AlbumData < BaseObj
|
38
|
+
def initialize(api)
|
39
|
+
super api
|
40
|
+
end
|
41
|
+
|
42
|
+
# the name of the album
|
43
|
+
attr_accessor :name
|
44
|
+
|
45
|
+
# the type of the object, always "a"
|
46
|
+
attr_accessor :type
|
47
|
+
|
48
|
+
# the URL to the cover art for the album
|
49
|
+
attr_accessor :icon
|
50
|
+
|
51
|
+
# the URL to the cover art for the album
|
52
|
+
attr_accessor :base_icon
|
53
|
+
|
54
|
+
# the URL of the album on the Rdio site
|
55
|
+
attr_accessor :url
|
56
|
+
|
57
|
+
# the name of the artist that released the album
|
58
|
+
attr_accessor :artist
|
59
|
+
|
60
|
+
# the URL of the artist that released the album on the Rdio site
|
61
|
+
attr_accessor :artist_url
|
62
|
+
|
63
|
+
# is the album explicit?
|
64
|
+
attr_accessor :is_explicit
|
65
|
+
|
66
|
+
# is the album clean?
|
67
|
+
attr_accessor :is_clean
|
68
|
+
|
69
|
+
# number of tracks on the album
|
70
|
+
attr_accessor :length
|
71
|
+
|
72
|
+
# the key of the artist that released the album
|
73
|
+
attr_accessor :artist_key
|
74
|
+
|
75
|
+
# the keys of the tracks on the album
|
76
|
+
attr_accessor :track_keys
|
77
|
+
|
78
|
+
# the price of the album in US cents
|
79
|
+
attr_accessor :price
|
80
|
+
|
81
|
+
# the album can be streamed
|
82
|
+
attr_accessor :can_stream
|
83
|
+
|
84
|
+
# the album can be previewed
|
85
|
+
attr_accessor :can_sample
|
86
|
+
|
87
|
+
# the album can be sync to mobile devices
|
88
|
+
attr_accessor :can_tether
|
89
|
+
|
90
|
+
# a short URL for the album
|
91
|
+
attr_accessor :short_url
|
92
|
+
|
93
|
+
# the URL of a SWF to embed the album
|
94
|
+
attr_accessor :embed_url
|
95
|
+
|
96
|
+
# the release date of the album, human readable
|
97
|
+
attr_accessor :display_date
|
98
|
+
|
99
|
+
# the release date of the album
|
100
|
+
attr_accessor :release_date
|
101
|
+
|
102
|
+
# the duration of the album in seconds
|
103
|
+
attr_accessor :duration
|
104
|
+
|
105
|
+
# the release date of the album in ISO format
|
106
|
+
attr_accessor :release_date_iso
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
class TrackData < BaseObj
|
111
|
+
def initialize(api)
|
112
|
+
super api
|
113
|
+
end
|
114
|
+
|
115
|
+
# the name of the track
|
116
|
+
attr_accessor :name
|
117
|
+
|
118
|
+
# the name of the artist who performed the track
|
119
|
+
attr_accessor :artist
|
120
|
+
|
121
|
+
# the name of the album that the track appears on
|
122
|
+
attr_accessor :album
|
123
|
+
|
124
|
+
# the key of the album that the track appears on
|
125
|
+
attr_accessor :album_key
|
126
|
+
|
127
|
+
# the URL of the album that the track appears on, on the Rdio web site
|
128
|
+
attr_accessor :album_url
|
129
|
+
|
130
|
+
# the key of the track's artist
|
131
|
+
attr_accessor :artist_key
|
132
|
+
|
133
|
+
# the URL of the track's artist on the Rdio web site
|
134
|
+
attr_accessor :artist_url
|
135
|
+
|
136
|
+
# the object type, always "t"
|
137
|
+
attr_accessor :type
|
138
|
+
|
139
|
+
# the number of tracks in the track, ie: 1
|
140
|
+
attr_accessor :length
|
141
|
+
|
142
|
+
# the duration of the track in seconds
|
143
|
+
attr_accessor :duration
|
144
|
+
|
145
|
+
# is the track explicit?
|
146
|
+
attr_accessor :is_explicit
|
147
|
+
|
148
|
+
# is the track clean?
|
149
|
+
attr_accessor :is_clean
|
150
|
+
|
151
|
+
# the URL of the track on the Rdio web site
|
152
|
+
attr_accessor :url
|
153
|
+
|
154
|
+
# the URL of the album-art for the track
|
155
|
+
attr_accessor :base_icon
|
156
|
+
|
157
|
+
# the name of the artist whose album the track appears on
|
158
|
+
attr_accessor :album_artist
|
159
|
+
|
160
|
+
# the key of the artist whose album the track appears on
|
161
|
+
attr_accessor :album_artist_key
|
162
|
+
|
163
|
+
# the track can be downloaded
|
164
|
+
attr_accessor :can_download
|
165
|
+
|
166
|
+
# the track can only be downloaded as part of an album download
|
167
|
+
attr_accessor :can_download_album_only
|
168
|
+
|
169
|
+
# the track can be streamed
|
170
|
+
attr_accessor :can_stream
|
171
|
+
|
172
|
+
# the track can be synced to mobile devices
|
173
|
+
attr_accessor :can_tether
|
174
|
+
|
175
|
+
# the track can be previewed
|
176
|
+
attr_accessor :can_sample
|
177
|
+
|
178
|
+
# the price of the track in US cents
|
179
|
+
attr_accessor :price
|
180
|
+
|
181
|
+
# a short URL for the track
|
182
|
+
attr_accessor :short_url
|
183
|
+
|
184
|
+
# the URL of a SWF to embed the track
|
185
|
+
attr_accessor :embed_url
|
186
|
+
|
187
|
+
# the partial URL of the album-art for the track
|
188
|
+
attr_accessor :icon
|
189
|
+
|
190
|
+
# the number of times this track has been played
|
191
|
+
attr_accessor :play_count
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
class PlaylistData < BaseObj
|
196
|
+
def initialize(api)
|
197
|
+
super api
|
198
|
+
end
|
199
|
+
|
200
|
+
# the name of the playlist
|
201
|
+
attr_accessor :name
|
202
|
+
|
203
|
+
# the number of tracks in the playlist
|
204
|
+
attr_accessor :length
|
205
|
+
|
206
|
+
# the object type, always "p"
|
207
|
+
attr_accessor :type
|
208
|
+
|
209
|
+
# the URL of the playlist on the Rdio site
|
210
|
+
attr_accessor :url
|
211
|
+
|
212
|
+
# the URL of an icon for the playlist
|
213
|
+
attr_accessor :icon
|
214
|
+
|
215
|
+
# the URL of an icon for the playlist
|
216
|
+
attr_accessor :base_icon
|
217
|
+
|
218
|
+
# the name of the user who created the playlist
|
219
|
+
attr_accessor :owner
|
220
|
+
|
221
|
+
# the URL on the Rdio site of the user who created the playlist
|
222
|
+
attr_accessor :owner_url
|
223
|
+
|
224
|
+
# the key of the user who created the playlist
|
225
|
+
attr_accessor :owner_key
|
226
|
+
|
227
|
+
# the icon of the user who created the playlist
|
228
|
+
attr_accessor :owner_icon
|
229
|
+
|
230
|
+
# when the playlist was last modified
|
231
|
+
attr_accessor :last_updated
|
232
|
+
|
233
|
+
# a short URL for the playlist
|
234
|
+
attr_accessor :short_url
|
235
|
+
|
236
|
+
# the URL of a SWF to embed the playlist
|
237
|
+
attr_accessor :embed_url
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
class UserData < BaseObj
|
242
|
+
def initialize(api)
|
243
|
+
super api
|
244
|
+
end
|
245
|
+
|
246
|
+
# the first name of the user
|
247
|
+
attr_accessor :first_name
|
248
|
+
|
249
|
+
# the last name of the user
|
250
|
+
attr_accessor :last_name
|
251
|
+
|
252
|
+
# the URL of an image of the user
|
253
|
+
attr_accessor :icon
|
254
|
+
|
255
|
+
# the URL of an image of the user
|
256
|
+
attr_accessor :base_icon
|
257
|
+
|
258
|
+
# the library version of the user, used to determine if a user's collection has changed
|
259
|
+
attr_accessor :library_version
|
260
|
+
|
261
|
+
# the URL of the user on the Rdio site
|
262
|
+
attr_accessor :url
|
263
|
+
|
264
|
+
# "m" or "f"
|
265
|
+
attr_accessor :gender
|
266
|
+
|
267
|
+
# the object type, always "s"
|
268
|
+
attr_accessor :type
|
269
|
+
|
270
|
+
# the user's vanity name
|
271
|
+
attr_accessor :username
|
272
|
+
|
273
|
+
# the last song the user has played
|
274
|
+
attr_accessor :last_song_played
|
275
|
+
|
276
|
+
# how to display the user's name
|
277
|
+
attr_accessor :display_name
|
278
|
+
|
279
|
+
# the number of tracks in the user's collection
|
280
|
+
attr_accessor :track_count
|
281
|
+
|
282
|
+
# when the last played song was played
|
283
|
+
attr_accessor :last_song_play_time
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|