diff options
| -rw-r--r-- | lib/pleroma/helpers/media_helper.ex | 70 | 
1 files changed, 60 insertions, 10 deletions
| diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index 01f42d9b0..a43352ae0 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -7,18 +7,66 @@ defmodule Pleroma.Helpers.MediaHelper do    Handles common media-related operations.    """ -  def image_resize(url, %{max_width: max_width, max_height: max_height} = options) do +  @tmp_base "/tmp/pleroma-media_preview-pipe" + +  def image_resize(url, options) do +    with executable when is_binary(executable) <- System.find_executable("convert"), +         {:ok, args} <- prepare_resize_args(options), +         url = Pleroma.Web.MediaProxy.url(url), +         {:ok, env} <- Pleroma.HTTP.get(url), +         {:ok, fifo_path} <- mkfifo() +    do +      run_fifo(fifo_path, env, executable, args) +    else +      nil -> {:error, {:convert, :command_not_found}} +      {:error, _} = error -> error +    end +  end + +  defp prepare_resize_args(%{max_width: max_width, max_height: max_height} = options) do      quality = options[:quality] || 85 +    resize = Enum.join([max_width, "x", max_height, ">"]) +    args = [ +    "-auto-orient", # Support for EXIF rotation +    "-resize", resize, +    "-quality", to_string(quality) +    ] +    {:ok, args} +  end -    cmd = ~s""" -    convert fd:0 -resize '#{max_width}x#{max_height}>' -quality #{quality} jpg:- -    """ +  defp prepare_resize_args(_), do: {:error, :missing_options} -    pid = Port.open({:spawn, cmd}, [:use_stdio, :stream, :exit_status, :binary]) -    {:ok, env} = url |> Pleroma.Web.MediaProxy.url() |> Pleroma.HTTP.get() -    image = env.body -    Port.command(pid, image) +  defp run_fifo(fifo_path, env, executable, args) do +    args = List.flatten([fifo_path, args, "jpg:fd:1"]) +    pid = Port.open({:spawn_executable, executable}, [:use_stdio, :stream, :exit_status, :binary, args: args]) +    fifo = Port.open(to_charlist(fifo_path), [:eof, :binary, :stream, :out]) +    true = Port.command(fifo, env.body) +    :erlang.port_close(fifo)      loop_recv(pid) +  after +    File.rm(fifo_path) +  end + +  defp mkfifo() do +    path = "#{@tmp_base}#{to_charlist(:erlang.phash2(self()))}" +    case System.cmd("mkfifo", [path]) do +      {_, 0} -> +        spawn(fifo_guard(path)) +        {:ok, path} +      {_, err} -> +        {:error, {:fifo_failed, err}} +    end +  end + +  defp fifo_guard(path) do +    pid = self() +    fn() -> +      ref = Process.monitor(pid) +      receive do +        {:DOWN, ^ref, :process, ^pid, _} -> +          File.rm(path) +      end +    end    end    defp loop_recv(pid) do @@ -29,12 +77,14 @@ defmodule Pleroma.Helpers.MediaHelper do      receive do        {^pid, {:data, data}} ->          loop_recv(pid, acc <> data) -        {^pid, {:exit_status, 0}} ->          {:ok, acc} -        {^pid, {:exit_status, status}} ->          {:error, status} +     after +       5000 -> +         :erlang.port_close(pid) +         {:error, :timeout}      end    end  end | 
