gracenote 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/lib/gracenote.rb +333 -20
  2. data/lib/gracenote/HTTP.rb +15 -6
  3. metadata +36 -12
  4. checksums.yaml +0 -7
@@ -1,63 +1,376 @@
1
- require "gracenote/version"
2
1
  require "gracenote/HTTP"
2
+ require "crack"
3
3
 
4
- class Gracenote << HTTP
4
+ class Gracenote
5
+
6
+ # class variables
7
+ @@ALL_RESULTS = '1'
8
+ @@BEST_MATCH_ONLY = '0'
9
+
10
+ # Function: initialize
11
+ # Sets the following instance variables
12
+ # clientID
13
+ # clientTag
14
+ # userID
15
+ # apiURL
5
16
  def initialize (spec)
6
- if(spec[:clientID].nil? || !(defined spec[:clientID] || spec[:clientID] == ""))
7
- p "clientID cannot be nil"
8
- return
17
+ if(spec[:clientID].nil? || spec[:clientID] == "")
18
+ raise "clientID cannot be nil"
9
19
  end
10
- if(spec[:clientTag].nil? || !(defined spec[:clientTag] || spec[:clientTag] == ""))
11
- p "clientTag cannot be nil"
20
+ if(spec[:clientTag].nil? || spec[:clientTag] == "")
21
+ raise "clientTag cannot be nil"
12
22
  end
13
23
 
14
24
  @clientID = spec[:clientID]
15
25
  @clientTag = spec[:clientTag]
16
- @userID = spec[:userID]
17
- @apiURL = "https://" + @clientID + ".web.cddbp.net/webapi/xml/1.0/"
26
+ @userID = spec[:userID].nil? ? nil : spec[:userID]
27
+ @apiURL = "https://c" + @clientID + ".web.cddbp.net/webapi/xml/1.0/"
18
28
  end
19
29
 
30
+ # public methods
20
31
  public
32
+
33
+ # Function: registerUser
34
+ # Registers a user and returns userID
21
35
  def registerUser (clientID = nil)
22
36
  if(clientID.nil?)
23
37
  clientID = @clientID + "-" + @clientTag
24
38
  end
25
39
 
26
- if(!(@userID.nil?) || defined @userID)
40
+ if not @userID.nil?
27
41
  p "user already registered. No need to register again"
28
42
  return @userID
29
43
  end
44
+
30
45
  #send req to server and get user ID
46
+ data = "<QUERIES>
47
+ <QUERY CMD='REGISTER'>
48
+ <CLIENT>"+ clientID +"</CLIENT>
49
+ </QUERY>
50
+ </QUERIES>"
51
+ resp = HTTP.post(@apiURL, data)
52
+ resp = checkRES resp
53
+ @userID = resp['RESPONSES']['RESPONSE']['USER']
54
+
31
55
  return @userID
32
56
  end
33
57
 
34
- def findTrack
58
+ # Function: findTrack
59
+ # Finds a track
60
+ # Arguments:
61
+ # artistName
62
+ # albumTitle
63
+ # trackTitle
64
+ # matchMode
65
+ def findTrack(artistName, albumTitle, trackTitle, matchMode = @@ALL_RESULTS)
66
+ if @userID == nil
67
+ registerUser
68
+ end
69
+ body = constructQueryBody(artistName, albumTitle, trackTitle, "", "ALBUM_SEARCH", matchMode)
70
+ data = constructQueryReq(body)
71
+ return api(data);
35
72
  end
36
73
 
37
- def findAlbum
74
+ # Function: findArtist
75
+ # Finds a Artist
76
+ # Arguments:
77
+ # artistName
78
+ # matchMode
79
+ def findArtist(artistName, matchMode = @@ALL_RESULTS)
80
+ return findTrack(artistName, "", "", matchMode)
81
+ end
82
+
83
+ # Function: findAlbum
84
+ # finds an Album
85
+ # Arguments:
86
+ # artistName
87
+ # albumTitle
88
+ # trackTitle
89
+ # matchMode
90
+ def findAlbum(artistName, albumTitle, matchMode = @@ALL_RESULTS)
91
+ return findTrack(artistName, albumTitle, "", matchMode)
38
92
  end
