netflix4r 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +28 -0
- data/LICENSE.txt +20 -0
- data/README.md +54 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/credentials.yml +3 -0
- data/lib/flix4r.rb +29 -0
- data/lib/net_flix/.DS_Store +0 -0
- data/lib/net_flix/api/.DS_Store +0 -0
- data/lib/net_flix/api/catalog/.DS_Store +0 -0
- data/lib/net_flix/api/catalog/titles.rb +22 -0
- data/lib/net_flix/authenticator.rb +74 -0
- data/lib/net_flix/builders/.DS_Store +0 -0
- data/lib/net_flix/builders/actor_builder.rb +29 -0
- data/lib/net_flix/builders/format_builder.rb +33 -0
- data/lib/net_flix/builders/title_builder.rb +66 -0
- data/lib/net_flix/credentials.rb +37 -0
- data/lib/net_flix/request.rb +56 -0
- data/lib/net_flix/title.rb +39 -0
- data/lib/net_flix.rb +28 -0
- data/lib/valuable.rb +82 -0
- data/log/netflix.log +160 -0
- data/netflix4r.gemspec +130 -0
- data/rakefile.rb +11 -0
- data/samples/.DS_Store +0 -0
- data/samples/actors.rb +5 -0
- data/samples/download_full_catalog.rb +7 -0
- data/samples/formats.rb +5 -0
- data/samples/search.rb +16 -0
- data/test/actor_builder_test.rb +35 -0
- data/test/authenticator_test.rb +75 -0
- data/test/credentials_test.rb +27 -0
- data/test/format_builder_test.rb +67 -0
- data/test/helper.rb +18 -0
- data/test/request_test.rb +32 -0
- data/test/test.rb +8 -0
- data/test/test_flix4r.rb +7 -0
- data/test/test_helpers.rb +20 -0
- data/test/tests.rb +3 -0
- data/test/title_builder_test.rb +167 -0
- data/test/title_test.rb +29 -0
- metadata +278 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
gem 'activesupport'
|
6
|
+
gem 'ruby-hmac'
|
7
|
+
gem 'json'
|
8
|
+
gem 'nokogiri'
|
9
|
+
|
10
|
+
# Add dependencies to develop your gem here.
|
11
|
+
# Include everything needed to run rake, tests, features, etc.
|
12
|
+
group :development do
|
13
|
+
gem "shoulda", ">= 0"
|
14
|
+
gem "bundler", "~> 1.0.0"
|
15
|
+
gem "jeweler", "~> 1.5.2"
|
16
|
+
gem "rcov", ">= 0"
|
17
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.0.9)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.5.2)
|
7
|
+
bundler (~> 1.0.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
json (1.5.3)
|
11
|
+
nokogiri (1.5.0)
|
12
|
+
rake (0.9.2)
|
13
|
+
rcov (0.9.9)
|
14
|
+
ruby-hmac (0.4.0)
|
15
|
+
shoulda (2.11.3)
|
16
|
+
|
17
|
+
PLATFORMS
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
activesupport
|
22
|
+
bundler (~> 1.0.0)
|
23
|
+
jeweler (~> 1.5.2)
|
24
|
+
json
|
25
|
+
nokogiri
|
26
|
+
rcov
|
27
|
+
ruby-hmac
|
28
|
+
shoulda
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 rossnelson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
flix4r
|
2
|
+
==========
|
3
|
+
|
4
|
+
a ruby client for the NetFlix API
|
5
|
+
|
6
|
+
Based on the "RESTful" resources provided by the NetFlix API, flix4r provides a clean, if _very_ incomplete, set of models for accessing NetFlixs' (sp?) resources.
|
7
|
+
|
8
|
+
Credentials
|
9
|
+
===========
|
10
|
+
|
11
|
+
You must add your netflix credentials to flix4r/lib/oauth.yml. You can get credentials from http://developer.netflix.com/.
|
12
|
+
|
13
|
+
Example
|
14
|
+
==========
|
15
|
+
|
16
|
+
list = NetFlix::Title.search(:term => 'sneakers', :max_results => 2)
|
17
|
+
[#<NetFlix::Title:0x57f0f58 ...>, #<NetFlix::Title:0x57f0f58 ...>] # shortened for readability
|
18
|
+
sneakers = list.first
|
19
|
+
sneakers.title
|
20
|
+
"Sneakers"
|
21
|
+
|
22
|
+
sneakers.delivery_formats
|
23
|
+
['instant', 'DVD']
|
24
|
+
|
25
|
+
sneakers.id
|
26
|
+
"http://api.netflix.com/catalog/titles/movies/60031755"
|
27
|
+
|
28
|
+
sneakers.web_page
|
29
|
+
"http://www.netflix.com/Movie/Sneakers/60031755"
|
30
|
+
|
31
|
+
sneakers.genres
|
32
|
+
["Thrillers", "Action Comedies", "Espionage Thrillers", "Action Thrillers", "Suspense", "Heist Films", "Universal Studios Home Entertainment"]
|
33
|
+
|
34
|
+
sneakers.actors
|
35
|
+
["Robert Redford", "Sidney Poitier", "Ben Kingsley", "Dan Aykroyd", "Mary McDonnell", "River Phoenix", "David Strathairn", "Donal Logue", "Timothy Busfield", "Eddie Jones", "George Hearn", "Lee Garlington", "Stephen Tobolowsky"]
|
36
|
+
|
37
|
+
|
38
|
+
Contributing to flix4r
|
39
|
+
=========
|
40
|
+
|
41
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
42
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
43
|
+
* Fork the project
|
44
|
+
* Start a feature/bugfix branch
|
45
|
+
* Commit and push until you are happy with your contribution
|
46
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
47
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
48
|
+
|
49
|
+
Copyright
|
50
|
+
==
|
51
|
+
|
52
|
+
Copyright (c) 2011 rossnelson. See LICENSE.txt for
|
53
|
+
further details.
|
54
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "netflix4r"
|
16
|
+
gem.homepage = "http://github.com/rossnelson/flix4r"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{copy of http://code.google.com/p/flix4r/}
|
19
|
+
gem.description = %Q{a ruby client for the NetFlix API}
|
20
|
+
gem.email = "axcess1@me.com"
|
21
|
+
gem.authors = ["rossnelson"]
|
22
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
23
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
+
gem.add_runtime_dependency 'activesupport'
|
25
|
+
gem.add_runtime_dependency 'ruby-hmac'
|
26
|
+
gem.add_runtime_dependency 'json'
|
27
|
+
gem.add_runtime_dependency 'nokogiri'
|
28
|
+
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
29
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
30
|
+
end
|
31
|
+
Jeweler::RubygemsDotOrgTasks.new
|
32
|
+
|
33
|
+
require 'rake/testtask'
|
34
|
+
Rake::TestTask.new(:test) do |test|
|
35
|
+
test.libs << 'lib' << 'test'
|
36
|
+
test.pattern = 'test/**/test_*.rb'
|
37
|
+
test.verbose = true
|
38
|
+
end
|
39
|
+
|
40
|
+
require 'rcov/rcovtask'
|
41
|
+
Rcov::RcovTask.new do |test|
|
42
|
+
test.libs << 'test'
|
43
|
+
test.pattern = 'test/**/test_*.rb'
|
44
|
+
test.verbose = true
|
45
|
+
end
|
46
|
+
|
47
|
+
task :default => :test
|
48
|
+
|
49
|
+
require 'rake/rdoctask'
|
50
|
+
Rake::RDocTask.new do |rdoc|
|
51
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
52
|
+
|
53
|
+
rdoc.rdoc_dir = 'rdoc'
|
54
|
+
rdoc.title = "flix4r #{version}"
|
55
|
+
rdoc.rdoc_files.include('README*')
|
56
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
57
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/credentials.yml
ADDED
data/lib/flix4r.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
this_dir = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'active_support'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'yaml'
|
8
|
+
require 'hmac-sha1'
|
9
|
+
require 'json'
|
10
|
+
require 'net/http'
|
11
|
+
require 'fileutils'
|
12
|
+
require 'nokogiri'
|
13
|
+
require 'valuable.rb'
|
14
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
15
|
+
|
16
|
+
lib = File.join("../", 'lib')
|
17
|
+
builders = File.join(lib, 'net_flix', 'builders')
|
18
|
+
|
19
|
+
require 'net_flix'
|
20
|
+
require 'net_flix/credentials'
|
21
|
+
require 'net_flix/authenticator'
|
22
|
+
require 'net_flix/request'
|
23
|
+
require 'net_flix/title'
|
24
|
+
require 'net_flix/api/catalog/titles'
|
25
|
+
|
26
|
+
|
27
|
+
require 'net_flix/builders/actor_builder'
|
28
|
+
require 'net_flix/builders/format_builder'
|
29
|
+
require 'net_flix/builders/title_builder'
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NetFlix
|
2
|
+
module API
|
3
|
+
module Catalog
|
4
|
+
module Titles
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# example Title.search(:term => 'sneakers', :max_results => 10)
|
9
|
+
def search(params = {})
|
10
|
+
NetFlix::Request.new(:url => 'http://api.netflix.com/catalog/titles', :parameters => params).send
|
11
|
+
end
|
12
|
+
|
13
|
+
def list(params = {})
|
14
|
+
NetFlix::Request.new(:url => 'http://api.netflix.com/catalog/titles/index', :parameters => params).send
|
15
|
+
end
|
16
|
+
|
17
|
+
end # class methods
|
18
|
+
|
19
|
+
end # class Titles
|
20
|
+
end # module Catalog
|
21
|
+
end # module API
|
22
|
+
end # module NetFlix
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module NetFlix
|
2
|
+
class Authenticator < Valuable
|
3
|
+
|
4
|
+
require 'base64'
|
5
|
+
require 'digest/sha1'
|
6
|
+
|
7
|
+
has_value :request
|
8
|
+
has_value :timestamp, :default => Time.now.to_i
|
9
|
+
has_value :nonce, :default => rand(1_000_000)
|
10
|
+
has_value :credentials
|
11
|
+
|
12
|
+
def access_token
|
13
|
+
credentials.access_token
|
14
|
+
end
|
15
|
+
|
16
|
+
def key
|
17
|
+
credentials.key
|
18
|
+
end
|
19
|
+
|
20
|
+
def secret
|
21
|
+
credentials.secret
|
22
|
+
end
|
23
|
+
|
24
|
+
def require_credentials
|
25
|
+
raise( ArgumentError, "You must configure your NetFlix API key and secret before using flix4r.") unless credentials.valid?
|
26
|
+
end
|
27
|
+
|
28
|
+
def signature_base_string
|
29
|
+
add_authentication_parameters!
|
30
|
+
[request.http_method, encoded_url, encoded_parameters].join('&')
|
31
|
+
end
|
32
|
+
|
33
|
+
def signature_key
|
34
|
+
"#{Request.encode(secret)}&#{Request.encode(access_token)}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def signature
|
38
|
+
Base64.encode64(HMAC::SHA1.digest(signature_key,signature_base_string)).chomp.gsub(/\n/,'')
|
39
|
+
end
|
40
|
+
|
41
|
+
def authentication_parameters
|
42
|
+
{
|
43
|
+
'oauth_consumer_key' => key,
|
44
|
+
'oauth_signature_method' => 'HMAC-SHA1',
|
45
|
+
'oauth_timestamp' => timestamp,
|
46
|
+
'oauth_nonce' => nonce,
|
47
|
+
'oauth_version' => '1.0'
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_authentication_parameters!
|
52
|
+
request.parameters = request.parameters.merge(authentication_parameters)
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_signature!
|
56
|
+
sign = {'oauth_signature' => Request.encode(signature)}
|
57
|
+
request.parameters = request.parameters.merge( sign )
|
58
|
+
end
|
59
|
+
|
60
|
+
def sign!
|
61
|
+
require_credentials
|
62
|
+
add_authentication_parameters!
|
63
|
+
add_signature!
|
64
|
+
end
|
65
|
+
|
66
|
+
def encoded_parameters
|
67
|
+
Request.encode request.parameter_string
|
68
|
+
end
|
69
|
+
|
70
|
+
def encoded_url
|
71
|
+
Request.encode request.url
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
Binary file
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class ActorBuilder
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def from_xml(xml)
|
6
|
+
Nokogiri.XML(xml).xpath('//people/person/name').map do |actor_data|
|
7
|
+
actor_data.content
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def from_movie(movie)
|
12
|
+
actors = movie.search('link[@rel="http://schemas.netflix.com/catalog/person.actor"]').map{|f| f['title'] }
|
13
|
+
|
14
|
+
actors + request_cast_for(movie)
|
15
|
+
end
|
16
|
+
|
17
|
+
def request_cast_for(movie)
|
18
|
+
cast_link_node = movie.search('link[@rel="http://schemas.netflix.com/catalog/people.cast"]').first
|
19
|
+
cast_link = cast_link_node['href'] if cast_link_node
|
20
|
+
if cast_link.nil?
|
21
|
+
[]
|
22
|
+
else
|
23
|
+
request = NetFlix::Request.new(:url => cast_link)
|
24
|
+
response = request.send
|
25
|
+
from_xml(response)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class FormatBuilder
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def from_xml(xml)
|
6
|
+
|
7
|
+
Nokogiri.XML(xml).xpath("//delivery_formats/availability[@available_from < #{Time.now.to_i} or not(@available_from)]/category").map do |format_data|
|
8
|
+
|
9
|
+
format_data['term']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def from_movie(movie)
|
14
|
+
|
15
|
+
formats = movie.xpath("//title_index_item/delivery_formats/availability[not(@available_from) or @available_from < #{Time.now.to_i}]/category[@scheme='http://api.netflix.com/categories/title_formats']").map{|f| f['term'] }
|
16
|
+
formats + request_cast_for(movie)
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def request_cast_for(movie)
|
21
|
+
cast_link_node = movie.search('link[@rel="http://schemas.netflix.com/catalog/titles/format_availability"]').first
|
22
|
+
cast_link = cast_link_node['href'] if cast_link_node
|
23
|
+
|
24
|
+
if cast_link.nil?
|
25
|
+
[]
|
26
|
+
else
|
27
|
+
request = NetFlix::Request.new(:url => cast_link)
|
28
|
+
response = request.send
|
29
|
+
from_xml(response)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class TitleBuilder
|
2
|
+
|
3
|
+
def self.from_xml(xml)
|
4
|
+
return [] unless xml
|
5
|
+
|
6
|
+
nxml = Nokogiri.XML(xml)
|
7
|
+
|
8
|
+
titles(nxml).map do |data|
|
9
|
+
TitleBuilder.new(data).title
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.titles(noko_xml)
|
14
|
+
titles = noko_xml.xpath('//catalog_title')
|
15
|
+
titles.empty? ? noko_xml.xpath('//title_index_item') : titles
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(data)
|
19
|
+
@data = data
|
20
|
+
@title = NetFlix::Title.new
|
21
|
+
|
22
|
+
set_actors
|
23
|
+
set_delivery_formats
|
24
|
+
set_genres
|
25
|
+
set_id
|
26
|
+
set_release_year
|
27
|
+
set_title
|
28
|
+
set_web_page
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_id
|
32
|
+
node = @data.search('id').first
|
33
|
+
@title.id = node.content if node
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_release_year
|
37
|
+
node = @data.search('release_year').first
|
38
|
+
@title.release_year = node.content if node
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_title
|
42
|
+
node = @data.search('title').first
|
43
|
+
@title.title = node['regular'] || node.content if node
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_web_page
|
47
|
+
node = @data.search('link[@title="web page"]').first
|
48
|
+
@title.web_page = node['href'] if node
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_delivery_formats
|
52
|
+
@title.delivery_formats = FormatBuilder.from_movie(@data)
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_genres
|
56
|
+
@title.genres = @data.search('category[@scheme="http://api.netflix.com/categories/genres"]').map{|f| f['label'] }
|
57
|
+
end
|
58
|
+
|
59
|
+
def set_actors
|
60
|
+
@title.actors = ActorBuilder.from_movie(@data)
|
61
|
+
end
|
62
|
+
|
63
|
+
def title
|
64
|
+
@title
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module NetFlix
|
2
|
+
class Credentials < Valuable
|
3
|
+
|
4
|
+
CONFIG_FILENAME = File.join( File.dirname(__FILE__), '..', '..', 'credentials.yml')
|
5
|
+
|
6
|
+
has_value :key
|
7
|
+
has_value :secret
|
8
|
+
has_value :access_token
|
9
|
+
|
10
|
+
def valid?
|
11
|
+
(key && secret) != nil
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
def from_file
|
17
|
+
new(config_file_exists? ? YAML.load(File.open(CONFIG_FILENAME)) : {})
|
18
|
+
end
|
19
|
+
|
20
|
+
def config_file_exists?
|
21
|
+
File.exist? CONFIG_FILENAME
|
22
|
+
end
|
23
|
+
|
24
|
+
end # class methods
|
25
|
+
|
26
|
+
def to_file!
|
27
|
+
credentials_store = File.new(CONFIG_FILENAME, 'w')
|
28
|
+
credentials_store.puts(self.to_yaml)
|
29
|
+
credentials_store.close
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_yaml
|
33
|
+
attributes.to_yaml
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module NetFlix
|
2
|
+
class Request < Valuable
|
3
|
+
|
4
|
+
RESERVED_CHARACTERS = /[^A-Za-z0-9\-\._~]/
|
5
|
+
|
6
|
+
has_value :http_method, :default => 'GET'
|
7
|
+
has_value :url, :default => 'http://api.netflix.com/catalog/titles/index'
|
8
|
+
has_value :parameters, :klass => HashWithIndifferentAccess, :default => {}
|
9
|
+
|
10
|
+
def ordered_keys
|
11
|
+
parameters.keys.sort
|
12
|
+
end
|
13
|
+
|
14
|
+
def parameter_string
|
15
|
+
string = ordered_keys.map do |key|
|
16
|
+
"#{key}=#{parameters[key]}"
|
17
|
+
end.join('&')
|
18
|
+
end
|
19
|
+
|
20
|
+
def authenticator
|
21
|
+
@auth ||= NetFlix::Authenticator.new(:request => self, :credentials => NetFlix.credentials)
|
22
|
+
end
|
23
|
+
|
24
|
+
def target
|
25
|
+
URI.parse "#{url}?#{parameter_string}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def log
|
29
|
+
NetFlix.log(target.to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
def send
|
33
|
+
authenticator.sign!
|
34
|
+
log
|
35
|
+
Net::HTTP.get(target)
|
36
|
+
end
|
37
|
+
|
38
|
+
def Request.encode(value)
|
39
|
+
URI.escape( value, RESERVED_CHARACTERS ) if value
|
40
|
+
end
|
41
|
+
|
42
|
+
# validation stuff
|
43
|
+
has_collection :errors
|
44
|
+
|
45
|
+
def valid?
|
46
|
+
errors.clear
|
47
|
+
validate_http_method
|
48
|
+
errors.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_http_method
|
52
|
+
errors << "HTTP method must be POST or GET, but I got #{http_method}" unless ['POST', 'GET'].include? http_method
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module NetFlix
|
2
|
+
class Title < Valuable
|
3
|
+
|
4
|
+
has_value :id
|
5
|
+
has_value :release_year
|
6
|
+
has_collection :genres
|
7
|
+
has_collection :actors
|
8
|
+
has_value :title
|
9
|
+
has_value :web_page
|
10
|
+
has_collection :delivery_formats
|
11
|
+
|
12
|
+
def to_json
|
13
|
+
attributes.to_json
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.from_json(data)
|
17
|
+
self.new(JSON.parse(data))
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
title || 'unknown title'
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
|
26
|
+
def complete_list
|
27
|
+
data = NetFlix::API::Catalog::Titles.index
|
28
|
+
TitleBuilder.from_xml(data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def search(params)
|
32
|
+
data = NetFlix::API::Catalog::Titles.search(params)
|
33
|
+
TitleBuilder.from_xml(data)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end # class Title
|
38
|
+
end # module NetFlix
|
39
|
+
|
data/lib/net_flix.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module NetFlix
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def credentials
|
6
|
+
@credentials ||= NetFlix::Credentials.from_file
|
7
|
+
end
|
8
|
+
|
9
|
+
def logfile
|
10
|
+
File.join( File.dirname(__FILE__), '..', 'log', 'netflix.log' )
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_logger
|
14
|
+
logdir = File.join( File.dirname(__FILE__), '..', 'log' )
|
15
|
+
Dir.mkdir(logdir) unless File.exists? logdir
|
16
|
+
|
17
|
+
Logger.new( logfile )
|
18
|
+
end
|
19
|
+
|
20
|
+
def logger
|
21
|
+
@logger ||= create_logger
|
22
|
+
end
|
23
|
+
|
24
|
+
def log(message)
|
25
|
+
NetFlix.logger.info("[#{Time.now.to_i}] #{message}")
|
26
|
+
end
|
27
|
+
end # class methods
|
28
|
+
end
|