subscene 0.0.1 → 0.0.2

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/.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>