From 56db3807aa6ac63279cf06e1e42677d4890c1960 Mon Sep 17 00:00:00 2001 From: "byte[]" Date: Mon, 25 Nov 2019 22:47:57 -0500 Subject: [PATCH] all processing code --- lib/philomena/application.ex | 1 + lib/philomena/image_intensities.ex | 17 +-- .../image_intensities/image_intensity.ex | 4 +- lib/philomena/images/image.ex | 18 +++ lib/philomena/processors.ex | 112 ++++++++++++++++-- lib/philomena/servers/image_processor.ex | 33 ++++++ 6 files changed, 156 insertions(+), 29 deletions(-) create mode 100644 lib/philomena/servers/image_processor.ex diff --git a/lib/philomena/application.ex b/lib/philomena/application.ex index e7e3c37c..2699cf26 100644 --- a/lib/philomena/application.ex +++ b/lib/philomena/application.ex @@ -15,6 +15,7 @@ defmodule Philomena.Application do # Starts a worker by calling: Philomena.Worker.start_link(arg) # {Philomena.Worker, arg}, Pow.Store.Backend.MnesiaCache, + Philomena.Servers.ImageProcessor, {Redix, name: :redix} ] diff --git a/lib/philomena/image_intensities.ex b/lib/philomena/image_intensities.ex index 4b0bc73d..5a4d130b 100644 --- a/lib/philomena/image_intensities.ex +++ b/lib/philomena/image_intensities.ex @@ -8,19 +8,6 @@ defmodule Philomena.ImageIntensities do alias Philomena.ImageIntensities.ImageIntensity - @doc """ - Returns the list of image_intensities. - - ## Examples - - iex> list_image_intensities() - [%ImageIntensity{}, ...] - - """ - def list_image_intensities do - Repo.all(ImageIntensity) - end - @doc """ Gets a single image_intensity. @@ -49,8 +36,8 @@ defmodule Philomena.ImageIntensities do {:error, %Ecto.Changeset{}} """ - def create_image_intensity(attrs \\ %{}) do - %ImageIntensity{} + def create_image_intensity(image, attrs \\ %{}) do + %ImageIntensity{image_id: image.id} |> ImageIntensity.changeset(attrs) |> Repo.insert() end diff --git a/lib/philomena/image_intensities/image_intensity.ex b/lib/philomena/image_intensities/image_intensity.ex index 2ab93a6d..83720c63 100644 --- a/lib/philomena/image_intensities/image_intensity.ex +++ b/lib/philomena/image_intensities/image_intensity.ex @@ -18,7 +18,7 @@ defmodule Philomena.ImageIntensities.ImageIntensity do @doc false def changeset(image_intensity, attrs) do image_intensity - |> cast(attrs, []) - |> validate_required([]) + |> cast(attrs, [:nw, :ne, :sw, :se]) + |> validate_required([:image_id, :nw, :ne, :sw, :se]) end end diff --git a/lib/philomena/images/image.ex b/lib/philomena/images/image.ex index e7894f6e..b76b98f1 100644 --- a/lib/philomena/images/image.ex +++ b/lib/philomena/images/image.ex @@ -108,6 +108,12 @@ defmodule Philomena.Images.Image do |> validate_required([]) end + def creation_changeset(image, attrs, attribution) do + image + |> cast(attrs, [:source_url, :description]) + |> change(attribution) + end + def image_changeset(image, attrs) do image |> cast(attrs, [ @@ -140,4 +146,16 @@ defmodule Philomena.Images.Image do |> TagDiffer.diff_input(old_tags, new_tags) |> TagValidator.validate_tags() end + + def thumbnail_changeset(image, attrs) do + image + |> cast(attrs, [:image_sha512_hash]) + |> change(thumbnails_generated: true, duplication_checked: true) + end + + def process_changeset(image, attrs) do + image + |> cast(attrs, [:image_sha512_hash]) + |> change(processed: true) + end end diff --git a/lib/philomena/processors.ex b/lib/philomena/processors.ex index 18096f2e..ef14e75f 100644 --- a/lib/philomena/processors.ex +++ b/lib/philomena/processors.ex @@ -1,7 +1,10 @@ defmodule Philomena.Processors do - # alias Philomena.Images.Image - # alias Philomena.Repo + alias Philomena.Images.Image + alias Philomena.ImageIntensities + alias Philomena.Repo + alias Philomena.Mime alias Philomena.Sha512 + alias Philomena.Servers.ImageProcessor @mimes %{ "image/gif" => "image/gif", @@ -29,7 +32,90 @@ defmodule Philomena.Processors do "video/webm" => Philomena.Processors.Webm } - def analysis_to_changes(analysis, file, upload_name) do + def after_upload(image, params) do + with upload when not is_nil(upload) <- params["image"], + file <- upload.path, + mime <- @mimes[Mime.file(file)], + analyzer when not is_nil(analyzer) <- @analyzers[mime], + analysis <- analyzer.analyze(file), + changes <- analysis_to_changes(analysis, file, upload.filename) + do + image + |> Image.image_changeset(changes) + else + _ -> + image + |> Image.image_changeset(%{}) + end + end + + def after_insert(image) do + File.cp!(image.uploaded_image, Path.join([image_file_root(), image.image])) + ImageProcessor.cast(self(), image.id) + end + + def process_image(image_id) do + image = Repo.get!(Image, image_id) + + mime = image.image_mime_type + file = image_file(image) + analyzer = @analyzers[mime] + analysis = analyzer.analyze(file) + processor = @processors[mime] + process = processor.process(analysis, file) + + apply_edit_script(image, process) + sha512 = Sha512.file(file) + changeset = Image.thumbnail_changeset(image, %{"image_sha512_hash" => sha512}) + image = Repo.update!(changeset) + + processor.post_process(analysis, file) + sha512 = Sha512.file(file) + changeset = Image.process_changeset(image, %{"image_sha512_hash" => sha512}) + Repo.update!(changeset) + end + + defp apply_edit_script(image, changes) do + for change <- changes do + apply_change(image, change) + end + end + + defp apply_change(image, {:intensities, intensities}) do + ImageIntensities.create_image_intensity(image, intensities) + end + + defp apply_change(image, {:replace_original, new_file}) do + file = image_file(image) + + File.cp(new_file, file) + File.chmod(file, 0o755) + end + + defp apply_change(image, {:thumbnails, thumbnails}) do + thumb_dir = image_thumb_dir(image) + + for thumbnail <- thumbnails do + apply_thumbnail(image, thumb_dir, thumbnail) + end + end + + defp apply_thumbnail(_image, thumb_dir, {:copy, new_file, destination}) do + new_destination = Path.join([thumb_dir, destination]) + + File.cp(new_file, new_destination) + File.chmod(new_destination, 0o755) + end + + defp apply_thumbnail(image, thumb_dir, {:symlink_original, destination}) do + file = image_file(image) + new_destination = Path.join([thumb_dir, destination]) + + File.ln_s(file, new_destination) + File.chmod(new_destination, 0o755) + end + + defp analysis_to_changes(analysis, file, upload_name) do {width, height} = analysis.dimensions %{size: size} = File.stat(file) sha512 = Sha512.file(file) @@ -50,16 +136,20 @@ defmodule Philomena.Processors do } end - def after_upload(image) do - File.cp(image.uploaded_image, Path.join([image_file_root(), image.image])) - end - defp aspect_ratio(_, 0), do: 0.0 defp aspect_ratio(w, h), do: w / h + defp image_file(image) do + Path.join([image_file_root(), image.image]) + end + + defp image_thumb_dir(image) do + Path.join([image_thumbnail_root(), time_identifier(image.created_at), to_string(image.id)]) + end + defp build_filename(extension) do [ - time_identifier(), + time_identifier(DateTime.utc_now()), "/", usec_identifier(), pid_identifier(), @@ -69,10 +159,8 @@ defmodule Philomena.Processors do |> Enum.join() end - defp time_identifier do - now = DateTime.utc_now() - - Enum.join([now.year, now.month, now.day], "/") + defp time_identifier(time) do + Enum.join([time.year, time.month, time.day], "/") end defp usec_identifier do diff --git a/lib/philomena/servers/image_processor.ex b/lib/philomena/servers/image_processor.ex new file mode 100644 index 00000000..96deca53 --- /dev/null +++ b/lib/philomena/servers/image_processor.ex @@ -0,0 +1,33 @@ +defmodule Philomena.Servers.ImageProcessor do + use GenServer + + def start_link(default) when is_list(default) do + GenServer.start_link(__MODULE__, default) + end + + def cast(pid, image_id) do + GenServer.cast(pid, {:enqueue, image_id}) + end + + @impl true + def init([]) do + {:ok, []} + end + + @impl true + def handle_cast({:enqueue, image_id}, []) do + # Ensure that tempfiles get cleaned up by reaping + # the process after it is done + Task.async(fn -> process(image_id) end) + |> Task.await() + + {:noreply, []} + end + + defp process(image_id) do + Philomena.Processors.process_image(image_id) + #rescue + # _ -> + # nil + end +end \ No newline at end of file