このコミットが含まれているのは:
@@ -1,64 +0,0 @@
|
||||
module Preview
|
||||
class Screenshot
|
||||
class Unavailable < StandardError; end
|
||||
class Busy < Unavailable; end
|
||||
class TimedOut < Unavailable; end
|
||||
|
||||
LIMIT = ENV.fetch('PREVIEW_SCREENSHOT_CONCURRENCY', 2).to_i.clamp(1, 10)
|
||||
TIMEOUT = ENV.fetch('PREVIEW_SCREENSHOT_TIMEOUT', 20).to_i.clamp(5, 60)
|
||||
@mutex = Mutex.new
|
||||
@active = 0
|
||||
|
||||
def self.capture(url)
|
||||
acquire!
|
||||
tempfile = nil
|
||||
pid = nil
|
||||
|
||||
begin
|
||||
tempfile = Tempfile.new(['preview-thumbnail', '.png'])
|
||||
tempfile.close
|
||||
status = nil
|
||||
pid = Process.spawn(
|
||||
'node', Rails.root.join('lib/screenshot.js').to_s,
|
||||
url, tempfile.path,
|
||||
out: File::NULL, err: File::NULL, pgroup: true)
|
||||
Timeout.timeout(TIMEOUT) do
|
||||
_finished_pid, status = Process.wait2(pid)
|
||||
end
|
||||
raise Unavailable, 'スクリーンショットを生成できませんでした.' unless status.success?
|
||||
raise Unavailable, 'スクリーンショットを生成できませんでした.' unless File.size?(tempfile.path)
|
||||
|
||||
File.binread(tempfile.path)
|
||||
rescue Timeout::Error
|
||||
terminate(pid)
|
||||
raise TimedOut, 'スクリーンショット生成が timeout しました.'
|
||||
ensure
|
||||
tempfile&.unlink
|
||||
release!
|
||||
end
|
||||
end
|
||||
|
||||
def self.acquire!
|
||||
@mutex.synchronize do
|
||||
raise Busy, 'スクリーンショット生成が混雑してゐます.' if @active >= LIMIT
|
||||
|
||||
@active += 1
|
||||
end
|
||||
end
|
||||
|
||||
def self.release!
|
||||
@mutex.synchronize { @active -= 1 }
|
||||
end
|
||||
|
||||
def self.terminate(pid)
|
||||
return unless pid
|
||||
|
||||
Process.kill('TERM', -pid)
|
||||
Process.wait(pid)
|
||||
rescue Errno::ESRCH, Errno::ECHILD
|
||||
nil
|
||||
end
|
||||
|
||||
private_class_method :acquire!, :release!, :terminate
|
||||
end
|
||||
end
|
||||
@@ -4,18 +4,15 @@ module Preview
|
||||
|
||||
def self.fetch(raw_url)
|
||||
uri, = UrlSafety.validate(raw_url)
|
||||
page = nil
|
||||
|
||||
known_url = KnownSiteExtractor.thumbnail_url(uri)
|
||||
image = fetch_image(known_url) if known_url
|
||||
return fetch_image(known_url) if known_url
|
||||
|
||||
unless image
|
||||
page = HttpFetcher.fetch(uri.to_s)
|
||||
metadata = HtmlMetadataExtractor.extract(page)
|
||||
image = fetch_image(metadata[:image_url]) if metadata[:image_url]
|
||||
end
|
||||
page = HttpFetcher.fetch(uri.to_s)
|
||||
metadata = HtmlMetadataExtractor.extract(page)
|
||||
raise GenerationFailed, 'サムネール画像が見つかりませんでした.' if metadata[:image_url].blank?
|
||||
|
||||
image || Screenshot.capture(uri.to_s)
|
||||
fetch_image(metadata[:image_url])
|
||||
end
|
||||
|
||||
def self.title(raw_url)
|
||||
@@ -25,11 +22,15 @@ module Preview
|
||||
|
||||
def self.fetch_image(url)
|
||||
response = HttpFetcher.fetch(url)
|
||||
return unless response.content_type.downcase.start_with?('image/')
|
||||
unless response.content_type.downcase.start_with?('image/')
|
||||
raise GenerationFailed, 'サムネール画像が見つかりませんでした.'
|
||||
end
|
||||
|
||||
response.body
|
||||
rescue HttpFetcher::FetchTimeout
|
||||
raise
|
||||
rescue HttpFetcher::FetchFailed
|
||||
nil
|
||||
raise GenerationFailed, 'サムネール画像を取得できませんでした.'
|
||||
end
|
||||
|
||||
private_class_method :fetch_image
|
||||
|
||||
@@ -22,7 +22,7 @@ module Preview
|
||||
raise UnsafeUrl, 'http または https の URL を指定してください.'
|
||||
end
|
||||
else
|
||||
value = "https://#{ value }"
|
||||
value = "http://#{ value }"
|
||||
end
|
||||
uri = URI.parse(value)
|
||||
|
||||
|
||||
新しい課題から参照
ユーザをブロックする