directlink 0.0.9.1 → 0.0.9.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '08e29b8a3032310a9443ca5f2bc001c449722aa8'
4
- data.tar.gz: 15ee2b72e8ea566e2250a0311afe530a5b9dbd36
3
+ metadata.gz: f745bed3faf2b74dfe4532357fbb772fe32598b1
4
+ data.tar.gz: 203127452f8e51be364fff16a4c1eafa8a0a21df
5
5
  SHA512:
6
- metadata.gz: b30d00607103ed669f5876b9360b8280e24a6ba66c06117f577e47b497eb1ed76eee953bf0cfc97a2525dffc7c0f1169377cbcb780d7d2c9c1a1d74e9aaec212
7
- data.tar.gz: 36a8ecce05a750e1b5aea43929037d67c38f4dcc0df8d022739aa7508a01d51d573e3f39f0ebbca14fca2dbb90d29eb2be6ae74949b77e1e735da7c2be16ac04
6
+ metadata.gz: 75bd9d5351c48c4f1df45b76f4835abc52bd057eabd01f4615a2056c16f4f9cf57546c3ff7123f479836206d1bb3164a740bebd2d81b84899b5507ee74d0e178
7
+ data.tar.gz: 6e20c47d7270b6bf3b0920ea964337b5143e255eb789baac70823218c9b689b13a6b16abdc729a3c134a2b5cf48fccfa074ec64bebac6ed835a32764b8f5726c
@@ -68,16 +68,7 @@ begin
68
68
  (t.is_a?(Array) ? t : [t]).each{ |s| puts "=> #{s.url}\n #{s.type} #{s.width}x#{s.height}" }
69
69
  end
70
70
  end
71
- rescue SocketError,
72
- Net::OpenTimeout,
73
- Errno::ECONNRESET,
74
- NetHTTPUtils::Error,
75
- FastImage::UnknownImageType,
76
- FastImage::ImageFetchFailure,
77
- # DirectLink::ErrorMissingEnvVar,
78
- # DirectLink::ErrorAssert,
79
- DirectLink::ErrorNotFound,
80
- DirectLink::ErrorBadLink => e
71
+ rescue *DirectLink::NORMAL_EXCEPTIONS => e
81
72
  puts e.backtrace if debug
82
73
  cause = e.cause if e.cause if e.respond_to? :cause
83
74
  c = e.class.to_s
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "directlink"
3
- spec.version = "0.0.9.1"
4
- spec.summary = "converts any kind of image hyperlink to direct link, type of image and its resolution"
3
+ spec.version = "0.0.9.2"
4
+ spec.summary = "obtains from any kind of hyperlink a link to an image, its format and resolution"
5
5
 
6
6
  spec.author = "Victor Maslov aka Nakilon"
7
7
  spec.email = "nakilon@gmail.com"
@@ -11,19 +11,18 @@ Gem::Specification.new do |spec|
11
11
 
12
12
  spec.add_dependency "fastimage", "~>2.1.3"
13
13
  spec.add_dependency "nokogiri"
14
- spec.add_dependency "nethttputils", "~>0.4.0.0"
15
- spec.add_dependency "reddit_bot", "~>1.7.0"
14
+ spec.add_dependency "nethttputils", "~>0.4.1.0"
15
+ spec.add_dependency "reddit_bot", "~>1.7.8"
16
16
  spec.add_dependency "kramdown"
17
17
  spec.add_dependency "addressable"
18
18
  spec.add_development_dependency "minitest"
19
- spec.add_development_dependency "byebug"
20
19
 
21
20
  spec.require_path = "lib"
22
21
  spec.bindir = "bin"
23
22
  spec.executable = "directlink"
24
23
  spec.test_file = "test.rb"
25
- spec.files = `git ls-files -z`.split(?\0) - spec.test_files
24
+ spec.files = %w{ LICENSE directlink.gemspec lib/directlink.rb bin/directlink }
26
25
 
27
- spec.requirements << "you may need to create apps and provide their API tokens:"
26
+ spec.requirements << "you may want to create apps and provide API tokens:"
28
27
  spec.requirements << "IMGUR_CLIENT_ID, FLICKR_API_KEY, REDDIT_SECRETS"
29
28
  end
@@ -2,11 +2,10 @@ module DirectLink
2
2
 
3
3
  class << self
4
4
  attr_accessor :silent
5
- end
6
- self.silent = false
7
- class << self
8
5
  attr_accessor :logger
6
+ attr_accessor :timeout
9
7
  end
8
+ self.silent = false
10
9
  self.logger = Object.new
11
10
  self.logger.define_singleton_method :error do |str|
12
11
  puts str unless Module.nesting.first.silent
@@ -35,6 +34,20 @@ module DirectLink
35
34
  end
36
35
  end
37
36
 
37
+ require "nethttputils"
38
+ require "fastimage"
39
+ NORMAL_EXCEPTIONS = [
40
+ SocketError,
41
+ Net::OpenTimeout,
42
+ Errno::ECONNRESET,
43
+ NetHTTPUtils::Error,
44
+ NetHTTPUtils::EOFError_from_rbuf_fill,
45
+ FastImage::UnknownImageType,
46
+ FastImage::ImageFetchFailure,
47
+ DirectLink::ErrorNotFound,
48
+ DirectLink::ErrorBadLink,
49
+ ] # the only exceptions gem user should expect and handle
50
+
38
51
 
39
52
  def self.google src, width = 0
