[#2497] Image preview proxy: implemented ffmpeg-based resizing, removed eimp & mogrify-based resizing.
This commit is contained in:
parent
978ccf8f97
commit
1871a5ddb4
6 changed files with 74 additions and 68 deletions
62
lib/pleroma/helpers/media_helper.ex
Normal file
62
lib/pleroma/helpers/media_helper.ex
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue