hulu-show 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # hulu-show
2
+
3
+ Hulu::Show fetches all episodes for the current season of a specified show.
4
+
5
+ ## Show attributes:
6
+
7
+ :network,
8
+ :title,
9
+ :genre,
10
+ :description,
11
+ :errors,
12
+ :episodes,
13
+ :url
14
+
15
+ ## Episode attributes:
16
+
17
+ :title,
18
+ :episode,
19
+ :running_time,
20
+ :air_date,
21
+ :season,
22
+ :url,
23
+ :beaconid,
24
+ :thumbnail_url,
25
+ :embed_html
26
+
27
+ ## Usage
28
+
29
+ `gem install hulu-show`
30
+
31
+ `require 'hulu/show'`
32
+
33
+ `burn_notice = Hulu::Show.new('Burn Notice')`
34
+
35
+ #<Hulu::Show:0x007f972a04f740 @title="Burn Notice", @episodes=[#<Hulu::Episode:0x007f972a02c7b8 @episode="13", @title="Damned If You Do", @url="http://www.hulu.com/watch/296648/burn-notice-damned-if-you-do?c=Action-and-Adventure#x-4,cEpisodes,1,0", @beaconid="296648", @running_time="43:09", @air_date="11/03/2011", @embed_html="<object width=\"512\" height=\"296\"><param name=\"movie\" value=\"http://www.hulu.com/embed/c96sZQru3w567PP7t3cGZQ\"></param><param name=\"flashvars\" value=\"ap=1\"></param><embed src=\"http://www.hulu.com/embed/c96sZQru3w567PP7t3cGZQ\" type=\"application/x-shockwave-flash\" width=\"512\" height=\"296\" flashvars=\"ap=1\"></embed></object>", @thumbnail_url="http://thumbnails.hulu.com/8/60000008/60000008_145x80_generated.jpg">], @doc=nil, @errors=[], @network="USA", @genre="Action and Adventure", @description="A \"burned\" spy returns to Miami where he uses his special ops training to help those in need, and bring justice against the men who wrongly burned him.", @url="http://www.hulu.com/burn-notice">
36
+
37
+ ## Retrieve show episodes
38
+
39
+ `burn_notice.episodes`
40
+
41
+ [#<Hulu::Episode:0x007f961a02be00 @episode="13", @title="Damned If You Do", @url="http://www.hulu.com/watch/296648/burn-notice-damned-if-you-do?c=Action-and-Adventure#x-4,cEpisodes,1,0", @beaconid="296648", @running_time="43:09", @air_date="11/03/2011", @embed_html="<object width=\"512\" height=\"296\"><param name=\"movie\" value=\"http://www.hulu.com/embed/c96sZQru3w567PP7t3cGZQ\"></param><param name=\"flashvars\" value=\"ap=1\"></param><embed src=\"http://www.hulu.com/embed/c96sZQru3w567PP7t3cGZQ\" type=\"application/x-shockwave-flash\" width=\"512\" height=\"296\" flashvars=\"ap=1\"></embed></object>", @thumbnail_url="http://thumbnails.hulu.com/8/60000008/60000008_145x80_generated.jpg">]
42
+
43
+
44
+ ## Retrieve multiple shows with their episodes at one time.
45
+
46
+ `Hulu.shows(['Burn Notice', 'Warehouse 13'])`
47
+
48
+
49
+ Copyright (c) 2011 Craig Williams. See LICENSE.txt for
50
+ further details.
51
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
data/hulu-show.gemspec CHANGED
@@ -5,16 +5,16 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "hulu-show"
8
- s.version = "0.1.0"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Craig Williams"]
12
- s.date = "2011-11-25"
12
+ s.date = "2011-11-27"
13
13
  s.description = "Really, that is about it. For each show, Hulu::Show will return an array of Episode objects with details about the episode."
14
14
  s.email = "cwilliams.allancraig@gmail.com"
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE.txt",
17
- "README.rdoc"
17
+ "README.md"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
23
23
  "Gemfile",
24
24
  "Gemfile.lock",
25
25
  "LICENSE.txt",
26
- "README.rdoc",
26
+ "README.md",
27
27
  "Rakefile",
28
28
  "VERSION",
29
29
  "hulu-show.gemspec",
@@ -31,9 +31,14 @@ Gem::Specification.new do |s|
31
31
  "lib/hulu/show.rb",
32
32
  "lib/hulu/show/base.rb",
33
33
  "lib/hulu/show/episode.rb",
34
+ "notes.txt",
35
+ "spec/hulu/base_spec.rb",
36
+ "spec/hulu/episode_spec.rb",
34
37
  "spec/hulu/show_spec.rb",
35
38
  "spec/spec_helper.rb",
36
- "spec/support/vcr_cassettes/burn_notice.yml"
39
+ "spec/support/vcr_cassettes/additional_attributes.yml",
40
+ "spec/support/vcr_cassettes/burn_notice.yml",
41
+ "spec/support/vcr_cassettes/non_existent_show.yml"
37
42
  ]
