rdio 0.0.1
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/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
|