39
93
 
40
- def findArtist
94
+ # Function: albumToc
95
+ # Fetches album metadata based on a table of contents.
96
+ # Arguments:
97
+ # toc
98
+ def albumToc(toc)
99
+ if @userID == nil
100
+ registerUser
101
+ end
102
+ body = "<TOC><OFFSETS>" + toc + "</OFFSETS></TOC>"
103
+ data = constructQueryBody(body, "ALBUM_TOC")
104
+ return api(data)
105
+ end
106
+
107
+ # Function: fetchOETData
108
+ # Gets data based on gn_id
109
+ # Arguments:
110
+ # gn_id
111
+ def fetchOETData(gn_id)
112
+ if @userID == nil
113
+ registerUser
114
+ end
115
+
116
+ body = "<GN_ID>" + gn_id + "</GN_ID>
117
+ <OPTION>
118
+ <PARAMETER>SELECT_EXTENDED</PARAMETER>
119
+ <VALUE>ARTIST_OET</VALUE>
120
+ </OPTION>
121
+ <OPTION>
122
+ <PARAMETER>SELECT_DETAIL</PARAMETER>
123
+ <VALUE>ARTIST_ORIGIN:4LEVEL,ARTIST_ERA:2LEVEL,ARTIST_TYPE:2LEVEL</VALUE>
124
+ </OPTION>"
125
+
126
+ data = constructQueryReq(body, "ALBUM_FETCH")
127
+ resp = HTTP.post(@apiURL, data)
128
+ resp = checkRES resp
129
+
130
+ json = resp["RESPONSES"]
131
+
132
+ output = Array.new()
133
+ output[:artist_origin] = json["RESPONSE"]["ALBUM"]["ARTIST_ORIGIN"].nil? ? "" : _getOETElem(json["RESPONSE"]["ALBUM"]["ARTIST_ORIGIN"])
134
+ output[:artist_era] = json["RESPONSE"]["ALBUM"]["ARTIST_ERA"].nil? ? "" : _getOETElem(json["RESPONSE"]["ALBUM"]["ARTIST_ERA"])
135
+ output[:artist_type] = json["RESPONSE"]["ALBUM"]["ARTIST_TYPE"].nil? ? "" : _getOETElem(json["RESPONSE"]["ALBUM"]["ARTIST_TYPE"])
136
+ return output
41
137
  end
42
138
 
139
+ # protected methods
43
140
  protected
44
- #execute a query on gracenote webapi
141
+ # Function: api
142
+ # execute a query on gracenote webapi
143
+ # Arguments:
144
+ # query
45
145
  def api (query)
46
-
146
+ resp = HTTP.post(@apiURL, query)
147
+ return parseRES(resp)
47
148
  end
48
149
 
49
- def constructQueryReq
150
+ # Function: constructQueryReq
151
+ # Constructs Query
152
+ # Arguments:
153
+ # body
154
+ # command
155
+ def constructQueryReq(body, command = "ALBUM_SEARCH")
50
156
  #construct the XML query
157
+ return "<QUERIES>
158
+ <AUTH>
159
+ <CLIENT>"+ @clientID + "-" + @clientTag + "</CLIENT>
160
+ <USER>"+ @userID + "</USER>
161
+ </AUTH>
162
+ <QUERY CMD=\"" + command + "\">
163
+ " + body + "
164
+ </QUERY>
165
+ </QUERIES>"
51
166
  end
52
167
 
53
- def constructQueryBody
168
+ # Function: constructQueryBody
169
+ # Constructs query body
170
+ # Arguments:
171
+ # artist
172
+ # album
173
+ # track
174
+ # gn_id
175
+ # command
176
+ # matchMode
177
+ def constructQueryBody(artist, album, track, gn_id, command = "ALBUM_SEARCH", matchMode = @@ALL_RESULTS)
178
+ body = ""
179
+ # If a fetch scenario, user the Gracenote ID.
180
+ if command == "ALBUM_FETCH"
181
+ body += "<GN_ID>" + gn_id + "</GN_ID>"
182
+ else
183
+ # Otherwise, just do a search.
184
+ # Only get the single best match if that's what the user wants.
185
+ if matchMode == @@BEST_MATCH_ONLY
186
+ body += "<MODE>SINGLE_BEST_COVER</MODE>"
187
+ end
188
+ # If a search scenario, then need the text input
189
+ if artist != ""
190
+ body += "<TEXT TYPE=\"ARTIST\">" + artist + "</TEXT>"
191
+ end
192
+ if track != ""
193
+ body += "<TEXT TYPE=\"TRACK_TITLE\">" + track + "</TEXT>"
194
+ end
195
+ if album != ""
196
+ body += "<TEXT TYPE=\"ALBUM_TITLE\">" + album + "</TEXT>"
197
+ end
198
+ end
199
+ # Include extended data.
200
+
201
+ body += "<OPTION>
202
+ <PARAMETER>SELECT_EXTENDED</PARAMETER>
203
+ <VALUE>COVER,REVIEW,ARTIST_BIOGRAPHY,ARTIST_IMAGE,ARTIST_OET,MOOD,TEMPO</VALUE>
204
+ </OPTION>"
205
+
206
+ # Include more detailed responses.
207
+ body += "<OPTION>
208
+ <PARAMETER>SELECT_DETAIL</PARAMETER>
209
+ <VALUE>GENRE:3LEVEL,MOOD:2LEVEL,TEMPO:3LEVEL,ARTIST_ORIGIN:4LEVEL,ARTIST_ERA:2LEVEL,ARTIST_TYPE:2LEVEL</VALUE>
210
+ </OPTION>"
211
+
212
+ # Only want the thumbnail cover art for now (LARGE,XLARGE,SMALL,MEDIUM,THUMBNAIL)
213
+ body += "<OPTION>
214
+ <PARAMETER>COVER_SIZE</PARAMETER>
215
+ <VALUE>MEDIUM</VALUE>
216
+ </OPTION>"
217
+
218
+ return body
54
219
  end
55
220
 
56
- def checkRES
221
+ # Function: checkRES
222
+ # Checks an XML response and converts it into json
223
+ # Arguments:
224
+ # resp
225
+ def checkRES resp
226
+ if resp.code.to_s != '200'
227
+ raise "Problem!! Got #{resp.code} with #{resp.message}"
228
+ end
229
+ json = nil
230
+ begin
231
+ json = Crack::XML.parse resp.body
232
+ rescue Exception => e
233
+ raise e
234
+ end
235
+
236
+ status = json['RESPONSES']['RESPONSE']['STATUS'].to_s
237
+ case status
238
+ when "ERROR"
239
+ raise "ERROR in response"
240
+ when "NO_MATCH"
241
+ raise "No match found"
242
+ else
243
+ if status != "OK"
244
+ raise "Problems found in the response"
245
+ end
246
+ end
247
+ return json
57
248
  end
58
249
 
59
- def parseRES
250
+ # Function: parseRES
251
+ # Parse's an XML response
252
+ # Arguments:
253
+ # resp
254
+ def parseRES resp
255
+ json = nil
256
+ begin
257
+ json = checkRES resp
258
+ rescue Exception => e
259
+ raise e
260
+ end
261
+ output = Array.new
262
+ data = Array.new
263
+ if json['RESPONSES']['RESPONSE']['ALBUM'].class.to_s != 'Array'
264
+ data.push json['RESPONSES']['RESPONSE']['ALBUM']
265
+ else
266
+ data = json['RESPONSES']['RESPONSE']['ALBUM']
267
+ end
268
+
269
+ data.each do |a|
270
+ obj = Hash.new
271
+
272
+ obj[:album_gnid] = a["GN_ID"].to_i
273
+ obj[:album_artist_name] = a["ARTIST"].to_s
274
+ obj[:album_title] = a["TITLE"].to_s
275
+ obj[:album_year] = a["DATE"].to_s
276
+ obj[:genre] = _getOETElem(a["GENRE"])
277
+ obj[:album_art_url] = _getAttribElem(a["URL"], "TYPE", "COVERART")
278
+
279
+ # Artist metadata
280
+ obj[:artist_image_url] = _getAttribElem(a["URL"], "TYPE", "ARTIST_IMAGE")
281
+ obj[:artist_bio_url] = _getAttribElem(a["URL"], "TYPE", "ARTIST_BIOGRAPHY")
282
+ obj[:review_url] = _getAttribElem(a["URL"], "TYPE", "REVIEW")
283
+
284
+ # If we have artist OET info, use it.
285
+ if not a["ARTIST_ORIGIN"].nil?
286
+ obj[:artist_era] = _getOETElem(a["ARTIST_ERA"])
287
+ obj[:artist_type] = _getOETElem(a["ARTIST_TYPE"])
288
+ obj[:artist_origin] = _getOETElem(a["ARTIST_ORIGIN"])
289
+ else
290
+ # If not available, do a fetch to try and get it instead.
291
+ obj = merge_recursively(obj, fetchOETData(a["GN_ID"]) )
292
+ end
293
+
294
+ # Parse track metadata if there is any.
295
+ obj[:tracks] = Array.new()
296
+ tracks = Array.new()
297
+ if a["TRACK"].class.to_s != 'Array'
298
+ tracks.push a["TRACK"]
299
+ else
300
+ tracks = a["TRACK"]
301
+ end
302
+ tracks.each do |t|
303
+ track = Hash.new()
304
+
305
+ track[:track_number] = t["TRACK_NUM"].to_s
306
+ track[:track_gnid] = t["GN_ID"].to_s
307
+ track[:track_title] = t["TITLE"].to_s
308
+ track[:track_artist_name] = t["ARTIST"].to_s
309
+
310
+ # If no specific track artist, use the album one.
311
+ if t["ARTIST"].nil?
312
+ track[:track_artist_name] = obj[:album_artist_name]
313
+ end
314
+
315
+ track[:mood] = _getOETElem(t["MOOD"])
316
+ track[:tempo] = _getOETElem(t["TEMPO"])
317
+
318
+ # If track level GOET data exists, overwrite metadata from album.
319
+ if not t["GENRE"].nil?
320
+ obj[:genre] = _getOETElem(t["GENRE"])
321
+ end
322
+ if not t["ARTIST_ERA"].nil?
323
+ obj[:artist_era] = _getOETElem(t["ARTIST_ERA"])
324
+ end
325
+ if not t["ARTIST_TYPE"].nil?
326
+ obj[:artist_type] = _getOETElem(t["ARTIST_TYPE"])
327
+ end
328
+ if not t["ARTIST_ORIGIN"].nil?
329
+ obj[:artist_origin] = _getOETElem(t["ARTIST_ORIGIN"])
330
+ end
331
+ obj[:tracks].push track
332
+ end
333
+ output.push obj
334
+ end
335
+ return output
336
+ end
337
+
338
+ # Function: merge_recursively
339
+ # Merges two hash maps
340
+ def merge_recursively(a, b)
341
+ a.merge(b) {|key, a_item, b_item| merge_recursively(a_item, b_item) }
60
342
  end
61
343
 
62
- end
344
+ # Function: _getAttribElem
345
+ # Gets key value pair from a url
346
+ # Arguments:
347
+ # data
348
+ # attribute
349
+ # value
350
+ def _getAttribElem(data, attribute, value)
351
+ data.each do |g|
352
+ attrib = Rack::Utils.parse_query URI(g).query
353
+ if(attrib[attribute] == value)
354
+ return g
355
+ end
356
+ end
357
+ end
63
358
 
359
+ # Function: _getOETElem
360
+ # Converts an Array to hashmap
361
+ # Arguments:
362
+ # data
363
+ def _getOETElem (data)
364
+ output = Array.new()
365
+ input = Array.new()
366
+ if data.class.to_s != 'Array'
367
+ input.push data
368
+ else
369
+ input = data
370
+ end
371
+ input.each do |g|
372
+ output.push({:id => g["ID"].to_i, :text => g})
373
+ end
374
+ return output
375
+ end
376
+ end
@@ -3,17 +3,20 @@
3
3
  #
4
4
  # Used to send get/post request to the webapi
5
5
  #
6
+ require 'net/http'
7
+ require 'net/https'
6
8
  require 'curb'
7
- require 'rake'
9
+ require 'rack'
10
+ require 'uri'
8
11
 
9
12
  class HTTP
10
- def self.get(path, data='', cookie='')
13
+ def self.get(path, cookie='')
11
14
  uri = URI(path)
12
15
  req = Net::HTTP.new(uri.host, uri.port)
13
16
  req.use_ssl = (uri.scheme == "https") ? true : false
17
+ headers = {'Cookie' => cookie}
14
18
 
15
- reqdata = Rack::Utils.build_nested_query(data)
16
- resp = req.get( uri.path, reqdata, headers)
19
+ resp = req.get( uri.path, headers)
17
20
  return resp
18
21
  end
19
22
 
@@ -21,8 +24,14 @@ class HTTP
21
24
  uri = URI(path)
22
25
  req = Net::HTTP.new(uri.host, uri.port)
23
26
  req.use_ssl = (uri.scheme == "https") ? true : false
24
-
25
- reqdata = Rack::Utils.build_nested_query(data)
27
+ headers = {'Cookie' => cookie, "Content-Type" => "application/xml"}
28
+
29
+ if data.class.to_s == 'String'
30
+ reqdata = data;
31
+ else
32
+ reqdata = Rack::Utils.build_nested_query(data)
33
+ end
34
+
26
35
  resp = req.request_post( uri.path, reqdata, headers)
27
36
  return resp
28
37
  end
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gracenote
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - nobelium
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-08-01 00:00:00.000000000 Z
12
+ date: 2013-09-05 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :development
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
@@ -27,29 +30,49 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: rake
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
- - - '>='
35
+ - - ! '>='
32
36
  - !ruby/object:Gem::Version
33
37
  version: '0'
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
- - - '>='
43
+ - - ! '>='
39
44
  - !ruby/object:Gem::Version
40
45
  version: '0'
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: curb
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
- - - '>='
51
+ - - ! '>='
46
52
  - !ruby/object:Gem::Version
47
53
  version: '0'
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
- - - '>='
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: crack
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
53
76
  - !ruby/object:Gem::Version
54
77
  version: '0'
55
78
  description: Gracenote web api gem
@@ -61,28 +84,29 @@ extra_rdoc_files: []
61
84
  files:
62
85
  - lib/gracenote.rb
63
86
  - lib/gracenote/HTTP.rb
64
- homepage: ''
87
+ homepage: http://rubygems.org/gems/gracenote
65
88
  licenses:
66
89
  - MIT
67
- metadata: {}
68
90
  post_install_message:
69
91
  rdoc_options: []
70
92
  require_paths:
71
93
  - lib
72
94
  required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
73
96
  requirements:
74
- - - '>='
97
+ - - ! '>='
75
98
  - !ruby/object:Gem::Version
76
99
  version: '0'
77
100
  required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
78
102
  requirements:
79
- - - '>='
103
+ - - ! '>='
80
104
  - !ruby/object:Gem::Version
81
105
  version: '0'
82
106
  requirements: []
83
107
  rubyforge_project:
84
- rubygems_version: 2.0.3
108
+ rubygems_version: 1.8.23
85
109
  signing_key:
86
- specification_version: 4
110
+ specification_version: 3
87
111
  summary: This gem is a wrapper for the gracenote web api
88
112
  test_files: []
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 978a68be9df31b09d56f3a6e23809b547c842a63
4
- data.tar.gz: bf5baaf385c0f8eb7ccb5b9863cad5fab1d220a9
5
- SHA512:
6
- metadata.gz: ecefd7a892b5b1e6023f1b87e65278247954860bac17a22761c544258adaafc8e9ad606de9192a1220f63cc7ad1d0bc6aeda321273004388cc16c0a99a63c1f7
7
- data.tar.gz: 2b5f880d963145ca8a30b93ea09a39d9b748dabe55ddb0861bc0a8cc9542ead9382680b750554c5f027ff5e2f3a2e2da44e13beac7a851c981020e3e6f283d27