From 455c3a8b53420c124c1fdbadefa993e998d6fa7b Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Thu, 18 Sep 2025 20:55:26 +0300 Subject: [PATCH] Serve artist images via assets --- lib/last_fm/artist.ex | 4 ++- lib/music_library/artists.ex | 13 ++++++++++ lib/music_library/search.ex | 5 +++- .../components/search_components.ex | 12 ++++++++- .../live/artist_live/form_component.ex | 11 +++++++- .../live/artist_live/show.ex | 26 ++++++++++++++++++- .../live/artist_live/show.html.heex | 2 +- .../universal_search_live/index.html.heex | 3 ++- lib/music_library_web/router.ex | 1 - 9 files changed, 69 insertions(+), 8 deletions(-) diff --git a/lib/last_fm/artist.ex b/lib/last_fm/artist.ex index d707bcb2..abdca4e9 100644 --- a/lib/last_fm/artist.ex +++ b/lib/last_fm/artist.ex @@ -11,7 +11,8 @@ defmodule LastFm.Artist do image: String.t(), play_count: non_neg_integer(), on_tour: boolean(), - base_url: String.t() + base_url: String.t(), + image_data_hash: String.t() | nil } embedded_schema do @@ -23,6 +24,7 @@ defmodule LastFm.Artist do field :play_count, :integer, default: 0 field :on_tour, :boolean, default: false field :base_url, :string + field :image_data_hash, :string end def from_api_response(api_response) do diff --git a/lib/music_library/artists.ex b/lib/music_library/artists.ex index 5eb4d6a3..cd1cd388 100644 --- a/lib/music_library/artists.ex +++ b/lib/music_library/artists.ex @@ -56,6 +56,19 @@ defmodule MusicLibrary.Artists do q |> Repo.all() end + def get_image_hashes(lastfm_artists) do + musicbrainz_ids = Enum.map(lastfm_artists, & &1.musicbrainz_id) + + q = + from ai in ArtistInfo, + where: ai.id in ^musicbrainz_ids, + select: {ai.id, ai.image_data_hash} + + q + |> Repo.all() + |> Enum.into(%{}) + end + def exists?(artist_id) do q = from ar in ArtistRecord, diff --git a/lib/music_library/search.ex b/lib/music_library/search.ex index 4e47046e..9f8c8a13 100644 --- a/lib/music_library/search.ex +++ b/lib/music_library/search.ex @@ -10,6 +10,7 @@ defmodule MusicLibrary.Search do import Ecto.Query, warn: false + alias MusicLibrary.Artists.ArtistInfo alias MusicLibrary.Records.ArtistRecord alias MusicLibrary.{Collection, Repo, Wishlist} @@ -64,10 +65,12 @@ defmodule MusicLibrary.Search do q = from ar in ArtistRecord, + join: ai in ArtistInfo, + on: ar.musicbrainz_id == ai.id, where: fragment("lower(unaccent(artist ->> '$.name')) LIKE ?", ^"%#{normalized_query}%"), group_by: ar.musicbrainz_id, - select: ar.artist, + select: %{artist: ar.artist, image_data_hash: ai.image_data_hash}, limit: ^limit, order_by: fragment("artist ->> '$.name'") diff --git a/lib/music_library_web/components/search_components.ex b/lib/music_library_web/components/search_components.ex index dc2fb4f7..4583c288 100644 --- a/lib/music_library_web/components/search_components.ex +++ b/lib/music_library_web/components/search_components.ex @@ -7,6 +7,7 @@ defmodule MusicLibraryWeb.SearchComponents do import MusicLibraryWeb.RecordComponents, only: [format_label: 1, type_label: 1, record_cover: 1] + alias MusicLibrary.Assets.Transform alias MusicLibrary.Records.Record @doc """ @@ -63,6 +64,7 @@ defmodule MusicLibraryWeb.SearchComponents do <.search_result_artist artist={artist} /> """ attr :artist, :map, required: true + attr :image_data_hash, :string, required: false attr :rest, :global, include: ~w(phx-click phx-value-id) def search_result_artist(assigns) do @@ -78,7 +80,7 @@ defmodule MusicLibraryWeb.SearchComponents do
{@artist.name} ~p"/images/cover-not-found.png" <> "';"} /> @@ -96,6 +98,14 @@ defmodule MusicLibraryWeb.SearchComponents do """ end + defp artist_image_path(image_data_hash) do + payload = + %Transform{hash: image_data_hash, width: 96} + |> Transform.encode!() + + ~p"/assets/#{payload}" + end + @doc """ Renders a search result group with a title and items. diff --git a/lib/music_library_web/live/artist_live/form_component.ex b/lib/music_library_web/live/artist_live/form_component.ex index e4b85f9a..b6cbc999 100644 --- a/lib/music_library_web/live/artist_live/form_component.ex +++ b/lib/music_library_web/live/artist_live/form_component.ex @@ -2,6 +2,7 @@ defmodule MusicLibraryWeb.ArtistLive.FormComponent do use MusicLibraryWeb, :live_component alias MusicLibrary.Artists + alias MusicLibrary.Assets.Transform alias Vix.Vips.Image @impl true @@ -46,7 +47,7 @@ defmodule MusicLibraryWeb.ArtistLive.FormComponent do :if={@uploads.image_data.entries == []} class="rounded-lg mx-auto w-full" alt={@artist.name} - src={~p"/artists/#{@artist_info.id}/image?vsn=#{@artist_info.image_data_hash || ""}"} + src={artist_image_path(@artist_info)} /> <.live_img_preview :for={entry <- @uploads.image_data.entries} @@ -154,4 +155,12 @@ defmodule MusicLibraryWeb.ArtistLive.FormComponent do end defp notify_parent(msg), do: send(self(), {__MODULE__, msg}) + + defp artist_image_path(artist_info) do + payload = + %Transform{hash: artist_info.image_hash, width: 96} + |> Transform.encode!() + + ~p"/assets/#{payload}" + end end diff --git a/lib/music_library_web/live/artist_live/show.ex b/lib/music_library_web/live/artist_live/show.ex index 9fc57504..4d890914 100644 --- a/lib/music_library_web/live/artist_live/show.ex +++ b/lib/music_library_web/live/artist_live/show.ex @@ -6,6 +6,7 @@ defmodule MusicLibraryWeb.ArtistLive.Show do alias MusicLibrary.Artists.ArtistInfo alias MusicLibrary.{Artists, Records} + alias(MusicLibrary.Assets.Transform) attr :country, :map, required: true @@ -63,7 +64,7 @@ defmodule MusicLibraryWeb.ArtistLive.Show do
{artist.name} ~p"/images/cover-not-found.png" <> "';"} @@ -212,6 +213,13 @@ defmodule MusicLibraryWeb.ArtistLive.Show do end) |> assign_async(:similar_artists, fn -> with {:ok, similar_artists} <- Artists.get_similar_artists(artist) do + artist_image_hashes = Artists.get_image_hashes(similar_artists) + + similar_artists = + Enum.map(similar_artists, fn artist -> + %{artist | image_data_hash: Map.get(artist_image_hashes, artist.musicbrainz_id)} + end) + {:ok, %{similar_artists: similar_artists}} end end) @@ -354,4 +362,20 @@ defmodule MusicLibraryWeb.ArtistLive.Show do attributes: [class: "mt-2 text-sm/7"] ) end + + defp artist_image_path(artist_info) do + payload = + %Transform{hash: artist_info.image_data_hash} + |> Transform.encode!() + + ~p"/assets/#{payload}" + end + + defp artist_thumb_path(artist_info) do + payload = + %Transform{hash: artist_info.image_data_hash, width: 300} + |> Transform.encode!() + + ~p"/assets/#{payload}" + end end diff --git a/lib/music_library_web/live/artist_live/show.html.heex b/lib/music_library_web/live/artist_live/show.html.heex index 5048799b..a5f592e0 100644 --- a/lib/music_library_web/live/artist_live/show.html.heex +++ b/lib/music_library_web/live/artist_live/show.html.heex @@ -106,7 +106,7 @@ {@artist.name} ~p"/images/cover-not-found.png" <> "';"} /> diff --git a/lib/music_library_web/live/universal_search_live/index.html.heex b/lib/music_library_web/live/universal_search_live/index.html.heex index 7774daba..27ed5089 100644 --- a/lib/music_library_web/live/universal_search_live/index.html.heex +++ b/lib/music_library_web/live/universal_search_live/index.html.heex @@ -91,8 +91,9 @@ class="border-t border-zinc-200 dark:border-zinc-700" > <.search_result_artist - :for={artist <- @search_results.artists} + :for={%{artist: artist, image_data_hash: image_data_hash} <- @search_results.artists} artist={artist} + image_data_hash={image_data_hash} phx-click="navigate_to_artist" phx-value-id={artist.musicbrainz_id} /> diff --git a/lib/music_library_web/router.ex b/lib/music_library_web/router.ex index f8c3d4b2..390543c5 100644 --- a/lib/music_library_web/router.ex +++ b/lib/music_library_web/router.ex @@ -37,7 +37,6 @@ defmodule MusicLibraryWeb.Router do get "/backup", ArchiveController, :backup get "/assets/:transform_payload", AssetController, :show - get "/artists/:musicbrainz_id/image", ArtistController, :image live_session :default, on_mount: [MusicLibraryWeb.Hooks.StaticAssets, MusicLibraryWeb.Hooks.GetTimezone] do