gracenote 0.0.1 → 1.0.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.
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