Basic PNG generation in Elixir
parent
135720df52
commit
e0511053a4
@ -1,4 +1,7 @@
|
|||||||
defmodule Asciinema.FileStore do
|
defmodule Asciinema.FileStore do
|
||||||
@doc "Serves file at given path in store"
|
@doc "Serves file at given path in store"
|
||||||
@callback serve_file(conn :: %Plug.Conn{}, path :: String.t, filename :: String.t) :: %Plug.Conn{}
|
@callback serve_file(conn :: %Plug.Conn{}, path :: String.t, filename :: String.t) :: %Plug.Conn{}
|
||||||
|
|
||||||
|
@doc "Opens the given path in store"
|
||||||
|
@callback open(path :: String.t) :: {:ok, File.io_device} | {:error, File.posix}
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
defmodule Asciinema.PngGenerator do
|
||||||
|
alias Asciinema.Asciicast
|
||||||
|
|
||||||
|
@doc "Generates PNG image from asciicast and returns path to it"
|
||||||
|
@callback generate(asciicast :: %Asciicast{}) :: {:ok, String.t} | {:error, term}
|
||||||
|
|
||||||
|
def generate(asciicast) do
|
||||||
|
Application.get_env(:asciinema, :png_generator).generate(asciicast)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,55 @@
|
|||||||
|
defmodule Asciinema.PngGenerator.A2png do
|
||||||
|
@behaviour Asciinema.PngGenerator
|
||||||
|
use GenServer
|
||||||
|
alias Asciinema.Asciicast
|
||||||
|
|
||||||
|
@result_timeout 30000
|
||||||
|
@acquire_timeout 5000
|
||||||
|
|
||||||
|
def generate(%Asciicast{} = asciicast) do
|
||||||
|
{:ok, tmp_dir_path} = Briefly.create(directory: true)
|
||||||
|
|
||||||
|
:poolboy.transaction(
|
||||||
|
:worker,
|
||||||
|
&GenServer.call(&1, {:gen_png, asciicast, tmp_dir_path}, @result_timeout), @acquire_timeout
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# GenServer API
|
||||||
|
|
||||||
|
def start_link(_) do
|
||||||
|
GenServer.start_link(__MODULE__, nil, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def init(_) do
|
||||||
|
{:ok, nil}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_call({:gen_png, asciicast, tmp_dir_path}, _from, state) do
|
||||||
|
{:reply, do_gen(asciicast, tmp_dir_path), state}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_gen(asciicast, 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")
|
||||||
|
snapshot_at = "#{asciicast.duration / 2}"
|
||||||
|
|
||||||
|
with {:ok, file} <- file_store().open(path),
|
||||||
|
{:ok, _} <- :file.copy(file, json_path),
|
||||||
|
%{status: 0} <- Porcelain.exec(bin_path(), [json_path, png_path, snapshot_at]) do
|
||||||
|
{:ok, png_path}
|
||||||
|
else
|
||||||
|
otherwise ->
|
||||||
|
otherwise
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def bin_path do
|
||||||
|
Keyword.get(Application.get_env(:asciinema, __MODULE__), :bin_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp file_store do
|
||||||
|
Application.get_env(:asciinema, :file_store)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,15 @@
|
|||||||
|
defmodule Asciinema.AsciicastImageController do
|
||||||
|
use Asciinema.Web, :controller
|
||||||
|
alias Asciinema.{Repo, Asciicast, PngGenerator}
|
||||||
|
alias Plug.MIME
|
||||||
|
|
||||||
|
def show(conn, %{"id" => id} = _params) do
|
||||||
|
asciicast = Repo.one!(Asciicast.by_id_or_secret_token(id))
|
||||||
|
{:ok, png_path} = PngGenerator.generate(asciicast)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_resp_header("content-type", MIME.path(png_path))
|
||||||
|
|> send_file(200, png_path)
|
||||||
|
|> halt
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue