diff options
| -rw-r--r-- | changelog.d/ffmpeg-limiter.add | 1 | ||||
| -rw-r--r-- | lib/pleroma/application.ex | 1 | ||||
| -rw-r--r-- | lib/pleroma/helpers/media_helper.ex | 49 | 
3 files changed, 33 insertions, 18 deletions
diff --git a/changelog.d/ffmpeg-limiter.add b/changelog.d/ffmpeg-limiter.add new file mode 100644 index 000000000..e4a5ef196 --- /dev/null +++ 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 index f2b234022..75154f94c 100644 --- 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 index 7864296fa..e44114d9d 100644 --- 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  | 
