itunes 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rspec +3 -0
- data/Gemfile +3 -0
- data/README.md +56 -44
- data/Rakefile +19 -0
- data/itunes.gemspec +43 -0
- data/lib/faraday/rashify.rb +30 -0
- data/lib/itunes.rb +20 -67
- data/lib/itunes/client.rb +26 -0
- data/lib/itunes/client/lookup.rb +21 -0
- data/lib/itunes/client/search.rb +81 -0
- data/lib/itunes/configuration.rb +56 -0
- data/lib/itunes/request.rb +30 -0
- data/lib/itunes/version.rb +2 -2
- data/spec/faraday/rashify_spec.rb +17 -0
- data/spec/fixtures/Faraday_Response_Rashify.yml +46 -0
- data/spec/fixtures/ITunes.yml +906 -0
- data/spec/fixtures/ITunes_Client.yml +936 -0
- data/spec/itunes/client_spec.rb +164 -0
- data/spec/itunes_spec.rb +55 -0
- data/spec/spec_helper.rb +20 -0
- metadata +242 -14
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -1,43 +1,48 @@
|
|
1
|
-
|
1
|
+
iTunes
|
2
|
+
======
|
3
|
+
|
2
4
|
A Ruby wrapper around the iTunes API that lets you search for any sort of data that is available on the iTunes store.
|
3
5
|
|
4
|
-
|
6
|
+
Example Response
|
7
|
+
----------------
|
5
8
|
|
6
9
|
{
|
7
|
-
"
|
8
|
-
"
|
9
|
-
"
|
10
|
-
"
|
11
|
-
"
|
12
|
-
"
|
13
|
-
"
|
14
|
-
"
|
15
|
-
"
|
16
|
-
"
|
17
|
-
"
|
18
|
-
"
|
19
|
-
"
|
10
|
+
"artist_id" : 954266,
|
11
|
+
"artist_name" : "Green Day",
|
12
|
+
"artist_view_url" : "http://itunes.apple.com/us/artist/green-day/id954266?uo=4",
|
13
|
+
"artwork_url100" : "http://a1.phobos.apple.com/us/r1000/049/Features/1e/17/05/dj.rpihtiig.100x100-75.jpg",
|
14
|
+
"artwork_url30" : "http://a1.phobos.apple.com/us/r1000/049/Features/1e/17/05/dj.rpihtiig.30x30-50.jpg",
|
15
|
+
"artwork_url60" : "http://a1.phobos.apple.com/us/r1000/049/Features/1e/17/05/dj.rpihtiig.60x60-50.jpg",
|
16
|
+
"collection_censored_name" : "Dookie",
|
17
|
+
"collection_explicitness" : "explicit",
|
18
|
+
"collection_id" : 5132583,
|
19
|
+
"collection_name" : "Dookie",
|
20
|
+
"collection_price" : 9.99,
|
21
|
+
"collection_view_url" : "http://itunes.apple.com/us/album/she/id5132583?i=5132563&uo=4",
|
22
|
+
"content_advisory_rating" : null,
|
20
23
|
"country" : "USA",
|
21
24
|
"currency" : "USD",
|
22
|
-
"
|
23
|
-
"
|
25
|
+
"disc_count" : 1,
|
26
|
+
"disc_number" : 1,
|
24
27
|
"kind" : "song",
|
25
|
-
"
|
26
|
-
"
|
27
|
-
"
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"
|
31
|
-
"
|
32
|
-
"
|
33
|
-
"
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
28
|
+
"preview_url" : "http://a1.phobos.apple.com/us/r1000/027/Music/0e/86/7a/mzm.wchstext.aac.p.m4a",
|
29
|
+
"primary_genre_name" : "Alternative",
|
30
|
+
"release_date" : "2003-04-22 07:00:00 Etc/GMT",
|
31
|
+
"track_censored_name" : "She",
|
32
|
+
"track_count" : 15,
|
33
|
+
"track_explicitness" : "notExplicit",
|
34
|
+
"track_id" : 5132563,
|
35
|
+
"track_name" : "She",
|
36
|
+
"track_number" : 8,
|
37
|
+
"track_price" : 1.29,
|
38
|
+
"track_time_millis" : 134293,
|
39
|
+
"track_view_url" : "http://itunes.apple.com/us/album/she/id5132583?i=5132563&uo=4",
|
40
|
+
"wrapper_type" : "track"
|
38
41
|
}
|
39
42
|
|
40
|
-
|
43
|
+
Available Methods
|
44
|
+
-----------------
|
45
|
+
|
41
46
|
- movie
|
42
47
|
- podcast
|
43
48
|
- music
|
@@ -47,31 +52,38 @@ A Ruby wrapper around the iTunes API that lets you search for any sort of data t
|
|
47
52
|
- tv_show
|
48
53
|
- all
|
49
54
|
|
50
|
-
|
55
|
+
Using the iTunes gem
|
56
|
+
--------------------
|
51
57
|
|
52
58
|
require 'itunes'
|
53
|
-
|
54
|
-
>> itunes = ITunes.new
|
59
|
+
|
60
|
+
>> itunes = ITunes::Client.new
|
55
61
|
>> songs = itunes.music('green day she')
|
56
|
-
=>
|
57
|
-
>> songs
|
58
|
-
>> puts "#{song
|
62
|
+
=> <#Hashie::Rash result_count=15 results=[...]>
|
63
|
+
>> songs.results.each do |song|
|
64
|
+
>> puts "#{song.track_name} - #{song.artist_name} (#{song.collection_name})"
|
59
65
|
>> end
|
60
66
|
=> She - Green Day (Dookie)
|
61
67
|
=> She - Green Day (Dookie)
|
62
68
|
=> She - Green Day (Dookie)
|
63
69
|
=> She - Green Day (Dookie)
|
64
70
|
=> She - Green Day (Dookie)
|
65
|
-
|
71
|
+
|
72
|
+
Search directly from the class
|
73
|
+
|
66
74
|
>> iron_man = ITunes.movie('iron man 2')
|
67
|
-
=>
|
68
|
-
|
75
|
+
=> <#Hashie::Rash result_count=1 results=[...]>
|
76
|
+
|
77
|
+
Limit the results:
|
78
|
+
|
69
79
|
>> foo_fighters = ITunes.music('foo fighters everlong', :limit => 1)
|
70
|
-
=>
|
80
|
+
=> <#Hashie::Rash result_count=1 results=[<#Hashie::Rash ...>]>
|
81
|
+
|
82
|
+
What is Hashie::Rash?
|
71
83
|
|
72
|
-
|
84
|
+
If you are familiar with Hashie, Rash is very similar to a Mash. The primary difference is that Rash gives us ruby-friendly keys so instead of artistId, Rash converts that to artist_id.
|
73
85
|
|
74
|
-
|
86
|
+
Copyright
|
87
|
+
---------
|
75
88
|
|
76
|
-
## Copyright
|
77
89
|
Copyright © 2010 Garrett Bjerkhoel. See [MIT-LICENSE](http://github.com/dewski/itunes/blob/master/MIT-LICENSE) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
namespace :doc do
|
10
|
+
require 'yard'
|
11
|
+
YARD::Rake::YardocTask.new do |task|
|
12
|
+
task.files = ['lib/**/*.rb']
|
13
|
+
task.options = [
|
14
|
+
'--protected',
|
15
|
+
'--output-dir', 'doc/yard',
|
16
|
+
'--markup', 'markdown',
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
data/itunes.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "itunes/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "itunes"
|
7
|
+
s.version = ITunes::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Garrett Bjerkhoel"]
|
10
|
+
s.email = %q{me@garrettbjerkhoel.com}
|
11
|
+
s.homepage = %q{http://github.com/dewski/itunes}
|
12
|
+
s.summary = %q{iTunes API}
|
13
|
+
|
14
|
+
s.rubyforge_project = "itunes"
|
15
|
+
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"MIT-LICENSE",
|
18
|
+
"README.md"
|
19
|
+
]
|
20
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
21
|
+
|
22
|
+
s.add_development_dependency('bundler', '~> 1.0')
|
23
|
+
s.add_development_dependency('rake', '~> 0.8')
|
24
|
+
s.add_development_dependency('rspec', '~> 2.5.0')
|
25
|
+
s.add_development_dependency('yard', '~> 0.6')
|
26
|
+
s.add_development_dependency('maruku', '~> 0.6')
|
27
|
+
s.add_development_dependency('simplecov', '~> 0.3')
|
28
|
+
s.add_development_dependency('webmock', '~> 1.6')
|
29
|
+
s.add_development_dependency('vcr', '~> 1.4.0')
|
30
|
+
s.add_development_dependency('json', '~> 1.5.1')
|
31
|
+
|
32
|
+
s.add_runtime_dependency('hashie', '~> 1.0.0')
|
33
|
+
s.add_runtime_dependency('rash', '~> 0.2.0')
|
34
|
+
s.add_runtime_dependency('faraday', '~> 0.5.4')
|
35
|
+
s.add_runtime_dependency('faraday_middleware', '~> 0.3.1')
|
36
|
+
s.add_runtime_dependency('multi_json', '~> 0.0.5')
|
37
|
+
|
38
|
+
s.files = `git ls-files`.split("\n")
|
39
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
40
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
# @private
|
4
|
+
module Faraday
|
5
|
+
# @private
|
6
|
+
class Response::Rashify < Response::Middleware
|
7
|
+
begin
|
8
|
+
require 'hashie'
|
9
|
+
require 'rash'
|
10
|
+
rescue LoadError, NameError => error
|
11
|
+
self.load_error = error
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.register_on_complete(env)
|
15
|
+
env[:response].on_complete do |response|
|
16
|
+
response_body = response[:body]
|
17
|
+
if response_body.is_a?(Hash)
|
18
|
+
response[:body] = ::Hashie::Rash.new(response_body)
|
19
|
+
elsif response_body.is_a?(Array)
|
20
|
+
response[:body] = response_body.map{|item| item.is_a?(Hash) ? ::Hashie::Rash.new(item) : item}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(app)
|
26
|
+
super
|
27
|
+
@parser = nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/itunes.rb
CHANGED
@@ -1,73 +1,26 @@
|
|
1
|
-
require '
|
1
|
+
require 'faraday_middleware'
|
2
|
+
require 'faraday/rashify'
|
3
|
+
require 'itunes/configuration'
|
4
|
+
require 'itunes/client'
|
2
5
|
|
3
|
-
|
4
|
-
|
6
|
+
module ITunes
|
7
|
+
extend Configuration
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def method_missing(name, *args)
|
12
|
-
methods = %q{movie podcast music music_video audiobook short_film tv_show all}
|
13
|
-
|
14
|
-
if methods.include?(name.to_s)
|
15
|
-
camelcase = name.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
16
|
-
camelcase[0] = camelcase[0].chr.downcase
|
17
|
-
|
18
|
-
@limit = args[1].delete(:limit) unless args[1].nil? # will return nil if there is no limit
|
19
|
-
|
20
|
-
request(args.first, camelcase)
|
21
|
-
else
|
22
|
-
super(name, *args)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def initialize(limit=nil)
|
27
|
-
@limit = limit
|
28
|
-
end
|
29
|
-
|
30
|
-
# So you don't have to create an instance if you don't need to
|
31
|
-
def self.music(terms, opts=nil)
|
32
|
-
self.new.music(terms, opts)
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.podcast(terms, opts=nil)
|
36
|
-
self.new.podcast(terms, opts)
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.movie(terms, opts=nil)
|
40
|
-
self.new.movie(terms, opts)
|
9
|
+
# Alias for ITunes::Client.new
|
10
|
+
#
|
11
|
+
# @return [ITunes::Client]
|
12
|
+
def self.client(options={})
|
13
|
+
ITunes::Client.new(options)
|
41
14
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def self.audiobook(terms, opts=nil)
|
48
|
-
self.new.audiobook(terms, opts)
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.short_film(terms, opts=nil)
|
52
|
-
self.new.short_film(terms, opts)
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.tv_show(terms, opts=nil)
|
56
|
-
self.new.tv_show(terms, opts)
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.all(terms, opts=nil)
|
60
|
-
self.new.all(terms, opts)
|
15
|
+
|
16
|
+
# Delegate to ITunes::Client
|
17
|
+
def self.method_missing(method, *args, &block)
|
18
|
+
return super unless client.respond_to?(method)
|
19
|
+
client.send(method, *args, &block)
|
61
20
|
end
|
62
21
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
#
|
68
|
-
query = { :term => term, :media => media }
|
69
|
-
query.merge!({ :limit => @limit }) unless @limit.nil? or @limit == 0
|
70
|
-
|
71
|
-
self.class.get('Search', { :query => query })
|
72
|
-
end
|
22
|
+
# Delegate to ITunes::Client
|
23
|
+
def self.respond_to?(method)
|
24
|
+
return client.respond_to?(method) || super
|
25
|
+
end
|
73
26
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../request', __FILE__)
|
2
|
+
|
3
|
+
module ITunes
|
4
|
+
class Client
|
5
|
+
|
6
|
+
# @private
|
7
|
+
attr_accessor *Configuration::VALID_OPTIONS_KEYS
|
8
|
+
|
9
|
+
# Creates a new Client
|
10
|
+
def initialize(options={})
|
11
|
+
options = ITunes.options.merge(options)
|
12
|
+
Configuration::VALID_OPTIONS_KEYS.each do |key|
|
13
|
+
send("#{key}=", options[key])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Dir[File.expand_path('../client/*.rb', __FILE__)].each{|f| require f}
|
18
|
+
|
19
|
+
alias :api_endpoint :endpoint
|
20
|
+
|
21
|
+
include Request
|
22
|
+
include Search
|
23
|
+
include Lookup
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ITunes
|
2
|
+
module Lookup
|
3
|
+
|
4
|
+
ID_TYPES = { :amg_artist => 'amgArtistId', :id => 'id', :amg_album => 'amgAlbumId', :upc => 'upc' }
|
5
|
+
|
6
|
+
# Performs a lookup request based on iTunes IDs, UPCs/ EANs, and All Music Guide (AMG) IDs
|
7
|
+
# @param [String] id
|
8
|
+
# @option options [Symbol] :id_type used to specify the option type being passed, valid types are: id, upc, amg_artist, amg_album
|
9
|
+
# @raise [ArgumentError] If an invalid id_type is specified in options
|
10
|
+
def lookup(id, options={})
|
11
|
+
id = id.split(',') if id.kind_of?(String)
|
12
|
+
id_type = options.delete(:id_type) || :id
|
13
|
+
|
14
|
+
raise ArgumentError, 'invalid id_type.' unless ID_TYPES.keys.include?(id_type.to_sym)
|
15
|
+
|
16
|
+
options.merge!({ ID_TYPES[id_type.to_sym] => id.join(',') })
|
17
|
+
request('Lookup', options)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ITunes
|
2
|
+
module Search
|
3
|
+
|
4
|
+
# Performs a music search
|
5
|
+
# @param [String] term The search term
|
6
|
+
# @option options [Symbol]
|
7
|
+
def music(term, options={})
|
8
|
+
search(term, 'music', options)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Performs a movie search
|
12
|
+
# @param [String] term The search term
|
13
|
+
# @option options [Symbol]
|
14
|
+
def movie(term, options={})
|
15
|
+
search(term, 'movie', options)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Performs a podcast search
|
19
|
+
# @param [String] term The search term
|
20
|
+
# @option options [Symbol]
|
21
|
+
def podcast(term, options={})
|
22
|
+
search(term, 'podcast', options)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Performs a music video search
|
26
|
+
# @param [String] term The search term
|
27
|
+
# @option options [Symbol]
|
28
|
+
def music_video(term, options={})
|
29
|
+
search(term, 'musicVideo', options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Performs an audiobook search
|
33
|
+
# @param [String] term The search term
|
34
|
+
# @option options [Symbol]
|
35
|
+
def audiobook(term, options={})
|
36
|
+
search(term, 'audiobook', options)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Performs a short film search
|
40
|
+
# @param [String] term The search term
|
41
|
+
# @option options [Symbol]
|
42
|
+
def short_film(term, options={})
|
43
|
+
search(term, 'shortFilm', options)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Performs a tv show search
|
47
|
+
# @param [String] term The search term
|
48
|
+
# @option options [Symbol]
|
49
|
+
def tv_show(term, options={})
|
50
|
+
search(term, 'tvShow', options)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Performs a search on all itunes content
|
54
|
+
# @param [String] term The search term
|
55
|
+
# @option options [Symbol]
|
56
|
+
def all(term, options={})
|
57
|
+
search(term, 'all', options)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def search(term, media='all', options={})
|
63
|
+
raise ArgumentError, 'you need to search for something, provide a term.' if term.nil? || term.length == 0
|
64
|
+
|
65
|
+
params = { :term => term, :media => media }
|
66
|
+
if options.has_key?(:limit)
|
67
|
+
params.merge!(:limit => options.delete(:limit))
|
68
|
+
elsif limit
|
69
|
+
params.merge!({ :limit => limit })
|
70
|
+
end
|
71
|
+
params.delete(:limit) if params[:limit] && params[:limit] == 0
|
72
|
+
params.merge!(options)
|
73
|
+
|
74
|
+
# clear empty key/value pairs
|
75
|
+
params.reject! { |key, value| value.nil? }
|
76
|
+
|
77
|
+
request('Search', params)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|