diff --git a/lib/music_library/assets.ex b/lib/music_library/assets.ex index cfec2bbb..38ba91d2 100644 --- a/lib/music_library/assets.ex +++ b/lib/music_library/assets.ex @@ -2,9 +2,22 @@ defmodule MusicLibrary.Assets do alias MusicLibrary.Assets.Asset alias MusicLibrary.Repo + @doc """ + Store any file type - the responsibility to correctly populate format and + properties is left to the caller. + """ def store(params) do %Asset{} |> Asset.changeset(params) |> Repo.insert() end + + @doc """ + Store image files - properties will be computed automatically. + """ + def store_image(params) do + %Asset{} + |> Asset.image_changeset(params) + |> Repo.insert() + end end diff --git a/lib/music_library/assets/asset.ex b/lib/music_library/assets/asset.ex index 69ead037..17b28239 100644 --- a/lib/music_library/assets/asset.ex +++ b/lib/music_library/assets/asset.ex @@ -3,6 +3,8 @@ defmodule MusicLibrary.Assets.Asset do import Ecto.Changeset + alias Vix.Vips.Image + @primary_key {:hash, :string, autogenerate: false} schema "assets" do field :content, :binary @@ -20,6 +22,15 @@ defmodule MusicLibrary.Assets.Asset do |> unique_constraint(:hash) end + def image_changeset(asset, attrs) do + asset + |> cast(attrs, [:content, :format]) + |> validate_required([:content, :format]) + |> generate_hash() + |> generate_properties() + |> unique_constraint(:hash) + end + defp generate_hash(changeset) do case get_change(changeset, :content) do nil -> @@ -30,6 +41,25 @@ defmodule MusicLibrary.Assets.Asset do end end + defp generate_properties(changeset) do + case get_change(changeset, :content) do + nil -> + changeset + + content -> + put_change(changeset, :properties, get_image_properties(content)) + end + end + + defp get_image_properties(content) do + {:ok, image} = Image.new_from_buffer(content) + + %{ + "width" => Image.width(image), + "height" => Image.height(image) + } + end + defp hash(content) do :crypto.hash(:sha256, content) |> Base.encode16() end diff --git a/test/music_library/assets_test.exs b/test/music_library/assets_test.exs index ac1efb63..4ccad283 100644 --- a/test/music_library/assets_test.exs +++ b/test/music_library/assets_test.exs @@ -2,6 +2,7 @@ defmodule MusicLibrary.AssetsTest do use MusicLibrary.DataCase alias MusicLibrary.Assets + alias MusicLibrary.Fixtures describe "store/1" do test "stores using the hash as key" do @@ -31,4 +32,19 @@ defmodule MusicLibrary.AssetsTest do assert [hash: {"has already been taken", _}] = changeset.errors end end + + describe "store_image/1" do + test "computes properties automatically" do + params = %{ + content: Fixtures.Records.marbles_cover_data(), + format: "image/jpeg" + } + + assert {:ok, asset} = Assets.store_image(params) + assert asset.hash == "599407DDF69907D4A60FE13CCAA824D25CF08DC124FD6AA3E8E7ECD98C885FFE" + assert asset.properties == %{"width" => 400, "height" => 396} + assert asset.content == Fixtures.Records.marbles_cover_data() + assert asset.format == "image/jpeg" + end + end end