Rename image* to cover*

This commit is contained in:
Claudio Ortolina
2024-10-02 16:33:42 +01:00
parent fb978406cd
commit 1e0d5486e0
13 changed files with 86 additions and 71 deletions
+8 -8
View File
@@ -4,7 +4,7 @@ defmodule MusicLibrary.Records do
alias MusicLibrary.Records.{MusicBrainz, Record} alias MusicLibrary.Records.{MusicBrainz, Record}
@fields [:id, :type, :format, :title, :release, :genres, :musicbrainz_id, :image_data_hash] @fields [:id, :type, :format, :title, :release, :genres, :musicbrainz_id, :cover_hash]
def list_records(opts \\ []) do def list_records(opts \\ []) do
limit = Keyword.get(opts, :limit, 20) limit = Keyword.get(opts, :limit, 20)
@@ -59,11 +59,11 @@ defmodule MusicLibrary.Records do
Repo.one!(q) Repo.one!(q)
end end
def get_image!(id) do def get_cover!(id) do
q = q =
from r in Record, from r in Record,
where: r.id == ^id, where: r.id == ^id,
select: %{image_data: r.image_data, image_data_hash: r.image_data_hash} select: %{cover_data: r.cover_data, cover_hash: r.cover_hash}
Repo.one!(q) Repo.one!(q)
end end
@@ -77,15 +77,15 @@ defmodule MusicLibrary.Records do
def import_from_musicbrainz(musicbrainz_id, opts \\ []) do def import_from_musicbrainz(musicbrainz_id, opts \\ []) do
with format = Keyword.get(opts, :format, "cd"), with format = Keyword.get(opts, :format, "cd"),
{:ok, release_group} <- MusicBrainz.get_release_group(musicbrainz_id), {:ok, release_group} <- MusicBrainz.get_release_group(musicbrainz_id),
{:ok, image_data} <- MusicBrainz.get_cover_art(musicbrainz_id), {:ok, cover_data} <- MusicBrainz.get_cover_art(musicbrainz_id),
record_params = build_record_params(release_group, image_data, format) do record_params = build_record_params(release_group, cover_data, format) do
create_record(record_params) create_record(record_params)
else else
error -> error error -> error
end end
end end
defp build_record_params(release_group, image_data, format) do defp build_record_params(release_group, cover_data, format) do
musicbrainz_id = release_group["id"] musicbrainz_id = release_group["id"]
artists_attrs = artists_attrs =
@@ -108,8 +108,8 @@ defmodule MusicLibrary.Records do
"type" => parse_subtype(release_group["primary-type"]), "type" => parse_subtype(release_group["primary-type"]),
"format" => format, "format" => format,
"genres" => Enum.map(release_group["genres"], fn g -> g["name"] end), "genres" => Enum.map(release_group["genres"], fn g -> g["name"] end),
"image_url" => "https://coverartarchive.org/release-group/#{musicbrainz_id}/front", "cover_url" => "https://coverartarchive.org/release-group/#{musicbrainz_id}/front",
"image_data" => image_data "cover_data" => cover_data
} }
end end
+22 -22
View File
@@ -81,50 +81,50 @@ defmodule MusicLibrary.Records.Importer do
end end
@doc """ @doc """
Pull the cover image from the stored url and keep a local, resized copy in Pull the cover from the stored url and keep a local, resized copy in
the database for fast access/use. the database for fast access/use.
""" """
def import_cover_image(record) do def import_cover(record) do
with {:ok, image_data} <- blob_get(record.image_url) do with {:ok, cover_data} <- blob_get(record.cover_url) do
{:ok, thumb} = Vix.Vips.Operation.thumbnail_buffer(image_data, 400) {:ok, thumb} = Vix.Vips.Operation.thumbnail_buffer(cover_data, 400)
{:ok, thumb_data} = Vix.Vips.Image.write_to_buffer(thumb, ".jpg") {:ok, thumb_data} = Vix.Vips.Image.write_to_buffer(thumb, ".jpg")
record record
|> Rec.add_image_data(thumb_data) |> Rec.add_cover_data(thumb_data)
|> Repo.update!() |> Repo.update!()
end end
end end
@doc """ @doc """
Given an already stored image in the database, resize it to a 400px wide thumbnail. Given an already stored cover, resize it to a 400px wide thumbnail.
""" """
def resize_cover_image(record) do def resize_cover(record) do
{:ok, thumb} = Vix.Vips.Operation.thumbnail_buffer(record.image_data, 400) {:ok, thumb} = Vix.Vips.Operation.thumbnail_buffer(record.cover_data, 400)
{:ok, thumb_data} = Vix.Vips.Image.write_to_buffer(thumb, ".jpg") {:ok, thumb_data} = Vix.Vips.Image.write_to_buffer(thumb, ".jpg")
record record
|> Rec.add_image_data(thumb_data) |> Rec.add_cover_data(thumb_data)
|> Repo.update!() |> Repo.update!()
end end
def import_all_cover_images do def import_all_covers do
Rec Rec
|> Repo.all() |> Repo.all()
|> Enum.each(fn r -> |> Enum.each(fn r ->
if r.image_data == nil do if r.cover_data == nil do
import_cover_image(r) import_cover(r)
IO.puts("Imported cover image for #{r.title}") IO.puts("Imported cover for #{r.title}")
end end
end) end)
end end
def resize_all_cover_images do def resize_all_covers do
Rec Rec
|> Repo.all() |> Repo.all()
|> Enum.each(fn r -> |> Enum.each(fn r ->
if r.image_data != nil do if r.cover_data != nil do
resize_cover_image(r) resize_cover(r)
IO.puts("Resized cover image for #{r.title}") IO.puts("Resized cover for #{r.title}")
end end
end) end)
end end
@@ -133,16 +133,16 @@ defmodule MusicLibrary.Records.Importer do
Rec Rec
|> Repo.all() |> Repo.all()
|> Enum.each(fn r -> |> Enum.each(fn r ->
if r.image_data != nil do if r.cover_data != nil do
generate_cover_image_hash(r) generate_cover_hash(r)
IO.puts("Generated cover image hash for #{r.title}") IO.puts("Generated cover hash for #{r.title}")
end end
end) end)
end end
def generate_cover_image_hash(record) do def generate_cover_hash(record) do
record record
|> Rec.generate_image_data_hash() |> Rec.generate_cover_hash()
|> Repo.update!() |> Repo.update!()
end end
+2 -2
View File
@@ -182,8 +182,8 @@ defmodule MusicLibrary.Records.MusicBrainz do
def get_cover_art(musicbrainz_id) do def get_cover_art(musicbrainz_id) do
url = "https://coverartarchive.org/release-group/#{musicbrainz_id}/front" url = "https://coverartarchive.org/release-group/#{musicbrainz_id}/front"
with {:ok, image_data} <- blob_get(url), with {:ok, cover_data} <- blob_get(url),
{:ok, thumb} = Vix.Vips.Operation.thumbnail_buffer(image_data, 400) do {:ok, thumb} = Vix.Vips.Operation.thumbnail_buffer(cover_data, 400) do
Vix.Vips.Image.write_to_buffer(thumb, ".jpg") Vix.Vips.Image.write_to_buffer(thumb, ".jpg")
end end
end end
+17 -17
View File
@@ -10,9 +10,9 @@ defmodule MusicLibrary.Records.Record do
field :type, Ecto.Enum, values: [:album, :ep, :live, :compilation, :single, :other] field :type, Ecto.Enum, values: [:album, :ep, :live, :compilation, :single, :other]
field :format, Ecto.Enum, values: @formats field :format, Ecto.Enum, values: @formats
field :title, :string field :title, :string
field :image_url, :string field :cover_url, :string
field :image_data, :binary field :cover_data, :binary
field :image_data_hash, :string field :cover_hash, :string
field :musicbrainz_id, Ecto.UUID field :musicbrainz_id, Ecto.UUID
field :genres, {:array, :string} field :genres, {:array, :string}
field :release, :string field :release, :string
@@ -37,11 +37,11 @@ defmodule MusicLibrary.Records.Record do
:musicbrainz_id, :musicbrainz_id,
:release, :release,
:genres, :genres,
:image_url, :cover_url,
:image_data :cover_data
]) ])
|> cast_embed(:artists, with: &artist_changeset/2) |> cast_embed(:artists, with: &artist_changeset/2)
|> generate_image_data_hash() |> generate_cover_hash()
|> validate_required([:type, :title, :musicbrainz_id, :release, :genres]) |> validate_required([:type, :title, :musicbrainz_id, :release, :genres])
end end
@@ -58,28 +58,28 @@ defmodule MusicLibrary.Records.Record do
|> put_embed(:artists, artists_attrs) |> put_embed(:artists, artists_attrs)
end end
def add_image_data(record, image_data) do def add_cover_data(record, cover_data) do
record record
|> change(image_data: image_data) |> change(cover_data: cover_data)
|> generate_image_data_hash() |> generate_cover_hash()
end end
def generate_image_data_hash(record = %__MODULE__{image_data: image_data}) do def generate_cover_hash(record = %__MODULE__{cover_data: cover_data}) do
hash = :crypto.hash(:sha256, image_data) |> Base.encode16() hash = :crypto.hash(:sha256, cover_data) |> Base.encode16()
record record
|> change() |> change()
|> put_change(:image_data_hash, hash) |> put_change(:cover_hash, hash)
end end
def generate_image_data_hash(changeset) do def generate_cover_hash(changeset) do
case get_change(changeset, :image_data) do case get_change(changeset, :cover_data) do
nil -> nil ->
changeset changeset
image_data -> cover_data ->
hash = :crypto.hash(:sha256, image_data) |> Base.encode16() hash = :crypto.hash(:sha256, cover_data) |> Base.encode16()
put_change(changeset, :image_data_hash, hash) put_change(changeset, :cover_hash, hash)
end end
end end
@@ -4,11 +4,11 @@ defmodule MusicLibraryWeb.ImageController do
alias MusicLibrary.Records alias MusicLibrary.Records
def show(conn, %{"record_id" => record_id}) do def show(conn, %{"record_id" => record_id}) do
case Records.get_image!(record_id) do case Records.get_cover!(record_id) do
nil -> nil ->
send_resp(conn, 404, "Not found") send_resp(conn, 404, "Not found")
%{image_data: image_data, image_data_hash: etag} -> %{cover_data: cover_data, cover_hash: etag} ->
case get_req_header(conn, "if-none-match") do case get_req_header(conn, "if-none-match") do
[^etag] -> [^etag] ->
send_resp(conn, 304, "") send_resp(conn, 304, "")
@@ -17,7 +17,7 @@ defmodule MusicLibraryWeb.ImageController do
conn conn
|> put_resp_content_type("image/jpeg", "utf-8") |> put_resp_content_type("image/jpeg", "utf-8")
|> put_resp_header("etag", etag) |> put_resp_header("etag", etag)
|> send_resp(200, image_data) |> send_resp(200, cover_data)
end end
end end
end end
@@ -7,7 +7,7 @@ defmodule MusicLibraryWeb.RecordLive.FormComponent do
def mount(socket) do def mount(socket) do
{:ok, {:ok,
socket socket
|> allow_upload(:image_data, accept: ~w(.jpg .jpeg), max_entries: 1)} |> allow_upload(:cover_data, accept: ~w(.jpg .jpeg), max_entries: 1)}
end end
@impl true @impl true
@@ -35,12 +35,12 @@ defmodule MusicLibraryWeb.RecordLive.FormComponent do
/> />
<.input field={@form[:release]} type="text" label="Release" /> <.input field={@form[:release]} type="text" label="Release" />
<div> <div>
<.label for={@uploads.image_data.ref}> <.label for={@uploads.cover_data.ref}>
Cover art Cover art
</.label> </.label>
<.live_file_input <.live_file_input
class="mt-2 block w-full rounded-lg text-zinc-900 focus:ring-0 sm:text-sm sm:leading-6" class="mt-2 block w-full rounded-lg text-zinc-900 focus:ring-0 sm:text-sm sm:leading-6"
upload={@uploads.image_data} upload={@uploads.cover_data}
/> />
</div> </div>
<:actions> <:actions>
@@ -68,19 +68,19 @@ defmodule MusicLibraryWeb.RecordLive.FormComponent do
end end
def handle_event("save", %{"record" => record_params}, socket) do def handle_event("save", %{"record" => record_params}, socket) do
uploaded_images = uploaded_covers =
consume_uploaded_entries(socket, :image_data, fn %{path: path}, _entry -> consume_uploaded_entries(socket, :cover_data, fn %{path: path}, _entry ->
{:ok, File.read!(path)} {:ok, File.read!(path)}
end) end)
save_record(socket, record_params, uploaded_images) save_record(socket, record_params, uploaded_covers)
end end
defp save_record(socket, record_params, uploaded_images) do defp save_record(socket, record_params, uploaded_covers) do
params = params =
case uploaded_images do case uploaded_covers do
[] -> record_params [] -> record_params
[image_path] -> Map.put(record_params, "image_data", image_path) [cover_data] -> Map.put(record_params, "cover_data", cover_data)
end end
case Records.update_record(socket.assigns.record, params) do case Records.update_record(socket.assigns.record, params) do
@@ -23,12 +23,12 @@
rows={@streams.records} rows={@streams.records}
row_click={fn {_id, record} -> JS.navigate(~p"/records/#{record}") end} row_click={fn {_id, record} -> JS.navigate(~p"/records/#{record}") end}
> >
<:col :let={{_id, record}} label="Image"> <:col :let={{_id, record}} label="Cover">
<span class="relative inline-block drop-shadow-xl"> <span class="relative inline-block drop-shadow-xl">
<img <img
class="max-w-16 rounded-lg" class="max-w-16 rounded-lg"
alt={record.title} alt={record.title}
src={~p"/images/#{record.id}?vsn=#{record.image_data_hash || ""}"} src={~p"/images/#{record.id}?vsn=#{record.cover_hash || ""}"}
/> />
<span class="absolute right-1 bottom-0 block text-white drop-shadow-md"> <span class="absolute right-1 bottom-0 block text-white drop-shadow-md">
<%= Records.Record.format_short_label(record.format) %> <%= Records.Record.format_short_label(record.format) %>
@@ -15,7 +15,7 @@
<span class="relative block md:inline-block drop-shadow"> <span class="relative block md:inline-block drop-shadow">
<img <img
class="w-full" class="w-full"
src={~p"/images/#{@record.id}?vsn=#{@record.image_data_hash}"} src={~p"/images/#{@record.id}?vsn=#{@record.cover_hash || ""}"}
alt={@record.title} alt={@record.title}
/> />
<span class="absolute right-2 bottom-1 block text-white drop-shadow-md"> <span class="absolute right-2 bottom-1 block text-white drop-shadow-md">
+1 -1
View File
@@ -1,3 +1,3 @@
defmodule Obsidian.Entry do defmodule Obsidian.Entry do
defstruct [:type, :musicbrainz_id, :title, :release, :image_url, :genres] defstruct [:type, :musicbrainz_id, :title, :release, :cover_url, :genres]
end end
+1 -1
View File
@@ -9,7 +9,7 @@ defmodule Obsidian.Parser do
musicbrainz_id: meta["id"], musicbrainz_id: meta["id"],
title: meta["title"], title: meta["title"],
release: meta["year"] |> parse_release(), release: meta["year"] |> parse_release(),
image_url: meta["image"], cover_url: meta["image"],
genres: meta["genres"] genres: meta["genres"]
}} }}
end end
@@ -0,0 +1,15 @@
defmodule MusicLibrary.Repo.Migrations.RenameImageColumnsToCover do
use Ecto.Migration
def up do
rename table(:records), :image_url, to: :cover_url
rename table(:records), :image_data, to: :cover_data
rename table(:records), :image_data_hash, to: :cover_hash
end
def down do
rename table(:records), :cover_url, to: :image_url
rename table(:records), :cover_data, to: :image_data
rename table(:records), :cover_hash, to: :image_data_hash
end
end
+2 -2
View File
@@ -15,7 +15,7 @@ defmodule Obsidian.ParserTest do
musicbrainz_id: "20790e26-98e4-3ad3-a67f-b674758b942d", musicbrainz_id: "20790e26-98e4-3ad3-a67f-b674758b942d",
title: "Marbles", title: "Marbles",
release: "2004", release: "2004",
image_url: cover_url:
"https://coverartarchive.org/release-group/20790e26-98e4-3ad3-a67f-b674758b942d/front", "https://coverartarchive.org/release-group/20790e26-98e4-3ad3-a67f-b674758b942d/front",
genres: [ genres: [
"alternative rock", "alternative rock",
@@ -36,7 +36,7 @@ defmodule Obsidian.ParserTest do
{:ok, {:ok,
%Entry{ %Entry{
genres: ["classic rock", "pop", "pop rock", "rock"], genres: ["classic rock", "pop", "pop rock", "rock"],
image_url: cover_url:
"https://coverartarchive.org/release-group/950092d6-45f6-4269-87da-99a9ff2fcc52/front", "https://coverartarchive.org/release-group/950092d6-45f6-4269-87da-99a9ff2fcc52/front",
musicbrainz_id: "950092d6-45f6-4269-87da-99a9ff2fcc52", musicbrainz_id: "950092d6-45f6-4269-87da-99a9ff2fcc52",
title: "Guardians of the Galaxy: Awesome Mix, Vol. 1", title: "Guardians of the Galaxy: Awesome Mix, Vol. 1",
+3 -3
View File
@@ -29,7 +29,7 @@ defmodule MusicLibrary.RecordsFixtures do
"Thick as a Brick" "Thick as a Brick"
] ]
# While it would be great to have this random, it's ok to use one single image # While it would be great to have this random, it's ok to use one single image
@image_data_path "#{__DIR__}/marillion-marbles.jpg" @cover_data_path "#{__DIR__}/marillion-marbles.jpg"
def record_fixture(attrs \\ %{}) do def record_fixture(attrs \\ %{}) do
musicbrainz_id = Ecto.UUID.generate() musicbrainz_id = Ecto.UUID.generate()
@@ -38,8 +38,8 @@ defmodule MusicLibrary.RecordsFixtures do
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
genres: Enum.take_random(@genres, :rand.uniform(3)), genres: Enum.take_random(@genres, :rand.uniform(3)),
image_url: "https://coverartarchive.org/release-group/#{musicbrainz_id}/front", cover_url: "https://coverartarchive.org/release-group/#{musicbrainz_id}/front",
image_data: File.read!(@image_data_path), cover_data: File.read!(@cover_data_path),
musicbrainz_id: musicbrainz_id, musicbrainz_id: musicbrainz_id,
title: Enum.random(@titles), title: Enum.random(@titles),
type: :album, type: :album,