[#2497] Image preview proxy: implemented ffmpeg-based resizing, removed eimp & mogrify-based resizing.
This commit is contained in:
parent
978ccf8f97
commit
1871a5ddb4
|
@ -393,7 +393,6 @@
|
||||||
# Note: media preview proxy depends on media proxy to be enabled
|
# Note: media preview proxy depends on media proxy to be enabled
|
||||||
config :pleroma, :media_preview_proxy,
|
config :pleroma, :media_preview_proxy,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
enable_eimp: true,
|
|
||||||
thumbnail_max_width: 400,
|
thumbnail_max_width: 400,
|
||||||
thumbnail_max_height: 200,
|
thumbnail_max_height: 200,
|
||||||
proxy_opts: [
|
proxy_opts: [
|
||||||
|
|
|
@ -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
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Helpers.MogrifyHelper
|
alias Pleroma.Helpers.MediaHelper
|
||||||
alias Pleroma.ReverseProxy
|
alias Pleroma.ReverseProxy
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
|
@ -82,51 +82,19 @@ defp thumbnail_max_dimensions(params) do
|
||||||
{thumbnail_max_width, thumbnail_max_height}
|
{thumbnail_max_width, thumbnail_max_height}
|
||||||
end
|
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
|
defp handle_preview("image/" <> _ = content_type, %{params: params} = conn, url) do
|
||||||
with {:ok, %{status: status, body: image_contents}} when status in 200..299 <-
|
with {thumbnail_max_width, thumbnail_max_height} <- thumbnail_max_dimensions(params),
|
||||||
url
|
media_proxy_url <- MediaProxy.url(url),
|
||||||
|> MediaProxy.url()
|
{:ok, thumbnail_binary} <-
|
||||||
|> Tesla.get(opts: [adapter: [timeout: preview_timeout()]]),
|
MediaHelper.ffmpeg_resize_remote(
|
||||||
{:ok, thumbnail_binary} <- thumbnail_binary(url, image_contents, params) do
|
media_proxy_url,
|
||||||
|
thumbnail_max_width,
|
||||||
|
thumbnail_max_height
|
||||||
|
) do
|
||||||
conn
|
conn
|
||||||
|> put_resp_header("content-type", content_type)
|
|> put_resp_header("content-type", content_type)
|
||||||
|> send_resp(200, thumbnail_binary)
|
|> send_resp(200, thumbnail_binary)
|
||||||
else
|
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.")
|
send_resp(conn, :failed_dependency, "Can't handle image preview.")
|
||||||
end
|
end
|
||||||
|
|
2
mix.exs
2
mix.exs
|
@ -146,7 +146,6 @@ defp deps do
|
||||||
github: "ninenines/gun", ref: "e1a69b36b180a574c0ac314ced9613fdd52312cc", override: true},
|
github: "ninenines/gun", ref: "e1a69b36b180a574c0ac314ced9613fdd52312cc", override: true},
|
||||||
{:jason, "~> 1.0"},
|
{:jason, "~> 1.0"},
|
||||||
{:mogrify, "~> 0.6.1"},
|
{:mogrify, "~> 0.6.1"},
|
||||||
{:eimp, "~> 1.0.14"},
|
|
||||||
{:ex_aws, "~> 2.1"},
|
{:ex_aws, "~> 2.1"},
|
||||||
{:ex_aws_s3, "~> 2.0"},
|
{:ex_aws_s3, "~> 2.0"},
|
||||||
{:sweet_xml, "~> 0.6.6"},
|
{:sweet_xml, "~> 0.6.6"},
|
||||||
|
@ -198,6 +197,7 @@ defp deps do
|
||||||
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
||||||
{:mox, "~> 0.5", only: :test},
|
{:mox, "~> 0.5", only: :test},
|
||||||
{:restarter, path: "./restarter"},
|
{:restarter, path: "./restarter"},
|
||||||
|
{:exexec, "~> 0.2"},
|
||||||
{:open_api_spex,
|
{:open_api_spex,
|
||||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git",
|
git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git",
|
||||||
ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"}
|
ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"}
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -32,6 +32,7 @@
|
||||||
"ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"},
|
"ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"},
|
||||||
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
||||||
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"},
|
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"},
|
||||||
|
"erlexec": {:hex, :erlexec, "1.10.9", "3cbb3476f942bfb8b68b85721c21c1835061cf6dd35f5285c2362e85b100ddc7", [:rebar3], [], "hexpm", "271e5b5f2d91cdb9887efe74d89026c199bfc69f074cade0d08dab60993fa14e"},
|
||||||
"esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"},
|
"esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"},
|
||||||
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm", "b14f1dc204321429479c569cfbe8fb287541184ed040956c8862cb7a677b8406"},
|
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm", "b14f1dc204321429479c569cfbe8fb287541184ed040956c8862cb7a677b8406"},
|
||||||
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"},
|
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"},
|
||||||
|
@ -42,6 +43,7 @@
|
||||||
"ex_machina": {:hex, :ex_machina, "2.3.0", "92a5ad0a8b10ea6314b876a99c8c9e3f25f4dde71a2a835845b136b9adaf199a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "b84f6af156264530b312a8ab98ac6088f6b77ae5fe2058305c81434aa01fbaf9"},
|
"ex_machina": {:hex, :ex_machina, "2.3.0", "92a5ad0a8b10ea6314b876a99c8c9e3f25f4dde71a2a835845b136b9adaf199a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "b84f6af156264530b312a8ab98ac6088f6b77ae5fe2058305c81434aa01fbaf9"},
|
||||||
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
|
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
|
||||||
"excoveralls": {:hex, :excoveralls, "0.12.2", "a513defac45c59e310ac42fcf2b8ae96f1f85746410f30b1ff2b710a4b6cd44b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "151c476331d49b45601ffc45f43cb3a8beb396b02a34e3777fea0ad34ae57d89"},
|
"excoveralls": {:hex, :excoveralls, "0.12.2", "a513defac45c59e310ac42fcf2b8ae96f1f85746410f30b1ff2b710a4b6cd44b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "151c476331d49b45601ffc45f43cb3a8beb396b02a34e3777fea0ad34ae57d89"},
|
||||||
|
"exexec": {:hex, :exexec, "0.2.0", "a6ffc48cba3ac9420891b847e4dc7120692fb8c08c9e82220ebddc0bb8d96103", [:mix], [{:erlexec, "~> 1.10", [hex: :erlexec, repo: "hexpm", optional: false]}], "hexpm", "312cd1c9befba9e078e57f3541e4f4257eabda6eb9c348154fe899d6ac633299"},
|
||||||
"fast_html": {:hex, :fast_html, "1.0.3", "2cc0d4b68496266a1530e0c852cafeaede0bd10cfdee26fda50dc696c203162f", [:make, :mix], [], "hexpm", "ab3d782b639d3c4655fbaec0f9d032c91f8cab8dd791ac7469c2381bc7c32f85"},
|
"fast_html": {:hex, :fast_html, "1.0.3", "2cc0d4b68496266a1530e0c852cafeaede0bd10cfdee26fda50dc696c203162f", [:make, :mix], [], "hexpm", "ab3d782b639d3c4655fbaec0f9d032c91f8cab8dd791ac7469c2381bc7c32f85"},
|
||||||
"fast_sanitize": {:hex, :fast_sanitize, "0.1.7", "2a7cd8734c88a2de6de55022104f8a3b87f1fdbe8bbf131d9049764b53d50d0d", [:mix], [{:fast_html, "~> 1.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f39fe8ea08fbac17487c30bf09b7d9f3e12472e51fb07a88ffeb8fd17da8ab67"},
|
"fast_sanitize": {:hex, :fast_sanitize, "0.1.7", "2a7cd8734c88a2de6de55022104f8a3b87f1fdbe8bbf131d9049764b53d50d0d", [:mix], [{:fast_html, "~> 1.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f39fe8ea08fbac17487c30bf09b7d9f3e12472e51fb07a88ffeb8fd17da8ab67"},
|
||||||
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
|
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
|
||||||
|
|
Loading…
Reference in New Issue