logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git
commit: 71a03732327409fca07c83da35e372307223f515
parent d80e0d6873a61159a68b337d7f42f46cbdad8e9d
Author: Haelwenn <contact+git.pleroma.social@hacktivis.me>
Date:   Wed, 17 Apr 2024 05:47:54 +0000

Merge branch 'ffmpeg-limiter' into 'develop'

Prevent Media Helper from respawning ffmpeg for bad media

See merge request pleroma/pleroma!4086

Diffstat:

Achangelog.d/ffmpeg-limiter.add1+
Mlib/pleroma/application.ex1+
Mlib/pleroma/helpers/media_helper.ex49+++++++++++++++++++++++++++++++------------------
3 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/changelog.d/ffmpeg-limiter.add b/changelog.d/ffmpeg-limiter.add @@ -0,0 +1 @@ +Framegrabs with ffmpeg will execute with a 5 second timeout and cache the URLs of failures with a TTL of 15 minutes to prevent excessive retries. diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex @@ -156,6 +156,7 @@ defmodule Pleroma.Application do build_cachex("web_resp", limit: 2500), build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10), build_cachex("failed_proxy_url", limit: 2500), + build_cachex("failed_media_helper_url", default_ttl: :timer.minutes(15), limit: 2_500), build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000), build_cachex("chat_message_id_idempotency_key", expiration: chat_message_id_idempotency_key_expiration(), diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex @@ -12,6 +12,8 @@ defmodule Pleroma.Helpers.MediaHelper do require Logger + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + def missing_dependencies do Enum.reduce([ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc -> if Pleroma.Utils.command_available?(executable) do @@ -43,29 +45,40 @@ defmodule Pleroma.Helpers.MediaHelper do @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, pid} <- StringIO.open(env.body) do body_stream = IO.binstream(pid, 1) - result = - Exile.stream!( - [ - executable, - "-i", - "pipe:0", - "-vframes", - "1", - "-f", - "mjpeg", - "pipe:1" - ], - input: body_stream, - ignore_epipe: true, - stderr: :disable - ) - |> Enum.into(<<>>) + 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}} - {:ok, result} + result -> + {:ok, result} + end else nil -> {:error, {:ffmpeg, :command_not_found}} {:error, _} = error -> error