2020-05-20 17:26:43 +00:00
|
|
|
# Pleroma: A lightweight social networking server
|
2023-01-02 20:38:50 +00:00
|
|
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
2020-05-20 17:26:43 +00:00
|
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
defmodule Pleroma.Helpers.MediaHelper do
|
|
|
|
@moduledoc """
|
|
|
|
Handles common media-related operations.
|
|
|
|
"""
|
|
|
|
|
2020-09-05 13:16:35 +00:00
|
|
|
alias Pleroma.HTTP
|
2022-11-09 18:45:57 +00:00
|
|
|
alias Vix.Vips.Operation
|
2020-09-05 13:16:35 +00:00
|
|
|
|
2020-09-26 16:32:16 +00:00
|
|
|
require Logger
|
|
|
|
|
2024-03-08 15:32:15 +00:00
|
|
|
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
|
|
|
|
2020-09-26 16:32:16 +00:00
|
|
|
def missing_dependencies do
|
2022-11-09 18:45:57 +00:00
|
|
|
Enum.reduce([ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc ->
|
2020-09-26 16:32:16 +00:00
|
|
|
if Pleroma.Utils.command_available?(executable) do
|
|
|
|
acc
|
|
|
|
else
|
|
|
|
[sym | acc]
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2020-08-26 14:12:34 +00:00
|
|
|
def image_resize(url, options) do
|
2022-11-09 18:45:57 +00:00
|
|
|
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
|
2020-08-26 14:12:34 +00:00
|
|
|
else
|
|
|
|
{:error, _} = error -> error
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-09-17 14:13:40 +00:00
|
|
|
# Note: video thumbnail is intentionally not resized (always has original dimensions)
|
2024-03-07 22:38:21 +00:00
|
|
|
@spec video_framegrab(String.t()) :: {:ok, binary()} | {:error, any()}
|
2020-08-27 17:31:55 +00:00
|
|
|
def video_framegrab(url) do
|
|
|
|
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
|
2024-03-08 15:32:15 +00:00
|
|
|
false <- @cachex.exists?(:failed_media_helper_cache, url),
|
2020-09-09 16:30:42 +00:00
|
|
|
{:ok, env} <- HTTP.get(url, [], pool: :media),
|
2024-01-22 15:01:29 +00:00
|
|
|
{:ok, pid} <- StringIO.open(env.body) do
|
|
|
|
body_stream = IO.binstream(pid, 1)
|
|
|
|
|
2024-03-08 15:32:15 +00:00
|
|
|
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}}
|
2024-03-07 22:38:21 +00:00
|
|
|
|
2024-03-08 15:32:15 +00:00
|
|
|
result ->
|
|
|
|
{:ok, result}
|
|
|
|
end
|
2020-08-27 17:31:55 +00:00
|
|
|
else
|
|
|
|
nil -> {:error, {:ffmpeg, :command_not_found}}
|
|
|
|
{:error, _} = error -> error
|
|
|
|
end
|
|
|
|
end
|
2020-05-20 17:26:43 +00:00
|
|
|
end
|