philomena/lib/philomena/images/thumbnailer.ex
2019-12-07 00:49:20 -05:00

122 lines
No EOL
3.3 KiB
Elixir

defmodule Philomena.Images.Thumbnailer do
@moduledoc """
Prevewing and thumbnailing logic for Images.
"""
alias Philomena.DuplicateReports
alias Philomena.ImageIntensities
alias Philomena.Images.Image
alias Philomena.Processors
alias Philomena.Analyzers
alias Philomena.Sha512
alias Philomena.Repo
@versions [
thumb_tiny: {50, 50},
thumb_small: {150, 150},
thumb: {250, 250},
small: {320, 240},
medium: {800, 600},
large: {1280, 1024},
tall: {1024, 4096},
full: nil
]
def generate_thumbnails(image_id) do
image = Repo.get!(Image, image_id)
file = image_file(image)
{:ok, analysis} = Analyzers.analyze(file)
apply_edit_script(image, Processors.process(analysis, file, @versions))
recompute_sha512(image, file, &Image.thumbnail_changeset/2)
generate_dupe_reports(image)
apply_edit_script(image, Processors.post_process(analysis, file))
recompute_sha512(image, file, &Image.process_changeset/2)
end
defp apply_edit_script(image, changes),
do: Enum.map(changes, &apply_change(image, &1))
defp apply_change(image, {:intensities, intensities}),
do: ImageIntensities.create_image_intensity(image, intensities)
defp apply_change(image, {:replace_original, new_file}),
do: copy(new_file, image_file(image))
defp apply_change(image, {:thumbnails, thumbnails}),
do: Enum.map(thumbnails, &apply_thumbnail(image, image_thumb_dir(image), &1))
defp apply_thumbnail(_image, thumb_dir, {:copy, new_file, destination}),
do: copy(new_file, Path.join(thumb_dir, destination))
defp apply_thumbnail(image, thumb_dir, {:symlink_original, destination}),
do: symlink(image_file(image), Path.join(thumb_dir, destination))
defp recompute_sha512(image, file, changeset_fn),
do: Repo.update!(changeset_fn.(image, %{"image_sha512_hash" => Sha512.file(file)}))
defp generate_dupe_reports(image),
do: DuplicateReports.generate_reports(image)
# Copy from source to destination, creating parent directories along
# the way and setting the appropriate permission bits when necessary.
defp copy(source, destination) do
prepare_dir(destination)
File.rm(destination)
File.cp!(source, destination)
set_perms(destination)
end
# Try to handle filesystems that don't support symlinks
# by falling back to a copy.
defp symlink(source, destination) do
source = Path.absname(source)
prepare_dir(destination)
case File.ln_s(source, destination) do
:ok ->
set_perms(destination)
_err ->
copy(source, destination)
end
end
# 0o644 = (S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH
defp set_perms(destination),
do: File.chmod(destination, 0o644)
# Prepare the directory by creating it if it does not yet exist.
defp prepare_dir(destination) do
destination
|> Path.dirname()
|> File.mkdir_p!()
end
defp image_file(%Image{image: image}),
do: Path.join(image_file_root(), image)
defp image_thumb_dir(%Image{created_at: created_at, id: id}),
do: Path.join([image_thumbnail_root(), time_identifier(created_at), to_string(id)])
defp time_identifier(time),
do: Enum.join([time.year, time.month, time.day], "/")
defp image_file_root,
do: Application.get_env(:philomena, :image_file_root)
defp image_thumbnail_root,
do: Application.get_env(:philomena, :image_file_root) <> "/thumbs"
end