addic7ed 3.0.0 → 4.0.0.pre.beta.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/addic7ed.rb +5 -10
  3. data/lib/addic7ed/common.rb +13 -1285
  4. data/lib/addic7ed/config.json +1417 -0
  5. data/lib/addic7ed/errors.rb +8 -7
  6. data/lib/addic7ed/models/episode.rb +112 -0
  7. data/lib/addic7ed/models/search.rb +90 -0
  8. data/lib/addic7ed/models/subtitle.rb +109 -0
  9. data/lib/addic7ed/models/subtitles_collection.rb +63 -0
  10. data/lib/addic7ed/models/video_file.rb +147 -0
  11. data/lib/addic7ed/service.rb +13 -0
  12. data/lib/addic7ed/services/check_compatibility.rb +44 -0
  13. data/lib/addic7ed/services/download_subtitle.rb +53 -0
  14. data/lib/addic7ed/services/get_shows_list.rb +29 -0
  15. data/lib/addic7ed/services/{addic7ed_comment_normalizer.rb → normalize_comment.rb} +4 -8
  16. data/lib/addic7ed/services/normalize_version.rb +22 -0
  17. data/lib/addic7ed/services/parse_page.rb +43 -0
  18. data/lib/addic7ed/services/parse_subtitle.rb +79 -0
  19. data/lib/addic7ed/services/url_encode_show_name.rb +46 -0
  20. data/lib/addic7ed/version.rb +1 -1
  21. metadata +63 -50
  22. data/bin/addic7ed +0 -144
  23. data/lib/addic7ed/episode.rb +0 -95
  24. data/lib/addic7ed/parser.rb +0 -105
  25. data/lib/addic7ed/services/addic7ed_version_normalizer.rb +0 -24
  26. data/lib/addic7ed/show_list.rb +0 -61
  27. data/lib/addic7ed/subtitle.rb +0 -72
  28. data/lib/addic7ed/video_file.rb +0 -41
  29. data/spec/lib/addic7ed/common_spec.rb +0 -21
  30. data/spec/lib/addic7ed/episode_spec.rb +0 -165
  31. data/spec/lib/addic7ed/services/addic7ed_comment_normalizer_spec.rb +0 -12
  32. data/spec/lib/addic7ed/services/addic7ed_version_normalizer_spec.rb +0 -73
  33. data/spec/lib/addic7ed/show_list_spec.rb +0 -42
  34. data/spec/lib/addic7ed/subtitle_spec.rb +0 -182
  35. data/spec/lib/addic7ed/video_file_spec.rb +0 -159
  36. data/spec/responses/basic_redirection.http +0 -13
  37. data/spec/responses/homepage.http +0 -921
  38. data/spec/responses/redirection_loop.http +0 -12
  39. data/spec/responses/walking-dead-3-2-1.http +0 -770
  40. data/spec/responses/walking-dead-3-2-48.http +0 -2117
  41. data/spec/responses/walking-dead-3-2-7.http +0 -659
  42. data/spec/responses/walking-dead-3-2-8.http +0 -815
  43. data/spec/responses/walking-dead-3-2-8_best_subtitle.http +0 -1928
  44. data/spec/responses/walking-dead-3-4-8.http +0 -732
  45. data/spec/responses/walking-dead-3-42-8.http +0 -13
  46. data/spec/spec_helper.rb +0 -26
@@ -0,0 +1,13 @@
1
+ module Addic7ed
2
+ module Service
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def call(*args)
9
+ new(*args).call
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,44 @@
1
+ module Addic7ed
2
+ class CheckCompatibility
3
+ include Service
4
+
5
+ attr_reader :subtitle, :group
6
+
7
+ def initialize(subtitle, group)
8
+ @subtitle = subtitle
9
+ @group = group
10
+ end
11
+
12
+ def call
13
+ defined_as_compatible? || generally_compatible? || commented_as_compatible?
14
+ end
15
+
16
+ private
17
+
18
+ def defined_as_compatible?
19
+ subtitle.version.split(",").include? group
20
+ end
21
+
22
+ def generally_compatible?
23
+ COMPATIBILITY_720P[subtitle.version] == group || COMPATIBILITY_720P[group] == subtitle.version
24
+ end
25
+
26
+ def commented_as_compatible?
27
+ return false if /(won'?t|doesn'?t|not) +work/i =~ subtitle.comment
28
+ return false if /resync +(from|of|for)/i =~ subtitle.comment
29
+ !!comment_matches_a_compatible_group?
30
+ end
31
+
32
+ def comment_matches_a_compatible_group?
33
+ Regexp.new("(#{compatible_groups.join('|')})", "i") =~ subtitle.comment
34
+ end
35
+
36
+ def compatible_groups
37
+ @compatible_groups ||= [
38
+ group,
39
+ COMPATIBILITY_720P[group],
40
+ COMPATIBILITY_720P[subtitle.version]
41
+ ].compact.uniq
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ module Addic7ed
2
+ class DownloadSubtitle
3
+ include Service
4
+
5
+ HTTP_REDIRECT_LIMIT = 8
6
+
7
+ attr_reader :url, :filename, :referer, :redirect_count
8
+
9
+ def initialize(url, filename, referer, redirect_count = 0)
10
+ @url = url
11
+ @filename = filename
12
+ @referer = referer
13
+ @redirect_count = redirect_count
14
+ end
15
+
16
+ def call
17
+ raise DownloadError, "Too many HTTP redirections" if redirect_count >= HTTP_REDIRECT_LIMIT
18
+ raise DailyLimitExceeded, "Daily limit exceeded" if %r{^/downloadexceeded.php} =~ url
19
+ return follow_redirection(response["location"]) if response.is_a? Net::HTTPRedirection
20
+ write(response.body)
21
+ end
22
+
23
+ private
24
+
25
+ def uri
26
+ @uri ||= URI(url)
27
+ end
28
+
29
+ def response
30
+ @response ||= Net::HTTP.start(uri.hostname, uri.port) do |http|
31
+ request = Net::HTTP::Get.new(uri.request_uri)
32
+ request["Referer"] = referer # Addic7ed requires the Referer to be correct
33
+ request["User-Agent"] = USER_AGENTS.sample
34
+ http.request(request)
35
+ end
36
+ rescue
37
+ raise DownloadError, "A network error occured"
38
+ end
39
+
40
+ def follow_redirection(location_header)
41
+ # Addic7ed is serving redirection URL not-encoded,
42
+ # but Ruby does not support it (see http://bugs.ruby-lang.org/issues/7396)
43
+ new_url = URI.escape(location_header)
44
+ DownloadSubtitle.call(new_url, filename, url, redirect_count + 1)
45
+ end
46
+
47
+ def write(content)
48
+ Kernel.open(filename, "w") { |f| f << content }
49
+ rescue
50
+ raise DownloadError, "Cannot write to disk"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,29 @@
1
+ require "singleton"
2
+
3
+ module Addic7ed
4
+ class GetShowsList
5
+ include Singleton
6
+
7
+ def self.call
8
+ instance.call
9
+ end
10
+
11
+ def call
12
+ @shows ||= homepage_body.css("select#qsShow option:not(:first-child)").map(&:text)
13
+ end
14
+
15
+ private
16
+
17
+ def homepage_body
18
+ @homepage_body ||= Oga.parse_html(addic7ed_homepage.body)
19
+ end
20
+
21
+ def addic7ed_homepage
22
+ Net::HTTP.start("www.addic7ed.com") do |http|
23
+ request = Net::HTTP::Get.new("/")
24
+ request["User-Agent"] = USER_AGENTS.sample
25
+ http.request(request)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,19 +1,15 @@
1
1
  module Addic7ed
2
- class Addic7edCommentNormalizer
2
+ class NormalizeComment
3
+ include Service
4
+
3
5
  attr_reader :comment
4
6
 
5
7
  def initialize(comment)
6
8
  @comment = comment || ""
7
9
  end
8
10
 
9
- def self.call(comment)
10
- new(comment).call
11
- end
12
-
13
11
  def call
14
- comment.downcase
12
+ comment.downcase.strip
15
13
  end
16
14
  end
17
-
18
- private
19
15
  end
@@ -0,0 +1,22 @@
1
+ module Addic7ed
2
+ class NormalizeVersion
3
+ include Service
4
+
5
+ attr_reader :version
6
+
7
+ def initialize(version)
8
+ @version = version || ""
9
+ end
10
+
11
+ def call
12
+ version
13
+ .gsub(/[[:space:]]/, "")
14
+ .upcase
15
+ .gsub(/,[\d\. ]+MBS$/, "")
16
+ .gsub(/(^VERSION *|720P|1080P|HDTV|PROPER|RERIP|INTERNAL|X\.?264)/, "")
17
+ .gsub(/[- \.\,]/, " ")
18
+ .strip
19
+ .gsub(/ +/, ",")
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ require "oga"
2
+ require "net/http"
3
+ require "open-uri"
4
+
5
+ module Addic7ed
6
+ class ParsePage
7
+ include Service
8
+
9
+ attr_reader :uri
10
+
11
+ def initialize(url)
12
+ @uri = URI(url)
13
+ end
14
+
15
+ def call
16
+ raise NoSubtitleFound unless subtitles_found?
17
+ subtitles_nodes.map { |subtitle_node| Addic7ed::ParseSubtitle.call(subtitle_node) }
18
+ end
19
+
20
+ private
21
+
22
+ def page_dom
23
+ raise EpisodeNotFound if server_response.body.nil? || server_response.body.empty?
24
+ @page_dom ||= Oga.parse_html(server_response.body)
25
+ end
26
+
27
+ def subtitles_nodes
28
+ @subtitles_nodes ||= page_dom.css("#container95m table.tabel95 table.tabel95")
29
+ end
30
+
31
+ def server_response
32
+ @server_response ||= Net::HTTP.start(uri.hostname, uri.port) do |http|
33
+ request = Net::HTTP::Get.new(uri.request_uri)
34
+ request["User-Agent"] = USER_AGENTS.sample
35
+ http.request(request)
36
+ end
37
+ end
38
+
39
+ def subtitles_found?
40
+ page_dom.css("select#filterlang ~ font[color='yellow']").empty? && !subtitles_nodes.empty?
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,79 @@
1
+ require "oga"
2
+
3
+ module Addic7ed
4
+ class ParseSubtitle
5
+ include Service
6
+
7
+ attr_reader :subtitle_node
8
+
9
+ FIELDS = [:version, :language, :status, :url, :source, :hi, :downloads, :comment].freeze
10
+
11
+ def initialize(subtitle_node)
12
+ @subtitle_node = subtitle_node
13
+ end
14
+
15
+ def call
16
+ Addic7ed::Subtitle.new(extract_fields)
17
+ end
18
+
19
+ private
20
+
21
+ def extract_fields
22
+ FIELDS.map do |field|
23
+ { field => send(:"extract_#{field}") }
24
+ end.reduce(:merge)
25
+ end
26
+
27
+ def extract_field(selector, options = { required: true })
28
+ node = subtitle_node.css(selector).first
29
+ raise Addic7ed::ParsingError if options[:required] && node.nil?
30
+ yield node
31
+ end
32
+
33
+ def extract_version
34
+ extract_field(".NewsTitle", &:text)
35
+ end
36
+
37
+ def extract_language
38
+ extract_field(".language") do |node|
39
+ node.text.gsub(/\A\W*/, "").gsub(/[^\w\)]*\z/, "")
40
+ end
41
+ end
42
+
43
+ def extract_status
44
+ extract_field("tr:nth-child(3) td:nth-child(4) b") do |node|
45
+ node.text.strip
46
+ end
47
+ end
48
+
49
+ def extract_url
50
+ extract_field("a.buttonDownload:last-of-type") do |node|
51
+ "http://www.addic7ed.com#{node['href']}"
52
+ end
53
+ end
54
+
55
+ def extract_source
56
+ extract_field("tr:nth-child(3) td:first-child a", required: false) do |node|
57
+ node["href"] unless node.nil?
58
+ end
59
+ end
60
+
61
+ def extract_hi
62
+ extract_field("tr:nth-child(4) td.newsDate img:last-of-type") do |node|
63
+ node.attribute("title") == "Hearing Impaired"
64
+ end
65
+ end
66
+
67
+ def extract_downloads
68
+ extract_field("tr:nth-child(4) td.newsDate") do |node|
69
+ /(?<downloads>\d*) Downloads/.match(node.text)[:downloads]
70
+ end
71
+ end
72
+
73
+ def extract_comment
74
+ extract_field("tr:nth-child(2) td.newsDate") do |node|
75
+ node.text.gsub(/<img[^>]+\>/i, "")
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,46 @@
1
+ module Addic7ed
2
+ class URLEncodeShowName
3
+ include Service
4
+
5
+ attr_reader :filename
6
+
7
+ def initialize(filename)
8
+ @filename = filename
9
+ end
10
+
11
+ # This service is unfortunately over complex because we have to compare the given show name to
12
+ # the actual Addic7ed shows list in order to find out how Addic7ed URL-encodes this show name
13
+ # (this is due to the inconsistency of their URL-encoding policy)
14
+ def call
15
+ matching_shows = matching_shows(ignore_year: false)
16
+ matching_shows = matching_shows(ignore_year: true) if matching_shows.empty?
17
+ raise ShowNotFound if matching_shows.empty?
18
+ matching_shows.last.tr(" ", "_")
19
+ end
20
+
21
+ private
22
+
23
+ def matching_shows(opts)
24
+ addic7ed_shows.select { |show_name| matching?(show_name, opts) }
25
+ end
26
+
27
+ def normalize(show_name, opts)
28
+ show_name
29
+ .downcase
30
+ .delete("'")
31
+ .gsub(/[_\.]+/, " ")
32
+ .gsub(/ (US|UK)( |$)/i, " (\\1)\\2")
33
+ .gsub(/ (\d{4})( |$)/i, " (\\1)\\2")
34
+ .strip
35
+ .tap { |showname| showname.gsub!(/ \(\d{4}\)( |$)/, '\1') if opts[:ignore_year] }
36
+ end
37
+
38
+ def matching?(addic7ed_show, opts)
39
+ normalize(addic7ed_show, opts) == normalize(filename, opts)
40
+ end
41
+
42
+ def addic7ed_shows
43
+ @addic7ed_shows ||= GetShowsList.call
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,3 @@
1
1
  module Addic7ed
