ファイル
btrc-hub/backend/app/services/preview/screenshot.rb
T
2026-07-09 23:11:31 +09:00

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