subscene 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ only:
3
+ - master
4
+ rvm:
5
+ - 1.9.2
6
+ - 1.9.3
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in subscene.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ end
data/README.md CHANGED
@@ -1,7 +1,14 @@
1
- # Subscene
1
+ # Subscene [![Build Status](https://travis-ci.org/jassa/subscene.png)](https://travis-ci.org/jassa/subscene)
2
2
 
3
3
  Ruby API Client for Subscene.com
4
4
 
5
+ ## Introduction
6
+
7
+ Subscene.com is a very complete and reliable catalog of subtitles.
8
+ If you're reading this you probably know they don't have an API.
9
+
10
+ This gem will help you communicate with Subscene.com easily.
11
+
5
12
  ## Installation
6
13
 
7
14
  Add this line to your application's Gemfile:
@@ -16,9 +23,51 @@ Or install it yourself as:
16
23
 
17
24
  $ gem install subscene
18
25
 
19
- ## Usage
26
+ ## Searching
27
+
28
+ Subscene.com handles two kinds of searches:
29
+
30
+ 1. Search by Film or TV Show 'title'
31
+
32
+ e.g. "The Big Bang Theory", "The Hobbit", etc.
33
+
34
+ 2. Search for a particular 'release'
35
+
36
+ e.g. "The Big Bang Theory s01e01" or "The Hobbit HDTV"
37
+
38
+ There are certain keywords that trigger one search or the other,
39
+ this gem initially will support the second type (by release)
40
+ so make sure to format your queries properly.
41
+
42
+ ## Examples
43
+
44
+ The `search` method will return subtitles that match the query.
45
+
46
+ Subscene.search('The Big Bang Theory s01e01')
47
+ # => [#<Subscene::SubtitleResult @attributes={:id=>"136037", :name=>"The.Big.Bang.Theory..",
48
+ #<Subscene::SubtitleResult @attributes={:id=>"138042", :name=>"The Big Bang.."]
49
+
50
+ Once you get the results, you can iterate over them and review your options.
51
+
52
+ The information within these results is limited to what the Subscene.com
53
+ search reveals: http://subscene.com/subtitles/release.aspx?q=the%20big%20bang%20theory%20s01e01
54
+
55
+ You can obtain additional information by `find`ing the id.
56
+
57
+ Subscene.find(subtitles.last.id) # or Subscene.find(151582) directly
58
+ => #<Subscene::Subtitle @id="151582", @name="Fringe.S01E01.DVDSCR.XviD-MEDiEVAL-EN", @lang="English", @user="Jap", @user_id="20904", @comment="Has no comment.", @rating="8", @downloads="9,549", @framerate="Not available", @created_at="6/18/2008 3:09 PM", @download_url="/subtitle/download?mac=yoPjbFZ9WFbUmWTWpOvaGbXDYN2b4yBreI8TJIBfpdynT-4hzba446VvrVyxamBM0", @hearing_impaired=false>
59
+
60
+ ## Language Preferences
61
+
62
+ You can set the language id for the search filter.
63
+ Ids can be found at http://subscene.com/filter.
64
+ Maximum 3, comma separated.
20
65
 
21
- TODO: Write usage instructions here
66
+ # Examples
67
+ Subscene.language = 13 # English
68
+ Subscene.search("...") # Results will be only English subtitles
69
+ Subscene.language = "13,38" # English, Spanish
70
+ ...
22
71
 
23
72
  ## Contributing
24
73
 
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new do |r|
5
+ r.verbose = false
6
+ end
7
+
8
+ task :default => :spec
@@ -1,5 +1,107 @@
1
+ require "faraday"
2
+ require "nokogiri"
3
+
1
4
  require "subscene/version"
5
+ require "subscene/error"
6
+ require "subscene/response"
7
+ require "subscene/response/raise_error"
8
+ require "subscene/response/html"
9
+ require "subscene/subtitle_result_set"
10
+ require "subscene/subtitle"
2
11
 
3
12
  module Subscene
