directlink 0.0.0.0

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/lib/directlink.rb ADDED
@@ -0,0 +1,219 @@
1
+ module DirectLink
2
+
3
+ class << self
4
+ attr_accessor :silent
5
+ end
6
+ self.silent = false
7
+ class << self
8
+ attr_accessor :logger
9
+ end
10
+ self.logger = Object.new
11
+ self.logger.define_singleton_method :error do |str|
12
+ puts str unless Module.nesting.first.silent
13
+ end
14
+
15
+ class ErrorMissingEnvVar < RuntimeError ; end
16
+ class ErrorAssert < RuntimeError
17
+ def initialize msg
18
+ super "#{msg} -- consider reporting this issue to GitHub"
19
+ end
20
+ end
21
+ @@LoggingError = Class.new RuntimeError do
22
+ def initialize msg
23
+ Module.nesting.first.logger.error msg
24
+ super msg
25
+ end
26
+ end
27
+ class ErrorNotFound < @@LoggingError ; end
28
+ class ErrorBadLink < @@LoggingError
29
+ def initialize link, sure = false
30
+ super "#{link.inspect}#{" -- if you think this link is valid, please report the issue" unless sure}"
31
+ end
32
+ end
33
+
34
+
35
+ def self.google src, width = 0
36
+ case src
37
+ when /\A(https:\/\/lh3\.googleusercontent\.com\/-[a-zA-Z0-9_-]{11}\/W[n-r][a-zA-Z0-9_-]{8}I\/AAAAAAAA[bA-V][a-zA-Z0-9]{2}\/[a-zA-Z0-9_-]{32}[gwAQ]CJoC\/)w[1-4]\d\d-h(318|353|727)-n(\/[^\/]+)\z/,
38
+ /\A(https:\/\/lh3\.googleusercontent\.com\/-[a-zA-Z0-9_-]{11}\/W[a-zA-Z0-9_-]{9}I\/AAAAAAA[a-zA-Z0-9_-]{4}\/[a-zA-Z0-9_-]{33}(?:CJoC|CL0B(?:GAs)?)\/)w530(?:-d)?-h[2-9]\d\d-n(\/[^\/]+)\z/
39
+ "#{$1}s#{width}#{$2}"
40
+ when /\A(\/\/lh3\.googleusercontent\.com\/proxy\/[a-zA-Z0-9_-]{66,523}=)(?:w(?:464|504|530)-h[2-7]\d\d-[np]|s530-p)\z/
41
+ "https:#{$1}s#{width}"
42
+ when /\A(\/\/lh3\.googleusercontent\.com\/-[a-zA-Z0-9-]{11}\/W[a-zA-Z0-9_-]{9}I\/AAAAAAA[AC][a-zA-Z0-9]{3}\/[a-zA-Z0-9_-]{32}[gw]CJoC\/)w530-h3\d\d-p(\/[^\/]+)\z/,
43
+ /\A(\/\/[14]\.bp\.blogspot\.com\/-[a-zA-Z0-9_-]{11}\/W[np][a-zA-Z0-9_-]{8}I\/AAAAAAAA[DE][a-zA-Z0-9_-]{2}\/[a-zA-Z0-9_-]{33}C(?:Lc|Kg)BGAs\/)w530-h278-p(\/[^\/]+)\z/
44
+ "https:#{$1}s#{width}#{$2}"
45
+ else
46
+ raise ErrorBadLink.new src
47
+ end
48
+ end
49
+
50
+ require "json"
51
+ require "nethttputils"
52
+
53
+ def self.imgur link
54
+ raise ErrorMissingEnvVar.new "define IMGUR_CLIENT_ID env var" unless ENV["IMGUR_CLIENT_ID"]
55
+
56
+ case link
57
+ when /\Ahttps?:\/\/(?:(?:i|m|www)\.)?imgur\.com\/(a|gallery)\/([a-zA-Z0-9]{5}(?:[a-zA-Z0-9]{2})?)\z/,
58
+ /\Ahttps?:\/\/imgur\.com\/(gallery)\/([a-zA-Z0-9]{5}(?:[a-zA-Z0-9]{2})?)\/new\z/
59
+ timeout = 1
60
+ json = begin
61
+ NetHTTPUtils.request_data "https://api.imgur.com/3/#{
62
+ $1 == "gallery" ? "gallery" : "album"
63
+ }/#{$2}/0.json", header: { Authorization: "Client-ID #{ENV["IMGUR_CLIENT_ID"]}" }
64
+ rescue NetHTTPUtils::Error => e
65
+ case e.code
66
+ when 500
67
+ logger.error "retrying in #{timeout} seconds because of Imgur HTTP ERROR 500"
68
+ sleep timeout
69
+ timeout *= 2
70
+ retry
71
+ when 404 ; raise ErrorNotFound.new link.inspect
72
+ else ; raise ErrorAssert.new "unexpected http error for #{link}"
73
+ end
74
+ end
75
+ data = JSON.load(json)["data"]
76
+ if data["error"]
77
+ raise ErrorAssert.new "unexpected error #{data.inspect} for #{link}"
78
+ elsif data["images"]
79
+ data["images"]
80
+ elsif data["type"] && data["type"].start_with?("image/")
81
+ [ data ]
82
+ # elsif data["comment"]
83
+ # fi["https://imgur.com/" + data["image_id"]]
84
+ else
85
+ # one day single-video item should hit this but somehow it didn't yet
86
+ raise ErrorAssert.new "unknown data format #{data.inspect} for #{link}"
87
+ end
88
+ when /\Ahttps?:\/\/(?:(?:i|m|www)\.)?imgur\.com\/([a-zA-Z0-9]{7})(?:\.(?:gifv|jpg))?\z/,
89
+ /\Ahttps?:\/\/(?:(?:i|m|www)\.)?imgur\.com\/([a-zA-Z0-9]{5})\.mp4\z/,
90
+ /\Ahttps?:\/\/imgur\.com\/([a-zA-Z0-9]{5}(?:[a-zA-Z0-9]{2})?)\z/,
91
+ /\Ahttps?:\/\/imgur\.com\/([a-zA-Z0-9]{7})(?:\?\S+)?\z/,
92
+ /\Ahttps?:\/\/imgur\.com\/r\/[0-9_a-z]+\/([a-zA-Z0-9]{7})\z/
93
+ json = begin
94
+ NetHTTPUtils.request_data "https://api.imgur.com/3/image/#{$1}/0.json", header: { Authorization: "Client-ID #{ENV["IMGUR_CLIENT_ID"]}" }
95
+ rescue NetHTTPUtils::Error => e
96
+ raise ErrorNotFound.new link.inspect if e.code == 404
97
+ raise ErrorAssert.new "unexpected http error for #{link}"
98
+ end
99
+ [ JSON.load(json)["data"] ]
100
+ else
101
+ raise ErrorBadLink.new link
102
+ end.map do |image|
103
+ case image["type"]
104
+ when "image/jpeg", "image/png", "image/gif", "video/mp4"
105
+ image.values_at "link", "width", "height", "type"
106
+ else
107
+ raise ErrorAssert.new "unknown type of #{link}: #{image}"
108
+ end
109
+ end
110
+ end
111
+
112
+ def self._500px link
113
+ raise ErrorBadLink.new link unless %r{\Ahttps://500px\.com/photo/(?<id>[^/]+)/[^/]+\z} =~ link
114
+ raise ErrorMissingEnvVar.new "define _500PX_CONSUMER_KEY env var !!! WARNING: the 500px.com deprecates their API in summer of 2018 !!!" unless ENV["_500PX_CONSUMER_KEY"]
115
+ JSON.load( NetHTTPUtils.request_data "https://api.500px.com/v1/photos/#{id}", form: {
116
+ image_size: 2048,
117
+ consumer_key: ENV["_500PX_CONSUMER_KEY"],
118
+ } )["photo"].values_at "width", "height", "image_url", "image_format" # could also get "camera" and "taken_at"
119
+ end
120
+
121
+ def self.flickr link
122
+ raise ErrorBadLink.new link unless %r{\Ahttps://www\.flickr\.com/photos/[^/]+/(?<id>[^/]+)} =~ link ||
123
+ %r{\Ahttps://flic\.kr/p/(?<id>[^/]+)\z} =~ link
124
+ raise ErrorMissingEnvVar.new "define FLICKR_API_KEY env var" unless ENV["FLICKR_API_KEY"]
125
+
126
+ flickr = lambda do |id, method|
127
+ JSON.load NetHTTPUtils.request_data "https://api.flickr.com/services/rest/", form: {
128
+ api_key: ENV["FLICKR_API_KEY"],
129
+ format: "json",
130
+ nojsoncallback: 1,
131
+ photo_id: id,
132
+ method: "flickr.photos.#{method}",
133
+ }
134
+ end
135
+ json = flickr.call id, "getSizes"
136
+ raise ErrorNotFound.new link.inspect if json == {"stat"=>"fail", "code"=>1, "message"=>"Photo not found"}
137
+ raise ErrorAssert.new "unhandled API response stat for #{link}: #{json}" unless json["stat"] == "ok"
138
+ json["sizes"]["size"].map do |_|
139
+ w, h, u = _.values_at("width", "height", "source")
140
+ [w.to_i, h.to_i, u]
141
+ end.max_by{ |w, h, u| w * h }
142
+ end
143
+
144
+ def self.wiki link
145
+ raise ErrorBadLink.new link unless %r{\Ahttps?://([a-z]{2}\.wikipedia|commons.wikimedia)\.org/wiki(/[^/]+)*/(?<id>File:.+)} =~ link
146
+ t = JSON.load json = NetHTTPUtils.request_data( "https://commons.wikimedia.org/w/api.php", form: {
147
+ format: "json",
148
+ action: "query",
149
+ prop: "imageinfo",
150
+ iiprop: "url",
151
+ titles: id,
152
+ } )
153
+ imageinfo = t["query"]["pages"].values.first["imageinfo"]
154
+ raise ErrorAssert.new "unexpected format of API response about #{link}: #{json}" unless imageinfo
155
+ imageinfo.first["url"]
156
+ end
157
+
158
+
159
+ class_variable_set :@@directlink, Struct.new(:url, :width, :height, :type)
160
+ end
161
+
162
+
163
+ require "fastimage"
164
+
165
+ def DirectLink link
166
+ begin
167
+ URI link
168
+ rescue URI::InvalidURIError
169
+ link = URI.escape link
170
+ end
171
+ raise DirectLink::ErrorBadLink.new link, true unless URI(link).host
172
+
173
+ struct = Module.const_get(__callee__).class_variable_get :@@directlink
174
+
175
+
176
+ if %w{ lh3 googleusercontent com } == URI(link).host.split(?.).last(3) ||
177
+ %w{ bp blogspot com } == URI(link).host.split(?.).last(3)
178
+ u = DirectLink.google link
179
+ f = FastImage.new(u, raise_on_failure: true, http_header: {"User-Agent" => "Mozilla"})
180
+ w, h = f.size
181
+ return struct.new u, w, h, f.type
182
+ end
183
+
184
+ if %w{ imgur com } == URI(link).host.split(?.).last(2) ||
185
+ %w{ i imgur com } == URI(link).host.split(?.).last(3) ||
186
+ %w{ m imgur com } == URI(link).host.split(?.).last(3) ||
187
+ %w{ www imgur com } == URI(link).host.split(?.).last(3)
188
+ imgur = DirectLink.imgur(link).sort_by{ |u, w, h, t| - w * h }.map do |u, w, h, t|
189
+ struct.new u, w, h, t
190
+ end
191
+ # `DirectLink.imgur` return value is always an Array
192
+ return imgur.size == 1 ? imgur.first : imgur
193
+ end
194
+
195
+ if %w{ 500px com } == URI(link).host.split(?.).last(2)
196
+ w, h, u, t = DirectLink._500px(link)
197
+ return struct.new u, w, h, t
198
+ end
199
+
200
+ if %w{ www flickr com } == URI(link).host.split(?.).last(3) ||
201
+ %w{ flic kr } == URI(link).host.split(?.).last(2)
202
+ w, h, u = DirectLink.flickr(link)
203
+ f = FastImage.new(u, raise_on_failure: true, http_header: {"User-Agent" => "Mozilla"})
204
+ return struct.new u, w, h, f.type
205
+ end
206
+
207
+ if %w{ wikipedia org } == URI(link).host.split(?.).last(2) ||
208
+ %w{ commons wikimedia org } == URI(link).host.split(?.).last(3)
209
+ u = DirectLink.wiki link
210
+ f = FastImage.new(u, raise_on_failure: true, http_header: {"User-Agent" => "Mozilla"})
211
+ w, h = f.size
212
+ return struct.new u, w, h, f.type
213
+ end
214
+
215
+ f = FastImage.new(link, raise_on_failure: true, http_header: {"User-Agent" => "Mozilla"})
216
+ w, h = f.size
217
+ struct.new link, w, h, f.type
218
+
219
+ end
data/test.rb ADDED
@@ -0,0 +1,374 @@
1
+ STDOUT.sync = true
2
+
3
+ require "minitest/autorun"
4
+ require "minitest/mock"
5
+
6
+ require_relative "lib/directlink"
7
+ DirectLink.silent = true
8
+ describe DirectLink do
9
+
10
+ describe "methods" do
11
+
12
+ describe "google" do
13
+ "
14
+ 1 //1.bp.blogspot.com/-rYk_u-qROQc/WngolJ8M0LI/AAAAAAAAD-w/woivnaIVzasBPG5C2T1t-VrWKRd_U6lMgCLcBGAs/w530-h278-p/i-469.jpg
15
+ 2 //4.bp.blogspot.com/-1RlHbRShONs/Wngp9WfxbfI/AAAAAAAAD-8/vnBBXpSZdvUMz4VjJPBpprLBrJ5QpBaqACLcBGAs/w530-h278-p/i-468.jpg
16
+ 3 //4.bp.blogspot.com/-5kP8ndL0kuM/Wpt82UCqvmI/AAAAAAAAEjI/ZbbZWs0-kgwRXEJ9JEGioR0bm6U8MOkvQCKgBGAs/w530-h278-p/IMG_20171223_093922.jpg
17
+ 4 //4.bp.blogspot.com/-bS1SZxFkvek/Wnjz3D5Il4I/AAAAAAAAD_I/bKYvRLGB7r4NGJ0aR1F-ugsbVm4HUjf5ACLcBGAs/w530-h278-p/i-466.jpg
18
+ 5 //lh3.googleusercontent.com/-1CF2jge1MYI/Wmr_kfCQQUI/AAAAAAAClKs/N1F2tz4v0q0i5N7rgso6G8SrhJNzsHEagCJoC/w530-h352-p/2809525%2B%25D0%2593%25D0%2590%25D0%259B%25D0%259E%2B%25D0%259F%25D0%2590%25D0%25A0%25D0%2593%25D0%2595%25D0%259B%25D0%2598%25D0%2599%2B-%2B%25D0%25A7%25D0%25A3%25D0%259A%25D0%259E%25D0%25A2%25D0%259A%25D0%2590%2B-%2B%25D0%25AE%25D0%25A0%25D0%2598%25D0%2599%2B%25D0%25A5%25D0%2590%25D0%25A0%25D0%25A7%25D0%2595%25D0%259D%25D0%259A%25D0%259E.jpg
19
+ 6 //lh3.googleusercontent.com/-h1siEcRFqJ8/Wiv1iDQhNVI/AAAAAAAAQV4/9gGepSmYkDMLyVTG_1EBCMmj-UhmclXWwCJoC/w530-h353-p/001
20
+ 7 //lh3.googleusercontent.com/-qnZygRb-Tn8/Wlyj4UuKbaI/AAAAAAAATqE/iKWSto-XBZwsh843xJ3cSd0JPt1LedU9gCJoC/w530-h330-p/001
21
+ 8 //lh3.googleusercontent.com/-rXO9PzbjNmk/WlNfjJkJBmI/AAAAAAAATNY/4dHLtYtWBzUKWufNXWkK82etw3RKA6jVgCJoC/w530-h361-p/001
22
+
23
+ 13 //lh3.googleusercontent.com/proxy/2fsWfEOeeCMNEP8MGvHiJPkBlg47Ifaf8DwpgDPJSPSYsh9iomTrfOA9OuwB60laZimtNr4-1GKA5J2csuVB6-1DLNO30K-fngB8p56ivICWRFAUuOwzizaztBx5gQ=w530-h707-p
24
+ 14 //lh3.googleusercontent.com/proxy/2qy3gOwhBT77Ie_qa9VgGz6kgN14tnOh1AMmV9FB65oiXOSAIll1_qmOZdupL3QCcJQL4701YskEgeDUnxJTnFGq4mFEzNFvjpNC-QMX9Jg7mdar3e89u_siA4AD-j1SJ6mrkl2MwSJSu1s2guYn0NR5ND1985lK34_iGiwzjoGHG2a65IbxKDSEzgSGbPQ_CQiQ6rBIvfcgvAxtS6E=s530-p
25
+ 28 //lh3.googleusercontent.com/proxy/7N0QCidzIFysO6OhmrG8twRJ74-sboNenI3Nhy3mIWP8C0SSa8J6xFQU3iyBTw4k3QeleiAgXOME3abYutPvBrHGx-0TBUcI-_LsgFa59Zaw6PAGdLndkxp5ReRJnsg=w530-h298-p
26
+ 31 //lh3.googleusercontent.com/proxy/7pN3aTE159nEXXQb_KajU1vcnDDzJVpGOv9vlWWPd3CbVuCKu-v4ndr423Mmhx-Zn5vqkVf6-TfZgPA5NyHECsr4w7kaw1jTmQDJbWO-9jU-Cs7ywrzcIRhyYTTZT-hPGiazwI1oEdbooIk=w464-h640-p
27
+ 48 //lh3.googleusercontent.com/proxy/DZtTi5KL7PqiBwJc8weNGLk_Wi2UTaQH0AC_h2kuURiu0AbwyI2ywOk2XgdAjL7ceg=w530-h354-n
28
+ 74 //lh3.googleusercontent.com/proxy/P4FQdTYwWN8cU_PWmFja1cm3oYu57L_flea7LmKvFjx0Ibdxl3glxcp-KFWsdRxMFTQ3gQkWsWzlrkyCm9QrSjCUcGIgnbsYBCXewnhEwSn0ZgT4XDg485GdXMoGWpNETrLYa8IGvG20_iw=w504-h597-p
29
+ 137 //lh3.googleusercontent.com/proxy/kX1ABK6_VjHDU12p-zZN6ciTOzqFcXm7IV41uR9F2sveM2t148nmWBEN1clA-VHvowpkd9T4NOPrAfDaip1o-jJBtBnRBElJuCsKo8FviSIkToZWCY9nQFEj06nHkMNuReBtdbVBlIRSodNJM-_cmqiJ-DnmWNe4dk62EF_kGLS28OWmckDLWVG0YFiZ-lVi8JhgR-ZgRmYWTs8bb_u6v6ucgfo8eh7ewchuZAD9_CoxUlQChAqz0QIfuVuKvmcXE06Ylv2H330btswQotjKDf8AXPe6xcyE9X1JXDYQTsPv-7lftLG9WQp1Mlc9Wu1XbHV7f541L-smt--7YpkW6fbDFYk4_87-pIOcg9UqHKB__DWyMCtH3gs8rv6VCmVwL1a-oAeBWSWk0Tc9R6uSuyCjt0nEBGlZY35aW1Pchz0jXr4lq8Il9m-wbRN9E7Kff1JR89YFOrV2N86w_72YTefFerLFfEsgzmhx3WYwok-4H12RdH7MyiHBQt04uRnb58GA4vl5VBQ=w530-h398-p
30
+
31
+ 245 https://lh3.googleusercontent.com/-2JzV0ErXvv8/Wsdr-m5Q_aI/AAAAAAAA_-I/Lw_e6qEMPUck4yW9yWhkC-nBNd5em8c3QCJoC/w530-h795-n/888052_800n.jpg
32
+ 346 https://lh3.googleusercontent.com/-8YmBt-SUaJo/War0Un_7YTI/AAAAAAAB9xs/kgXp9QrHZX8mpDfyLwuS9cvUSsXIoH6NgCL0BGAs/w530-d-h443-n/%25D0%259B%25D0%25B5%25D1%2581%2B%25D0%25BD%25D0%25B0%2B%25D0%25B8%25D1%2581%25D1%2585%25D0%25BE%25D0%25B4%25D0%25B5%2B%25D0%25BB%25D0%25B5%25D1%2582%25D0%25B0.JPG
33
+ 1317 https://lh3.googleusercontent.com/-tGAyJv4S6gM/WSLy4A7AFaI/AAAAAAABxk4/eq0Aprp2koM6GEti9xEIn-0PLh87bRRdQCL0B/w530-d-h660-n/%25D0%2592%25D0%25B5%25D1%2581%25D0%25BD%25D1%258B%2B%25D1%2586%25D0%25B2%25D0%25B5%25D1%2582%25D0%25B5%25D0%25BD%25D0%25B8%25D0%25B5.JPG
34
+
35
+ 178 https://lh3.googleusercontent.com/--Ggbfs9HJZM/Wog1DA2WcFI/AAAAAAAAbdQ/SZKPcnXFIr8MFfl9pddfiLPQnOiJ37muQCJoC/w409-h727-n/11a1c54f37c07d3c04588b40d108b4c2.jpg
36
+ 260 https://lh3.googleusercontent.com/-3Fe6CL9RH4o/WnipWY4889I/AAAAAAAAI4I/CPqdKpjqdlwkaBlCqGQ3RdZ9bLJ4nzBwwCJoC/w239-h318-n/gplus1855963830.jpg
37
+ 303 https://lh3.googleusercontent.com/-5vPhLnUILsA/WqFhJhu7VlI/AAAAAAAAAhQ/wWSrC8dqZTA7OuzeU3UoXJkNG19tqtAGwCJoC/w265-h353-n/DSC08260.JPG
38
+ 668 https://lh3.googleusercontent.com/-NLW68hNxEzU/WrSNfBxFX2I/AAAAAAAAAT8/7cvRM-DUZLoIdlb6CS8pL5-eJ3wbwohlQCJoC/w179-h318-n/23.03.2018%2B-%2B3
39
+ 669 https://lh3.googleusercontent.com/-NVJgqmI_2Is/WqMM2OMYg-I/AAAAAAAALrk/5-p3JL3iZt0Ho9dOf_p3gpddzqwr3Wp0ACJoC/w424-h318-n/001
40
+ 783 https://lh3.googleusercontent.com/-SqAvt_F__bg/Wq0huHcX2NI/AAAAAAAAVj0/XfnwCU7JwhUh0Knw8rcXA-bhpKYkI4hdwCJoC/w358-h318-n/IMG_20180317_120218-01-01.jpeg
41
+ 830 https://lh3.googleusercontent.com/-VB3YlLVL_tQ/Wo21yTbKl_I/AAAAAAAAApU/3DURmsYBklcv0kif2ZRjhoLG4mTHYf8OwCJoC/w254-h318-n/180218_00001.jpg
42
+ 1002 https://lh3.googleusercontent.com/-d8FkfLWq-_4/WpQXzEfFgBI/AAAAAAAADww/J32xKSAYUkgIc3pyrZnNmRec6kjdnJBHgCJoC/w239-h318-n/gplus-485739203.jpg
43
+ ".scan(/(\S+) (\S+)/).each do |i, url|
44
+ it "##{i}" do
45
+ assert_kind_of String, DirectLink.google(url)
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "imgur" do
51
+ %w{
52
+ https://imgur.com/a/badlinkpattern
53
+ http://example.com/
54
+ https://imgur.com/gallery/YO49F.
55
+ }.each_with_index do |url, i|
56
+ it "ErrorBadLink_##{i + 1}" do
57
+ assert_raises DirectLink::ErrorBadLink do
58
+ DirectLink.imgur url
59
+ end
60
+ end
61
+ end
62
+
63
+ valid_imgur_image_url = "https://i.imgur.com/BLCesav.jpg"
64
+ it 200 do
65
+ assert_equal [["https://i.imgur.com/BLCesav.jpg", 1000, 1500, "image/jpeg"]],
66
+ DirectLink.imgur(valid_imgur_image_url)
67
+ end
68
+ it 404 do
69
+ assert_raises DirectLink::ErrorNotFound do
70
+ NetHTTPUtils.stub :request_data, ->*{ raise NetHTTPUtils::Error.new "", 404 } do
71
+ DirectLink.imgur valid_imgur_image_url
72
+ end
73
+ end
74
+ end
75
+ it 400 do
76
+ e = assert_raises DirectLink::ErrorAssert do
77
+ NetHTTPUtils.stub :request_data, ->*{ raise NetHTTPUtils::Error.new "", 400 } do
78
+ DirectLink.imgur valid_imgur_image_url
79
+ end
80
+ end
81
+ assert_equal 400, e.cause.code if Exception.instance_methods.include? :cause # Ruby 2.1
82
+ end
83
+
84
+ [ # TODO: move these end line comments to test names; and do the same in other tests
85
+ ["https://imgur.com/a/Aoh6l", "https://i.imgur.com/BLCesav.jpg", 1000, 1500, "image/jpeg"],
86
+ ["http://i.imgur.com/7xcxxkR.gifv", "http://i.imgur.com/7xcxxkRh.gif", 718, 404, "image/gif"],
87
+ ["https://imgur.com/9yaMdJq", "https://i.imgur.com/9yaMdJq.mp4", 720, 404, "video/mp4"],
88
+ ["http://imgur.com/gallery/dCQprEq/new", "https://i.imgur.com/dCQprEq.jpg"],
89
+ ["http://imgur.com/HQHBBBD", "https://i.imgur.com/HQHBBBD.jpg"], # http
90
+ ["https://imgur.com/BGDh6eu", "https://i.imgur.com/BGDh6eu.jpg"], # https
91
+ ["https://imgur.com/a/qNCOo", 6, "https://i.imgur.com/vwqfi3s.jpg", "https://i.imgur.com/CnSMWvo.jpg"], # https album
92
+ ["http://imgur.com/a/0MiUo", 49, "https://i.imgur.com/kJ1jrjO.jpg", "https://i.imgur.com/TMQJARX.jpg"], # album, zoomable
93
+ ["http://imgur.com/a/WswMg", 20, "https://i.imgur.com/Bt3RWV7.png", "https://i.imgur.com/sRc2lqN.png"], # album image, not zoomable
94
+ ["http://imgur.com/a/AdJUK", 3, "https://i.imgur.com/Yunpxnx.jpg", "https://i.imgur.com/2epn2nT.jpg"], # needs https because of authorship # WAT?
95
+ ["http://imgur.com/gallery/vR4Am", 7, "https://i.imgur.com/yuUQI25.jpg", "https://i.imgur.com/RdxyAMQ.jpg"],
96
+ ["http://imgur.com/gallery/qP2RQtL", "https://i.imgur.com/qP2RQtL.png"], # single image gallery?
97
+ ["http://imgur.com/gallery/nKAwE/new", 28, "https://i.imgur.com/VQhR8hB.jpg", "https://i.imgur.com/axlzNRL.jpg"],
98
+ ["http://m.imgur.com/rarOohr", "https://i.imgur.com/rarOohr.jpg"],
99
+ ["http://imgur.com/r/wallpaper/j39dKMi", "https://i.imgur.com/j39dKMi.jpg"],
100
+ ["http://imgur.com/gallery/jm0OKQM", DirectLink::ErrorNotFound], # this id does not belong to gallery but to an image, that is probably irrelevant
101
+ ["http://imgur.com/gallery/oZXfZ", DirectLink::ErrorNotFound], # this id does not belong to gallery but to an album, that is probably irrelevant
102
+ ["http://imgur.com/gallery/dCQprEq/new", "https://i.imgur.com/dCQprEq.jpg", 5760, 3840, "image/jpeg"],
103
+ ["https://imgur.com/S5u2xRB?third_party=1#_=_", "https://i.imgur.com/S5u2xRB.jpg", 2448, 2448, "image/jpeg"],
104
+ ["https://imgur.com/3eThW", "https://i.imgur.com/3eThW.jpg", 2560, 1600, "image/jpeg"],
105
+ ["https://i.imgur.com/RGO6i.mp4", "https://i.imgur.com/RGO6i.gif", 339, 397, "image/gif"],
106
+ ["https://imgur.com/a/oacI3gl", 2, "https://i.imgur.com/9j4KdkJ.png", "https://i.imgur.com/QpOBvRY.png"],
107
+ # some other tests not sure we need them
108
+ ["http://i.imgur.com/7xcxxkR.gifv", "http://i.imgur.com/7xcxxkRh.gif"],
109
+ ["http://imgur.com/HQHBBBD", "https://i.imgur.com/HQHBBBD.jpg", 1024, 768, "image/jpeg"],
110
+ ["http://imgur.com/a/AdJUK", 3, "https://i.imgur.com/Yunpxnx.jpg", "https://i.imgur.com/2epn2nT.jpg", "https://i.imgur.com/3afw2aF.jpg"],
111
+ ["https://imgur.com/9yaMdJq", "https://i.imgur.com/9yaMdJq.mp4", 720, 404, "video/mp4"],
112
+ ["http://imgur.com/gallery/dCQprEq/new", "https://i.imgur.com/dCQprEq.jpg", 5760, 3840, "image/jpeg"],
113
+ ].each_with_index do |t, i|
114
+ url, n, first, last, type = t
115
+ it "##{i + 1}" do
116
+ case last
117
+ when NilClass
118
+ if n.is_a? Class
119
+ assert_raises n do
120
+ DirectLink.imgur url
121
+ end
122
+ else
123
+ real = DirectLink.imgur url
124
+ assert_equal 1, real.size
125
+ assert_equal n, real.first.first
126
+ end
127
+ when Numeric
128
+ real = DirectLink.imgur url
129
+ assert_equal 1, real.size
130
+ assert_equal [n, first, last, type], real.first
131
+ when String
132
+ real = DirectLink.imgur url
133
+ assert_equal n, real.size
134
+ assert_equal first, real.first.first
135
+ assert_equal last, real.last.first
136
+ else
137
+ fail "bug in tests"
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ [
144
+ [ :_500px, [
145
+ ["https://500px.com/photo/112134597/milky-way-by-tom-hall", [4928, 2888, "https://drscdn.500px.org/photo/112134597/m%3D2048_k%3D1_a%3D1/v2?client_application_id=48351&webp=true&sig=6b9f6ac5aed6d051aa66911555657ef57262e7d998f406e40f50e6f61515808f", "jpeg"]],
146
+ ] ],
147
+ [ :flickr, [
148
+ ["https://www.flickr.com/photos/tomas-/17220613278/", DirectLink::ErrorNotFound],
149
+ ["https://www.flickr.com/photos/16936123@N07/18835195572", DirectLink::ErrorNotFound],
150
+ ["https://www.flickr.com/photos/44133687@N00/17380073505/", [3000, 2000, "https://farm8.staticflickr.com/7757/17380073505_ed5178cc6a_o.jpg"]], # trailing slash
151
+ ["https://www.flickr.com/photos/jacob_schmidt/18414267018/in/album-72157654235845651/", DirectLink::ErrorNotFound], # username in-album
152
+ ["https://www.flickr.com/photos/tommygi/5291099420/in/dateposted-public/", [1600, 1062, "https://farm6.staticflickr.com/5249/5291099420_3bf8f43326_o.jpg"]], # username in-public
153
+ ["https://www.flickr.com/photos/132249412@N02/18593786659/in/album-72157654521569061/", DirectLink::ErrorNotFound],
154
+ ["https://www.flickr.com/photos/130019700@N03/18848891351/in/dateposted-public/", [4621, 3081, "https://farm4.staticflickr.com/3796/18848891351_f751b35aeb_o.jpg"]], # userid in-public
155
+ ["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", [1024, 681, "https://farm3.staticflickr.com/2499/3778768209_280f82abab_b.jpg"]],
156
+ ["https://www.flickr.com/photos/patricksloan/18230541413/sizes/l", [2048, 491, "https://farm6.staticflickr.com/5572/18230541413_fec4783d79_k.jpg"]],
157
+ ["https://flic.kr/p/vPvCWJ", [2048, 1365, "https://farm1.staticflickr.com/507/19572004110_d44d1b4ead_k.jpg"]],
158
+ ] ],
159
+ [ :wiki, [
160
+ ["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"],
161
+ ["https://en.wikipedia.org/wiki/Third_Party_System#/media/File:United_States_presidential_election_results,_1876-1892.svg", DirectLink::ErrorAssert],
162
+ ["http://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg", "https://upload.wikimedia.org/wikipedia/commons/0/0d/Eduard_Bohlen_anagoria.jpg"],
163
+ ] ],
164
+ ].each do |method, tests|
165
+ describe method do
166
+ tests.each_with_index do |(input, expectation), i|
167
+ it "##{i + 1}" do
168
+ if expectation.is_a? Class
169
+ assert_raises expectation do
170
+ DirectLink.method(method).call input
171
+ end
172
+ else
173
+ result = DirectLink.method(method).call input
174
+ assert_equal expectation, result, "#{input} :: #{result.inspect} != #{expectation.inspect}"
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ {
182
+ google: %w{
183
+ https://lh3.googleusercontent.com/-NVJgqmI_2Is/WqMM2OMYg-I/AAAAAAAALrk/5-p3JL3iZt0Ho9dOf_p3gpddzqwr3Wp0ACJoC/w424-h318-n/001
184
+ https://lh3.googleusercontent.com/-2JzV0ErXvv8/Wsdr-m5Q_aI/AAAAAAAA_-I/Lw_e6qEMPUck4yW9yWhkC-nBNd5em8c3QCJoC/w530-h795-n/888052_800n.jpg
185
+ //lh3.googleusercontent.com/proxy/DZtTi5KL7PqiBwJc8weNGLk_Wi2UTaQH0AC_h2kuURiu0AbwyI2ywOk2XgdAjL7ceg=w530-h354-n
186
+ //lh3.googleusercontent.com/-h1siEcRFqJ8/Wiv1iDQhNVI/AAAAAAAAQV4/9gGepSmYkDMLyVTG_1EBCMmj-UhmclXWwCJoC/w530-h353-p/001
187
+ //1.bp.blogspot.com/-rYk_u-qROQc/WngolJ8M0LI/AAAAAAAAD-w/woivnaIVzasBPG5C2T1t-VrWKRd_U6lMgCLcBGAs/w530-h278-p/i-469.jpg
188
+ },
189
+ imgur: %w{
190
+ https://imgur.com/3eThW
191
+ https://i.imgur.com/3eThW
192
+ https://m.imgur.com/3eThW
193
+ https://www.imgur.com/3eThW
194
+ },
195
+ _500px: %w{
196
+ https://500px.com/photo/112134597/milky-way-by-tom-hall
197
+ },
198
+ flickr: %w{
199
+ https://www.flickr.com/photos/tomas-/17220613278/
200
+ https://flic.kr/p/vPvCWJ
201
+ },
202
+ wiki: %w{
203
+ https://en.wikipedia.org/wiki/Third_Party_System#/media/File:United_States_presidential_election_results,_1876-1892.svg
204
+ http://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg
205
+ },
206
+ }.each do |method, tests|
207
+ describe "DirectLink() calls #{method}" do
208
+ tests.each_with_index do |input, i|
209
+ it "##{i + 1}" do
210
+ called = false
211
+ m = DirectLink.method method
212
+ DirectLink.stub method, ->link{ called = true } do
213
+ DirectLink.method(method).call input
214
+ assert called, "DirectLink.#{method} was not called"
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ describe "throws ErrorBadLink if method does not match the link" do
222
+ %i{ google imgur flickr _500px wiki }.each do |method|
223
+ it method do
224
+ assert_raises DirectLink::ErrorBadLink do
225
+ DirectLink.method(method).call ""
226
+ end
227
+ end
228
+ end
229
+ end
230
+
231
+ end
232
+
233
+ describe "DirectLink()" do
234
+
235
+ it "throws ErrorBadLink if link is invalid" do
236
+ e = assert_raises DirectLink::ErrorBadLink do
237
+ DirectLink "test"
238
+ end
239
+ assert_equal "test".inspect, e.message
240
+ end
241
+
242
+ describe "does not shadow the internal exception" do
243
+ [
244
+ [:google, "//lh3.googleusercontent.com/proxy/DZtTi5KL7PqiBwJc8weNGLk_Wi2UTaQH0AC_h2kuURiu0AbwyI2ywOk2XgdAjL7ceg=w530-h354-n"],
245
+ [:imgur, "http://imgur.com/HQHBBBD"],
246
+ [:flickr, "https://www.flickr.com/photos/tomas-/17220613278/"],
247
+ [:_500px, "https://500px.com/photo/112134597/milky-way-by-tom-hall"],
248
+ [:wiki, "http://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg"],
249
+ ].each do |method, link|
250
+ it method do
251
+ e = assert_raises DirectLink::ErrorBadLink do
252
+ DirectLink.stub method, ->*{ raise DirectLink::ErrorBadLink.new "test" } do
253
+ DirectLink link
254
+ end
255
+ end
256
+ assert_equal "\"test\" -- if you think this link is valid, please report the issue", e.message
257
+ end
258
+ end
259
+ it "raises FastImage::ImageFetchFailure on Net::HTTP failure" do
260
+ assert_raises FastImage::ImageFetchFailure do
261
+ NetHTTPUtils.stub :get_response, ->*{ raise SocketError.new } do
262
+ DirectLink "http://example.com/404"
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ describe "some other tests" do
269
+ [
270
+ ["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]],
271
+ ["http://example.com", FastImage::UnknownImageType], # was URL2Dimensions::ErrorUnknown
272
+ ["http://minus.com/lkP3hgRJd9npi", FastImage::ImageFetchFailure], # was URL2Dimensions::Error404
273
+ ["https://i.redd.it/si758zk7r5xz.jpg", FastImage::ImageFetchFailure], # was URL2Dimensions::Error404
274
+ ["http://www.cutehalloweencostumeideas.org/wp-content/uploads/2017/10/Niagara-Falls_04.jpg", FastImage::ImageFetchFailure], # was URL2Dimensions::Error404
275
+ ].each_with_index do |(input, expectation), i|
276
+ it "##{i + 1}" do
277
+ if expectation.is_a? Class
278
+ assert_raises expectation do
279
+ DirectLink input
280
+ end
281
+ else
282
+ u, w, h, t = expectation
283
+ result = DirectLink input
284
+ assert_equal DirectLink.class_variable_get(:@@directlink).new(u, w, h, t),
285
+ result, "#{input} :: #{result.inspect} != #{expectation.inspect}"
286
+ end
287
+ end
288
+ end
289
+ end
290
+
291
+ end
292
+
293
+ describe "./bin" do
294
+ require "open3"
295
+
296
+ describe "shows usage help if misused" do
297
+ [
298
+ ["usage: directlink [--debug] [--json] <link1> <link2> <link3> ...\n", nil],
299
+ ["usage: directlink [--debug] [--json] <link1> <link2> <link3> ...\n", "--help"],
300
+ ["usage: directlink [--debug] [--json] <link1> <link2> <link3> ...\n", "-h"],
301
+ ["DirectLink::ErrorBadLink: \"--\"\n", "--"],
302
+ ["DirectLink::ErrorBadLink: \"-\"\n", "-"],
303
+ ["DirectLink::ErrorBadLink: \"-\"\n", "- -"],
304
+ ["DirectLink::ErrorBadLink: \"asd\"\n", "asd"],
305
+ ].each_with_index do |(expected_output, param), i|
306
+ it "##{i + 1}" do
307
+ string, status = Open3.capture2e "ruby -Ilib bin/directlink #{param}"
308
+ assert_equal [1, expected_output], [status.exitstatus, string]
309
+ end
310
+ end
311
+ end
312
+
313
+ # that hack around `export` is for crossplatform Travis test, since Ubuntu uses `sh` that can't `source`
314
+ # TODO: but maybe I could make Open3 use the shell I need, since `source` works fine for `.travis.yaml`'s `:script`
315
+
316
+ describe "fails" do
317
+ [
318
+ [1, "http://example.com/", "FastImage::UnknownImageType"], # TODO: is it possible to obtain it from `.cause`?
319
+ [1, "http://example.com/404", "FastImage::ImageFetchFailure"],
320
+ [1, "http://imgur.com/HQHBBBD", "DirectLink::ErrorMissingEnvVar: define IMGUR_CLIENT_ID env var", " && unset IMGUR_CLIENT_ID"], # TODO: make similar test for ./lib
321
+ # by design it should be impossible to write a test for DirectLink::ErrorAssert
322
+ [1, "https://flic.kr/p/DirectLinkErrorNotFound", "DirectLink::ErrorNotFound: \"https://flic.kr/p/DirectLinkErrorNotFound\""],
323
+ [1, "https://imgur.com/a/badlinkpattern", "DirectLink::ErrorBadLink: \"https://imgur.com/a/badlinkpattern\" -- if you think this link is valid, please report the issue"],
324
+ ].each_with_index do |(expected_exit_code, link, expected_output, unset), i|
325
+ it "##{i + 1}" do
326
+ string, status = Open3.capture2e "export #{File.read("api_tokens_for_travis.sh").gsub(/\n?export/, ?\s).strip}#{unset} && ruby -Ilib bin/directlink #{link}"
327
+ assert_equal [expected_exit_code, "#{expected_output}\n"], [status.exitstatus, string]
328
+ end
329
+ end
330
+ end
331
+
332
+ valid_imgur_image_urls = "https://avatars1.githubusercontent.com/u/2870363?100 https://imgur.com/a/oacI3gl"
333
+ [
334
+ ['https://avatars1.githubusercontent.com/u/2870363?100
335
+ jpeg 460x460
336
+
337
+ https://i.imgur.com/QpOBvRY.png
338
+ image/png 460x460
339
+ https://i.imgur.com/9j4KdkJ.png
340
+ image/png 100x100
341
+ '.gsub(/^ {8}/, "")],
342
+ ['[
343
+ {
344
+ "url": "https://avatars1.githubusercontent.com/u/2870363?100",
345
+ "width": 460,
346
+ "height": 460,
347
+ "type": "jpeg"
348
+ },
349
+ [
350
+ {
351
+ "url": "https://i.imgur.com/QpOBvRY.png",
352
+ "width": 460,
353
+ "height": 460,
354
+ "type": "image/png"
355
+ },
356
+ {
357
+ "url": "https://i.imgur.com/9j4KdkJ.png",
358
+ "width": 100,
359
+ "height": 100,
360
+ "type": "image/png"
361
+ }
362
+ ]
363
+ ]
364
+ '.gsub(/^ {8}/, ""), "json"],
365
+ ].each do |expected_output, param|
366
+ it "#{param || "default"} succeeds" do
367
+ string, status = Open3.capture2e "export #{File.read("api_tokens_for_travis.sh").gsub(/\n?export/, ?\s).strip} && ruby -Ilib bin/directlink#{" --#{param}" if param} #{valid_imgur_image_urls}"
368
+ assert_equal [0, expected_output], [status.exitstatus, string]
369
+ end
370
+ end
371
+
372
+ end
373
+
374
+ end