2
- VERSION = "3.0.0"
2
+ VERSION = "4.0.0-beta.5".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: addic7ed
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 4.0.0.pre.beta.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Baudino
@@ -66,6 +66,48 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: inch
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
69
111
  - !ruby/object:Gem::Dependency
70
112
  name: oga
71
113
  requirement: !ruby/object:Gem::Requirement
@@ -96,42 +138,30 @@ dependencies:
96
138
  version: 1.8.3
97
139
  description: Ruby script (cli) to fetch subtitles on Addic7ed
98
140
  email: michael.baudino@alpine-lab.com
99
- executables:
100
- - addic7ed
141
+ executables: []
101
142
  extensions: []
102
143
  extra_rdoc_files: []
103
144
  files:
104
145
  - LICENSE.md
105
- - bin/addic7ed
106
146
  - lib/addic7ed.rb
107
147
  - lib/addic7ed/common.rb
108
- - lib/addic7ed/episode.rb
148
+ - lib/addic7ed/config.json
109
149
  - lib/addic7ed/errors.rb
110
- - lib/addic7ed/parser.rb
111
- - lib/addic7ed/services/addic7ed_comment_normalizer.rb
112
- - lib/addic7ed/services/addic7ed_version_normalizer.rb
113
- - lib/addic7ed/show_list.rb
114
- - lib/addic7ed/subtitle.rb
150
+ - lib/addic7ed/models/episode.rb
151
+ - lib/addic7ed/models/search.rb
152
+ - lib/addic7ed/models/subtitle.rb
153
+ - lib/addic7ed/models/subtitles_collection.rb
154
+ - lib/addic7ed/models/video_file.rb
155
+ - lib/addic7ed/service.rb
156
+ - lib/addic7ed/services/check_compatibility.rb
157
+ - lib/addic7ed/services/download_subtitle.rb
158
+ - lib/addic7ed/services/get_shows_list.rb
159
+ - lib/addic7ed/services/normalize_comment.rb
160
+ - lib/addic7ed/services/normalize_version.rb
161
+ - lib/addic7ed/services/parse_page.rb
162
+ - lib/addic7ed/services/parse_subtitle.rb
163
+ - lib/addic7ed/services/url_encode_show_name.rb
115
164
  - lib/addic7ed/version.rb
