aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorIvan Tashkinov <ivantashkinov@gmail.com>2020-05-20 20:26:43 +0300
committerIvan Tashkinov <ivantashkinov@gmail.com>2020-05-20 20:26:43 +0300
commit1871a5ddb4a803ebe4fae6943a9b9c94f1f9c1a8 (patch)
tree4068e23fc380346f5fdf531ce3672cd051d363d2 /lib
parent978ccf8f974e7ee398faa3e5bfb7c081144e9325 (diff)
downloadpleroma-1871a5ddb4a803ebe4fae6943a9b9c94f1f9c1a8.tar.gz
[#2497] Image preview proxy: implemented ffmpeg-based resizing, removed eimp & mogrify-based resizing.
Diffstat (limited to 'lib')
-rw-r--r--lib/pleroma/helpers/media_helper.ex62
-rw-r--r--lib/pleroma/helpers/mogrify_helper.ex25
-rw-r--r--lib/pleroma/web/media_proxy/media_proxy_controller.ex50
3 files changed, 71 insertions, 66 deletions
diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex
new file mode 100644
index 000000000..6d1f8ab22
--- /dev/null
+++ b/lib/pleroma/helpers/media_helper.ex
@@ -0,0 +1,62 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Helpers.MediaHelper do
+ @moduledoc """
+ Handles common media-related operations.
+ """
+
+ @ffmpeg_opts [{:sync, true}, {:stdout, true}]
+
+ def ffmpeg_resize_remote(uri, max_width, max_height) do
+ cmd = ~s"""
+ curl -L "#{uri}" |
+ ffmpeg -i pipe:0 -vf \
+ "scale='min(#{max_width},iw)':min'(#{max_height},ih)':force_original_aspect_ratio=decrease" \
+ -f image2 pipe:1 | \
+ cat
+ """
+
+ with {:ok, [stdout: stdout_list]} <- Exexec.run(cmd, @ffmpeg_opts) do
+ {:ok, Enum.join(stdout_list)}
+ end
+ end
+
+ @doc "Returns a temporary path for an URI"
+ def temporary_path_for(uri) do
+ name = Path.basename(uri)
+ random = rand_uniform(999_999)
+ Path.join(System.tmp_dir(), "#{random}-#{name}")
+ end
+
+ @doc "Stores binary content fetched from specified URL as a temporary file."
+ @spec store_as_temporary_file(String.t(), binary()) :: {:ok, String.t()} | {:error, atom()}
+ def store_as_temporary_file(url, body) do
+ path = temporary_path_for(url)
+ with :ok <- File.write(path, body), do: {:ok, path}
+ end
+
+ @doc "Modifies image file at specified path by resizing to specified limit dimensions."
+ @spec mogrify_resize_to_limit(String.t(), String.t()) :: :ok | any()
+ def mogrify_resize_to_limit(path, resize_dimensions) do
+ with %Mogrify.Image{} <-
+ path
+ |> Mogrify.open()
+ |> Mogrify.resize_to_limit(resize_dimensions)
+ |> Mogrify.save(in_place: true) do
+ :ok
+ end
+ end
+
+ defp rand_uniform(high) do
+ Code.ensure_loaded(:rand)
+
+ if function_exported?(:rand, :uniform, 1) do
+ :rand.uniform(high)
+ else
+ # Erlang/OTP < 19
+ apply(:crypto, :rand_uniform, [1, high])
+ end
+ end
+end
diff --git a/lib/pleroma/helpers/mogrify_helper.ex b/lib/pleroma/helpers/mogrify_helper.ex
deleted file mode 100644
index 67edb35c3..000000000
--- a/lib/pleroma/helpers/mogrify_helper.ex
+++ /dev/null
@@ -1,25 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Helpers.MogrifyHelper do
- @moduledoc """
- Handles common Mogrify operations.
- """
-
- @spec store_as_temporary_file(String.t(), binary()) :: {:ok, String.t()} | {:error, atom()}
- @doc "Stores binary content fetched from specified URL as a temporary file."
- def store_as_temporary_file(url, body) do
- path = Mogrify.temporary_path_for(%{path: url})
- with :ok <- File.write(path, body), do: {:ok, path}
- end
-
- @spec store_as_temporary_file(String.t(), String.t()) :: Mogrify.Image.t() | any()
- @doc "Modifies file at specified path by resizing to specified limit dimensions."
- def in_place_resize_to_limit(path, resize_dimensions) do
- path
- |> Mogrify.open()
- |> Mogrify.resize_to_limit(resize_dimensions)
- |> Mogrify.save(in_place: true)
- end
-end
diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
index 8d8d073e9..fb4b80379 100644
--- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
@@ -6,7 +6,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
use Pleroma.Web, :controller
alias Pleroma.Config
- alias Pleroma.Helpers.MogrifyHelper
+ alias Pleroma.Helpers.MediaHelper
alias Pleroma.ReverseProxy
alias Pleroma.Web.MediaProxy
@@ -82,51 +82,19 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
{thumbnail_max_width, thumbnail_max_height}
end
- defp thumbnail_binary(url, body, params) do
- {thumbnail_max_width, thumbnail_max_height} = thumbnail_max_dimensions(params)
-
- with true <- Config.get([:media_preview_proxy, :enable_eimp]),
- {:ok, [type: image_type, width: source_width, height: source_height]} <-
- :eimp.identify(body),
- scale_factor <-
- Enum.max([source_width / thumbnail_max_width, source_height / thumbnail_max_height]),
- {:ok, thumbnail_binary} =
- :eimp.convert(body, image_type, [
- {:scale, {round(source_width / scale_factor), round(source_height / scale_factor)}}
- ]) do
- {:ok, thumbnail_binary}
- else
- _ ->
- mogrify_dimensions = "#{thumbnail_max_width}x#{thumbnail_max_height}"
-
- with {:ok, path} <- MogrifyHelper.store_as_temporary_file(url, body),
- %Mogrify.Image{} <-
- MogrifyHelper.in_place_resize_to_limit(path, mogrify_dimensions),
- {:ok, thumbnail_binary} <- File.read(path),
- _ <- File.rm(path) do
- {:ok, thumbnail_binary}
- else
- _ -> :error
- end
- end
- end
-
defp handle_preview("image/" <> _ = content_type, %{params: params} = conn, url) do
- with {:ok, %{status: status, body: image_contents}} when status in 200..299 <-
- url
- |> MediaProxy.url()
- |> Tesla.get(opts: [adapter: [timeout: preview_timeout()]]),
- {:ok, thumbnail_binary} <- thumbnail_binary(url, image_contents, params) do
+ with {thumbnail_max_width, thumbnail_max_height} <- thumbnail_max_dimensions(params),
+ media_proxy_url <- MediaProxy.url(url),
+ {:ok, thumbnail_binary} <-
+ MediaHelper.ffmpeg_resize_remote(
+ media_proxy_url,
+ thumbnail_max_width,
+ thumbnail_max_height
+ ) do
conn
|> put_resp_header("content-type", content_type)
|> send_resp(200, thumbnail_binary)
else
- {_, %{status: _}} ->
- send_resp(conn, :failed_dependency, "Can't fetch the image.")
-
- {:error, :recv_response_timeout} ->
- send_resp(conn, :failed_dependency, "Downstream timeout.")
-
_ ->
send_resp(conn, :failed_dependency, "Can't handle image preview.")
end