netflix4r 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
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
@@ -0,0 +1,3 @@
1
+ ---
2
+ :key: 3n5x56cp5fbh23b3z2xsgdmu
3
+ :secret: true3kTsmF
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