4
- # Your code goes here...
13
+
14
+ # Subscene.com is a very complete and reliable catalog of subtitles.
15
+ # If you're reading this you probably know they don't have an API.
16
+ #
17
+ # This gem will help you communicate with Subscene.com easily.
18
+ #
19
+ # == Searches
20
+ #
21
+ # Subscene.com handles two kinds of searches:
22
+ #
23
+ # a) Search by Film or TV Show 'title'
24
+ # e.g. "The Big Bang Theory", "The Hobbit", etc.
25
+ #
26
+ # b) Search for a particular 'release'
27
+ # e.g. "The Big Bang Theory s01e01" or "The Hobbit HDTV"
28
+ #
29
+ # There are certain keywords that trigger one search or the other,
30
+ # this gem initially will support the second type (by release)
31
+ # so make sure to format your queries properly.
32
+ #
33
+ extend self
34
+
35
+ ENDPOINT = "http://subscene.com"
36
+ RELEASE_PATH = "subtitles/release.aspx"
37
+
38
+ # Public: Search for a particular release.
39
+ #
40
+ # query - The String to be found.
41
+ #
42
+ # Examples
43
+ #
44
+ # Subscene.search('The Big Bang Theory s01e01')
45
+ # # => [#<Subscene::SubtitleResult:0x007feb7c9473b0
46
+ # @attributes={:id=>"136037", :name=>"The.Big.Bang.Theory.."]
47
+ #
48
+ # Returns the Subtitles found.
49
+ def search(query=nil)
50
+ params = { q: query } unless query.nil?
51
+ params ||= {}
52
+
53
+ response = connection.get do |req|
54
+ req.url RELEASE_PATH, params
55
+ req.headers['Cookie'] = "LanguageFilter=#{@lang_id};" if @lang_id
56
+ end
57
+
58
+ html = response.body
59
+ SubtitleResultSet.build(html).instances
60
+ end
61
+
62
+ # Public: Find a subtitle by id.
63
+ #
64
+ # id - The id of the subtitle.
65
+ #
66
+ # Examples
67
+ #
68
+ # Subscene.find(136037)
69
+ # # => TODO: display example result
70
+ #
71
+ # Returns the complete information of the Subtitle.
72
+ def find(id)
73
+ response = connection.get(id.to_s)
74
+ html = response.body
75
+
76
+ subtitle = Subtitle.build(html)
77
+ subtitle.id = id
78
+ subtitle
79
+ end
80
+
81
+ # Public: Set the language id for the search filter.
82
+ #
83
+ # lang_id - The id of the language. Maximum 3, comma separated.
84
+ # Ids can be found at http://subscene.com/filter
85
+ #
86
+ # Examples
87
+ #
88
+ # Subscene.language = 13 # English
89
+ # Subscene.search("...") # Results will be only English subtitles
90
+ # Subscene.language = "13,38" # English, Spanish
91
+ # ...
92
+ #
93
+ def language=(lang_id)
94
+ @lang_id = lang_id
95
+ end
96
+
97
+ private
98
+
99
+ def connection
100
+ @connection ||= Faraday.new(url: ENDPOINT) do |faraday|
101
+ faraday.response :logger if ENV['DEBUG']
102
+ faraday.adapter Faraday.default_adapter
103
+ faraday.use Subscene::Response::HTML
104
+ faraday.use Subscene::Response::RaiseError
105
+ end
106
+ end
5
107
  end
