itunes 0.3.0 → 0.4.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.
- 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
|