]> granicus.if.org Git - ejabberd/commitdiff
Use eimp instead of ImageMagick calls for thumbnails creation
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Mon, 25 Sep 2017 09:41:12 +0000 (12:41 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Mon, 25 Sep 2017 09:41:12 +0000 (12:41 +0300)
rebar.config
src/misc.erl
src/mod_avatar.erl
src/mod_http_upload.erl

index 0e8213cae96d19d5762bfaa5e9d9a1a56b770358..f1d96bae26650285225a90ba657f7e814b80f540 100644 (file)
@@ -44,7 +44,7 @@
                                            {tag, "1.0.2"}}}},
         {if_var_true, riak, {riakc, ".*", {git, "https://github.com/processone/riak-erlang-client.git",
                                            {tag, "2.5.3"}}}},
-       {if_var_true, graphics, {eimp, ".*", {git, "https://github.com/processone/eimp.git", "dc5bfe0"}}},
+       {if_var_true, graphics, {eimp, ".*", {git, "https://github.com/processone/eimp.git", "685d2a2c9b"}}},
        %% Elixir support, needed to run tests
         {if_var_true, elixir, {elixir, ".*", {git, "https://github.com/elixir-lang/elixir",
                                               {tag, {if_version_above, "17", "v1.4.4", "v1.1.1"}}}}},
index 32699e76b286e986193c03f213f2e08e9b4e42a9..06d81cb88ab62cfccb3c996ac5de88cf90f0b2e3 100644 (file)
@@ -33,7 +33,7 @@
         atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1,
         l2i/1, i2l/1, i2l/2, expr_to_term/1, term_to_expr/1,
         now_to_usec/1, usec_to_now/1, encode_pid/1, decode_pid/2,
-        compile_exprs/2, join_atoms/2, try_read_file/1]).
+        compile_exprs/2, join_atoms/2, try_read_file/1, have_eimp/0]).
 
 %% Deprecated functions
 -export([decode_base64/1, encode_base64/1]).
@@ -213,6 +213,12 @@ try_read_file(Path) ->
            erlang:error(badarg)
     end.
 
+-ifdef(GRAPHICS).
+have_eimp() -> true.
+-else.
+have_eimp() -> false.
+-endif.
+
 %%%===================================================================
 %%% Internal functions
 %%%===================================================================
index 4d91e1fb92443e41d8f574773e4d8e8804f994ce..dde58abf1273d541954ffd592ef827cf28bee91f 100644 (file)
@@ -38,7 +38,7 @@
 %%% API
 %%%===================================================================
 start(Host, _Opts) ->
-    case have_eimp() of
+    case misc:have_eimp() of
        true ->
            ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE,
                               pubsub_publish_item, 50),
@@ -416,12 +416,6 @@ decode_mime_type(MimeType) ->
 encode_mime_type(Type) ->
     <<"image/", (atom_to_binary(Type, latin1))/binary>>.
 
--ifdef(GRAPHICS).
-have_eimp() -> true.
--else.
-have_eimp() -> false.
--endif.
-
 mod_opt_type({convert, png}) ->
     fun(jpeg) -> jpeg;
        (webp) -> webp;
index f117434ed61fa797d8104d2fd36da94067f7a2f7..56c05dc359092e8f1b21d7527835851fd1c0ca6f 100644 (file)
@@ -25,7 +25,7 @@
 
 -module(mod_http_upload).
 -author('holger@zedat.fu-berlin.de').
-
+-compile(export_all).
 -protocol({xep, 363, '0.1'}).
 
 -define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds.
         slots = #{}            :: map()}).
 
 -record(media_info,
-       {type   :: binary(),
+       {type   :: atom(),
         height :: integer(),
         width  :: integer()}).
 
@@ -236,12 +236,14 @@ init([ServerHost, Opts]) ->
     end,
     case Thumbnail of
        true ->
-           case string:str(os:cmd("identify"), "Magick") of
-             0 ->
-                 ?ERROR_MSG("Cannot find 'identify' command, please install "
-                            "ImageMagick or disable thumbnail creation", []);
-             _ ->
-                 ok
+           case misc:have_eimp() of
+               false ->
+                   ?ERROR_MSG("ejabberd is built without graphics support, "
+                              "please rebuild it with --enable-graphics or "
+                              "set 'thumbnail: false' for module '~s' in "
+                              "ejabberd.yml", [?MODULE]);
+               _ ->
+                   ok
            end;
        false ->
            ok
@@ -726,15 +728,15 @@ parse_http_request(#request{host = Host, path = Path}) ->
 store_file(Path, Data, FileMode, DirMode, GetPrefix, Slot, Thumbnail) ->
     case do_store_file(Path, Data, FileMode, DirMode) of
        ok when Thumbnail ->
-           case identify(Path) of
+           case identify(Path, Data) of
                {ok, MediaInfo} ->
-                   case convert(Path, MediaInfo) of
-                       {ok, OutPath} ->
+                   case convert(Path, Data, MediaInfo) of
+                       {ok, OutPath, OutMediaInfo} ->
                            [UserDir, RandDir | _] = Slot,
                            FileName = filename:basename(OutPath),
                            URL = str:join([GetPrefix, UserDir,
                                            RandDir, FileName], <<$/>>),
-                           ThumbEl = thumb_el(OutPath, URL),
+                           ThumbEl = thumb_el(OutMediaInfo, URL),
                            {ok,
                             [{<<"Content-Type">>,
                               <<"text/xml; charset=utf-8">>}],
@@ -830,59 +832,68 @@ code_to_message(_Code) -> <<"">>.
 %% Image manipulation stuff.
 %%--------------------------------------------------------------------
 
--spec identify(binary()) -> {ok, media_info()} | pass.
-
-identify(Path) ->
-    Cmd = io_lib:format("identify -format 'ok %m %h %w' ~s", [Path]),
-    Res = string:strip(os:cmd(Cmd), right, $\n),
-    case string:tokens(Res, " ") of
-       ["ok", T, H, W] ->
-           {ok, #media_info{type = list_to_binary(string:to_lower(T)),
-                            height = list_to_integer(H),
-                            width = list_to_integer(W)}};
-       _ ->
-           ?DEBUG("Cannot identify type of ~s: ~s", [Path, Res]),
+-spec identify(binary(), binary()) -> {ok, media_info()} | pass.
+
+identify(Path, Data) ->
+    case misc:have_eimp() of
+       true ->
+           case eimp:identify(Data) of
+               {ok, Info} ->
+                   {ok, #media_info{
+                           type = proplists:get_value(type, Info),
+                           width = proplists:get_value(width, Info),
+                           height = proplists:get_value(height, Info)}};
+               {error, Why} ->
+                   ?DEBUG("Cannot identify type of ~s: ~s",
+                          [Path, eimp:format_error(Why)]),
+                   pass
+           end;
+       false ->
            pass
     end.
 
--spec convert(binary(), media_info()) -> {ok, binary()} | pass.
+-spec convert(binary(), binary(), media_info()) -> {ok, binary(), media_info()} | pass.
 
-convert(Path, #media_info{type = T, width = W, height = H}) ->
+convert(Path, Data, #media_info{type = T, width = W, height = H} = Info) ->
     if W * H >= 25000000 ->
            ?DEBUG("The image ~s is more than 25 Mpix", [Path]),
            pass;
        W =< 300, H =< 300 ->
-           {ok, Path};
-       T == <<"gif">>; T == <<"jpeg">>; T == <<"png">>; T == <<"webp">> ->
+           {ok, Path, Info};
+       true ->
            Dir = filename:dirname(Path),
-           FileName = <<(randoms:get_string())/binary, $., T/binary>>,
+           Ext = atom_to_binary(T, latin1),
+           FileName = <<(randoms:get_string())/binary, $., Ext/binary>>,
            OutPath = filename:join(Dir, FileName),
-           Cmd = io_lib:format("convert -resize 300 ~s ~s", [Path, OutPath]),
-           case os:cmd(Cmd) of
-               "" ->
-                   {ok, OutPath};
-               Err ->
+           {W1, H1} = if W > H -> {300, round(H*300/W)};
+                         H > W -> {round(W*300/H), 300};
+                         true -> {300, 300}
+                      end,
+           OutInfo = #media_info{type = T, width = W1, height = H1},
+           case eimp:convert(Data, T, [{scale, {W1, H1}}]) of
+               {ok, OutData} ->
+                   case file:write_file(OutPath, OutData) of
+                       ok ->
+                           {ok, OutPath, OutInfo};
+                       {error, Why} ->
+                           ?ERROR_MSG("Failed to write to ~s: ~s",
+                                      [OutPath, file:format_error(Why)]),
+                           pass
+                   end;
+               {error, Why} ->
                    ?ERROR_MSG("Failed to convert ~s to ~s: ~s",
-                              [Path, OutPath, string:strip(Err, right, $\n)]),
+                              [Path, OutPath, eimp:format_error(Why)]),
                    pass
-           end;
-       true ->
-           ?DEBUG("Won't call 'convert' for unknown type ~s", [T]),
-           pass
+           end
     end.
 
--spec thumb_el(binary(), binary()) -> xmlel().
-
-thumb_el(Path, URI) ->
-    ContentType = guess_content_type(Path),
-    xmpp:encode(
-      case identify(Path) of
-         {ok, #media_info{height = H, width = W}} ->
-             #thumbnail{'media-type' = ContentType, uri = URI,
-                        height = H, width = W};
-         pass ->
-             #thumbnail{uri = URI, 'media-type' = ContentType}
-      end).
+-spec thumb_el(media_info(), binary()) -> xmlel().
+
+thumb_el(#media_info{type = T, height = H, width = W}, URI) ->
+    MimeType = <<"image/", (atom_to_binary(T, latin1))/binary>>,
+    Thumb = #thumbnail{'media-type' = MimeType, uri = URI,
+                      height = H, width = W},
+    xmpp:encode(Thumb).
 
 %%--------------------------------------------------------------------
 %% Remove user.