38
43
  s.homepage = "http://github.com/CraigWilliams/hulu-show"
39
44
  s.licenses = ["BSD"]
data/lib/hulu/show.rb CHANGED
@@ -1,21 +1,53 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hulu/show/base'
2
4
  require 'hulu/show/episode'
3
5
 
4
6
  class Hulu::Show < Hulu::Base
5
- attr_accessor :name
7
+ attr_accessor :network,
8
+ :title,
9
+ :genre,
10
+ :description,
11
+ :errors,
12
+ :episodes,
13
+ :url
6
14
 
7
- def initialize(name)
8
- @name = name
15
+ def initialize(title)
16
+ @title = title
9
17
  @episodes = []
18
+ @doc = Hulu.query(@title)
19
+ @errors = []
20
+
21
+ if error_404
22
+ @errors << "404: Show not found"
23
+ return
24
+ end
25
+
26
+ parse_show_details
27
+ parse_episodes
28
+ set_url
29
+
30
+ @doc = nil
31
+ end
32
+
33
+ def set_url
34
+ @url = "#{Hulu::BASE_URI}/#{Hulu::Base.prepare_name(@title)}"
35
+ end
36
+
37
+ def error_404
38
+ error = @doc.css('.fixed-lg .section .gr').text.strip
39
+ error =~ /404/ ? true : false
10
40
  end
11
41
 
12
- def episodes
13
- doc = Hulu.query(self.name)
14
- parse_episodes(doc)
42
+ def parse_show_details
43
+ details = @doc.css(".fixed-lg.container .section.details .relative .info")
44
+ @network = details[0].text.strip
45
+ @genre = details[2].text.split('|').first.gsub(/\302\240/, ' ').strip
46
+ @description = details[3].text.strip
15
47
  end
16
48
 
17
- def parse_episodes(doc)
18
- episodes = doc.css("#show-expander table")
49
+ def parse_episodes
50
+ episodes = @doc.css("#show-expander table")
19
51
 
20
52
  episodes.each_with_index do |episode, i|
21
53
  # There are several tables under the show-expander
@@ -27,7 +59,6 @@ class Hulu::Show < Hulu::Base
27
59
  end
28
60
  end
29
61
  end
30
- @episodes
31
62
  end
32
63
 
33
64
  end
@@ -29,4 +29,12 @@ module Hulu
29
29
  show_name = Hulu::Base.prepare_name(show_name)
30
30
  Hulu::Fetcher::Page.get("#{BASE_URI}/#{show_name}")
31
31
  end
32
+
33
+ def self.shows(titles)
34
+ shows = []
35
+ titles.each do |title|
36
+ shows << Hulu::Show.new(title)
37
+ end
38
+ shows
39
+ end
32
40
  end
@@ -3,15 +3,29 @@ class Hulu::Episode < Hulu::Base
3
3
  :episode,
4
4
  :running_time,
5
5
  :air_date,
6
- :season
7
-
6
+ :season,
7
+ :url,
8
+ :beaconid,
9
+ :thumbnail_url,
10
+ :embed_html
11
+
8
12
  def initialize
9
13
  yield self if block_given?
