defmodule Eakins.Image.Display do
@moduledoc """
A displayable image instance
When created with a `Eakins.Image.Stored` image, this module creates an image suitable for display
via our image proxy.
"""
defstruct uri: nil,
default?: false,
width: nil,
height: nil,
content_type: nil,
key: nil,
original: nil,
gravity: nil,
aspect: nil
alias Eakins.Image
@type t :: %__MODULE__{
uri: String.t(),
default?: boolean,
width: pos_integer(),
height: pos_integer(),
content_type: String.t(),
original: Eakins.image(),
key: Eakins.key(),
gravity: Eakins.gravity()
}
@imgproxy_gravity_map %{
"n" => "no",
"s" => "so",
"e" => "ea",
"w" => "we",
"ne" => "noea",
"nw" => "nowe",
"se" => "soea",
"sw" => "sowe",
"center" => "ce",
"smart" => "sm"
}
def new(nil, _, _) do
nil
end
def new(%Image.Stored{} = image, aspect, named_height) when is_atom(named_height) do
height = Image.resolve_height(named_height)
new(image, aspect, height)
end
def new(%Image.Stored{} = image, :square, height) when is_integer(height) do
new(image, {1, 1}, height)
end
def new(%Image.Stored{} = image, aspect, height) do
{width, height} = Image.apply_aspect_ratio(aspect, height)
scaled_uri = generate_url(image.key, width, height, image.gravity, :fill, image.uri)
%__MODULE__{
content_type: image.content_type,
default?: image.default?,
height: height,
key: image.key,
original: image,
uri: scaled_uri,
width: width,
gravity: image.gravity,
aspect: aspect
}
end
defp generate_url(key, width, height, gravity, type, source_url) do
extension = extract_extension(key, source_url)
encoded_source_url = Base.url_encode64(source_url, padding: false) <> extension
imgproxy_gravity = Map.fetch!(@imgproxy_gravity_map, gravity)
path =
[
"",
type,
width,
height,
imgproxy_gravity,
1,
encoded_source_url
]
|> Enum.map_join("/", &to_string/1)
sig = sign(path)
imgproxy_scheme() <> "://" <> imgproxy_host() <> "/" <> sig <> path
end
defp extract_extension(key, url) do
uri = URI.parse(url)
extension = Path.extname(uri.path)
key_string = to_string(key)
cond do
String.starts_with?(key_string, "logo_") ->
extension
extension == ".png" ->
".jpg"
true ->
extension
end
end
defp sign(path) do
:hmac
|> :crypto.mac(:sha256, imgproxy_key(), imgproxy_salt() <> path)
|> Base.url_encode64(padding: false)
end
defp imgproxy_host do
:eakins
|> Application.get_env(Eakins, [])
|> Keyword.fetch!(:imgproxy_host)
end
defp imgproxy_key do
:eakins
|> Application.get_env(Eakins, [])
|> Keyword.get(:imgproxy_key, "")
|> Base.decode16!(case: :lower)
end
defp imgproxy_salt do
:eakins
|> Application.get_env(Eakins, [])
|> Keyword.get(:imgproxy_salt, "")
|> Base.decode16!(case: :lower)
end
def imgproxy_scheme do
:eakins
|> Application.get_env(Eakins, [])
|> Keyword.get(:imgproxy_scheme, "https")
end
end