lib/echonest/api.rb in ruby-echonest-0.0.6 vs lib/echonest/api.rb in ruby-echonest-0.1.1
- old
+ new
@@ -1,198 +1,144 @@
+# -*- coding: utf-8 -*-
require 'digest/md5'
require 'httpclient'
+require 'json'
module Echonest
class Api
- VERSION = '3'
- BASE_URL = 'http://developer.echonest.com/api/'
+ BASE_URL = 'http://developer.echonest.com/api/v4/'
USER_AGENT = '%s/%s' % ['ruby-echonest', ::Echonest::VERSION]
+ include TraditionalApiMethods
+
class Error < StandardError; end
attr_reader :user_agent
def initialize(api_key)
@api_key = api_key
@user_agent = HTTPClient.new(:agent_name => USER_AGENT)
end
- def get_bars(filename)
- get_analysys(:get_bars, filename) do |analysis|
- analysis.map do |bar|
- Bar.new(bar.content.to_f, bar['confidence'].to_f)
- end
- end
+ def track
+ ApiMethods::Track.new(self)
end
- def get_beats(filename)
- get_analysys(:get_beats, filename) do |analysis|
- analysis.map do |beat|
- Beat.new(beat.content.to_f, beat['confidence'].to_f)
- end
- end
+ def build_params(params)
+ params = params.
+ merge(:format => 'json').
+ merge(:api_key => @api_key)
end
- def get_segments(filename)
- get_analysys(:get_segments, filename) do |analysis|
- analysis.map do |segment|
- max_loudness = loudness = nil
+ def request(name, method, params, file = nil)
+ if file
+ query = build_params(params).sort_by do |param|
+ param[0].to_s
+ end.inject([]) do |m, param|
+ m << [URI.encode(param[0].to_s), URI.encode(param[1])].join('=')
+ end.join('&')
- segment.find('loudness/dB').map do |db|
- if db['type'] == 'max'
- max_loudness = Loudness.new(db['time'].to_f, db.content.to_f)
- else
- loudness = Loudness.new(db['time'].to_f, db.content.to_f)
- end
- end
+ uri = URI.join(BASE_URL, name.to_s)
+ uri.query = query
- pitches = segment.find('pitches/pitch').map do |pitch|
- pitch.content.to_f
- end
-
- timbre = segment.find('timbre/coeff').map do |coeff|
- coeff.content.to_f
- end
-
- Segment.new(
- segment['start'].to_f,
- segment['duration'].to_f,
- loudness,
- max_loudness,
- pitches,
- timbre
- )
- end
+ response_body = @user_agent.__send__(
+ method.to_s + '_content',
+ uri,
+ file.read,
+ {
+ 'Content-Type' => 'application/octet-stream'
+ })
+ else
+ response_body = @user_agent.__send__(
+ method.to_s + '_content',
+ URI.join(BASE_URL, name.to_s),
+ build_params(params))
end
- end
- def get_tempo(filename)
- get_analysys(:get_tempo, filename) do |analysis|
- analysis.first.content.to_f
+ response = Response.new(response_body)
+ unless response.success?
+ raise Error.new(response.status.message)
end
- end
- def get_sections(filename)
- get_analysys(:get_sections, filename) do |analysis|
- analysis.map do |section|
- Section.new(
- section['start'].to_f,
- section['duration'].to_f
- )
- end
- end
+ response
+ rescue HTTPClient::BadResponseError => e
+ raise Error.new('%s: %s' % [name, e.message])
end
+ end
- def get_duration(filename)
- get_analysys(:get_duration, filename) do |analysis|
- analysis.first.content.to_f
+ module ApiMethods
+ class Base
+ def initialize(api)
+ @api = api
end
end
- def get_end_of_fade_in(filename)
- get_analysys(:get_end_of_fade_in, filename) do |analysis|
- analysis.first.content.to_f
+ class Track < Base
+ def profile(options)
+ @api.request('track/profile',
+ :get,
+ options.merge(:bucket => 'audio_summary'))
end
- end
- def get_key(filename)
- get_analysys(:get_key, filename) do |analysis|
- ValueWithConfidence.new(analysis.first.content.to_i, analysis.first['confidence'].to_f)
+ def analyze(options)
+ @api.request('track/analyze',
+ :post,
+ options.merge(:bucket => 'audio_summary'))
end
- end
- def get_loudness(filename)
- get_analysys(:get_loudness, filename) do |analysis|
- analysis.first.content.to_f
- end
- end
+ def upload(options)
+ options.update(:bucket => 'audio_summary')
- def get_metadata(filename)
- get_analysys(:get_metadata, filename) do |analysis|
- analysis.inject({}) do |memo, key|
- memo[key.name] = key.content
- memo
- end
- end
- end
+ if options.has_key?(:filename)
+ filename = options.delete(:filename)
+ filetype = filename.to_s.match(/\.(mp3|au|ogg)$/)[1]
- def get_mode(filename)
- get_analysys(:get_mode, filename) do |analysis|
- ValueWithConfidence.new(analysis.first.content.to_i, analysis.first['confidence'].to_f)
- end
- end
-
- def get_start_of_fade_out(filename)
- get_analysys(:get_start_of_fade_out, filename) do |analysis|
- analysis.first.content.to_f
- end
- end
-
- def get_tatums(filename)
- get_analysys(:get_tatums, filename) do |analysis|
- analysis.map do |tatum|
- Tatum.new(tatum.content.to_f, tatum['confidence'].to_f)
+ open(filename) do |f|
+ @api.request('track/upload',
+ :post,
+ options.merge(:filetype => filetype),
+ f)
+ end
+ else
+ @api.request('track/upload', :post, options)
end
end
- end
- def get_time_signature(filename)
- get_analysys(:get_time_signature, filename) do |analysis|
- ValueWithConfidence.new(analysis.first.content.to_i, analysis.first['confidence'].to_f)
+ def analysis(filename)
+ analysis_url = analysis_url(filename)
+ Analysis.new_from_url(analysis_url)
end
- end
- def build_params(params)
- params = params.
- merge(:version => VERSION).
- merge(:api_key => @api_key)
- end
+ def analysis_url(filename)
+ md5 = Digest::MD5.hexdigest(open(filename).read)
- def get_analysys(method, filename)
- get_trackinfo(method, filename) do |response|
- yield response.xml.find_first('/response/analysis')
- end
- end
+ while true
+ begin
+ response = profile(:md5 => md5)
+ rescue Api::Error => e
+ if e.message =~ /^The Identifier specified does not exist/
+ response = upload(:filename => filename)
+ else
+ raise
+ end
+ end
- def get_trackinfo(method, filename, &block)
- content = open(filename).read
- md5 = Digest::MD5.hexdigest(content)
+ case response.body.track.status
+ when 'unknown'
+ upload(:filename => filename)
+ when 'pending'
+ sleep 60
+ when 'complete'
+ return response.body.track.audio_summary.analysis_url
+ when 'error'
+ raise Error.new(response.body.track.status)
+ when 'unavailable'
+ analyze(:md5 => md5)
+ end
- begin
- response = request(method, :md5 => md5)
-
- block.call(response)
- rescue Echonest::Api::Error => e
- case e.message
- when /Analysis not ready/
- sleep 20 # wait for serverside analysis
- get_trackinfo(method, filename, &block)
- when 'Invalid parameter: unknown MD5 file hash'
- upload(filename)
- sleep 60 # wait for serverside analysis
- get_trackinfo(method, filename, &block)
- else
- raise
+ sleep 5
end
end
- end
-
- def upload(filename)
- open(filename) do |f|
- request(:upload, :file => f)
- end
- end
-
- def request(name, params)
- method = (name == :upload ? 'post' : 'get')
- response_body = @user_agent.__send__(method + '_content', URI.join(BASE_URL, name.to_s), build_params(params))
- response = Response.new(response_body)
-
- unless response.success?
- raise Error.new(response.status.message)
- end
-
- response
end
end
end
class HTTPClient