116
- - lib/addic7ed/video_file.rb
117
- - spec/lib/addic7ed/common_spec.rb
118
- - spec/lib/addic7ed/episode_spec.rb
119
- - spec/lib/addic7ed/services/addic7ed_comment_normalizer_spec.rb
120
- - spec/lib/addic7ed/services/addic7ed_version_normalizer_spec.rb
121
- - spec/lib/addic7ed/show_list_spec.rb
122
- - spec/lib/addic7ed/subtitle_spec.rb
123
- - spec/lib/addic7ed/video_file_spec.rb
124
- - spec/responses/basic_redirection.http
125
- - spec/responses/homepage.http
126
- - spec/responses/redirection_loop.http
127
- - spec/responses/walking-dead-3-2-1.http
128
- - spec/responses/walking-dead-3-2-48.http
129
- - spec/responses/walking-dead-3-2-7.http
130
- - spec/responses/walking-dead-3-2-8.http
131
- - spec/responses/walking-dead-3-2-8_best_subtitle.http
132
- - spec/responses/walking-dead-3-4-8.http
133
- - spec/responses/walking-dead-3-42-8.http
134
- - spec/spec_helper.rb
135
165
  homepage: https://github.com/michaelbaudino/addic7ed-ruby
136
166
  licenses:
137
167
  - MIT
@@ -147,31 +177,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
147
177
  version: '0'
148
178
  required_rubygems_version: !ruby/object:Gem::Requirement
149
179
  requirements:
150
- - - ">="
180
+ - - ">"
151
181
  - !ruby/object:Gem::Version
152
- version: '0'
182
+ version: 1.3.1
153
183
  requirements: []
154
184
  rubyforge_project:
155
185
  rubygems_version: 2.4.8
156
186
  signing_key:
157
187
  specification_version: 4
158
188
  summary: Addic7ed auto-downloader
159
- test_files:
160
- - spec/lib/addic7ed/common_spec.rb
161
- - spec/lib/addic7ed/episode_spec.rb
162
- - spec/lib/addic7ed/services/addic7ed_comment_normalizer_spec.rb
163
- - spec/lib/addic7ed/services/addic7ed_version_normalizer_spec.rb
164
- - spec/lib/addic7ed/show_list_spec.rb
165
- - spec/lib/addic7ed/subtitle_spec.rb
166
- - spec/lib/addic7ed/video_file_spec.rb
167
- - spec/responses/basic_redirection.http
168
- - spec/responses/homepage.http
169
- - spec/responses/redirection_loop.http
170
- - spec/responses/walking-dead-3-2-1.http
171
- - spec/responses/walking-dead-3-2-48.http
172
- - spec/responses/walking-dead-3-2-7.http
173
- - spec/responses/walking-dead-3-2-8.http
174
- - spec/responses/walking-dead-3-2-8_best_subtitle.http
175
- - spec/responses/walking-dead-3-4-8.http
176
- - spec/responses/walking-dead-3-42-8.http
177
- - spec/spec_helper.rb
189
+ test_files: []
190
+ has_rdoc: