addic7ed 3.0.0 → 4.0.0.pre.beta.5

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