40
53
  # this can handle links without schema because it's used for parsing community HTML pages
@@ -76,10 +89,9 @@ module DirectLink
76
89
  end
77
90
 
78
91
  require "json"
79
- require "nethttputils"
80
92
 
81
93
  # TODO make the timeout handling respect the way the Directlink method works with timeouts
82
- def self.imgur link, timeout = 1000
94
+ def self.imgur link, timeout = 2000
83
95
  raise ErrorMissingEnvVar.new "define IMGUR_CLIENT_ID env var" unless ENV["IMGUR_CLIENT_ID"]
84
96
 
85
97
  request_data = lambda do |url|
@@ -107,16 +119,16 @@ module DirectLink
107
119
  elsif data["images"]
108
120
  raise ErrorNotFound.new link.inspect if data["images"].empty?
109
121
  data["images"]
110
- elsif data["type"] && data["type"].start_with?("image/")
122
+ elsif data["type"] && %w{ image/jpeg image/png image/gif video/mp4 }.include?(data["type"])
111
123
  # TODO check if this branch is possible at all
112
124
  [ data ]
113
125
  # elsif data["comment"]
114
126
  # fi["https://imgur.com/" + data["image_id"]]
115
127
  else
116
128
  # one day single-video item should hit this but somehow it didn't yet
117
- raise ErrorAssert.new "unknown data format #{data.inspect} for #{link}"
129
+ raise ErrorAssert.new "unknown data format #{json} for #{link}"
118
130
  end
119
- when /\Ahttps?:\/\/(?:(?:i|m|www)\.)?imgur\.com\/([a-zA-Z0-9]{7,8})(?:\.(?:gifv|jpg(?:\?fb)?|png))?\z/,
131
+ when /\Ahttps?:\/\/(?:(?:i|m|www)\.)?imgur\.com\/([a-zA-Z0-9]{7,8})(?:\.(?:gifv|jpe?g(?:\?fb)?|png))?\z/,
120
132
  /\Ahttps?:\/\/(?:(?:i|m|www)\.)?imgur\.com\/([a-zA-Z0-9]{5})\.mp4\z/,
121
133
  /\Ahttps?:\/\/imgur\.com\/([a-zA-Z0-9]{5}(?:[a-zA-Z0-9]{2})?)\z/,
122
134
  /\Ahttps?:\/\/imgur\.com\/([a-zA-Z0-9]{7})(?:\?\S+)?\z/,
@@ -128,7 +140,7 @@ module DirectLink
128
140
  raise ErrorBadLink.new link
129
141
  end.map do |image|
130
142
  case image["type"]
131
- when "image/jpeg", "image/png", "image/gif", "video/mp4"
143
+ when *%w{ image/jpeg image/png image/gif video/mp4 }
132
144
  image.values_at "link", "width", "height", "type"
133
145
  else
134
146
  raise ErrorAssert.new "unknown type of #{link}: #{image}"
@@ -191,11 +203,11 @@ module DirectLink
191
203
  attr_accessor :reddit_bot
192
204
  end
193
205
  def self.reddit link, timeout = 1000
194
- unless id = URI(link).path[/\A(?:\/r\/[0-9a-zA-Z_]+)?(?:\/comments|\/duplicates)?\/([0-9a-z]{5,6})(?:\/|\z)/, 1]
195
- raise DirectLink::ErrorBadLink.new link unless URI(link).host &&
196
- URI(link).host.split(?.) == %w{ i redd it } &&
197
- URI(link).path[/\A\/[a-z0-9]{12,13}\.(gif|jpg)\z/]
198
- return [true, link]
206
+ return [true, link] if URI(link).host &&
207
+ URI(link).host.split(?.) == %w{ i redd it } &&
208
+ URI(link).path[/\A\/[a-z0-9]{12,13}\.(gif|jpg)\z/]
209
+ unless id = link[/\Ahttps:\/\/www\.reddit\.com\/gallery\/([0-9a-z]{5,6})\z/, 1]
210
+ raise DirectLink::ErrorBadLink.new link unless id = URI(link).path[/\A(?:\/r\/[0-9a-zA-Z_]+)?(?:\/comments|\/duplicates)?\/([0-9a-z]{5,6})(?:\/|\z)/, 1]
199
211
  end
200
212
  retry_on_json_parseerror = lambda do |&b|
201
213
  t = 1
@@ -223,16 +235,17 @@ module DirectLink
223
235
  end
224
236
  # TODO: do we handle linking Imgur albums?
225
237
  data = json["data"]["children"].first["data"]
226
- if data["media"]["reddit_video"]
227
- return [true, data["media"]["reddit_video"]["fallback_url"]]
228
- else
238
+ if data["media"]
239
+ return [true, data["media"]["reddit_video"]["fallback_url"]] if data["media"]["reddit_video"]
229
240
  raise ErrorAssert.new "our knowledge about Reddit API seems to be outdated" unless data["media"].keys.sort == %w{ oembed type } && %w{ youtube.com gfycat.com imgur.com }.include?(data["media"]["type"])
230
241
  return [true, data["media"]["oembed"]["thumbnail_url"]]
