Merge pull request 'Uploads: replace exiftool with the built-in exif_stripper' (#8028) from lambadalambda/pleroma:feature/exif-stripper into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/8028
This commit is contained in:
commit
3e9dc477c9
44 changed files with 518 additions and 381 deletions
|
|
@ -162,38 +162,20 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
)
|
||||
|> Path.expand()
|
||||
|
||||
{strip_uploads_location_message, strip_uploads_location_default} =
|
||||
if Pleroma.Utils.command_available?("exiftool") do
|
||||
{"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n)",
|
||||
"y"}
|
||||
else
|
||||
{"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)",
|
||||
"n"}
|
||||
end
|
||||
|
||||
strip_uploads_location =
|
||||
get_option(
|
||||
options,
|
||||
:strip_uploads_location,
|
||||
strip_uploads_location_message,
|
||||
strip_uploads_location_default
|
||||
"Do you want to strip location (GPS) data from uploaded images? (y/n)",
|
||||
"y"
|
||||
) === "y"
|
||||
|
||||
{read_uploads_description_message, read_uploads_description_default} =
|
||||
if Pleroma.Utils.command_available?("exiftool") do
|
||||
{"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as installed. (y/n)",
|
||||
"y"}
|
||||
else
|
||||
{"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)",
|
||||
"n"}
|
||||
end
|
||||
|
||||
read_uploads_description =
|
||||
get_option(
|
||||
options,
|
||||
:read_uploads_description,
|
||||
read_uploads_description_message,
|
||||
read_uploads_description_default
|
||||
"Do you want to read metadata from uploaded images so clients can prefill the media description field? (y/n)",
|
||||
"y"
|
||||
) === "y"
|
||||
|
||||
anonymize_uploads =
|
||||
|
|
@ -324,14 +306,14 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
defp upload_filters(filters) when is_map(filters) do
|
||||
enabled_filters =
|
||||
if filters.strip_location do
|
||||
[Pleroma.Upload.Filter.Exiftool.StripLocation]
|
||||
[Pleroma.Upload.Filter.Exif.StripLocation]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
enabled_filters =
|
||||
if filters.read_description do
|
||||
enabled_filters ++ [Pleroma.Upload.Filter.Exiftool.ReadDescription]
|
||||
enabled_filters ++ [Pleroma.Upload.Filter.Exif.ReadDescription]
|
||||
else
|
||||
enabled_filters
|
||||
end
|
||||
|
|
|
|||
|
|
@ -168,8 +168,6 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
|
||||
defp check_system_commands!(:ok) do
|
||||
filter_commands_statuses = [
|
||||
check_filter(Pleroma.Upload.Filter.Exiftool.StripLocation, "exiftool"),
|
||||
check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"),
|
||||
check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "ffprobe")
|
||||
|
|
|
|||
|
|
@ -22,40 +22,44 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
|
||||
@logger_formatter_config_knobs [:colors, :format, :metadata, :truncate, :utc_log]
|
||||
|
||||
@exiftool_filter_replacements %{
|
||||
Pleroma.Upload.Filter.Exiftool => Pleroma.Upload.Filter.Exif.StripLocation,
|
||||
Pleroma.Upload.Filter.Exiftool.StripLocation => Pleroma.Upload.Filter.Exif.StripLocation,
|
||||
Pleroma.Upload.Filter.Exiftool.ReadDescription => Pleroma.Upload.Filter.Exif.ReadDescription
|
||||
}
|
||||
|
||||
def check_exiftool_filter do
|
||||
filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, [])
|
||||
|
||||
if Pleroma.Upload.Filter.Exiftool in filters do
|
||||
Logger.warning("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later:
|
||||
deprecated_filters =
|
||||
filters
|
||||
|> Enum.filter(&Map.has_key?(@exiftool_filter_replacements, &1))
|
||||
|> Enum.uniq()
|
||||
|
||||
```
|
||||
config :pleroma, Pleroma.Upload,
|
||||
filters: [Pleroma.Upload.Filter.Exiftool]
|
||||
```
|
||||
case deprecated_filters do
|
||||
[] ->
|
||||
:ok
|
||||
|
||||
Is now
|
||||
deprecated_filters ->
|
||||
replacements =
|
||||
Enum.map_join(deprecated_filters, fn filter ->
|
||||
"\n* `#{inspect(filter)}` is now `#{inspect(@exiftool_filter_replacements[filter])}`"
|
||||
end)
|
||||
|
||||
Logger.warning("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using Exiftool upload filters. Pleroma no longer needs exiftool, the filters were replaced by built-in ones. The old names should work for now, but you are advised to change to the new configuration to prevent possible issues later:
|
||||
#{replacements}
|
||||
""")
|
||||
|
||||
```
|
||||
config :pleroma, Pleroma.Upload,
|
||||
filters: [Pleroma.Upload.Filter.Exiftool.StripLocation]
|
||||
```
|
||||
""")
|
||||
new_config =
|
||||
filters
|
||||
|> Enum.map(&Map.get(@exiftool_filter_replacements, &1, &1))
|
||||
|> Enum.uniq()
|
||||
|
||||
new_config =
|
||||
filters
|
||||
|> Enum.map(fn
|
||||
Pleroma.Upload.Filter.Exiftool -> Pleroma.Upload.Filter.Exiftool.StripLocation
|
||||
filter -> filter
|
||||
end)
|
||||
Config.put([Pleroma.Upload, :filters], new_config)
|
||||
|
||||
Config.put([Pleroma.Upload, :filters], new_config)
|
||||
|
||||
:error
|
||||
else
|
||||
:ok
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
32
lib/pleroma/upload/filter/exif/read_description.ex
Normal file
32
lib/pleroma/upload/filter/exif/read_description.ex
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Upload.Filter.Exif.ReadDescription do
|
||||
@moduledoc """
|
||||
Reads a description from image metadata when the upload has none yet.
|
||||
|
||||
Checks EXIF ImageDescription, then IPTC Caption-Abstract (and XMP for GIF). Blank
|
||||
values and values longer than `[:instance, :description_limit]` are ignored.
|
||||
"""
|
||||
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
@impl true
|
||||
def filter(%Pleroma.Upload{description: description}) when is_binary(description),
|
||||
do: {:ok, :noop}
|
||||
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _} = upload) do
|
||||
max_length = Pleroma.Config.get([:instance, :description_limit])
|
||||
|
||||
case ExifStripper.Description.read_file(file, max_length: max_length) do
|
||||
description when is_binary(description) ->
|
||||
{:ok, :filtered, %{upload | description: description}}
|
||||
|
||||
nil ->
|
||||
{:ok, :noop}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(_), do: {:ok, :noop}
|
||||
end
|
||||
25
lib/pleroma/upload/filter/exif/strip_location.ex
Normal file
25
lib/pleroma/upload/filter/exif/strip_location.ex
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Upload.Filter.Exif.StripLocation do
|
||||
@moduledoc """
|
||||
Strips GPS metadata from uploaded images and overwrites the file in place.
|
||||
|
||||
Supports JPEG, PNG, WebP, TIFF and ISO-BMFF images (HEIC, AVIF). This is lossless:
|
||||
the image data is not re-encoded. Other image types are left untouched.
|
||||
"""
|
||||
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
@impl true
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
|
||||
case ExifStripper.GpsStripper.strip_gps_data(file, file) do
|
||||
:ok -> {:ok, :filtered}
|
||||
:noop -> {:ok, :noop}
|
||||
{:error, _} = error -> error
|
||||
end
|
||||
end
|
||||
|
||||
def filter(_), do: {:ok, :noop}
|
||||
end
|
||||
14
lib/pleroma/upload/filter/exiftool.ex
Normal file
14
lib/pleroma/upload/filter/exiftool.ex
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Upload.Filter.Exiftool do
|
||||
@moduledoc """
|
||||
Deprecated: use `Pleroma.Upload.Filter.Exif.StripLocation`. This module delegates to it.
|
||||
"""
|
||||
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
@impl true
|
||||
defdelegate filter(upload), to: Pleroma.Upload.Filter.Exif.StripLocation
|
||||
end
|
||||
|
|
@ -4,51 +4,11 @@
|
|||
|
||||
defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do
|
||||
@moduledoc """
|
||||
Gets a valid description from the related EXIF tags and provides them in the response if no description is provided yet.
|
||||
It will first check ImageDescription, when that doesn't probide a valid description, it will check iptc:Caption-Abstract.
|
||||
A valid description means the fields are filled in and not too long (see `:instance, :description_limit`).
|
||||
Deprecated: use `Pleroma.Upload.Filter.Exif.ReadDescription`. This module delegates to it.
|
||||
"""
|
||||
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
def filter(%Pleroma.Upload{description: description})
|
||||
when is_binary(description),
|
||||
do: {:ok, :noop}
|
||||
|
||||
def filter(%Pleroma.Upload{tempfile: file} = upload),
|
||||
do: {:ok, :filtered, upload |> Map.put(:description, read_description_from_exif_data(file))}
|
||||
|
||||
def filter(_, _), do: {:ok, :noop}
|
||||
|
||||
defp read_description_from_exif_data(file) do
|
||||
nil
|
||||
|> read_when_empty(file, "-ImageDescription")
|
||||
|> read_when_empty(file, "-iptc:Caption-Abstract")
|
||||
end
|
||||
|
||||
defp read_when_empty(current_description, _, _) when is_binary(current_description),
|
||||
do: current_description
|
||||
|
||||
defp read_when_empty(_, file, tag) do
|
||||
if File.exists?(file) do
|
||||
try do
|
||||
{tag_content, 0} =
|
||||
System.cmd("exiftool", ["-m", "-b", "-s3", tag, file],
|
||||
stderr_to_stdout: false,
|
||||
parallelism: true
|
||||
)
|
||||
|
||||
tag_content = String.trim(tag_content)
|
||||
|
||||
if tag_content != "" and
|
||||
String.length(tag_content) <=
|
||||
Pleroma.Config.get([:instance, :description_limit]),
|
||||
do: tag_content,
|
||||
else: nil
|
||||
rescue
|
||||
_ in ErlangError -> nil
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
@impl true
|
||||
defdelegate filter(upload), to: Pleroma.Upload.Filter.Exif.ReadDescription
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,30 +4,11 @@
|
|||
|
||||
defmodule Pleroma.Upload.Filter.Exiftool.StripLocation do
|
||||
@moduledoc """
|
||||
Strips GPS related EXIF tags and overwrites the file in place.
|
||||
Also strips or replaces filesystem metadata e.g., timestamps.
|
||||
Deprecated: use `Pleroma.Upload.Filter.Exif.StripLocation`. This module delegates to it.
|
||||
"""
|
||||
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
# Formats not compatible with exiftool at this time
|
||||
def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop}
|
||||
def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop}
|
||||
def filter(%Pleroma.Upload{content_type: "image/svg" <> _}), do: {:ok, :noop}
|
||||
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
|
||||
try do
|
||||
case System.cmd("exiftool", ["-m", "-overwrite_original", "-gps:all=", "-png:all=", file],
|
||||
stderr_to_stdout: true,
|
||||
parallelism: true
|
||||
) do
|
||||
{_response, 0} -> {:ok, :filtered}
|
||||
{error, _} -> {:error, error}
|
||||
end
|
||||
rescue
|
||||
e in ErlangError ->
|
||||
{:error, "#{__MODULE__}: #{inspect(e)}"}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(_), do: {:ok, :noop}
|
||||
@impl true
|
||||
defdelegate filter(upload), to: Pleroma.Upload.Filter.Exif.StripLocation
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue