X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fhelpers%2Fmedia_helper.ex;h=cb95d0e68c45dee9c5aa4a02919c4485ddd3f70e;hb=07a48b9293e4046c50b5d424d60a1bf16c7cc198;hp=3256802a043eed23e869e4e3ca182a1a90be6b1a;hpb=2c95533ead56217ec27e09e0ead0050e110dff22;p=akkoma diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index 3256802a0..cb95d0e68 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.MediaHelper do @@ -7,15 +7,26 @@ defmodule Pleroma.Helpers.MediaHelper do Handles common media-related operations. """ - @tmp_base "/tmp/pleroma-media_preview-pipe" + alias Pleroma.HTTP + + require Logger + + def missing_dependencies do + Enum.reduce([imagemagick: "convert", ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc -> + if Pleroma.Utils.command_available?(executable) do + acc + else + [sym | acc] + end + end) + end def image_resize(url, options) do with executable when is_binary(executable) <- System.find_executable("convert"), - {:ok, args} <- prepare_resize_args(options), - url = Pleroma.Web.MediaProxy.url(url), - {:ok, env} <- Pleroma.HTTP.get(url), - {:ok, fifo_path} <- mkfifo() - do + {:ok, args} <- prepare_image_resize_args(options), + {:ok, env} <- HTTP.get(url, [], []), + {:ok, fifo_path} <- mkfifo() do + args = List.flatten([fifo_path, args]) run_fifo(fifo_path, env, executable, args) else nil -> {:error, {:convert, :command_not_found}} @@ -23,36 +34,93 @@ defmodule Pleroma.Helpers.MediaHelper do end end - defp prepare_resize_args(%{max_width: max_width, max_height: max_height} = options) do + defp prepare_image_resize_args( + %{max_width: max_width, max_height: max_height, format: "png"} = options + ) do + quality = options[:quality] || 85 + resize = Enum.join([max_width, "x", max_height, ">"]) + + args = [ + "-resize", + resize, + "-quality", + to_string(quality), + "png:-" + ] + + {:ok, args} + end + + defp prepare_image_resize_args(%{max_width: max_width, max_height: max_height} = options) do quality = options[:quality] || 85 resize = Enum.join([max_width, "x", max_height, ">"]) + args = [ - "-interlace", "Plane", - "-resize", resize, - "-quality", to_string(quality) + "-interlace", + "Plane", + "-resize", + resize, + "-quality", + to_string(quality), + "jpg:-" ] + {:ok, args} end - defp prepare_resize_args(_), do: {:error, :missing_options} + defp prepare_image_resize_args(_), do: {:error, :missing_options} + + # Note: video thumbnail is intentionally not resized (always has original dimensions) + def video_framegrab(url) do + with executable when is_binary(executable) <- System.find_executable("ffmpeg"), + {:ok, env} <- HTTP.get(url, [], []), + {:ok, fifo_path} <- mkfifo(), + args = [ + "-y", + "-i", + fifo_path, + "-vframes", + "1", + "-f", + "mjpeg", + "-loglevel", + "error", + "-" + ] do + run_fifo(fifo_path, env, executable, args) + else + nil -> {:error, {:ffmpeg, :command_not_found}} + {:error, _} = error -> error + end + end defp run_fifo(fifo_path, env, executable, args) do - args = List.flatten([fifo_path, args, "jpg:-"]) - pid = Port.open({:spawn_executable, executable}, [:use_stdio, :stream, :exit_status, :binary, args: args]) - fifo = Port.open(to_charlist(fifo_path), [:eof, :binary, :stream, :out]) - true = Port.command(fifo, env.body) - :erlang.port_close(fifo) + pid = + Port.open({:spawn_executable, executable}, [ + :use_stdio, + :stream, + :exit_status, + :binary, + args: args + ]) + + fifo = File.open!(fifo_path, [:append, :binary]) + fix = Pleroma.Helpers.QtFastStart.fix(env.body) + IO.binwrite(fifo, fix) + File.close(fifo) loop_recv(pid) after File.rm(fifo_path) end - defp mkfifo() do - path = "#{@tmp_base}#{to_charlist(:erlang.phash2(self()))}" + defp mkfifo do + path = Path.join(System.tmp_dir!(), "pleroma-media-preview-pipe-#{Ecto.UUID.generate()}") + case System.cmd("mkfifo", [path]) do {_, 0} -> spawn(fifo_guard(path)) {:ok, path} + {_, err} -> {:error, {:fifo_failed, err}} end @@ -60,8 +128,10 @@ defmodule Pleroma.Helpers.MediaHelper do defp fifo_guard(path) do pid = self() - fn() -> + + fn -> ref = Process.monitor(pid) + receive do {:DOWN, ^ref, :process, ^pid, _} -> File.rm(path) @@ -77,14 +147,16 @@ defmodule Pleroma.Helpers.MediaHelper do receive do {^pid, {:data, data}} -> loop_recv(pid, acc <> data) + {^pid, {:exit_status, 0}} -> {:ok, acc} + {^pid, {:exit_status, status}} -> {:error, status} - after - 5000 -> - :erlang.port_close(pid) - {:error, :timeout} + after + 5000 -> + :erlang.port_close(pid) + {:error, :timeout} end end end