berrypod/lib/simpleshop_theme/images/variant_cache.ex

102 lines
2.8 KiB
Elixir
Raw Normal View History

defmodule SimpleshopTheme.Images.VariantCache do
@moduledoc """
Ensures all image variants exist on startup.
This GenServer runs at startup and checks for:
1. Database images with incomplete variants_status or missing disk files
2. Mockup source files missing their generated variants
For any images missing variants, it enqueues Oban jobs to regenerate them.
"""
use GenServer
require Logger
alias SimpleshopTheme.Repo
alias SimpleshopTheme.Media.Image, as: ImageSchema
alias SimpleshopTheme.Images.{Optimizer, OptimizeWorker}
import Ecto.Query
@mockup_dir "priv/static/mockups"
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@impl true
def init(_opts) do
Task.start(fn -> ensure_all_variants() end)
{:ok, %{}}
end
defp ensure_all_variants do
Logger.info("[VariantCache] Checking image variant cache...")
File.mkdir_p!(Optimizer.cache_dir())
ensure_database_image_variants()
ensure_mockup_variants()
end
defp ensure_database_image_variants do
incomplete =
ImageSchema
|> where([i], i.variants_status != "complete" or is_nil(i.variants_status))
|> where([i], i.is_svg == false)
|> Repo.all()
complete_missing =
ImageSchema
|> where([i], i.variants_status == "complete")
|> where([i], i.is_svg == false)
|> where([i], not is_nil(i.source_width))
|> Repo.all()
|> Enum.reject(fn img ->
Optimizer.disk_variants_exist?(img.id, img.source_width)
end)
to_process = incomplete ++ complete_missing
if to_process == [] do
Logger.info("[VariantCache] All database image variants up to date")
else
Logger.info("[VariantCache] Enqueueing #{length(to_process)} database images for processing")
Enum.each(to_process, fn image ->
image
|> ImageSchema.changeset(%{variants_status: "pending"})
|> Repo.update!()
OptimizeWorker.enqueue(image.id)
end)
end
end
defp ensure_mockup_variants do
if File.dir?(@mockup_dir) do
sources =
Path.wildcard(Path.join(@mockup_dir, "*.webp"))
|> Enum.reject(&is_variant?/1)
missing = Enum.reject(sources, &mockup_variants_exist?/1)
if missing == [] do
Logger.info("[VariantCache] All mockup variants up to date")
else
Logger.info("[VariantCache] Enqueueing #{length(missing)} mockups for processing")
Enum.each(missing, &OptimizeWorker.enqueue_mockup/1)
end
end
end
defp is_variant?(path) do
basename = Path.basename(path) |> Path.rootname()
String.match?(basename, ~r/-(400|800|1200|thumb)$/)
end
defp mockup_variants_exist?(source_path) do
basename = Path.basename(source_path) |> Path.rootname()
dir = Path.dirname(source_path)
File.exists?(Path.join(dir, "#{basename}-800.webp"))
end
end