65 行
1.8 KiB
Ruby
65 行
1.8 KiB
Ruby
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
|