diff --git a/config/config.exs b/config/config.exs index 177bcc9bf..632d7111c 100644 --- a/config/config.exs +++ b/config/config.exs @@ -67,6 +67,7 @@ config :mobilizon, Mobilizon.Web.Upload, filters: [ Mobilizon.Web.Upload.Filter.Dedupe, Mobilizon.Web.Upload.Filter.AnalyzeMetadata, + Mobilizon.Web.Upload.Filter.Resize, Mobilizon.Web.Upload.Filter.Optimize ], allow_list_mime_types: ["image/gif", "image/jpeg", "image/png", "image/webp"], diff --git a/lib/web/upload/filter/resize.ex b/lib/web/upload/filter/resize.ex new file mode 100644 index 000000000..424d578aa --- /dev/null +++ b/lib/web/upload/filter/resize.ex @@ -0,0 +1,42 @@ +defmodule Mobilizon.Web.Upload.Filter.Resize do + @moduledoc """ + Resize the pictures if they're bigger than maximum size. + + This filter requires `Mobilizon.Web.Upload.Filter.AnalyzeMetadata` to be performed before. + """ + + @behaviour Mobilizon.Web.Upload.Filter + + @maximum_width 1_920 + @maximum_height 1_080 + + def filter(%Mobilizon.Web.Upload{ + tempfile: file, + content_type: "image" <> _, + width: width, + height: height + }) do + file + |> Mogrify.open() + |> Mogrify.resize(string(limit_sizes({width, height}))) + |> Mogrify.save(in_place: true) + + {:ok, :filtered} + end + + def filter(_), do: {:ok, :noop} + + def limit_sizes({width, height}) when width > @maximum_width do + new_height = round(@maximum_width * height / width) + limit_sizes({@maximum_width, new_height}) + end + + def limit_sizes({width, height}) when height > @maximum_height do + new_width = round(@maximum_height * width / height) + limit_sizes({new_width, @maximum_height}) + end + + def limit_sizes({width, height}), do: {width, height} + + defp string({width, height}), do: "#{width}x#{height}" +end diff --git a/lib/web/upload/upload.ex b/lib/web/upload/upload.ex index 5e19cec7f..13169ddc7 100644 --- a/lib/web/upload/upload.ex +++ b/lib/web/upload/upload.ex @@ -60,9 +60,11 @@ defmodule Mobilizon.Web.Upload do tempfile: String.t(), content_type: String.t(), path: String.t(), - size: integer() + size: integer(), + width: integer(), + height: integer() } - defstruct [:id, :name, :tempfile, :content_type, :path, :size] + defstruct [:id, :name, :tempfile, :content_type, :path, :size, :width, :height] @spec store(source, options :: [option()]) :: {:ok, map()} | {:error, any()} def store(upload, opts \\ []) do diff --git a/test/web/upload/filter/resize_test.exs b/test/web/upload/filter/resize_test.exs new file mode 100644 index 000000000..5faf21efb --- /dev/null +++ b/test/web/upload/filter/resize_test.exs @@ -0,0 +1,20 @@ +defmodule Mobilizon.Web.Upload.Filter.ResizeTest do + use Mobilizon.DataCase, async: true + alias Mobilizon.Web.Upload.Filter.Resize + + test "does not resize if dimensions are ok" do + assert {100, 150} == Resize.limit_sizes({100, 150}) + end + + test "does resize only width if needed" do + assert {1_920, 960} == Resize.limit_sizes({2_000, 1_000}) + end + + test "does resize only height if needed" do + assert {540, 1_080} == Resize.limit_sizes({1_000, 2_000}) + end + + test "does resize if dimentions are really big, and keeps ratio" do + assert {724, 1080} == Resize.limit_sizes({10_050, 15_000}) + end +end