flix4r 0.2.3
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/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/flix4r.gemspec +75 -0
- data/init.rb +2 -0
- data/lib/flix4r.rb +22 -0
- data/lib/net_flix.rb +36 -0
- data/lib/net_flix/authenticator.rb +78 -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/credentials.rb +41 -0
- data/lib/net_flix/movie.rb +10 -0
- data/lib/net_flix/request.rb +82 -0
- data/lib/net_flix/television.rb +14 -0
- data/lib/net_flix/title.rb +113 -0
- data/lib/valuable.rb +82 -0
- data/test/actor_builder_test.rb +34 -0
- data/test/authenticator_test.rb +69 -0
- data/test/credentials_test.rb +27 -0
- data/test/fixtures/autocomplete.xml +15 -0
- data/test/fixtures/cast.xml +10 -0
- data/test/fixtures/directors.xml +15 -0
- data/test/fixtures/movies.xml +56 -0
- data/test/fixtures/synopsis.xml +3 -0
- data/test/fixtures/titles.xml +69 -0
- data/test/format_builder_test.rb +67 -0
- data/test/movie_test.rb +54 -0
- data/test/request_test.rb +39 -0
- data/test/test_helper.rb +40 -0
- data/test/title_test.rb +59 -0
- metadata +95 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Brian Dunn
|
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.rdoc
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
=Flix4r
|
2
|
+
===a ruby client for the NetFlix API
|
3
|
+
|
4
|
+
this is a gemification of the google code project found here: "http://code.google.com/p/flix4r/"
|
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
|
+
You must add your netflix credentials to flix4r/lib/oauth.yml. You can get credentials from http://developer.netflix.com/.
|
10
|
+
|
11
|
+
==Example Usage
|
12
|
+
>> list = NetFlix::Title.search(:term => 'sneakers', :max_results => 2)
|
13
|
+
=> [#<NetFlix::Title:0x57f0f58 ...>, #<NetFlix::Title:0x57f0f58 ...>] # shortened for readability
|
14
|
+
>> sneakers = list.first
|
15
|
+
>> sneakers.title
|
16
|
+
=> "Sneakers"
|
17
|
+
|
18
|
+
>> sneakers.delivery_formats
|
19
|
+
=> ['instant', 'DVD']
|
20
|
+
|
21
|
+
>> sneakers.id
|
22
|
+
=> "http://api.netflix.com/catalog/titles/movies/60031755"
|
23
|
+
|
24
|
+
>> sneakers.web_page
|
25
|
+
=> "http://www.netflix.com/Movie/Sneakers/60031755"
|
26
|
+
|
27
|
+
>> sneakers.genres
|
28
|
+
=> ["Thrillers", "Action Comedies", "Espionage Thrillers", "Action Thrillers", "Suspense", "Heist Films", "Universal Studios Home Entertainment"]
|
29
|
+
|
30
|
+
>> sneakers.actors
|
31
|
+
=> ["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"]
|
32
|
+
|
33
|
+
===Movie Retrival
|
34
|
+
more data on each title is available through the NetFlix::Title#movie method. continuing the above example:
|
35
|
+
>> sneakers.movie.synopsis
|
36
|
+
=> "Shadowy U.S. intelligence agents blackmail <a href=\"http://www.netflix.com/RoleDisplay/Robert_Redford/76851\">Robert Redford</a> and his eccentric team of security experts into stealing a code-breaking \"black box\" from a Soviet-funded genius. But Redford uncovers a bigger conspiracy, and he and his \"sneakers\" (<a href=\"http://www.netflix.com/RoleDisplay/Sidney_Poitier/74185\">Sidney Poitier</a>, <a href=\"http://www.netflix.com/RoleDisplay/Dan_Aykroyd/4025\">Dan Aykroyd</a>, <a href=\"http://www.netflix.com/RoleDisplay/River_Phoenix/73423\">River Phoenix</a> and <a href=\"http://www.netflix.com/RoleDisplay/David_Strathairn/89582\">David Strathairn</a>) must save themselves and the world economy by stealing the box back from the blackmailers."
|
37
|
+
|
38
|
+
==NetFlix vs Netflix
|
39
|
+
The great Panda has pointed out to me that the official name is Netflix. meh.= flix4r
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "flix4r"
|
8
|
+
gem.summary = %Q{a port of the google code project: http://code.google.com/p/flix4r/}
|
9
|
+
gem.email = "brianpatrickdunn@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/briandunn/flix4r"
|
11
|
+
gem.authors = ["Brian Dunn"]
|
12
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'rake/rdoctask'
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
44
|
+
if File.exist?('VERSION.yml')
|
45
|
+
config = YAML.load(File.read('VERSION.yml'))
|
46
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
47
|
+
else
|
48
|
+
version = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "flix4r #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
56
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.4
|
data/flix4r.gemspec
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{flix4r}
|
5
|
+
s.version = "0.2.3"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Brian Dunn"]
|
9
|
+
s.date = %q{2009-10-05}
|
10
|
+
s.email = %q{brianpatrickdunn@gmail.com}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.rdoc"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".document",
|
17
|
+
".gitignore",
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION",
|
22
|
+
"flix4r.gemspec",
|
23
|
+
"init.rb",
|
24
|
+
"lib/flix4r.rb",
|
25
|
+
"lib/net_flix.rb",
|
26
|
+
"lib/net_flix/authenticator.rb",
|
27
|
+
"lib/net_flix/builders/actor_builder.rb",
|
28
|
+
"lib/net_flix/builders/format_builder.rb",
|
29
|
+
"lib/net_flix/credentials.rb",
|
30
|
+
"lib/net_flix/movie.rb",
|
31
|
+
"lib/net_flix/request.rb",
|
32
|
+
"lib/net_flix/television.rb",
|
33
|
+
"lib/net_flix/title.rb",
|
34
|
+
"lib/valuable.rb",
|
35
|
+
"test/actor_builder_test.rb",
|
36
|
+
"test/authenticator_test.rb",
|
37
|
+
"test/credentials_test.rb",
|
38
|
+
"test/fixtures/autocomplete.xml",
|
39
|
+
"test/fixtures/cast.xml",
|
40
|
+
"test/fixtures/directors.xml",
|
41
|
+
"test/fixtures/movies.xml",
|
42
|
+
"test/fixtures/synopsis.xml",
|
43
|
+
"test/fixtures/titles.xml",
|
44
|
+
"test/format_builder_test.rb",
|
45
|
+
"test/movie_test.rb",
|
46
|
+
"test/request_test.rb",
|
47
|
+
"test/test_helper.rb",
|
48
|
+
"test/title_test.rb"
|
49
|
+
]
|
50
|
+
s.homepage = %q{http://github.com/briandunn/flix4r}
|
51
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
52
|
+
s.require_paths = ["lib"]
|
53
|
+
s.rubygems_version = %q{1.3.5}
|
54
|
+
s.summary = %q{a port of the google code project: http://code.google.com/p/flix4r/}
|
55
|
+
s.test_files = [
|
56
|
+
"test/actor_builder_test.rb",
|
57
|
+
"test/authenticator_test.rb",
|
58
|
+
"test/credentials_test.rb",
|
59
|
+
"test/format_builder_test.rb",
|
60
|
+
"test/movie_test.rb",
|
61
|
+
"test/request_test.rb",
|
62
|
+
"test/test_helper.rb",
|
63
|
+
"test/title_test.rb"
|
64
|
+
]
|
65
|
+
|
66
|
+
if s.respond_to? :specification_version then
|
67
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
68
|
+
s.specification_version = 3
|
69
|
+
|
70
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
71
|
+
else
|
72
|
+
end
|
73
|
+
else
|
74
|
+
end
|
75
|
+
end
|
data/init.rb
ADDED
data/lib/flix4r.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
this_dir = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'activesupport'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'yaml'
|
8
|
+
require 'hmac-sha1'
|
9
|
+
require 'json'
|
10
|
+
require 'net/http'
|
11
|
+
require 'ftools'
|
12
|
+
require 'nokogiri'
|
13
|
+
require 'crack'
|
14
|
+
gem 'mloughran-api_cache'
|
15
|
+
require 'api_cache'
|
16
|
+
require File.join( this_dir, 'valuable' )
|
17
|
+
|
18
|
+
builders = File.join( this_dir, 'net_flix', 'builders')
|
19
|
+
|
20
|
+
ActiveSupport::Dependencies.load_paths << this_dir
|
21
|
+
ActiveSupport::Dependencies.load_paths << builders
|
22
|
+
|
data/lib/net_flix.rb
ADDED
@@ -0,0 +1,36 @@
|
|
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 ||= defined?(Rails) ? RAILS_DEFAULT_LOGGER : create_logger
|
22
|
+
end
|
23
|
+
|
24
|
+
def log_requests?
|
25
|
+
@log_requests
|
26
|
+
end
|
27
|
+
|
28
|
+
def log_requests= bool=true
|
29
|
+
@log_requests = bool
|
30
|
+
end
|
31
|
+
|
32
|
+
def log(message)
|
33
|
+
NetFlix.logger.info("[#{Time.now.to_i}] #{message}") if log_requests?
|
34
|
+
end
|
35
|
+
end # class methods
|
36
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module NetFlix
|
2
|
+
class Authenticator < Valuable
|
3
|
+
|
4
|
+
has_value :request
|
5
|
+
has_value :nonce, :default => rand(1_000_000)
|
6
|
+
has_value :credentials
|
7
|
+
|
8
|
+
def access_token
|
9
|
+
credentials.access_token
|
10
|
+
end
|
11
|
+
|
12
|
+
def timestamp=stamp
|
13
|
+
@timestamp=stamp
|
14
|
+
end
|
15
|
+
|
16
|
+
def timestamp
|
17
|
+
@timestamp ||= Time.now.to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def key
|
21
|
+
credentials.key
|
22
|
+
end
|
23
|
+
|
24
|
+
def secret
|
25
|
+
credentials.secret
|
26
|
+
end
|
27
|
+
|
28
|
+
def require_credentials
|
29
|
+
raise( ArgumentError, "You must configure your NetFlix API key and secret before using flix4r.") unless credentials.valid?
|
30
|
+
end
|
31
|
+
|
32
|
+
def signature_base_string
|
33
|
+
add_authentication_parameters!
|
34
|
+
[request.http_method, encoded_url, encoded_parameters].join('&')
|
35
|
+
end
|
36
|
+
|
37
|
+
def signature_key
|
38
|
+
"#{Request.encode(secret)}&#{Request.encode(access_token)}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def signature
|
42
|
+
Base64.encode64(HMAC::SHA1.digest(signature_key,signature_base_string)).chomp.gsub(/\n/,'')
|
43
|
+
end
|
44
|
+
|
45
|
+
def authentication_parameters
|
46
|
+
{
|
47
|
+
'oauth_consumer_key' => key,
|
48
|
+
'oauth_signature_method' => 'HMAC-SHA1',
|
49
|
+
'oauth_timestamp' => timestamp,
|
50
|
+
'oauth_nonce' => nonce,
|
51
|
+
'oauth_version' => '1.0'
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_authentication_parameters!
|
56
|
+
request.parameters.merge!(authentication_parameters)
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_signature!
|
60
|
+
sign = {'oauth_signature' => Request.encode(signature)}
|
61
|
+
request.parameters.merge!( sign )
|
62
|
+
end
|
63
|
+
|
64
|
+
def sign!
|
65
|
+
require_credentials
|
66
|
+
add_authentication_parameters!
|
67
|
+
add_signature!
|
68
|
+
end
|
69
|
+
|
70
|
+
def encoded_parameters
|
71
|
+
Request.encode request.parameter_string
|
72
|
+
end
|
73
|
+
|
74
|
+
def encoded_url
|
75
|
+
Request.encode request.url
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -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,41 @@
|
|
1
|
+
module NetFlix
|
2
|
+
class Credentials < Valuable
|
3
|
+
|
4
|
+
@@config_file_name = 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 config_file_name= file_name
|
17
|
+
@@config_file_name = file_name
|
18
|
+
end
|
19
|
+
|
20
|
+
def from_file
|
21
|
+
new(config_file_exists? ? YAML.load(File.open(@@config_file_name)) : {})
|
22
|
+
end
|
23
|
+
|
24
|
+
def config_file_exists?
|
25
|
+
File.exist? @@config_file_name
|
26
|
+
end
|
27
|
+
|
28
|
+
end # class methods
|
29
|
+
|
30
|
+
def to_file!
|
31
|
+
credentials_store = File.new(@@config_file_name, 'w')
|
32
|
+
credentials_store.puts(self.to_yaml)
|
33
|
+
credentials_store.close
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_yaml
|
37
|
+
attributes.to_yaml
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|