under_fire 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Guardfile +23 -0
- data/LICENSE.txt +22 -0
- data/README.md +3 -0
- data/Rakefile +10 -0
- data/bin/under-fire +4 -0
- data/lib/under_fire/album_fetch.rb +50 -0
- data/lib/under_fire/album_search.rb +64 -0
- data/lib/under_fire/album_toc_search.rb +45 -0
- data/lib/under_fire/api_request.rb +50 -0
- data/lib/under_fire/api_response.rb +57 -0
- data/lib/under_fire/base_query.rb +30 -0
- data/lib/under_fire/cli.rb +66 -0
- data/lib/under_fire/client.rb +72 -0
- data/lib/under_fire/configuration.rb +39 -0
- data/lib/under_fire/registration.rb +29 -0
- data/lib/under_fire/toc_reader.rb +19 -0
- data/lib/under_fire/version.rb +10 -0
- data/lib/under_fire.rb +13 -0
- data/spec/lib/under_fire/album_fetch_spec.rb +47 -0
- data/spec/lib/under_fire/album_search_spec.rb +57 -0
- data/spec/lib/under_fire/album_toc_search_spec.rb +44 -0
- data/spec/lib/under_fire/api_request_spec.rb +0 -0
- data/spec/lib/under_fire/api_response.rb +9 -0
- data/spec/lib/under_fire/api_response_spec.rb +16 -0
- data/spec/lib/under_fire/base_query_spec.rb +15 -0
- data/spec/lib/under_fire/configuration_spec.rb +18 -0
- data/spec/sample/response.xml +130 -0
- data/spec/sample/toc.txt +1 -0
- data/spec/spec_helper.rb +3 -0
- data/tags +81 -0
- data/todo.txt +6 -0
- data/under_fire.gemspec +40 -0
- metadata +288 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bb1de4137e3e56a58f163f7410cd950f0c9353be
|
4
|
+
data.tar.gz: 86cc6bc36b6ae0b03d190627e7efdbbdfa37cd27
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cf75d52b9dc93330343d74dd287bf48d39a86a2af86045ecd7bfc6417d1bc68f7819b754735ba9b177114583c4cc121c80b39d96dfff5880e2e7307e1c6bf13a
|
7
|
+
data.tar.gz: e1ec64051f8a67670dbf316495e8db128338d098943e73edb5cc7bef784812e58142450e799c27d613e0887496cf147f14a1be1d78932e42a72f91d8be13f39e
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :minitest do
|
5
|
+
# with Minitest::Spec
|
6
|
+
watch(%r{^spec/lib/under_fire/(.*)_spec\.rb})
|
7
|
+
watch(%r{^lib/under_fire/(.+)\.rb}) { |m| "spec/lib/under_fire/#{m[1]}_spec.rb" }
|
8
|
+
watch(%r{^spec/spec_helper\.rb}) { 'spec' }
|
9
|
+
|
10
|
+
# Rails 4
|
11
|
+
# watch(%r{^app/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
|
12
|
+
# watch(%r{^app/controllers/application_controller\.rb}) { 'test/controllers' }
|
13
|
+
# watch(%r{^app/controllers/(.+)_controller\.rb}) { |m| "test/integration/#{m[1]}_test.rb" }
|
14
|
+
# watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
|
15
|
+
# watch(%r{^lib/(.+)\.rb}) { |m| "test/lib/#{m[1]}_test.rb" }
|
16
|
+
# watch(%r{^test/.+_test\.rb})
|
17
|
+
# watch(%r{^test/test_helper\.rb}) { 'test' }
|
18
|
+
|
19
|
+
# Rails < 4
|
20
|
+
# watch(%r{^app/controllers/(.*)\.rb}) { |m| "test/functional/#{m[1]}_test.rb" }
|
21
|
+
# watch(%r{^app/helpers/(.*)\.rb}) { |m| "test/helpers/#{m[1]}_test.rb" }
|
22
|
+
# watch(%r{^app/models/(.*)\.rb}) { |m| "test/unit/#{m[1]}_test.rb" }
|
23
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jason Thompson
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/under-fire
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'under_fire/base_query'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
module UnderFire
|
5
|
+
# Builds XML for Gracenote's ALBUM_FETCH query.
|
6
|
+
#
|
7
|
+
# @see https://developer.gracenote.com/sites/default/files/web/html/index.html#Music%20Web%20API/ALBUM_FETCH.html#_Toc344907262
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# search = UnderFire::Album_Fetch.new(:gn_id => '86372321-2C7F28ADC369EB90E53A7F6CA3A70D56')
|
11
|
+
# search.query => Response = The Beatles, Help!
|
12
|
+
class AlbumFetch < BaseQuery
|
13
|
+
# @return [String] XML string for query.
|
14
|
+
attr_reader :query
|
15
|
+
|
16
|
+
# @return [Hash] Search parameters with :mode removed.
|
17
|
+
attr_reader :parameters
|
18
|
+
|
19
|
+
# @return [String] Gracenote ID for album
|
20
|
+
attr_accessor :gn_id
|
21
|
+
|
22
|
+
# Requires album :gn_id or track :gn_id
|
23
|
+
#
|
24
|
+
# @param [Hash] args the arguments for Album_Fetch
|
25
|
+
# @option args [String] :gn_id Gracenote ID of album or track
|
26
|
+
# @option args [String] :mode Either 'SINGLE_BEST' or 'SINGLE_BEST_COVER'
|
27
|
+
# (Only needed if track :gn_id used)
|
28
|
+
def initialize(args)
|
29
|
+
super args
|
30
|
+
@parameters = args.reject {|k,v| k == :mode}
|
31
|
+
parameters.each do |k,v| send("#{k}=", v) end
|
32
|
+
@query = build_query
|
33
|
+
end
|
34
|
+
|
35
|
+
# Builds ALBUM_FETCH-specific part of ALBUM_FETCH query and adds it to the base query
|
36
|
+
# common to all query types. Called by constructor.
|
37
|
+
#
|
38
|
+
# @return [String] XML string for ALBUM_FETCH query.
|
39
|
+
def build_query
|
40
|
+
build_base_query do |builder|
|
41
|
+
builder.query(cmd: "ALBUM_FETCH"){
|
42
|
+
builder.mode "SINGLE_BEST_COVER"
|
43
|
+
parameters.each do |k,v|
|
44
|
+
builder.gn_id gn_id
|
45
|
+
end
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'under_fire/base_query'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
module UnderFire
|
5
|
+
# Builds XML for Gracenote's ALBUM_SEARCH query.
|
6
|
+
#
|
7
|
+
# @see https://developer.gracenote.com/sites/default/files/web/html/index.html#Music%20Web%20API/ALBUM_SEARCH.html#_Toc344907249
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# search = UnderFire::AlbumSearch.new(:artist => 'Radiohead')
|
11
|
+
# search.query #=> XML for Gracenote ALBUM_SEARCH query for albums by Radiohead.
|
12
|
+
#
|
13
|
+
# search = UnderFire::AlbumSearch.new(:track_title = > 'Paranoid Android',
|
14
|
+
# :artist => 'Radiohead',
|
15
|
+
# :mode => 'SINGLE_BEST_COVER')
|
16
|
+
# search.query #=> XML for ALBUM_SEARCH
|
17
|
+
#
|
18
|
+
#
|
19
|
+
class AlbumSearch < BaseQuery
|
20
|
+
# @return [String] XML string for query.
|
21
|
+
attr_reader :query
|
22
|
+
|
23
|
+
# @return [Hash] search parameters without :mode
|
24
|
+
attr_reader :parameters
|
25
|
+
|
26
|
+
# @return [String]
|
27
|
+
attr_accessor :artist
|
28
|
+
|
29
|
+
# @return [String]
|
30
|
+
attr_accessor :track_title
|
31
|
+
|
32
|
+
# @return [String]
|
33
|
+
attr_accessor :album_title
|
34
|
+
|
35
|
+
# At least one of :artist, :track_title, :album_title is required (:mode is optional).
|
36
|
+
#
|
37
|
+
# @param [Hash] args the arguments for an Album_Search.
|
38
|
+
# @option args [String] :artist Name of the artist.
|
39
|
+
# @option args [String] :track_title Name of the song/track.
|
40
|
+
# @option args [String] :album_title Name of the album.
|
41
|
+
# @option args [String] :mode Either 'SINGLE_BEST' or 'SINGLE_BEST_COVER'
|
42
|
+
def initialize(args={})
|
43
|
+
super args[:mode]
|
44
|
+
@parameters = args.reject {|k,v| k == :mode}
|
45
|
+
parameters.each do |k,v| send("#{k}=", v) end
|
46
|
+
@query = build_query
|
47
|
+
end
|
48
|
+
|
49
|
+
# Builds ALBUM_SEARCH-specific part of ALBUM_SEARCH query and adds it to the base query
|
50
|
+
# common to all query types. Called by constructor.
|
51
|
+
#
|
52
|
+
# @return [String] XML string for ALBUM_SEARCH query.
|
53
|
+
def build_query
|
54
|
+
build_base_query do |builder|
|
55
|
+
builder.query(cmd: "ALBUM_SEARCH"){
|
56
|
+
builder.mode "SINGLE_BEST_COVER"
|
57
|
+
parameters.each do |k,v|
|
58
|
+
builder.text(v, type: k.to_s.upcase)
|
59
|
+
end
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'under_fire/base_query'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
module UnderFire
|
5
|
+
# Builds XML for Gracenote's ALBUM_TOC query
|
6
|
+
#
|
7
|
+
# @see 'https://developer.gracenote.com/sites/default/files/web/html/index.html#Music%20Web%20API/ALBUM_TOC.html#_Toc344907258'
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# search = UnderFire::AlbumTOCSearch.new(:toc => '182 10762 22515 32372 43735 53335 63867 78305 89792 98702 110612 122590 132127 141685')
|
11
|
+
# search.query #=> XML query string for ALBUM_TOC query.
|
12
|
+
class AlbumTOCSearch < BaseQuery
|
13
|
+
# @return [String] CD Table of contents.
|
14
|
+
attr_reader :toc
|
15
|
+
|
16
|
+
# @return [String] XML string for ALBUM_TOC query.
|
17
|
+
attr_reader :query
|
18
|
+
|
19
|
+
# :toc is required (:mode is optional).
|
20
|
+
#
|
21
|
+
# @param [Hash] args arguments to create an ALBUM_TOC query.
|
22
|
+
# @option [String] :toc CD table of contents (space-separated list of track start frames)
|
23
|
+
# @option [String] :mode Either 'SINGLE_BEST' or 'SINGLE_BEST_COVER'
|
24
|
+
def initialize(args)
|
25
|
+
super args[:mode]
|
26
|
+
@toc = args[:toc]
|
27
|
+
@query = build_query
|
28
|
+
end
|
29
|
+
|
30
|
+
# Builds TOC-specific part of ALBUM_TOC query and adds it to the base query
|
31
|
+
# common to all query types. Called by constructor.
|
32
|
+
#
|
33
|
+
# @return [String] XML string for ALBUM_TOC query.
|
34
|
+
def build_query
|
35
|
+
build_base_query do |builder|
|
36
|
+
builder.query(cmd: "ALBUM_TOC"){
|
37
|
+
builder.mode mode
|
38
|
+
builder.toc {
|
39
|
+
builder.offsets toc
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'uri'
|
4
|
+
require 'open-uri'
|
5
|
+
|
6
|
+
module UnderFire
|
7
|
+
# HTTP requests required for Gracenote API.
|
8
|
+
#
|
9
|
+
# @todo Error handling
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# response = UnderFire::ApiRequest.post(query_xml, api_url)
|
13
|
+
#
|
14
|
+
# response = UnderFire::ApiRequest.get_file(image_url, filename)
|
15
|
+
class APIRequest
|
16
|
+
# @param [String] query XML query string
|
17
|
+
# @param [String] api_url url for your application
|
18
|
+
# @return [Net::HTTPResponse]
|
19
|
+
def self.post(query, api_url)
|
20
|
+
uri = URI(api_url)
|
21
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
22
|
+
http.use_ssl = true
|
23
|
+
http.ssl_version = 'SSLv3'
|
24
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
25
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
26
|
+
req.body = query
|
27
|
+
req['Content-Type'] = 'application/xml'
|
28
|
+
res = http.request(req)
|
29
|
+
res
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param [String] url URL that points to file.
|
33
|
+
# @param [String] filename Filename and path for saving downloaded file.
|
34
|
+
def self.get_file(url, filename)
|
35
|
+
uri = URI url
|
36
|
+
|
37
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
38
|
+
request = Net::HTTP::Get.new uri
|
39
|
+
|
40
|
+
http.request request do |response|
|
41
|
+
open filename, 'w' do |io|
|
42
|
+
response.read_body do |chunk|
|
43
|
+
io.write chunk
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'nori'
|
2
|
+
|
3
|
+
module UnderFire
|
4
|
+
# Wraps query response, providing to_h and to_s methods for easy processing.
|
5
|
+
class APIResponse
|
6
|
+
# @return [Hash] Response as Hash.
|
7
|
+
attr_reader :response
|
8
|
+
|
9
|
+
# @param [String] response XML string.
|
10
|
+
def initialize(response)
|
11
|
+
@response = parse_response(response)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Hash] Hash representation of response.
|
15
|
+
def to_h
|
16
|
+
response[:responses]
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [String] String represenation suitable for command line.
|
20
|
+
def to_s
|
21
|
+
recursive_to_s(to_h)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean] Did the query return something?
|
25
|
+
def success?
|
26
|
+
response[:responses][:response][:@status] == 'OK'
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Recursively walks nested hash structure to return string representation.
|
32
|
+
# @return [String] Flat string representation of nest Hash.
|
33
|
+
def recursive_to_s(hash)
|
34
|
+
output = ""
|
35
|
+
hash.each do |k,v|
|
36
|
+
if v.is_a? Hash
|
37
|
+
output << "\n"
|
38
|
+
output << "#{k}:\n#{recursive_to_s(v)}\n"
|
39
|
+
elsif v.is_a? Array
|
40
|
+
output << "\n"
|
41
|
+
v.each {|i| output << "#{recursive_to_s(i)}\n" }
|
42
|
+
else
|
43
|
+
output << "#{k}: #{v}\n"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
output
|
47
|
+
end
|
48
|
+
|
49
|
+
# Builds hash from XML response.
|
50
|
+
# @param [String] response XML response string.
|
51
|
+
# @return [Hash] Hash representation of response.
|
52
|
+
def parse_response(response)
|
53
|
+
parser = Nori.new(:convert_tags_to => lambda {|tag| tag.snakecase.to_sym })
|
54
|
+
parser.parse(response)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'under_fire/configuration'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
module UnderFire
|
5
|
+
# Builds an XML query with information common to all queries (to be subclassed by individual queries).
|
6
|
+
class BaseQuery
|
7
|
+
# @return [String]
|
8
|
+
attr_reader :mode
|
9
|
+
|
10
|
+
# @param [String] mode Either 'SINGLE_BEST' or 'SINGLE_BEST_COVER' (defaults to 'SINGLE_BEST_COVER').
|
11
|
+
def initialize(mode="SINGLE_BEST_COVER")
|
12
|
+
@mode = mode || "SINGLE_BEST_COVER"
|
13
|
+
end
|
14
|
+
|
15
|
+
# @yield [Builder] builder object used by subclass's build_query method.
|
16
|
+
def build_base_query(&block)
|
17
|
+
builder = Builder::XmlMarkup.new
|
18
|
+
builder.queries {
|
19
|
+
builder.auth {
|
20
|
+
builder.client Configuration.client_id
|
21
|
+
builder.user Configuration.user_id
|
22
|
+
}
|
23
|
+
builder.lang "eng"
|
24
|
+
builder.country "canada"
|
25
|
+
builder.app_info %Q{app="under-fire #{VERSION}", os="#{RUBY_PLATFORM}"}
|
26
|
+
yield builder
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'under_fire/api_request'
|
3
|
+
require 'under_fire/api_response'
|
4
|
+
require 'under_fire/album_toc_search'
|
5
|
+
require 'under_fire/album_search'
|
6
|
+
require 'under_fire/toc_reader'
|
7
|
+
|
8
|
+
module UnderFire
|
9
|
+
# Command Line interface
|
10
|
+
class CLI < Thor
|
11
|
+
include UnderFire
|
12
|
+
|
13
|
+
def initialize(*)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "toc", "Uses `discid` to get a CD's table of contents and then " +
|
18
|
+
"uses the TOC to query Gracenote for album information."
|
19
|
+
def toc
|
20
|
+
search = AlbumTOCSearch.new(:toc => TOCReader.read)
|
21
|
+
response = APIRequest.post(search.query, Configuration.api_url)
|
22
|
+
say APIResponse.new(response.body).to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "album", "Queries Gracenote with album <title>, <song> title, or <artist> name"
|
26
|
+
method_option :album_title,
|
27
|
+
:aliases => '-t',
|
28
|
+
:desc => "Specify album title",
|
29
|
+
:required => false
|
30
|
+
method_option :track_title,
|
31
|
+
:aliases => ['-s', :song_title],
|
32
|
+
:desc => "Specify song title",
|
33
|
+
:required => false
|
34
|
+
method_option :artist,
|
35
|
+
:aliases => '-a',
|
36
|
+
:desc => "Specify artist name",
|
37
|
+
:required => false
|
38
|
+
def album
|
39
|
+
search = AlbumSearch.new(options)
|
40
|
+
request = APIRequest.post(search.query, Configuration.api_url)
|
41
|
+
say APIResponse.new(request.body).to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "id", "Fetches album info using given Gracenote ID."
|
45
|
+
method_option :gn_id, :aliases => ['-i', '--id'], :required => true,
|
46
|
+
:desc => "Gracenote album or song GN_ID"
|
47
|
+
def id
|
48
|
+
search = AlbumFetch.new(options)
|
49
|
+
request = APIRequest.post(search.query, Configuration.api_url)
|
50
|
+
say APIResponse.new(request.body).to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "cover", "Gets cover from Gracenote."
|
54
|
+
method_option :url, :aliases => '-u', :required => true,
|
55
|
+
:desc => "URL provided by Gracenote for downloading cover image."
|
56
|
+
method_option :file_name, :aliases => '-f', :required => false,
|
57
|
+
:desc => "File name for saving image."
|
58
|
+
def cover
|
59
|
+
say "Fetching cover"
|
60
|
+
url = options[:url]
|
61
|
+
file_name = options[:file_name].empty? ? "cover.jpg" : options[:file_name]
|
62
|
+
APIRequest.get_file(url, file_name)
|
63
|
+
say "saved #{file_name} in #{File.dirname __FILE__}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'under_fire/album_search'
|
2
|
+
require 'under_fire/album_toc_search'
|
3
|
+
require 'under_fire/album_fetch'
|
4
|
+
require 'under_fire/api_request'
|
5
|
+
require 'under_fire/api_response'
|
6
|
+
require 'under_fire/configuration'
|
7
|
+
require 'under_fire/toc_reader'
|
8
|
+
|
9
|
+
require 'pry'
|
10
|
+
|
11
|
+
module UnderFire
|
12
|
+
# Public interface to UnderFire's functionality.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# client = UnderFire::Client.new
|
16
|
+
# client.album_search(:artist => 'Miles Davis') #=> lots of results
|
17
|
+
#
|
18
|
+
# client = UnderFire::Client.new
|
19
|
+
# client.find_by_toc
|
20
|
+
class Client
|
21
|
+
include UnderFire
|
22
|
+
|
23
|
+
# @return [String] API URL for application.
|
24
|
+
attr_reader :api_url
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@api_url = Configuration.api_url
|
28
|
+
end
|
29
|
+
|
30
|
+
# Searches for album using TOC of CD in drive.
|
31
|
+
# @return [APIResponse]
|
32
|
+
# @see UnderFire::AlbumTOCSearch
|
33
|
+
def find_by_toc
|
34
|
+
search = AlbumTOCSearch.new(get_toc)
|
35
|
+
response = APIRequest.post(search.query, api_url)
|
36
|
+
APIResponse.new(response.body)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Finds album using one or more of :artist, :track_title and :album_title
|
40
|
+
# @return [APIResponse]
|
41
|
+
# @see UnderFire::AlbumSearch Description of arguments.
|
42
|
+
def find_album(args)
|
43
|
+
search = AlbumSearch.new(args)
|
44
|
+
response = APIRequest.post(search.query, api_url)
|
45
|
+
APIResponse.new(response.body)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Fetches album with given album :gn_id or track :gn_id
|
49
|
+
# @return [APIResponse]
|
50
|
+
# @see UnderFire::AlbumFetch Description of arguments.
|
51
|
+
def fetch_album(args)
|
52
|
+
search = AlbumFetch.new(args)
|
53
|
+
response = APIRequest.post(search.query, api_url)
|
54
|
+
APIResponse.new(response.body)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Fetches cover art using results of query.
|
58
|
+
# @param [APIResponse] response
|
59
|
+
def fetch_cover(response)
|
60
|
+
res = response.to_h
|
61
|
+
response_url = res['RESPONSE']['ALBUM']['URL']
|
62
|
+
title = res['RESPONSE']['ALBUM']['TITLE']
|
63
|
+
APIRequest.get_file(response_url, "#{title}-cover.jpeg")
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def get_toc
|
69
|
+
TOCReader.read
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module UnderFire
|
2
|
+
# Configuration information. Currently stored as environment variables.
|
3
|
+
module Configuration
|
4
|
+
# Gracenote client id stored in environment variable.
|
5
|
+
# @return [String]
|
6
|
+
def client_id
|
7
|
+
ENV['GRACENOTE_CLIENT_ID']
|
8
|
+
end
|
9
|
+
|
10
|
+
# Part of client id before the hyphen (used by api_url).
|
11
|
+
# @return [String]
|
12
|
+
def client_id_string
|
13
|
+
ci_string, _ = ENV['GRACENOTE_CLIENT_ID'].split('-')
|
14
|
+
ci_string
|
15
|
+
end
|
16
|
+
|
17
|
+
# Part of client id after hyphen
|
18
|
+
# @return [String]
|
19
|
+
def client_tag
|
20
|
+
_, ct = ENV['GRACENOTE_CLIENT_ID'].split('-')
|
21
|
+
ct
|
22
|
+
end
|
23
|
+
|
24
|
+
# Gracenote user id stored as environment variable.
|
25
|
+
# @return [String]
|
26
|
+
def user_id
|
27
|
+
ENV['GRACENOTE_USER_ID']
|
28
|
+
end
|
29
|
+
|
30
|
+
# Gracenote API url for use in queries.
|
31
|
+
# @return [String]
|
32
|
+
def api_url
|
33
|
+
"https://c#{client_id_string}.web.cddbp.net/webapi/xml/1.0/"
|
34
|
+
end
|
35
|
+
|
36
|
+
module_function :client_id, :client_tag, :api_url, :user_id,
|
37
|
+
:client_id_string
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'under_fire/configuration'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
module UnderFire
|
5
|
+
# Register an application using client_id (only needs to be done once per application).
|
6
|
+
#
|
7
|
+
# @see https://developer.gracenote.com/sites/default/files/web/html/index.html#Music%20Web%20API/Registration%20and%20Authentication.html#_Toc344907213
|
8
|
+
class Registration
|
9
|
+
# @return [String] XML string for query
|
10
|
+
attr_reader :query
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@query = build_query
|
14
|
+
end
|
15
|
+
|
16
|
+
# Builds XML for REGISTRATION query.
|
17
|
+
#
|
18
|
+
# @return [String] XML string for REGISTRATION
|
19
|
+
def build_query
|
20
|
+
builder = Builder::XmlMarkup.new
|
21
|
+
xml = builder.queries {
|
22
|
+
builder.query(cmd: 'REGISTER'){
|
23
|
+
builder.client UnderFire::Configuration.client_id
|
24
|
+
}
|
25
|
+
}
|
26
|
+
xml
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'discid'
|
2
|
+
|
3
|
+
module UnderFire
|
4
|
+
# Reads CD and returns table of contents (start frame of each track).
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# UnderFire.TOCReader.read
|
8
|
+
class TOCReader
|
9
|
+
# @return [Array<String>] array of start frame strings
|
10
|
+
def self.read
|
11
|
+
device = "/dev/cdrom"
|
12
|
+
disc = DiscId.read(device)
|
13
|
+
toc = []
|
14
|
+
disc.tracks {|t| toc << t.offset.to_s}
|
15
|
+
toc.join(" ")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module UnderFire
|
2
|
+
# Because I always forget:
|
3
|
+
#
|
4
|
+
# Given a version number MAJOR.MINOR.PATCH, increment the:
|
5
|
+
# MAJOR version when you make incompatible API changes,
|
6
|
+
# MINOR version when you add functionality in a backwards-compatible manner, and
|
7
|
+
# PATCH version when you make backwards-compatible bug fixes.
|
8
|
+
# Refer to: http://semver.org/
|
9
|
+
VERSION = "0.6.1"
|
10
|
+
end
|
data/lib/under_fire.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'under_fire/version'
|
2
|
+
require 'under_fire/configuration'
|
3
|
+
require 'under_fire/album_search'
|
4
|
+
require 'under_fire/album_fetch'
|
5
|
+
require 'under_fire/registration'
|
6
|
+
require 'under_fire/api_request'
|
7
|
+
require 'under_fire/api_response'
|
8
|
+
require 'under_fire/album_toc_search'
|
9
|
+
require 'under_fire/cli'
|
10
|
+
|
11
|
+
# A wrapper library for the Gracenote web API that also provides a command line executable (under-fire).
|
12
|
+
module UnderFire
|
13
|
+
end
|