defmodule Asciinema.PngGenerator.A2png do @behaviour Asciinema.PngGenerator use GenServer alias Asciinema.Asciicasts.Asciicast alias Asciinema.PngGenerator.PngParams @pool_name :worker @acquire_timeout 5000 @a2png_timeout 30000 @result_timeout 35000 def generate(%Asciicast{} = asciicast, %PngParams{} = png_params) do {:ok, tmp_dir_path} = Briefly.create(directory: true) try do :poolboy.transaction( @pool_name, (fn pid -> try do GenServer.call(pid, {:generate, asciicast, png_params, tmp_dir_path}, @result_timeout) catch :exit, {:timeout, _} -> {:error, :timeout} end end), @acquire_timeout ) catch :exit, {:timeout, _} -> {:error, :busy} end end # GenServer API def start_link(_) do GenServer.start_link(__MODULE__, nil, []) end def init(_) do {:ok, nil} end def handle_call({:generate, asciicast, png_params, tmp_dir_path}, _from, state) do {:reply, do_generate(asciicast, png_params, tmp_dir_path), state} end def poolboy_config do [{:name, {:local, @pool_name}}, {:worker_module, __MODULE__}, {:size, pool_size()}, {:max_overflow, 0}] end defp do_generate(asciicast, png_params, tmp_dir_path) do path = Asciicast.json_store_path(asciicast) json_path = Path.join(tmp_dir_path, "tmp.json") png_path = Path.join(tmp_dir_path, "tmp.png") args = [ json_path, png_path, Float.to_string(png_params.snapshot_at), png_params.theme, Integer.to_string(png_params.scale) ] :ok = file_store().download_file(path, json_path) process = Porcelain.spawn(bin_path(), args, err: :string) case Porcelain.Process.await(process, @a2png_timeout) do {:ok, %{status: 0}} -> {:ok, png_path} {:ok, %Porcelain.Result{} = result} -> {:error, result} otherwise -> otherwise end end defp bin_path do Keyword.get(Application.get_env(:asciinema, __MODULE__), :bin_path) end defp pool_size do Keyword.get(Application.get_env(:asciinema, __MODULE__), :pool_size) end defp file_store do Application.get_env(:asciinema, :file_store) end end