+ def image_resize(url, options) do
+ with executable when is_binary(executable) <- System.find_executable("convert"),
+ {:ok, args} <- prepare_image_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_image_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 = [
+ "-interlace", "Plane",
+ "-resize", resize,
+ "-quality", to_string(quality),
+ "jpg:-"
+ ]
+ {:ok, args}
+ end
+
+ defp prepare_image_resize_args(_), do: {:error, :missing_options}
+
+ defp run_fifo(fifo_path, env, executable, args) do
+ args = List.flatten([fifo_path, args])
+ 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
+ loop_recv(pid, <<>>)
+ end