under_fire 0.6.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.
- 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
|