summaryrefslogtreecommitdiff
path: root/lib/pleroma/helpers/media_helper.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma/helpers/media_helper.ex')
-rw-r--r--lib/pleroma/helpers/media_helper.ex173
1 files changed, 49 insertions, 124 deletions
diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex
index 24c845fcd..e44114d9d 100644
--- a/lib/pleroma/helpers/media_helper.ex
+++ b/lib/pleroma/helpers/media_helper.ex
@@ -8,11 +8,14 @@ defmodule Pleroma.Helpers.MediaHelper do
"""
alias Pleroma.HTTP
+ alias Vix.Vips.Operation
require Logger
+ @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
+
def missing_dependencies do
- Enum.reduce([imagemagick: "convert", ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc ->
+ Enum.reduce([ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc ->
if Pleroma.Utils.command_available?(executable) do
acc
else
@@ -22,141 +25,63 @@ defmodule Pleroma.Helpers.MediaHelper do
end
def image_resize(url, options) do
- with executable when is_binary(executable) <- System.find_executable("convert"),
- {:ok, args} <- prepare_image_resize_args(options),
- {:ok, env} <- HTTP.get(url, [], pool: :media),
- {:ok, fifo_path} <- mkfifo() do
- args = List.flatten([fifo_path, args])
- run_fifo(fifo_path, env, executable, args)
+ with {:ok, env} <- HTTP.get(url, [], pool: :media),
+ {:ok, resized} <-
+ Operation.thumbnail_buffer(env.body, options.max_width,
+ height: options.max_height,
+ size: :VIPS_SIZE_DOWN
+ ) do
+ if options[:format] == "png" do
+ Operation.pngsave_buffer(resized, Q: options[:quality])
+ else
+ Operation.jpegsave_buffer(resized, Q: options[:quality], interlace: true)
+ end
else
- nil -> {:error, {:convert, :command_not_found}}
{:error, _} = error -> error
end
end
- 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),
- "jpg:-"
- ]
-
- {:ok, args}
- end
-
- defp prepare_image_resize_args(_), do: {:error, :missing_options}
-
# Note: video thumbnail is intentionally not resized (always has original dimensions)
+ @spec video_framegrab(String.t()) :: {:ok, binary()} | {:error, any()}
def video_framegrab(url) do
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
+ false <- @cachex.exists?(:failed_media_helper_cache, url),
{:ok, env} <- HTTP.get(url, [], pool: :media),
- {:ok, fifo_path} <- mkfifo(),
- args = [
- "-y",
- "-i",
- fifo_path,
- "-vframes",
- "1",
- "-f",
- "mjpeg",
- "-loglevel",
- "error",
- "-"
- ] do
- run_fifo(fifo_path, env, executable, args)
+ {:ok, pid} <- StringIO.open(env.body) do
+ body_stream = IO.binstream(pid, 1)
+
+ task =
+ Task.async(fn ->
+ Exile.stream!(
+ [
+ executable,
+ "-i",
+ "pipe:0",
+ "-vframes",
+ "1",
+ "-f",
+ "mjpeg",
+ "pipe:1"
+ ],
+ input: body_stream,
+ ignore_epipe: true,
+ stderr: :disable
+ )
+ |> Enum.into(<<>>)
+ end)
+
+ case Task.yield(task, 5_000) do
+ nil ->
+ Task.shutdown(task)
+ @cachex.put(:failed_media_helper_cache, url, nil)
+ {:error, {:ffmpeg, :timeout}}
+
+ result ->
+ {:ok, result}
+ end
else
nil -> {:error, {:ffmpeg, :command_not_found}}
{:error, _} = error -> error
end
end
-
- defp run_fifo(fifo_path, env, executable, args) do
- 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])
- fix = Pleroma.Helpers.QtFastStart.fix(env.body)
- true = Port.command(fifo, fix)
- :erlang.port_close(fifo)
- loop_recv(pid)
- after
- File.rm(fifo_path)
- end
-
- 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
- end
-
- defp fifo_guard(path) do
- pid = self()
-
- fn ->
- ref = Process.monitor(pid)
-
- receive do
- {:DOWN, ^ref, :process, ^pid, _} ->
- File.rm(path)
- end
- end
- end
-
- defp loop_recv(pid) do
- loop_recv(pid, <<>>)
- end
-
- defp loop_recv(pid, acc) 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}
- end
- end
end