defmodule SimpleshopTheme.Media do @moduledoc """ The Media context for managing images and file uploads. """ import Ecto.Query, warn: false alias SimpleshopTheme.Repo alias SimpleshopTheme.Media.Image, as: ImageSchema @thumbnail_size 200 @doc """ Uploads an image and stores it in the database. Automatically generates a thumbnail for non-SVG images if the Image library is available and working. ## Examples iex> upload_image(%{image_type: "logo", filename: "logo.png", ...}) {:ok, %Image{}} """ def upload_image(attrs) do attrs = maybe_generate_thumbnail(attrs) %ImageSchema{} |> ImageSchema.changeset(attrs) |> Repo.insert() end @doc """ Uploads an image from a LiveView upload entry. This handles consuming the upload and extracting metadata from the entry. ## Examples iex> upload_from_entry(socket, :logo_upload, fn path, entry -> ... end) {:ok, %Image{}} """ def upload_from_entry(path, entry, image_type) do file_binary = File.read!(path) upload_image(%{ image_type: image_type, filename: entry.client_name, content_type: entry.client_type, file_size: entry.client_size, data: file_binary }) end defp maybe_generate_thumbnail(%{data: data, content_type: content_type} = attrs) when is_binary(data) do if String.starts_with?(content_type || "", "image/svg") do attrs else case generate_thumbnail(data) do {:ok, thumbnail_data} -> Map.put(attrs, :thumbnail_data, thumbnail_data) {:error, _reason} -> attrs end end end defp maybe_generate_thumbnail(attrs), do: attrs defp generate_thumbnail(image_data) do try do with {:ok, image} <- Image.from_binary(image_data), {:ok, thumbnail} <- Image.thumbnail(image, @thumbnail_size), {:ok, binary} <- Image.write(thumbnail, :memory, suffix: ".jpg") do {:ok, binary} end rescue _e -> {:error, :thumbnail_generation_failed} end end @doc """ Gets a single image by ID. ## Examples iex> get_image(id) %Image{} iex> get_image("nonexistent") nil """ def get_image(id) do Repo.get(ImageSchema, id) end @doc """ Gets the current logo image. ## Examples iex> get_logo() %Image{} """ def get_logo do Repo.one(from i in ImageSchema, where: i.image_type == "logo", order_by: [desc: i.inserted_at], limit: 1) end @doc """ Gets the current header image. ## Examples iex> get_header() %Image{} """ def get_header do Repo.one(from i in ImageSchema, where: i.image_type == "header", order_by: [desc: i.inserted_at], limit: 1) end @doc """ Deletes an image. ## Examples iex> delete_image(image) {:ok, %Image{}} """ def delete_image(%ImageSchema{} = image) do Repo.delete(image) end @doc """ Lists all images of a specific type. ## Examples iex> list_images_by_type("logo") [%Image{}, ...] """ def list_images_by_type(type) do Repo.all(from i in ImageSchema, where: i.image_type == ^type, order_by: [desc: i.inserted_at]) end end