231
- end if data["media"]
242
+ end
232
243
  return [true, data["media_metadata"].values.map do |media|
233
- [media["m"], *media["p"].max_by{ |_| _["x"] * _["y"] }.values_at("x", "y", "u")]
234
- end] if data["media_metadata"]
235
- return [true, data["url"]] if data["crosspost_parent"]
244
+ next if media == {"status"=>"failed"}
245
+ raise ErrorAssert.new "our knowledge about Reddit API seems to be outdated" unless media["status"] == "valid"
246
+ [media["m"], *media["s"].values_at("x", "y"), CGI.unescapeHTML(media["s"]["u"])]
247
+ end.compact] if data["media_metadata"]
248
+ return [true, "#{"https://www.reddit.com" if /\A\/r\/[0-9a-zA-Z_]+\/comments\/[0-9a-z]{5,6}\// =~ data["url"]}#{data["url"]}"] if data["crosspost_parent"]
236
249
  return [true, data["url"]] unless data["is_self"]
237
250
  raise ErrorAssert.new "our knowledge about Reddit API seems to be outdated" if data["url"] != "https://www.reddit.com" + data["permalink"]
238
251
  return [false, data["selftext"]]
@@ -242,7 +255,7 @@ module DirectLink
242
255
  id, mtd, field, f = case link
243
256
  when %r{\Ahttps://vk\.com/id(?<user_id>\d+)\?z=photo(?<id>\k<user_id>_\d+)(%2F(album\k<user_id>_0|photos\k<user_id>))?\z},
244
257
  %r{\Ahttps://vk\.com/[a-z_]+\?z=photo(?<_>)(?<id>(?<user_id>\d+)_\d+)%2Fphotos\k<user_id>\z},
245
- %r{\Ahttps://vk\.com/photo(?<_>)(?<id>-?\d+_\d+)(\?all=1)?\z},
258
+ %r{\Ahttps://vk\.com/photo(?<_>)(?<id>-?\d+_\d+)(\?(?:all|rev)=1)?\z},
246
259
  %r{\Ahttps://vk\.com/feed\?section=likes&z=photo(?<_>)(?<id>-(?<user_id>\d+)_\d+)%2F(liked\d+|album\k<user_id>_0)\z},
247
260
  %r{\Ahttps://vk\.com/[a-z_]+\?z=photo(?<_>)(?<id>(?<user_id>-\d+)_\d+)%2F(wall\k<user_id>_\d+|album\k<user_id>_0)\z},
248
261
  %r{\Ahttps://vk\.com/wall(?<user_id>-\d+)_\d+\?z=photo(?<id>\k<user_id>_\d+)%2F(wall\k<user_id>_\d+|album\k<user_id>_00%2Frev|\d+)\z}
@@ -286,9 +299,8 @@ module DirectLink
286
299
  end
287
300
 
288
301
 
289
- require "fastimage"
290
-
291
- def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
302
+ def DirectLink link, timeout = nil, proxy = nil, giveup: false, ignore_meta: false
303
+ timeout ||= DirectLink.timeout
292
304
  ArgumentError.new("link should be a <String>, not <#{link.class}>") unless link.is_a? String
293
305
  begin
294
306
  URI link
@@ -326,7 +338,7 @@ def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
326
338
  **( %w{ reddit com } == URI(link).host.split(?.).last(2) ||
327
339
  %w{ redd it } == URI(link).host.split(?.) ? {Cookie: "over18=1"} : {} ),
328
340
  }
329
- head = NetHTTPUtils.request_data link, :head, header: header, **(timeout ? {
341
+ head = NetHTTPUtils.request_data link, :HEAD, header: header, **(proxy ? {proxy: proxy} : {}), **(timeout ? {
330
342
  timeout: timeout,
331
343
  max_start_http_retry_delay: timeout,
332
344
  max_read_retry_delay: timeout
@@ -383,7 +395,7 @@ def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
383
395
  f = ->_{ _.type == :a ? _.attr["href"] : _.children.flat_map(&f) }
384
396
  require "kramdown"
385
397
  return f[Kramdown::Document.new(u).root].flat_map do |sublink|
386
- DirectLink URI.join(link, sublink).to_s, timeout, giveup: giveup
398
+ DirectLink URI.join(link, sublink).to_s, timeout, giveup: giveup # TODO: maybe subtract from timeout the time we've already wasted
387
399
  end
388
400
  end
389
401
  if u.is_a? Hash
@@ -393,8 +405,8 @@ def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
393
405
  struct.new u, x, y, t
394
406
  end
395
407
  end
396
- return DirectLink u
397
- fail if link == u
408
+ raise DirectLink::ErrorNotFound.new link.inspect if link == u
409
+ return DirectLink u, timeout, giveup: giveup
398
410
  rescue DirectLink::ErrorMissingEnvVar
399
411
  end if %w{ reddit com } == URI(link).host.split(?.).last(2) ||
400
412
  %w{ redd it } == URI(link).host.split(?.)
@@ -407,11 +419,15 @@ def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
407
419
  end if %w{ vk com } == URI(link).host.split(?.)
408
420
 
409
421
  begin
410
- f = FastImage.new(link, raise_on_failure: true, timeout: 5, http_header: {"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36"})
422
+ f = FastImage.new link,
423
+ raise_on_failure: true,
424
+ timeout: timeout,
425
+ **(proxy ? {proxy: "http://#{proxy}"} : {}),
426
+ http_header: {"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36"}
411
427
  rescue FastImage::UnknownImageType
412
428
  raise if giveup
413
429
  require "nokogiri"
414
- head = NetHTTPUtils.request_data link, :head, header: {"User-Agent" => "Mozilla"},
430
+ head = NetHTTPUtils.request_data link, :HEAD, header: {"User-Agent" => "Mozilla"},
415
431
  max_start_http_retry_delay: timeout,
416
432
  timeout: timeout, # NetHTTPUtild passes this as read_timeout to Net::HTTP.start
417
433
  max_read_retry_delay: timeout # and then compares accumulated delay to this
@@ -423,7 +439,7 @@ def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
423
439
  html = Nokogiri::HTML NetHTTPUtils.request_data link, header: {"User-Agent" => "Mozilla"}
424
440
  if t = html.at_css("meta[@property='og:image']")
425
441
  begin
426
- return DirectLink URI.join(link, t[:content]).to_s, nil, giveup: true
442
+ return DirectLink URI.join(link, t[:content]).to_s, nil, *proxy, giveup: true
427
443
  rescue URI::InvalidURIError
428
444
  end
429
445
  end unless ignore_meta
data/test.rb CHANGED
@@ -13,6 +13,8 @@ fail unless ENV.include? "REDDIT_SECRETS"
13
13
 
14
14
  require_relative "lib/directlink"
15
15
  DirectLink.silent = true
16
+ DirectLink.timeout = 30 # TODO: tests about this attribute
17
+
16
18
  describe DirectLink do
17
19
 
18
20
  describe "./lib" do
@@ -329,9 +331,11 @@ describe DirectLink do
329
331
  ["https://imgur.com/9yaMdJq", "https://i.imgur.com/9yaMdJq.mp4", 720, 404, "video/mp4"],
330
332
  ["http://imgur.com/gallery/dCQprEq/new", "https://i.imgur.com/dCQprEq.jpg", 5760, 3840, "image/jpeg"],
331
333
  ["https://i.imgur.com/fFUTSJu.jpg?fb", "https://i.imgur.com/fFUTSJu.jpg", 1469, 2200, "image/jpeg"], # from reddit.com/93mtba
334
+ ["https://i.imgur.com/IxUrhGX.jpeg", "https://i.imgur.com/IxUrhGX.jpg", 4384, 3012, "image/jpeg"], # jpEg
335
+ ["https://imgur.com/gallery/9f2s9EE", "https://i.imgur.com/9f2s9EE.mp4", 960, 1438, "video/mp4"], # mp4
332
336
  ].each_with_index do |t, i|
333
337
  url, n, first, last, type = t
334
- it "##{i + 1}" do
338
+ it "kinds of post ##{i + 1}" do
335
339
  case last
336
340
  when NilClass
337
341
  if n.is_a? Class
@@ -377,7 +381,7 @@ describe DirectLink do
377
381
  ["https://www.flickr.com/photos/130019700@N03/18848891351/in/dateposted-public/", [4621, 3081, "https://live.staticflickr.com/3796/18848891351_f751b35aeb_o.jpg"]], # userid in-public
378
382
  ["https://www.flickr.com/photos/frank3/3778768209/in/photolist-6KVb92-eCDTCr-ur8K-7qbL5z-c71afh-c6YvXW-7mHG2L-c71ak9-c71aTq-c71azf-c71aq5-ur8Q-6F6YkR-eCDZsD-eCEakg-eCE6DK-4ymYku-7ubEt-51rUuc-buujQE-ur8x-9fuNu7-6uVeiK-qrmcC6-ur8D-eCEbei-eCDY9P-eCEhCk-eCE5a2-eCH457-eCHrcq-eCEdZ4-eCH6Sd-c71b5o-c71auE-eCHa8m-eCDSbz-eCH1dC-eCEg3v-7JZ4rh-9KwxYL-6KV9yR-9tUSbU-p4UKp7-eCHfwS-6KVbAH-5FrdbP-eeQ39v-eeQ1UR-4jHAGN", [4096, 2723, "https://live.staticflickr.com/2499/3778768209_dfa75a41cc_4k.jpg"]],
379
383
  ["https://www.flickr.com/photos/patricksloan/18230541413/sizes/l", [2048, 491, "https://live.staticflickr.com/5572/18230541413_fec4783d79_k.jpg"]],
380
- ["https://flic.kr/p/vPvCWJ", [2048, 1365, "https://live.staticflickr.com/507/19572004110_d44d1b4ead_k.jpg"]],
384
+ ["https://flic.kr/p/vPvCWJ", [5120, 3413, "https://live.staticflickr.com/507/19572004110_1bd49c5ebd_5k.jpg"]],
381
385
  ] ],
382
386
  [ :wiki, [
383
387
  ["https://en.wikipedia.org/wiki/Prostitution_by_country#/media/File:Prostitution_laws_of_the_world.PNG", "https://upload.wikimedia.org/wikipedia/commons/e/e8/Prostitution_laws_of_the_world.PNG"],
@@ -396,26 +400,18 @@ describe DirectLink do
396
400
  ["http://redd.it/988889", [true, "https://i.redd.it/3h5xls6ehrg11.jpg"]],
397
401
  ["https://www.reddit.com/r/CatsStandingUp/duplicates/abn0ua/cat/", [true, "https://v.redd.it/s9b86afb6w721/DASH_2_4_M?source=fallback"]],
398
402
  ["https://www.reddit.com/r/hangers/comments/97you5/tara_radovic/", [true, "https://i.imgur.com/rbLqgOu.jpg"]], # "crossport" from Imgur
399
- ] ],
400
- [ :vk, [
401
- ["https://vk.com/wall-105984091_7806", [[960, 1280, "https://sun9-41.userapi.com/c855224/v855224900/a72f1/7OZ8ux9Wcwo.jpg"]]],
402
- ["https://vk.com/wall298742340_4715", [[1080, 1080, "https://sun9-24.userapi.com/c857136/v857136625/15e38b/CsCqsJD174A.jpg"]]],
403
- ["https://vk.com/id57030827?z=photo57030827_456241143%2Falbum57030827_0", [[1920, 1440, "https://sun9-66.userapi.com/c845322/v845322944/167836/bP9z41BybhI.jpg"]]],
404
- ["https://vk.com/id57030827?z=photo57030827_456241143", [[1920, 1440, "https://sun9-66.userapi.com/c845322/v845322944/167836/bP9z41BybhI.jpg"]]],
405
- ["https://vk.com/wall-185182611_454?z=photo-185182611_457239340%2Fwall-185182611_454", [[1280, 960, "https://sun9-46.userapi.com/c851028/v851028578/1a62f6/VB4SdR1O6Tg.jpg"]]],
406
- ["https://vk.com/wall-105984091_7946?z=photo-105984091_457243312%2Falbum-105984091_00%2Frev", [[1280, 875, "https://sun9-37.userapi.com/c852020/v852020134/1b6b36/0IsDFb-Hda4.jpg"]]],
407
- ["https://vk.com/photo533531776_456239427?all=1", [[750, 938, "https://sun9-25.userapi.com/c849416/v849416600/14b949/V01Ch1gYjhc.jpg"]]],
408
- ["https://vk.com/photo-155488973_456242404", [[1486, 1000, "https://sun9-7.userapi.com/c852132/v852132877/8578e/m6AJWiskiKE.jpg"]]],
409
- ["https://vk.com/id2272074?z=photo2272074_264578776%2Fphotos2272074", [[604, 484, "https://sun9-10.userapi.com/c10472/u2272074/-7/x_407b2ba2.jpg"]]],
410
- ["https://vk.com/feed?section=likes&z=photo-117564754_456261460%2Fliked3902406", [[1024, 1335, "https://sun9-72.userapi.com/c854028/v854028353/895b6/izQJresLdf0.jpg"]]],
411
- ["https://vk.com/likizimy?z=photo-42543351_456239941%2Fwall-42543351_1908", [[1179, 1731, "https://sun9-47.userapi.com/c855036/v855036571/60f7b/ryCPJIMyMkI.jpg"]]],
412
- ["https://vk.com/e_rod?z=photo298742340_457247118%2Fphotos298742340", [[1728, 2160, "https://sun9-53.userapi.com/c858320/v858320596/c7714/oImGe4o1ZJI.jpg"]]],
403
+ ["https://www.reddit.com/gallery/i1u6rb", [true, [["image/jpg", 1440, 1440, "https://preview.redd.it/x31msdj6vee51.jpg?width=1440&format=pjpg&auto=webp&s=b79952f8364bb98692d978944347f19e28774d1b"], ["image/jpg", 2441, 2441, "https://preview.redd.it/mwkzq6j6vee51.jpg?width=2441&format=pjpg&auto=webp&s=455e669356550351e6b8768d8009de616c11142a"], ["image/jpg", 1440, 1440, "https://preview.redd.it/0ws1j8j6vee51.jpg?width=1440&format=pjpg&auto=webp&s=061582da8478e7601a7ce7a97fa1663852873726"], ["image/jpg", 1440, 1440, "https://preview.redd.it/2un68aj6vee51.jpg?width=1440&format=pjpg&auto=webp&s=a980f0e5814c2360f5d7a0fb12f391e304942c06"], ["image/jpg", 3024, 3780, "https://preview.redd.it/5bsfaej6vee51.jpg?width=3024&format=pjpg&auto=webp&s=9b96b4b7262eebacc7571a9f0ad902e2034bf990"], ["image/jpg", 1440, 1440, "https://preview.redd.it/0z010ej6vee51.jpg?width=1440&format=pjpg&auto=webp&s=f0c29be6ec98b835a482c7584cca43fd16217bc8"], ["image/jpg", 1440, 1440, "https://preview.redd.it/aylm2ej6vee51.jpg?width=1440&format=pjpg&auto=webp&s=39cf471b14020a1f137bc9bbb294bf5489cab3e7"]]]], # TODO: find smaller gallery
404
+ ["https://www.reddit.com/i1u6rb", [true, [["image/jpg", 1440, 1440, "https://preview.redd.it/x31msdj6vee51.jpg?width=1440&format=pjpg&auto=webp&s=b79952f8364bb98692d978944347f19e28774d1b"], ["image/jpg", 2441, 2441, "https://preview.redd.it/mwkzq6j6vee51.jpg?width=2441&format=pjpg&auto=webp&s=455e669356550351e6b8768d8009de616c11142a"], ["image/jpg", 1440, 1440, "https://preview.redd.it/0ws1j8j6vee51.jpg?width=1440&format=pjpg&auto=webp&s=061582da8478e7601a7ce7a97fa1663852873726"], ["image/jpg", 1440, 1440, "https://preview.redd.it/2un68aj6vee51.jpg?width=1440&format=pjpg&auto=webp&s=a980f0e5814c2360f5d7a0fb12f391e304942c06"], ["image/jpg", 3024, 3780, "https://preview.redd.it/5bsfaej6vee51.jpg?width=3024&format=pjpg&auto=webp&s=9b96b4b7262eebacc7571a9f0ad902e2034bf990"], ["image/jpg", 1440, 1440, "https://preview.redd.it/0z010ej6vee51.jpg?width=1440&format=pjpg&auto=webp&s=f0c29be6ec98b835a482c7584cca43fd16217bc8"], ["image/jpg", 1440, 1440, "https://preview.redd.it/aylm2ej6vee51.jpg?width=1440&format=pjpg&auto=webp&s=39cf471b14020a1f137bc9bbb294bf5489cab3e7"]]]], # TODO: find smaller gallery
405
+ ["https://www.reddit.com/gallery/i3y7pc", [true, "https://www.reddit.com/gallery/i3y7pc"]], # deleted gallery
406
+ ["https://www.reddit.com/ik6c6a", [true, "https://www.reddit.com/r/Firewatch/comments/ik6brf/new_wallpaper_for_my_triple_monitor_setup/"]], # deleted gallery
407
+ ["https://www.reddit.com/kbjdwc", [true, [["image/jpg", 500, 500, "https://preview.redd.it/71t8ljeexo461.jpg?width=500&format=pjpg&auto=webp&s=df211fe0699e3970681ffe493ed1af79725857e8"], ["image/jpg", 720, 446, "https://preview.redd.it/c11nt7hexo461.jpg?width=720&format=pjpg&auto=webp&s=5e34ab0e6d54c0acfdb47f1daaf283087c5ad6a6"], ["image/jpg", 713, 588, "https://preview.redd.it/67mqvllexo461.jpg?width=713&format=pjpg&auto=webp&s=969dfb52bedd6f0055249aa8b7454b23adaa946e"]]]], # failed media
408
+ # TODO: empty result? https://redd.it/9hhtsq
413
409
  ] ],
414
410
  ].each do |method, tests|
415
- next if method == :vk && ENV.include?("TRAVIS")
416
- describe method do
411
+ next if method == :vk && ENV.include?("CI")
412
+ describe "kinds of links #{method}" do
417
413
  tests.each_with_index do |(input, expectation), i|
418
- it "#{method} ##{i + 1}" do
414
+ it "##{i + 1}" do
419
415
  if expectation.is_a? Class
420
416
  assert_raises expectation, input do
421
417
  DirectLink.method(method).call input
@@ -429,6 +425,37 @@ describe DirectLink do
429
425
  end
430
426
  end
431
427
 
428
+ describe "kinds of links vk" do
429
+ next if ENV.include? "CI"
430
+ [
431
+ ["https://vk.com/wall-105984091_7806", [960, 1280, "https://userapi.com/impf/c855224/v855224900/a72f1/7OZ8ux9Wcwo.jpg"]],
432
+ # ["https://vk.com/wall298742340_4715", [1080, 1080, "https://userapi.com/impf/c857136/v857136625/15e38b/CsCqsJD174A.jpg"]], # TODO: it's now 404
433
+ ["https://vk.com/wall-185182611_454?z=photo-185182611_457239340%2Fwall-185182611_454", [1280, 960, "https://userapi.com/impf/c851028/v851028578/1a62f6/VB4SdR1O6Tg.jpg"]],
434
+ ["https://vk.com/wall-105984091_7946?z=photo-105984091_457243312%2Falbum-105984091_00%2Frev", [1280, 875, "https://userapi.com/impf/c852020/v852020134/1b6b36/0IsDFb-Hda4.jpg"]],
435
+ ["https://vk.com/id57030827?z=photo57030827_456241143%2Falbum57030827_0", [1920, 1440, "https://userapi.com/impf/c845322/v845322944/167836/bP9z41BybhI.jpg"]],
436
+ ["https://vk.com/id57030827?z=photo57030827_456241143", [1920, 1440, "https://userapi.com/impf/c845322/v845322944/167836/bP9z41BybhI.jpg"]],
437
+ ["https://vk.com/photo1_215187843?all=1", [2560, 1913, "https://userapi.com/impf/c210/v210001/6/53_VwoACy4I.jpg"]],
438
+ ["https://vk.com/photo298742340_456243948?rev=1", [1583, 1080, "https://userapi.com/impf/c852224/v852224479/321be/9rZaJ2QTdz4.jpg"]],
439
+ ["https://vk.com/photo-155488973_456242404", [1486, 1000, "https://userapi.com/impf/c852132/v852132877/8578e/m6AJWiskiKE.jpg"]],
440
+ # ["https://vk.com/id2272074?z=photo2272074_264578776%2Fphotos2272074", [604, 484, "https://userapi.com/impf/c10472/u2272074/-7/x_407b2ba2.jpg"]], # TODO: it's now 404
441
+ ["https://vk.com/feed?section=likes&z=photo-117564754_456261460%2Fliked3902406", [1024, 1335, "https://userapi.com/impf/c854028/v854028353/895b6/izQJresLdf0.jpg"]],
442
+ ["https://vk.com/likizimy?z=photo-42543351_456239941%2Fwall-42543351_1908", [1179, 1731, "https://userapi.com/impf/c855036/v855036571/60f7b/ryCPJIMyMkI.jpg"]],
443
+ ["https://vk.com/e_rod?z=photo298742340_457247118%2Fphotos298742340", [1728, 2160, "https://userapi.com/impf/c858320/v858320596/c7714/oImGe4o1ZJI.jpg"]],
444
+ ].each_with_index do |(input, expectation), i|
445
+ it "##{i + 1}" do
446
+ result = DirectLink.method(:vk).call input
447
+ assert_equal 1, result.size
448
+ result[0][-1].tap do |url|
449
+ url.replace( URI.parse(url).tap do |_|
450
+ _.host = _.host.split(?.).drop(1).join(?.)
451
+ _.query = nil
452
+ end.to_s )
453
+ end
454
+ assert_equal [expectation], result, "#{input} :: #{result.inspect} != #{expectation.inspect}"
455
+ end
456
+ end
457
+ end
458
+
432
459
  {
433
460
  google: [
434
461
  "https://lh3.googleusercontent.com/-NVJgqmI_2Is/WqMM2OMYg-I/AAAAAAAALrk/5-p3JL3iZt0Ho9dOf_p3gpddzqwr3Wp0ACJoC/w424-h318-n/001",
@@ -461,13 +488,13 @@ describe DirectLink do
461
488
  ["http://redd.it/32tq0i", "https://www.reddit.com/comments/32tq0i"],
462
489
  ["https://reddit.com/123456", "https://www.reddit.com/r/funny/comments/123456/im_thinking_about_getting_a_dog_and_youtubed_ways/"],
463
490
  # ["https://www.reddit.com/r/travel/988889", "https://www.reddit.com/r/travel/comments/988889/playa_miramar_in_guaymas_sonora/"],
464
- "https://www.reddit.com/r/KsyushaEgorova/comments/beuqs2/a_little_shy/", # NSFW causes redirect to /over_18? if the special cookie not provided
491
+ "https://www.reddit.com/r/PareidoliaGoneWild/comments/hzrlq6/beard_trimmer_on_display_at_best_buy_they_knew/", # NSFW causes redirect to /over_18? if the special cookie not provided
465
492
  ],
466
493
  vk: [
467
494
  "https://vk.com/id57030827?z=photo57030827_456241143",
468
495
  ],
469
496
  }.each do |method, tests|
470
- describe "DirectLink() calls #{method}" do
497
+ describe "DirectLink() sees domain name and calls #{method}" do
471
498
  tests.each_with_index do |(input, expected), i|
472
499
  it "##{i + 1}" do
473
500
  DirectLink.stub method, ->link{
@@ -603,6 +630,16 @@ describe DirectLink do
603
630
  )
604
631
  end
605
632
 
633
+ it "throws ErrorNotFound when Reddit gallery is removed" do
634
+ assert_raises DirectLink::ErrorNotFound do
635
+ DirectLink "https://www.reddit.com/gallery/i3y7pc"
636
+ end
637
+ end
638
+
639
+ it "follows Reddit crosspost" do
640
+ assert_equal %w{ image/png image/png }, DirectLink("https://www.reddit.com/ik6c6a").map(&:type)
641
+ end
642
+
606
643
  it "throws ErrorBadLink if link is invalid" do
607
644
  assert_equal "test".inspect, (
608
645
  assert_raises DirectLink::ErrorBadLink do
@@ -655,7 +692,7 @@ describe DirectLink do
655
692
  [
656
693
  # ["http://www.aeronautica.difesa.it/organizzazione/REPARTI/divolo/PublishingImages/6%C2%B0%20Stormo/2013-decollo%20al%20tramonto%20REX%201280.jpg", ["http://www.aeronautica.difesa.it/organizzazione/REPARTI/divolo/PublishingImages/6%C2%B0%20Stormo/2013-decollo%20al%20tramonto%20REX%201280.jpg", 1280, 853, :jpeg], nil, 1], # website is dead?
657
694
  # ["http://minus.com/lkP3hgRJd9npi", SocketError, /nodename nor servname provided, or not known|No address associated with hostname/, 0],
658
- ["http://www.cutehalloweencostumeideas.org/wp-content/uploads/2017/10/Niagara-Falls_04.jpg", SocketError, /nodename nor servname provided, or not known|Name or service not known/, 0],
695
+ ["http://www.cutehalloweencostumeideas.org/wp-content/uploads/2017/10/Niagara-Falls_04.jpg", SocketError, /nodename nor servname provided, or not known|Name or service not known|getaddrinfo: Name does not resolve/, 0],
659
696
  ].each_with_index do |(input, expectation, message_string_or_regex, max_redirect_resolving_retry_delay), i|
660
697
  it "##{i + 1}" do
661
698
  if expectation.is_a? Class
@@ -696,19 +733,20 @@ describe DirectLink do
696
733
  case expectation
697
734
  when Class
698
735
  e = assert_raises expectation, "for #{input} (giveup = #{giveup})" do
699
- DirectLink input, 10, giveup: giveup
736
+ DirectLink input, 5, *ENV["PROXY"], giveup: giveup
700
737
  end
701
738
  assert_equal expectation.to_s, e.class.to_s, "for #{input} (giveup = #{giveup})"
702
739
  when String
703
- result = DirectLink input, 10, giveup: giveup
740
+ result = DirectLink input, 5, *ENV["PROXY"], giveup: giveup
704
741
  assert_equal expectation, result.url, "for #{input} (giveup = #{giveup})"
705
742
  else
706
- result = DirectLink input, 10, giveup: giveup
743
+ result = DirectLink input, 5, *ENV["PROXY"], giveup: giveup
707
744
  result = [result] unless result.is_a? Array # we can't do `Array(<Struct>)` because it splats by elements
708
745
  assert_equal expectation, result.size, ->{
709
746
  "for #{input} (giveup = #{giveup}): #{result.map &:url}"
710
747
  }
711
748
  end
749
+ # weird that this test may take longer than 5 sec
712
750
  ensure
713
751
  ENV["IMGUR_CLIENT_ID"] = ti if ti
714
752
  ENV["REDDIT_SECRETS"] = tr if tr
@@ -751,21 +789,28 @@ describe DirectLink do
751
789
 
752
790
  describe "fails" do
753
791
  [
754
- [1, "http://example.com/", "FastImage::UnknownImageType"],
755
- [1, "http://example.com/404", "NetHTTPUtils::Error: HTTP error #404 "],
792
+ [1, "http://example.com/", /\AFastImage::UnknownImageType\n\z/],
793
+ [1, "http://example.com/404", /\ANetHTTPUtils::Error: HTTP error #404 \n\z/],
756
794
 
757
795
  # TODO: a test when the giveup=false fails and reraises the DirectLink::ErrorMissingEnvVar
758
796
  # maybe put it to ./lib tests
759
797
 
760
798
  # by design it should be impossible to write a test for DirectLink::ErrorAssert
761
- [1, "https://flic.kr/p/DirectLinkErrorNotFound", "NetHTTPUtils::Error: HTTP error #404 "],
799
+ [1, "https://flic.kr/p/DirectLinkErrorNotFound", /\ANetHTTPUtils::Error: HTTP error #404 \n\z/],
762
800
 
763
- [1, "https://imgur.com/a/badlinkpattern", "NetHTTPUtils::Error: HTTP error #404 "],
801
+ [1, "https://imgur.com/a/badlinkpattern", /\ANetHTTPUtils::Error: HTTP error #404 \n\z/],
764
802
  # TODO: a test that it appends the `exception.cause`
803
+
804
+ [1, "https://groundingpositivity.com/2020/08/13/new-quantum-app-will-make-you-wonder-do-we-live-in-a-simulation/", (
805
+ Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.4.0") ?
806
+ /\ANetHTTPUtils::EOFError_from_rbuf_fill: probably the old Ruby empty backtrace EOFError exception from net\/protocol\.rb: end of file reached\n\z/ :
807
+ /\A\S+\/net\/protocol\.rb:\d+:in `rbuf_fill': end of file reached \(EOFError\)\n/
808
+ ) ], # TODO: add also a test to nethttputils gem
765
809
  ].each_with_index do |(expected_exit_code, link, expected_output, unset), i| # TODO: unset is not used anymore or I have to go sleep?
766
810
  it "##{i + 1}" do
767
811
  string, status = Open3.capture2e "export #{(File.read("api_tokens_for_travis.sh") + File.read("vk.secret")).scan(/(?<=^export )\S+=\S+/).join(" ")}#{unset} && RUBYOPT='-rbundler/setup #{$-I.map{ |i| "-I #{i}" }.join " "}' ./bin/directlink #{link}"
768
- assert_equal [expected_exit_code, "#{expected_output}\n"], [status.exitstatus, string], "for #{link}"
812
+ assert_equal expected_exit_code, status.exitstatus, "for #{link}"
813
+ assert string[expected_output], "for #{link}"
769
814
  end
770
815
  end
771
816
  end
@@ -820,12 +865,13 @@ describe DirectLink do
820
865
  # TODO: test about --json
821
866
  it "uses <meta> tag" do
822
867
  string, status = Open3.capture2e "RUBYOPT='-rbundler/setup' ./bin/directlink --json https://www.kp.ru/daily/26342.7/3222103/"
823
- assert_equal [0, "https://s9.stc.all.kpcdn.net/share/i/12/8054352/cr-1200-630.wm-asnpmfru-100-tr-0-0.t-13-3222103-ttps-47-8-0083CD-1010-l-85-b-41.t-13-3222103-ttps-47-8-FFF-1010-l-85-b-42.t-207-5-asb-37-10-FFF-788-l-370-t-68.m2018-03-14x02-10-20.jpg"], [status.exitstatus, JSON.load(string).fetch("url")]
824
- end
825
- it "ignores <meta> tag" do
826
- string, status = Open3.capture2e "RUBYOPT='-rbundler/setup' ./bin/directlink --json --ignore-meta https://www.kp.ru/daily/26342.7/3222103/"
827
- assert_equal [0, 21, "https://s11.stc.all.kpcdn.net/share/i/12/8024261/inx960x640.jpg"], [status.exitstatus, JSON.load(string).size, JSON.load(string).first.fetch("url")]
868
+ assert_equal [0, "https://s11.stc.all.kpcdn.net/share/i/12/8054352/cr-1200-630.wm-asnplfru-100-tr-0-0.t-13-3222103-ttps-54-14-0083CD-1010-l-85-b-42.t-13-3222103-ttps-54-14-FFF-1010-l-85-b-42.t-207-5-asb-37-10-FFF-788-l-370-t-68.m2018-03-14T02-10-20.jpg"], [status.exitstatus, JSON.load(string).fetch("url")]
828
869
  end
870
+ # TODO: kp.ru broke the page -- images are gone
871
+ # it "ignores <meta> tag" do
872
+ # string, status = Open3.capture2e "RUBYOPT='-rbundler/setup' ./bin/directlink --json --ignore-meta https://www.kp.ru/daily/26342.7/3222103/"
873
+ # assert_equal [0, 21, "https://s11.stc.all.kpcdn.net/share/i/12/8024261/inx960x640.jpg"], [status.exitstatus, JSON.load(string).size, JSON.load(string).first.fetch("url")]
874
+ # end
829
875
 
830
876
  end
831
877