@@ -0,0 +1,3 @@
1
+ module Subscene
2
+ class SearchNotSupported < StandardError; end
3
+ end
@@ -0,0 +1,22 @@
1
+ module Subscene
2
+ class Response < Faraday::Response::Middleware
3
+ CONTENT_TYPE = 'Content-Type'.freeze
4
+
5
+ class << self
6
+ attr_accessor :parser
7
+ end
8
+
9
+ # Store a Proc that receives the body and returns the parsed result.
10
+ def self.define_parser(&block)
11
+ @parser = block
12
+ end
13
+
14
+ def response_type(env)
15
+ env[:response_headers][CONTENT_TYPE].to_s
16
+ end
17
+
18
+ def parse_response?(env)
19
+ env[:body].respond_to? :to_str
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Subscene
2
+ class Response::HTML < Response
3
+
4
+ dependency 'nokogiri'
5
+
6
+ define_parser do |body|
7
+ Nokogiri::HTML body
8
+ end
9
+
10
+ def parse(body)
11
+ case body
12
+ when String
13
+ self.class.parser.call body
14
+ else
15
+ body
16
+ end
17
+ end
18
+
19
+ end # Response::HTML
20
+ end # Subscene
@@ -0,0 +1,20 @@
1
+ module Subscene
2
+ class Response::RaiseError < Faraday::Response::Middleware
3
+ ClientErrorStatuses = 400...600
4
+
5
+ def on_complete(env)
6
+ case env[:status]
7
+ when 301, 302
8
+ raise Subscene::SearchNotSupported, response_values(env)
9
+ end
10
+ end
11
+
12
+ def response_values(env)
13
+ {
14
+ :status => env[:status],
15
+ :headers => env[:response_headers],
16
+ :body => env[:body]
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,56 @@
1
+ class Subscene::Subtitle
2
+
3
+ attr_accessor :attributes, :id, :name, :lang, :user, :user_id, :comment, :rating,
4
+ :downloads, :download_url, :framerate, :created_at, :hearing_impaired
5
+
6
+ def initialize(attributes = {})
7
+ @attributes = attributes
8
+ @id = @attributes[:id]
9
+ @name = @attributes[:name]
10
+ @lang = @attributes[:lang]
11
+ @user = @attributes[:user]
12
+ @user_id = @attributes[:user_id]
13
+ @comment = @attributes[:comment]
14
+ @rating = @attributes[:rating]
15
+ @downloads = @attributes[:downloads]
16
+ @framerate = @attributes[:framerate]
17
+ @created_at = @attributes[:created_at]
18
+ @download_url = @attributes[:download_url]
19
+ @hearing_impaired = @attributes[:hearing_impaired]
20
+ end
21
+
22
+
23
+ # Public: Download a Subtitle file.
24
+ #
25
+ # Examples
26
+ #
27
+ # Subscene.find(136037).download
28
+ # # => #<Faraday::Response #@env={:body=>"PK\u00...",
29
+ # :response_headers=>{"content-type"=>"application/x-zip-compressed"} [..]>
30
+ #
31
+ # Returns a Faraday::Response instance.
32
+ def download
33
+ conn = Faraday.new(url: Subscene::ENDPOINT)
34
+ conn.post do |req|
35
+ req.url download_url
36
+ req.headers['Referer'] = "#{Subscene::ENDPOINT}/#{id}"
37
+ end
38
+ end
39
+
40
+ def self.build(html)
41
+ new({
42
+ id: (html.css("nav.comment-sub a").to_s.match(/subtitleId=(\d+)/)[1] rescue nil),
43
+ name: (html.css("li.release").children.last.text.strip rescue nil),
44
+ lang: (html.css("a#downloadButton").text.match(/Download (.*)\n/)[1] rescue nil),
45
+ user: (html.css("li.author").text.strip rescue nil),
46
+ user_id: (html.css("li.author a").attribute("href").value.match(/\d+/).to_s rescue nil),
47
+ comment: (html.css("div.comment").text.strip rescue nil),
48
+ rating: (html.css("div.rating").text.strip rescue nil),
49
+ downloads: (html.css("div.details li[contains('Downloads')]").children.last.text.strip rescue nil),
50
+ framerate: (html.css("div.details li[contains('Framerate')]").children.last.text.strip rescue nil),
51
+ created_at: (html.css("div.details li[contains('Online')]").children.last.text.strip rescue nil),
52
+ download_url: (html.css("a#downloadButton").attr("href").value rescue nil),
53
+ hearing_impaired: (html.css("div.details li[contains('Hearing')]").children.last.text.strip != "No" rescue nil)
54
+ })
55
+ end
56
+ end
@@ -0,0 +1,35 @@
1
+ class Subscene::SubtitleResult
2
+
3
+ attr_accessor :attributes, :id, :name, :url, :lang,
4
+ :user, :user_id, :comment, :files_count, :hearing_impaired
5
+
6
+ def initialize(attributes)
7
+ @attributes = attributes
8
+
9
+ @id = @attributes[:id]
10
+ @name = @attributes[:name]
11
+ @url = @attributes[:url]
12
+ @lang = @attributes[:lang]
13
+ @user = @attributes[:user]
14
+ @user_id = @attributes[:user_id]
15
+ @comment = @attributes[:comment]
16
+ @files_count = @attributes[:files_count]
17
+ @hearing_impaired = @attributes[:hearing_impaired]
18
+ end
19
+
20
+ def self.build(html)
21
+ subtitle_url = (html.css("td.a1 a").attribute("href").value rescue nil)
22
+
23
+ new({
24
+ id: (subtitle_url.match(/\d+$/).to_s rescue nil),
25
+ name: (html.css("td.a1 span[2]").text.strip rescue nil),
26
+ url: subtitle_url,
27
+ lang: (html.css("td.a1 span[1]").text.strip rescue nil),
28
+ user: (html.css("td.a5").text.strip rescue nil),
29
+ user_id: (html.css("td.a5 a").attribute("href").value.match(/\d+/).to_s rescue nil),
30
+ comment: (html.css("td.a6").text.strip rescue nil),
31
+ files_count: (html.css("td.a3").text.to_i rescue nil),
32
+ hearing_impaired: html.at_css("td.a40").nil?
33
+ })
34
+ end
35
+ end
@@ -0,0 +1,19 @@
1
+ require "subscene/subtitle_result"
2
+
3
+ class Subscene::SubtitleResultSet
4
+ attr_reader :instances
5
+
6
+ def initialize(attributes)
7
+ @instances = attributes[:instances]
8
+ end
9
+
10
+ def self.build(html)
11
+ instances = html.css("tbody > tr").collect do |item|
12
+ Subscene::SubtitleResult.build(item)
13
+ end
14
+
15
+ new({
16
+ instances: instances
17
+ })
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module Subscene
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,31 @@
1
+ <tr>
2
+ <td class="a1">
3
+ <a href="/subtitles/the-big-bang-theory-first-season/english/136037">
4
+ <div class="visited">
5
+ <span class="l r positive-icon">
6
+ English
7
+ </span>
8
+ <span>
9
+ The.Big.Bang.Theory.S01E01-08.HDTV.XviD-XOR
10
+ </span>
11
+ </div>
12
+ </a>
13
+ </td>
14
+ <td class="a3">
15
+ 8
16
+ </td>
17
+ <td class="a40">
18
+ &nbsp;
19
+ </td>
20
+ <td class="a5">
21
+
22
+ <a href="/u/26733">
23
+ rogard
24
+ </a>
25
+ </td>
26
+ <td class="a6">
27
+ <div>
28
+ Episodes 1 to 8&nbsp;
29
+ </div>
30
+ </td>
31
+ </tr>
@@ -0,0 +1,96 @@
1
+ <tbody>
2
+ <tr>
3
+ <td class="a1">
4
+ <a href="/subtitles/the-big-bang-theory-first-season/english/136037">
5
+ <div class="visited">
6
+ <span class="l r positive-icon">
7
+ English
8
+ </span>
9
+ <span>
10
+ The.Big.Bang.Theory.S01E01-08.HDTV.XviD-XOR
11
+ </span>
12
+ </div>
13
+ </a>
14
+ </td>
15
+ <td class="a3">
16
+ 8
17
+ </td>
18
+ <td class="a40">
19
+ &nbsp;
20
+ </td>
21
+ <td class="a5">
22
+
23
+ <a href="/u/26733">
24
+ rogard
25
+ </a>
26
+ </td>
27
+ <td class="a6">
28
+ <div>
29
+ Episodes 1 to 8&nbsp;
30
+ </div>
31
+ </td>
32
+ </tr>
33
+
34
+ <tr>
35
+ <td class="a1">
36
+ <a href="/subtitles/the-big-bang-theory-first-season/english/171327">
37
+ <div class="visited">
38
+ <span class="l r positive-icon">
39
+ English
40
+ </span>
41
+ <span>
42
+ The.Big.Bang.Theory.S01.DVDRop.XviD-FoV
43
+ </span>
44
+ </div>
45
+ </a>
46
+ </td>
47
+ <td class="a3">
48
+ 17
49
+ </td>
50
+ <td class="a41">
51
+ &nbsp;
52
+ </td>
53
+ <td class="a5">
54
+ <a href="/u/93617">
55
+ ray148
56
+ </a>
57
+ </td>
58
+ <td class="a6">
59
+ <div>
60
+ &nbsp;
61
+ </div>
62
+ </td>
63
+ </tr>
64
+
65
+ <tr>
66
+ <td class="a1">
67
+ <a href="/subtitles/the-big-bang-theory-second-season/english/173831">
68
+ <div class="visited">
69
+ <span class="l r positive-icon">
70
+ English
71
+ </span>
72
+ <span>
73
+ The.Big.Bang.Theory.S02E02.HDTV.XviD-DOT [Edited]
74
+ </span>
75
+ </div>
76
+ </a>
77
+ </td>
78
+ <td class="a3">
79
+ 1
80
+ </td>
81
+ <td class="a40">
82
+ &nbsp;
83
+ </td>
84
+ <td class="a5">
85
+
86
+ <a href="/u/51235">
87
+ verdikt
88
+ </a>
89
+ </td>
90
+ <td class="a6">
91
+ <div>
92
+ The edited version of S02E02. Enjoi :)&nbsp;
93
+ </div>
94
+ </td>
95
+ </tr>
96
+ </tbody>