From 3d3e758a7ad0c91d5bcaf54e0d8bdc60768b59ec Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Sun, 27 Apr 2025 21:18:08 +0100 Subject: [PATCH] Add endpoint to display an artist image --- lib/music_library/artists.ex | 31 ++++++++++++++ lib/music_library/records/artist_info.ex | 17 ++++++++ .../controllers/artist_controller.ex | 41 +++++++++++++++++++ lib/music_library_web/router.ex | 1 + 4 files changed, 90 insertions(+) create mode 100644 lib/music_library_web/controllers/artist_controller.ex diff --git a/lib/music_library/artists.ex b/lib/music_library/artists.ex index 4f0167d2..4fcaf714 100644 --- a/lib/music_library/artists.ex +++ b/lib/music_library/artists.ex @@ -73,6 +73,24 @@ defmodule MusicLibrary.Artists do end end + def get_artist_info!(artist_id) do + Repo.get!(ArtistInfo, artist_id) + end + + def fetch_image(artist_id) do + artist_info = get_artist_info!(artist_id) + + with {:ok, image} <- ArtistInfo.extract_image(artist_info), + {:ok, image_data} <- Discogs.get_artist_image(image.url) do + artist_info + |> ArtistInfo.changeset(%{ + image_data: image_data, + image_data_width: image.width + }) + |> Repo.update() + end + end + def fetch_artist_info_async(artist_id) do meta = %{} params = %{"id" => artist_id} @@ -82,6 +100,19 @@ defmodule MusicLibrary.Artists do |> BackgroundRepo.insert() end + def get_image(artist_id) do + q = + from ai in ArtistInfo, + where: ai.id == ^artist_id, + select: %{ + image_data: ai.image_data, + image_data_width: ai.image_data_width, + image_data_hash: ai.image_data_hash + } + + Repo.one(q) + end + defp get_collected_artist_ids do q = from ar in ArtistRecord, diff --git a/lib/music_library/records/artist_info.ex b/lib/music_library/records/artist_info.ex index bb9ccaea..3e543630 100644 --- a/lib/music_library/records/artist_info.ex +++ b/lib/music_library/records/artist_info.ex @@ -41,4 +41,21 @@ defmodule MusicLibrary.Records.ArtistInfo do put_change(changeset, :image_data_hash, Cover.hash(image_data)) end end + + def extract_image(artist_info) when is_nil(artist_info.discogs_data) do + {:error, :no_discogs_data} + end + + def extract_image(artist_info) do + primary_image = + Enum.find(artist_info.discogs_data["images"], fn image -> + image["type"] == "primary" + end) + + if primary_image do + {:ok, %{url: primary_image["resource_url"], width: primary_image["width"]}} + else + {:error, :image_not_found} + end + end end diff --git a/lib/music_library_web/controllers/artist_controller.ex b/lib/music_library_web/controllers/artist_controller.ex new file mode 100644 index 00000000..ebad726a --- /dev/null +++ b/lib/music_library_web/controllers/artist_controller.ex @@ -0,0 +1,41 @@ +defmodule MusicLibraryWeb.ArtistController do + use MusicLibraryWeb, :controller + + alias MusicLibrary.Artists + + # 1 year in seconds + @cache_duration 60 * 60 * 24 * 365 + + def image(conn, %{"musicbrainz_id" => artist_id}) do + case Artists.get_image(artist_id) do + nil -> + not_found(conn) + + %{image_data: image_data, image_data_hash: etag} -> + case get_req_header(conn, "if-none-match") do + [^etag] -> extend_cache(conn) + _ -> respond_with_cache(conn, image_data, etag) + end + end + end + + defp not_found(conn) do + conn + |> put_status(:not_found) + |> text("Not found") + end + + defp extend_cache(conn) do + conn + |> put_resp_header("cache-control", "public, max-age=#{@cache_duration}") + |> send_resp(304, "") + end + + defp respond_with_cache(conn, cover_data, etag) do + conn + |> put_resp_content_type("image/jpeg", "utf-8") + |> put_resp_header("cache-control", "public, max-age=#{@cache_duration}") + |> put_resp_header("etag", etag) + |> send_resp(200, cover_data) + end +end diff --git a/lib/music_library_web/router.ex b/lib/music_library_web/router.ex index ab624802..4cfa3e82 100644 --- a/lib/music_library_web/router.ex +++ b/lib/music_library_web/router.ex @@ -35,6 +35,7 @@ defmodule MusicLibraryWeb.Router do get "/backup", ArchiveController, :backup get "/covers/:record_id", CoverController, :show + get "/artists/:musicbrainz_id/image", ArtistController, :image live "/", StatsLive.Index, :index