pandoru 0.1.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +29 -0
- data/LICENSE +21 -0
- data/README.md +263 -0
- data/lib/pandoru/client.rb +298 -0
- data/lib/pandoru/client_builder.rb +526 -0
- data/lib/pandoru/errors.rb +147 -0
- data/lib/pandoru/models/_base.rb +363 -0
- data/lib/pandoru/models/bookmark.rb +81 -0
- data/lib/pandoru/models/playlist.rb +91 -0
- data/lib/pandoru/models/search.rb +75 -0
- data/lib/pandoru/models/station.rb +249 -0
- data/lib/pandoru/models/track_explanation.rb +41 -0
- data/lib/pandoru/models.rb +19 -0
- data/lib/pandoru/transport.rb +395 -0
- data/lib/pandoru/version.rb +3 -0
- data/lib/pandoru.rb +69 -0
- metadata +212 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
|
|
3
|
+
module Pandoru
|
|
4
|
+
module Models
|
|
5
|
+
# Simple base model for Pandora API objects
|
|
6
|
+
class Base
|
|
7
|
+
attr_reader :data
|
|
8
|
+
|
|
9
|
+
def self.fields
|
|
10
|
+
@fields ||= {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.field(name, json_key = nil, type: nil, &block)
|
|
14
|
+
json_key ||= name.to_s
|
|
15
|
+
fields[name] = { json_key: json_key, type: type, formatter: block }
|
|
16
|
+
attr_accessor name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.date_field(name, json_key = nil)
|
|
20
|
+
field(name, json_key, type: :date)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.from_json(api_client, data)
|
|
24
|
+
return nil unless data
|
|
25
|
+
instance = new(data, api_client)
|
|
26
|
+
instance.populate_from_json(data)
|
|
27
|
+
instance
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.from_json_list(api_client, data_list)
|
|
31
|
+
return [] unless data_list
|
|
32
|
+
data_list.map { |data| from_json(api_client, data) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def initialize(data = {}, api_client = nil)
|
|
36
|
+
@data = data
|
|
37
|
+
@api_client = api_client
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def populate_from_json(data)
|
|
41
|
+
self.class.fields.each do |name, config|
|
|
42
|
+
json_key = config[:json_key]
|
|
43
|
+
type = config[:type]
|
|
44
|
+
formatter = config[:formatter]
|
|
45
|
+
|
|
46
|
+
value = data[json_key]
|
|
47
|
+
|
|
48
|
+
# Apply type conversion
|
|
49
|
+
value = case type
|
|
50
|
+
when :date
|
|
51
|
+
parse_date(value)
|
|
52
|
+
when :boolean
|
|
53
|
+
parse_boolean(value)
|
|
54
|
+
else
|
|
55
|
+
value
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Apply custom formatter if provided
|
|
59
|
+
value = formatter.call(value) if formatter && value
|
|
60
|
+
|
|
61
|
+
instance_variable_set("@#{name}", value)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def to_h
|
|
66
|
+
hash = {}
|
|
67
|
+
self.class.fields.each do |name, _|
|
|
68
|
+
hash[name] = instance_variable_get("@#{name}")
|
|
69
|
+
end
|
|
70
|
+
hash
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def inspect
|
|
74
|
+
attrs = self.class.fields.keys.map do |name|
|
|
75
|
+
value = instance_variable_get("@#{name}")
|
|
76
|
+
"#{name}=#{value.inspect}"
|
|
77
|
+
end.join(' ')
|
|
78
|
+
"#<#{self.class.name} #{attrs}>"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def parse_date(value)
|
|
84
|
+
return nil unless value
|
|
85
|
+
if value.is_a?(Hash) && value['time']
|
|
86
|
+
Time.at(value['time'] / 1000.0).utc
|
|
87
|
+
elsif value.is_a?(Numeric)
|
|
88
|
+
Time.at(value / 1000.0).utc
|
|
89
|
+
else
|
|
90
|
+
value
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def parse_boolean(value)
|
|
95
|
+
case value
|
|
96
|
+
when true, false
|
|
97
|
+
value
|
|
98
|
+
when 'true', '1', 1
|
|
99
|
+
true
|
|
100
|
+
when 'false', '0', 0
|
|
101
|
+
false
|
|
102
|
+
else
|
|
103
|
+
value
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Base class for collections of models
|
|
109
|
+
class Collection < Base
|
|
110
|
+
include Enumerable
|
|
111
|
+
|
|
112
|
+
def initialize(data = {}, api_client = nil)
|
|
113
|
+
super
|
|
114
|
+
@items = []
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def <<(item)
|
|
118
|
+
@items << item
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def [](index)
|
|
122
|
+
@items[index]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def each(&block)
|
|
126
|
+
@items.each(&block)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def length
|
|
130
|
+
@items.length
|
|
131
|
+
end
|
|
132
|
+
alias_method :size, :length
|
|
133
|
+
alias_method :count, :length
|
|
134
|
+
|
|
135
|
+
def empty?
|
|
136
|
+
@items.empty?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def first
|
|
140
|
+
@items.first
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def last
|
|
144
|
+
@items.last
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def to_a
|
|
148
|
+
@items
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# PandoraModel base class similar to Python implementation
|
|
153
|
+
class PandoraModel
|
|
154
|
+
def self.fields
|
|
155
|
+
@fields ||= {}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def self.field(name, json_key = nil, type: nil, &block)
|
|
159
|
+
json_key ||= name.to_s
|
|
160
|
+
fields[name] = { json_key: json_key, type: type, formatter: block }
|
|
161
|
+
attr_accessor name
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def self.date_field(name, json_key = nil)
|
|
165
|
+
field(name, json_key, type: :date)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def self.from_json(api_client, data)
|
|
169
|
+
return nil unless data
|
|
170
|
+
instance = new(api_client)
|
|
171
|
+
instance.populate_from_json(data)
|
|
172
|
+
instance
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self.from_json_list(api_client, data_list)
|
|
176
|
+
return [] unless data_list
|
|
177
|
+
data_list.map { |data| from_json(api_client, data) }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def initialize(api_client)
|
|
181
|
+
@api_client = api_client
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def populate_from_json(data)
|
|
185
|
+
self.class.fields.each do |name, config|
|
|
186
|
+
json_key = config[:json_key]
|
|
187
|
+
type = config[:type]
|
|
188
|
+
formatter = config[:formatter]
|
|
189
|
+
|
|
190
|
+
value = data[json_key]
|
|
191
|
+
|
|
192
|
+
# Apply type conversion
|
|
193
|
+
value = case type
|
|
194
|
+
when :date
|
|
195
|
+
parse_date(value)
|
|
196
|
+
when :boolean
|
|
197
|
+
parse_boolean(value)
|
|
198
|
+
else
|
|
199
|
+
value
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Apply custom formatter if provided
|
|
203
|
+
value = formatter.call(value) if formatter && value
|
|
204
|
+
|
|
205
|
+
instance_variable_set("@#{name}", value)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def to_h
|
|
210
|
+
hash = {}
|
|
211
|
+
self.class.fields.each do |name, _|
|
|
212
|
+
hash[name] = instance_variable_get("@#{name}")
|
|
213
|
+
end
|
|
214
|
+
hash
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def inspect
|
|
218
|
+
attrs = self.class.fields.keys.map do |name|
|
|
219
|
+
value = instance_variable_get("@#{name}")
|
|
220
|
+
"#{name}=#{value.inspect}"
|
|
221
|
+
end.join(' ')
|
|
222
|
+
"#<#{self.class.name} #{attrs}>"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
private
|
|
226
|
+
|
|
227
|
+
def parse_date(value)
|
|
228
|
+
return nil unless value
|
|
229
|
+
if value.is_a?(Hash) && value['time']
|
|
230
|
+
Time.at(value['time'] / 1000.0).utc
|
|
231
|
+
elsif value.is_a?(Numeric)
|
|
232
|
+
Time.at(value / 1000.0).utc
|
|
233
|
+
else
|
|
234
|
+
value
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def parse_boolean(value)
|
|
239
|
+
case value
|
|
240
|
+
when true, false
|
|
241
|
+
value
|
|
242
|
+
when 'true', '1', 1
|
|
243
|
+
true
|
|
244
|
+
when 'false', '0', 0
|
|
245
|
+
false
|
|
246
|
+
else
|
|
247
|
+
value
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# List Model for indexed collections
|
|
253
|
+
class PandoraListModel < PandoraModel
|
|
254
|
+
include Enumerable
|
|
255
|
+
|
|
256
|
+
def self.from_json(api_client, data)
|
|
257
|
+
instance = new(api_client)
|
|
258
|
+
instance.populate_from_json(data)
|
|
259
|
+
|
|
260
|
+
# Extract the list items
|
|
261
|
+
list_key = instance.class.list_key
|
|
262
|
+
list_model = instance.class.list_model
|
|
263
|
+
|
|
264
|
+
if list_key && list_model && data[list_key]
|
|
265
|
+
items = list_model.from_json_list(api_client, data[list_key])
|
|
266
|
+
instance.instance_variable_set(:@items, items)
|
|
267
|
+
|
|
268
|
+
# Create index if specified
|
|
269
|
+
if instance.class.index_key
|
|
270
|
+
index = {}
|
|
271
|
+
items.each { |item| index[item.send(instance.class.index_key)] = item }
|
|
272
|
+
instance.instance_variable_set(:@index, index)
|
|
273
|
+
end
|
|
274
|
+
else
|
|
275
|
+
instance.instance_variable_set(:@items, [])
|
|
276
|
+
instance.instance_variable_set(:@index, {})
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
instance
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def self.list_key
|
|
283
|
+
@list_key
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def self.list_model
|
|
287
|
+
@list_model
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def self.index_key
|
|
291
|
+
@index_key
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def self.set_list_config(list_key:, list_model:, index_key: nil)
|
|
295
|
+
@list_key = list_key
|
|
296
|
+
@list_model = list_model
|
|
297
|
+
@index_key = index_key
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def initialize(api_client)
|
|
301
|
+
super(api_client)
|
|
302
|
+
@items = []
|
|
303
|
+
@index = {}
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def each(&block)
|
|
307
|
+
@items.each(&block)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def [](key)
|
|
311
|
+
if key.is_a?(Integer)
|
|
312
|
+
@items[key]
|
|
313
|
+
else
|
|
314
|
+
@index[key] if @index
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def []=(index, value)
|
|
319
|
+
@items[index] = value if index.is_a?(Integer)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def size
|
|
323
|
+
@items.size
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def length
|
|
327
|
+
@items.length
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def empty?
|
|
331
|
+
@items.empty?
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Dictionary List Model
|
|
336
|
+
# For models that have both array and hash-like access patterns
|
|
337
|
+
class PandoraDictListModel < PandoraListModel
|
|
338
|
+
# Additional dictionary-like methods can be added here
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Date Field similar to Python implementation
|
|
342
|
+
class DateField
|
|
343
|
+
attr_reader :field
|
|
344
|
+
|
|
345
|
+
def initialize(field)
|
|
346
|
+
@field = field
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def formatter(api_client, data, value)
|
|
350
|
+
return nil unless value
|
|
351
|
+
return nil if value.is_a?(Hash) && value.empty?
|
|
352
|
+
|
|
353
|
+
if value.is_a?(Hash) && value["time"]
|
|
354
|
+
Time.at(value["time"] / 1000.0).utc
|
|
355
|
+
elsif value.is_a?(Numeric)
|
|
356
|
+
Time.at(value / 1000.0).utc
|
|
357
|
+
else
|
|
358
|
+
value
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require_relative '_base'
|
|
2
|
+
|
|
3
|
+
module Pandoru
|
|
4
|
+
module Models
|
|
5
|
+
class Bookmark < Base
|
|
6
|
+
field :bookmark_token, 'bookmarkToken'
|
|
7
|
+
field :music_token, 'musicToken'
|
|
8
|
+
field :artist_name, 'artistName'
|
|
9
|
+
field :song_name, 'songName'
|
|
10
|
+
field :art_url, 'artUrl'
|
|
11
|
+
field :sample_url, 'sampleUrl'
|
|
12
|
+
field :sample_gain, 'sampleGain'
|
|
13
|
+
field :album_name, 'albumName'
|
|
14
|
+
date_field :date_created, 'dateCreated'
|
|
15
|
+
|
|
16
|
+
def song_bookmark?
|
|
17
|
+
!song_name.nil?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def artist_bookmark?
|
|
21
|
+
song_name.nil?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def delete
|
|
25
|
+
return false unless @api_client
|
|
26
|
+
if song_bookmark?
|
|
27
|
+
@api_client.delete_song_bookmark(bookmark_token)
|
|
28
|
+
else
|
|
29
|
+
@api_client.delete_artist_bookmark(bookmark_token)
|
|
30
|
+
end
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class BookmarkList < Collection
|
|
36
|
+
def self.from_json(api_client, data)
|
|
37
|
+
instance = new(data, api_client)
|
|
38
|
+
|
|
39
|
+
# Add song bookmarks
|
|
40
|
+
if data['songs']
|
|
41
|
+
Bookmark.from_json_list(api_client, data['songs']).each do |bookmark|
|
|
42
|
+
instance << bookmark
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Add artist bookmarks (ensure song_name is nil for artists)
|
|
47
|
+
if data['artists']
|
|
48
|
+
data['artists'].each do |artist_data|
|
|
49
|
+
artist_data['songName'] = nil unless artist_data.key?('songName')
|
|
50
|
+
bookmark = Bookmark.from_json(api_client, artist_data)
|
|
51
|
+
instance << bookmark
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
instance
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def song_bookmarks
|
|
59
|
+
select(&:song_bookmark?)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def artist_bookmarks
|
|
63
|
+
select(&:artist_bookmark?)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def find_song_bookmark(song_name, artist_name = nil)
|
|
67
|
+
song_bookmarks.find do |bookmark|
|
|
68
|
+
matches_song = bookmark.song_name&.casecmp(song_name) == 0
|
|
69
|
+
matches_artist = artist_name.nil? || bookmark.artist_name&.casecmp(artist_name) == 0
|
|
70
|
+
matches_song && matches_artist
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def find_artist_bookmark(artist_name)
|
|
75
|
+
artist_bookmarks.find do |bookmark|
|
|
76
|
+
bookmark.artist_name&.casecmp(artist_name) == 0
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require_relative '_base'
|
|
2
|
+
|
|
3
|
+
module Pandoru
|
|
4
|
+
module Models
|
|
5
|
+
class PlaylistItem < Base
|
|
6
|
+
field :track_token, "trackToken"
|
|
7
|
+
field :artist_name, "artistName"
|
|
8
|
+
field :album_name, "albumName"
|
|
9
|
+
field :song_name, "songName"
|
|
10
|
+
field :song_rating, "songRating"
|
|
11
|
+
field :track_length, "trackLength"
|
|
12
|
+
field :allow_feedback, "allowFeedback", type: :boolean
|
|
13
|
+
field :ad_token, "adToken"
|
|
14
|
+
field :audio_url_map, "audioUrlMap"
|
|
15
|
+
field :is_ad, "isAd", type: :boolean
|
|
16
|
+
|
|
17
|
+
def initialize(data = {}, api_client = nil)
|
|
18
|
+
super(data, api_client)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def is_ad?
|
|
22
|
+
@is_ad || false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def audio_url(quality = nil)
|
|
26
|
+
return nil unless @audio_url_map
|
|
27
|
+
|
|
28
|
+
quality ||= @api_client&.default_audio_quality || "mediumQuality"
|
|
29
|
+
@audio_url_map[quality]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def thumbs_up
|
|
33
|
+
return unless @api_client && @allow_feedback
|
|
34
|
+
@api_client.add_feedback(@track_token, true)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def thumbs_down
|
|
38
|
+
return unless @api_client && @allow_feedback
|
|
39
|
+
@api_client.add_feedback(@track_token, false)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def bookmark_song
|
|
43
|
+
return unless @api_client
|
|
44
|
+
@api_client.add_song_bookmark(@track_token)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def bookmark_artist
|
|
48
|
+
return unless @api_client
|
|
49
|
+
@api_client.add_artist_bookmark(@track_token)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def sleep
|
|
53
|
+
return unless @api_client
|
|
54
|
+
@api_client.sleep_song(@track_token)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class Playlist < Collection
|
|
59
|
+
def self.from_json(api_client, data)
|
|
60
|
+
playlist = new({}, api_client)
|
|
61
|
+
playlist.populate_from_json(data)
|
|
62
|
+
|
|
63
|
+
# Add playlist items
|
|
64
|
+
if data["items"]
|
|
65
|
+
data["items"].each do |item_data|
|
|
66
|
+
item = PlaylistItem.from_json(api_client, item_data)
|
|
67
|
+
playlist << item
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
playlist
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class AdItem < PlaylistItem
|
|
76
|
+
field :ad_token, "adToken"
|
|
77
|
+
field :company_name, "companyName"
|
|
78
|
+
field :title, "title"
|
|
79
|
+
field :click_through_url, "clickThroughUrl"
|
|
80
|
+
field :image_url, "imageUrl"
|
|
81
|
+
field :tracking_tokens, "trackingTokens"
|
|
82
|
+
|
|
83
|
+
attr_accessor :station_id
|
|
84
|
+
|
|
85
|
+
def initialize(data = {}, api_client = nil)
|
|
86
|
+
super(data, api_client)
|
|
87
|
+
@is_ad = true
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require_relative '_base'
|
|
2
|
+
|
|
3
|
+
module Pandoru
|
|
4
|
+
module Models
|
|
5
|
+
class SearchResultItem < Base
|
|
6
|
+
field :artist_name, 'artistName'
|
|
7
|
+
field :song_name, 'songName'
|
|
8
|
+
field :music_token, 'musicToken'
|
|
9
|
+
field :score, 'score'
|
|
10
|
+
field :likely_match, 'likelyMatch', type: :boolean
|
|
11
|
+
|
|
12
|
+
def song?
|
|
13
|
+
!song_name.nil?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def artist?
|
|
17
|
+
song_name.nil?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create_station
|
|
21
|
+
return nil unless @api_client
|
|
22
|
+
if song?
|
|
23
|
+
@api_client.create_station(song_token: music_token)
|
|
24
|
+
else
|
|
25
|
+
@api_client.create_station(artist_token: music_token)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class SearchResult < Collection
|
|
31
|
+
field :near_matches_available, 'nearMatchesAvailable', type: :boolean
|
|
32
|
+
field :explanation, 'explanation'
|
|
33
|
+
|
|
34
|
+
def self.from_json(api_client, data)
|
|
35
|
+
instance = new(data, api_client)
|
|
36
|
+
instance.populate_from_json(data)
|
|
37
|
+
|
|
38
|
+
# Add songs
|
|
39
|
+
if data['songs']
|
|
40
|
+
SearchResultItem.from_json_list(api_client, data['songs']).each do |item|
|
|
41
|
+
instance << item
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Add artists (convert to SearchResultItem format)
|
|
46
|
+
if data['artists']
|
|
47
|
+
data['artists'].each do |artist_data|
|
|
48
|
+
item_data = {
|
|
49
|
+
'artistName' => artist_data['artistName'],
|
|
50
|
+
'musicToken' => artist_data['musicToken'],
|
|
51
|
+
'score' => artist_data['score'],
|
|
52
|
+
'likelyMatch' => artist_data['likelyMatch']
|
|
53
|
+
}
|
|
54
|
+
item = SearchResultItem.from_json(api_client, item_data)
|
|
55
|
+
instance << item
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
instance
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def songs
|
|
63
|
+
select(&:song?)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def artists
|
|
67
|
+
select(&:artist?)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def best_match
|
|
71
|
+
max_by(&:score)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|