14
+
15
+ set_additional_attributes
16
+ end
17
+
18
+ def set_additional_attributes
19
+ info = additional_attributes
20
+ @embed_html = info['html']
21
+ @thumbnail_url = info['thumbnail_url']
10
22
  end
11
23
 
12
24
  def process(episode)
13
- @episode = episode.css('td.c0').text.strip
14
- @title = episode.css('td.c1 a').text.strip
25
+ @episode = episode.css('td.c0').text.strip rescue ''
26
+ @title = episode.css('td.c1 a').text.strip rescue ''
27
+ @url = episode.css('td.c1 a').attr('href').text.strip rescue ''
28
+ @beaconid = episode.css('td.c1 a').attr('beaconid').text.strip rescue ''
15
29
  parse_running_time(episode)
16
30
  end
17
31
 
@@ -20,11 +34,16 @@ class Hulu::Episode < Hulu::Base
20
34
 
21
35
  if run_time.include?(':')
22
36
  @running_time = run_time
23
- @air_date = episode.css('td.c4').text.strip
37
+ @air_date = episode.css('td.c4').text.strip rescue ''
24
38
  else
25
- @running_time = episode.css('td.c4').text.strip
26
- @air_date = episode.css('td.c5').text.strip
39
+ @running_time = episode.css('td.c4').text.strip rescue ''
40
+ @air_date = episode.css('td.c5').text.strip rescue ''
27
41
  end
28
42
  end
29
43
 
44
+ def additional_attributes
45
+ url = "http://www.hulu.com/api/oembed.json?url=http://www.hulu.com/watch/#{@beaconid}/#{Hulu::Base.prepare_name(@title)}"
46
+ Hulu::Fetcher::Page.get(url).parsed_response
47
+ end
48
+
30
49
  end
data/notes.txt ADDED
@@ -0,0 +1,25 @@
1
+ #==============================================================
2
+ # Fetching more data about a show
3
+ #==============================================================
4
+ # http://www.hulu.com/api/oembed.json?url=http://www.hulu.com/watch/20807/late-night-with-conan-obrien-wed-may-21-2008
5
+ # http://www.hulu.com/api/oembed.json?url=http://www.hulu.com/watch/261293/burn-notice-mind-games
6
+
7
+ {
8
+ "thumbnail_width": 145,
9
+ "type": "video",
10
+ "title": "Shadows (Warehouse 13)",
11
+ "embed_url": "http://www.hulu.com/embed/Gp_eWf7PQr697Ol0j7OfEg",
12
+ "thumbnail_height": 80,
13
+ "height": 296,
14
+ "provider_name": "Hulu",
15
+ "width": 512,
16
+ "html": "<object width=\"512\" height=\"296\"><param name=\"movie\" value=\"http://www.hulu.com/embed/Gp_eWf7PQr697Ol0j7OfEg\"></param><param name=\"flashvars\" value=\"ap=1\"></param><embed src=\"http://www.hulu.com/embed/Gp_eWf7PQr697Ol0j7OfEg\" type=\"application/x-shockwave-flash\" width=\"512\" height=\"296\" flashvars=\"ap=1\"></embed></object>",
17
+ "provider_url": "http://www.hulu.com/",
18
+ "duration": 2584.25,
19
+ "version": "1.0",
20
+ "cache_age": 3600,
21
+ "air_date": "Mon Sep 12 00:00:00 UTC 2011",
22
+ "thumbnail_url": "http://thumbnails.hulu.com/188/40038188/40038188_145x80_generated.jpg",
23
+ "author_name": "Syfy"
24
+ }
25
+
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hulu do
4
+
5
+ it "returns an array of shows" do
6
+ titles = ['Burn Notice', 'Warehouse 13', 'Terra Nova']
7
+ shows = Hulu.shows(titles)
8
+
9
+ shows.count.should == 3
10
+ # shows.first.episodes.first.title.should == 'What'
11
+ end
12
+
13
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hulu::Episode do
4
+
5
+ before { VCR.insert_cassette('additional_attributes') }
6
+ after { VCR.eject_cassette }
7
+
8
+ it "episode can fetch additional attributes" do
9
+ episode = Hulu::Episode.new do |episode|
10
+ episode.beaconid = '296648'
11
+ episode.title = 'Damned If You Do'
12
+ episode.additional_attributes
13
+ end
14
+
15
+ episode.thumbnail_url.should == "http://thumbnails.hulu.com/8/60000008/60000008_145x80_generated.jpg"
16
+ episode.embed_html.should_not be_nil
17
+ end
18
+ end
@@ -1,37 +1,93 @@
1
+ # encoding: utf-8
2
+
3
+ require 'json'
4
+
1
5
  require 'spec_helper'
2
6
 
3
7
  describe Hulu::Show do
4
- let(:burn_notice_erb) {
5
- {
6
- title: 'Burn baby burn',
7
- running_time: '43:09',
8
- air_date: '11/03/2011',
9
- episode: '13'
8
+
9
+ context "When show is found" do
10
+ let(:burn_notice_erb) {
11
+ {
12
+ title: 'Burn baby burn',
13
+ running_time: '43:09',
14
+ air_date: '11/03/2011',
15
+ episode: '13',
16
+ network: 'USA',
17
+ genre: '<b><a href=\"http://www.hulu.com/genres/Action-and-Adventure\"
18
+ class=\"show-title\">Action and Adventure</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href=\"http://www.hulu.com/genres/Drama\"
19
+ class=\"show-title\">Drama</a></b>',
20
+ description: "A &quot;burned&quot; spy returns to Miami where he uses his special ops training to help those in need, and bring justice against the men who wrongly burned him."
21
+ }
10
22
  }
11
- }
12
23
 
13
- it "should know it's name" do
14
- Hulu::Show.new('Bones').name.should == 'Bones'
15
- end
24
+ let(:additional_attributes) {
25
+ {
26
+ "thumbnail_width" => 145,
27
+ "type" => "video",
28
+ "title" => "Shadows (Warehouse 13)",
29
+ "embed_url" => "http://www.hulu.com/embed/Gp_eWf7PQr697Ol0j7OfEg",
30
+ "thumbnail_height" => 80,
31
+ "height" => 296,
32
+ "provider_name" => "Hulu",
33
+ "width" => 512,
34
+ "html" => "<object width=\"512\" height=\"296\"><param name=\"movie\" value=\"http://www.hulu.com/embed/Gp_eWf7PQr697Ol0j7OfEg\"></param><param name=\"flashvars\" value=\"ap=1\"></param><embed src=\"http://www.hulu.com/embed/Gp_eWf7PQr697Ol0j7OfEg\" type=\"application/x-shockwave-flash\" width=\"512\" height=\"296\" flashvars=\"ap=1\"></embed></object>",
35
+ "provider_url" => "http://www.hulu.com/",
36
+ "duration" => 2584.25,
37
+ "version" => "1.0",
38
+ "cache_age" => 3600,
39
+ "air_date" => "Mon Sep 12 00:00:00 UTC 2011",
40
+ "thumbnail_url" => "http://thumbnails.hulu.com/188/40038188/40038188_145x80_generated.jpg",
41
+ "author_name" => "Syfy"
42
+ }
43
+ }
44
+
45
+ before do
46
+ Hulu::Episode.any_instance.stub(:additional_attributes).and_return(additional_attributes)
47
+ VCR.insert_cassette('burn_notice', erb: burn_notice_erb)
48
+ end
49
+
50
+ after { VCR.eject_cassette }
51
+
52
+ it "show attributes should be set" do
53
+ show = Hulu::Show.new('Burn Notice')
54
+ show.title.should == 'Burn Notice'
55
+ show.network.should == 'USA'
56
+ show.genre.should == 'Action and Adventure'
57
+ show.description.should == "A \"burned\" spy returns to Miami where he uses his special ops training to help those in need, and bring justice against the men who wrongly burned him."
58
+ show.url == "http://www.hulu.com/burn-notice"
59
+ end
16
60
 
17
- it "created episodes with correct attributes" do
18
- VCR.use_cassette('burn_notice', erb: burn_notice_erb) do
61
+ it "episodes return correct attributes" do
19
62
  episodes = Hulu::Show.new('Burn Notice').episodes
20
63
  episode = episodes.first
21
64
 
22
- episode.title.should == 'Burn baby burn'
65
+ episode.title.should == 'Burn baby burn'
23
66
  episode.running_time.should == '43:09'
24
- episode.air_date.should == '11/03/2011'
25
- episode.episode.should == '13'
67
+ episode.air_date.should == '11/03/2011'
68
+ episode.episode.should == '13'
69
+ episode.url == "http://www.hulu.com/watch/296648/burn-notice-damned-if-you-do#x-4,cEpisodes,1,0"
70
+ episode.beaconid == '296648'
71
+
72
+ # additional_attributes
73
+ episode.thumbnail_url.should == "http://thumbnails.hulu.com/188/40038188/40038188_145x80_generated.jpg"
26
74
  end
27
- end
28
75
 
29
- it "has the correct number of episodes" do
30
- VCR.use_cassette('burn_notice', erb: burn_notice_erb) do
76
+ it "has the correct number of episodes" do
31
77
  episodes = Hulu::Show.new('Burn Notice').episodes
32
-
33
78
  episodes.count.should == 1
34
79
  end
80
+
81
+ end
82
+
83
+ context "When show not found" do
84
+ before { VCR.insert_cassette('non_existent_show') }
85
+ after { VCR.eject_cassette }
86
+
87
+ it "contains an error when show not found" do
88
+ show = Hulu::Show.new("Non-existent show")
89
+ show.errors.should include("404: Show not found")
90
+ end
35
91
  end
36
92
 
37
93
  end
data/spec/spec_helper.rb CHANGED
@@ -11,8 +11,9 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
11
11
  VCR.config do |c|
12
12
  c.cassette_library_dir = 'spec/support/vcr_cassettes'
13
13
  c.stub_with :fakeweb
14
+ c.allow_http_connections_when_no_cassette = true
14
15
  end
15
16
 
16
17
  RSpec.configure do |config|
17
18
  config.extend VCR::RSpec::Macros
18
- end
19
+ end
@@ -0,0 +1,33 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: http://www.hulu.com:80/api/oembed.json?url=http://www.hulu.com/watch/296648/damned-if-you-do
6
+ body:
7
+ headers:
8
+ response: !ruby/struct:VCR::Response
9
+ status: !ruby/struct:VCR::ResponseStatus
10
+ code: 200
11
+ message: OK
12
+ headers:
13
+ server:
14
+ - nginx
15
+ content-type:
16
+ - application/json; charset=utf-8
17
+ status:
18
+ - 200 OK
19
+ x-ua-compatible:
20
+ - IE=EmulateIE9; IE=EmulateIE7
21
+ content-length:
22
+ - '803'
23
+ cache-control:
24
+ - max-age=3544
25
+ date:
26
+ - Sat, 26 Nov 2011 23:08:44 GMT
27
+ body: ! '{"author_name":"USA","type":"video","thumbnail_width":145,"thumbnail_height":80,"title":"Damned
28
+ If You Do (Burn Notice)","width":512,"provider_name":"Hulu","embed_url":"http://www.hulu.com/embed/c96sZQru3w567PP7t3cGZQ","version":"1.0","duration":2589.67,"height":296,"provider_url":"http://www.hulu.com/","air_date":"Thu
29
+ Nov 03 00:00:00 UTC 2011","html":"<object width=\"512\" height=\"296\"><param
30
+ name=\"movie\" value=\"http://www.hulu.com/embed/c96sZQru3w567PP7t3cGZQ\"></param><param
31
+ name=\"flashvars\" value=\"ap=1\"></param><embed src=\"http://www.hulu.com/embed/c96sZQru3w567PP7t3cGZQ\"
32
+ type=\"application/x-shockwave-flash\" width=\"512\" height=\"296\" flashvars=\"ap=1\"></embed></object>","cache_age":3600,"thumbnail_url":"http://thumbnails.hulu.com/8/60000008/60000008_145x80_generated.jpg"}'
33
+ http_version: '1.1'