From f57485e509734148632c056378957015d80dac60 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Sun, 6 Jul 2025 16:57:30 +0100 Subject: [PATCH] Allow manual upload of artist image --- lib/music_library/artists.ex | 10 ++ .../live/artist_live/form_component.ex | 157 ++++++++++++++++++ .../live/artist_live/show.ex | 25 ++- .../live/artist_live/show.html.heex | 28 ++++ lib/music_library_web/router.ex | 1 + priv/gettext/default.pot | 17 ++ priv/gettext/en/LC_MESSAGES/default.po | 17 ++ 7 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 lib/music_library_web/live/artist_live/form_component.ex diff --git a/lib/music_library/artists.ex b/lib/music_library/artists.ex index d0c54e19..5eb4d6a3 100644 --- a/lib/music_library/artists.ex +++ b/lib/music_library/artists.ex @@ -151,6 +151,16 @@ defmodule MusicLibrary.Artists do Repo.one(q) end + def change_artist_info(artist_info, attrs \\ %{}) do + ArtistInfo.changeset(artist_info, attrs) + end + + def update_artist_info(artist_info, attrs) do + artist_info + |> ArtistInfo.changeset(attrs) + |> Repo.update() + end + defp get_collected_artist_ids do q = from ar in ArtistRecord, diff --git a/lib/music_library_web/live/artist_live/form_component.ex b/lib/music_library_web/live/artist_live/form_component.ex new file mode 100644 index 00000000..a4f840de --- /dev/null +++ b/lib/music_library_web/live/artist_live/form_component.ex @@ -0,0 +1,157 @@ +defmodule MusicLibraryWeb.ArtistLive.FormComponent do + use MusicLibraryWeb, :live_component + + alias MusicLibrary.Artists + alias Vix.Vips.Image + + @impl true + def mount(socket) do + {:ok, + socket + |> allow_upload(:image_data, accept: ~w(.jpg .jpeg .png .webp), max_entries: 1)} + end + + @impl true + def render(assigns) do + ~H""" +
+
+

+ {@artist.name} +

+
+ + <.simple_form + for={@form} + id="artist-info-form" + phx-target={@myself} + phx-change="validate" + phx-auto-recover="recover_form" + phx-submit="save" + > +
+ <.label for={@uploads.image_data.ref}> + {gettext("Artist image")} + +
+
+ {@artist.name} + <.live_img_preview + :for={entry <- @uploads.image_data.entries} + class="mx-auto w-24" + entry={entry} + /> +
+ <%= for entry <- @uploads.image_data.entries do %> + {entry.progress}% + <% end %> +
+
+ +

{gettext("or drag and drop")}

+
+

+ {gettext("PNG, JPG, WEBP up to 8MB")} +

+
+
+
+ <:actions> +
+ <.button variant="solid" class="w-full md:w-auto" phx-disable-with={gettext("Saving...")}> + {gettext("Save")} + +
+ + +
+ """ + end + + @impl true + def update(%{artist_info: artist_info} = assigns, socket) do + {:ok, + socket + |> assign(assigns) + |> assign_new(:form, fn -> + to_form(Artists.change_artist_info(artist_info)) + end)} + end + + @impl true + def handle_event("validate", params, socket) do + artist_info_params = params["artist_info"] || %{} + changeset = Artists.change_artist_info(socket.assigns.artist_info, artist_info_params) + {:noreply, assign(socket, form: to_form(changeset, action: :validate))} + end + + def handle_event("save", params, socket) do + artist_info_params = params["artist_info"] || %{} + + uploaded_images = + consume_uploaded_entries(socket, :image_data, fn %{path: path}, _entry -> + {:ok, File.read!(path)} + end) + + save_artist_info(socket, artist_info_params, uploaded_images) + end + + def handle_event("recover_form", params, socket) do + handle_event("validate", params, socket) + end + + defp save_artist_info(socket, artist_info_params, uploaded_images) do + params = + case uploaded_images do + [] -> + artist_info_params + + [image_data] -> + {:ok, image} = Image.new_from_buffer(image_data) + + image_width = Image.width(image) + + Map.merge(artist_info_params, %{ + "image_data" => image_data, + "image_data_width" => image_width + }) + end + + case Artists.update_artist_info(socket.assigns.artist_info, params) do + {:ok, artist_info} -> + notify_parent({:saved, artist_info}) + + {:noreply, + socket + |> put_flash(:info, gettext("Artist updated successfully")) + |> push_patch(to: socket.assigns.patch)} + + {:error, %Ecto.Changeset{} = changeset} -> + {:noreply, assign(socket, form: to_form(changeset))} + end + end + + defp notify_parent(msg), do: send(self(), {__MODULE__, msg}) +end diff --git a/lib/music_library_web/live/artist_live/show.ex b/lib/music_library_web/live/artist_live/show.ex index 253b84f9..4510746c 100644 --- a/lib/music_library_web/live/artist_live/show.ex +++ b/lib/music_library_web/live/artist_live/show.ex @@ -230,7 +230,19 @@ defmodule MusicLibraryWeb.ArtistLive.Show do socket |> assign(:page_title, gettext("Add more · Artist")) |> assign(:initial_query, "arid:#{socket.assigns.artist.musicbrainz_id}") - |> assign(:record, nil) + end + + defp apply_action(socket, :edit, params) do + socket = + if get_in(socket.assigns, [:streams, :collection_records]) == nil do + socket + |> apply_action(:show, params) + else + socket + end + + socket + |> assign(:page_title, gettext("Add more · Artist")) end defp assign_records(socket, artist_musicbrainz_id) do @@ -257,6 +269,17 @@ defmodule MusicLibraryWeb.ArtistLive.Show do ) end + defp page_title(:edit, artist) do + Enum.join( + [ + artist.name, + "·", + gettext("Edit") + ], + " " + ) + end + defp page_title(:import, artist) do Enum.join( [ 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 c26923bb..8654f0ad 100644 --- a/lib/music_library_web/live/artist_live/show.html.heex +++ b/lib/music_library_web/live/artist_live/show.html.heex @@ -17,6 +17,18 @@ /> <.focus_wrap id={"actions-#{@artist.musicbrainz_id}-focus-wrap"}> + <.dropdown_link + id={"actions-#{@artist.musicbrainz_id}-edit"} + patch={~p"/artists/#{@artist.musicbrainz_id}/edit"} + > + <.icon + name="hero-pencil-square" + class="h-4 w-4 mr-1 phx-click-loading:animate-bounce" + aria-hidden="true" + data-slot="icon" + /> + {gettext("Edit")} + <.dropdown_link id={"actions-#{@artist.musicbrainz_id}-refresh-image"} phx-click={JS.push("refresh_artist_image", value: %{id: @artist.musicbrainz_id})} @@ -223,3 +235,19 @@ icon_name="hero-plus" /> + +<.modal + :if={@live_action == :edit} + id="artist-info-modal" + open + on_close={JS.patch(~p"/artists/#{@artist.musicbrainz_id}")} +> + <.live_component + module={MusicLibraryWeb.ArtistLive.FormComponent} + id={@artist_info.id} + action={@live_action} + artist_info={@artist_info} + artist={@artist} + patch={~p"/artists/#{@artist.musicbrainz_id}"} + /> + diff --git a/lib/music_library_web/router.ex b/lib/music_library_web/router.ex index ae5840ec..31c8be44 100644 --- a/lib/music_library_web/router.ex +++ b/lib/music_library_web/router.ex @@ -60,6 +60,7 @@ defmodule MusicLibraryWeb.Router do live "/artists/:musicbrainz_id", ArtistLive.Show, :show live "/artists/:musicbrainz_id/import", ArtistLive.Show, :import + live "/artists/:musicbrainz_id/edit", ArtistLive.Show, :edit live "/scrobble-rules", ScrobbleRulesLive.Index, :index live "/scrobble-rules/new", ScrobbleRulesLive.Index, :new diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 5e6b7eb5..93c66457 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -41,6 +41,8 @@ msgid "Delete" msgstr "" #: lib/music_library_web/components/record_components.ex +#: lib/music_library_web/live/artist_live/show.ex +#: lib/music_library_web/live/artist_live/show.html.heex #: lib/music_library_web/live/collection_live/show.ex #: lib/music_library_web/live/collection_live/show.html.heex #: lib/music_library_web/live/scrobble_rules_live/index.html.heex @@ -154,11 +156,13 @@ msgid "Record updated successfully" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #, elixir-autogen, elixir-format msgid "Save" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #: lib/music_library_web/live/scrobble_rules_live/form_component.ex #, elixir-autogen, elixir-format msgid "Saving..." @@ -565,16 +569,19 @@ msgid "Similar artists" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #, elixir-autogen, elixir-format msgid "PNG, JPG, WEBP up to 8MB" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #, elixir-autogen, elixir-format msgid "Upload a file" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #, elixir-autogen, elixir-format msgid "or drag and drop" msgstr "" @@ -1159,3 +1166,13 @@ msgstr "" #, elixir-autogen, elixir-format msgid "Search records and artists..." msgstr "" + +#: lib/music_library_web/live/artist_live/form_component.ex +#, elixir-autogen, elixir-format +msgid "Artist image" +msgstr "" + +#: lib/music_library_web/live/artist_live/form_component.ex +#, elixir-autogen, elixir-format +msgid "Artist updated successfully" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index f2d5891a..4a22ae5c 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -41,6 +41,8 @@ msgid "Delete" msgstr "" #: lib/music_library_web/components/record_components.ex +#: lib/music_library_web/live/artist_live/show.ex +#: lib/music_library_web/live/artist_live/show.html.heex #: lib/music_library_web/live/collection_live/show.ex #: lib/music_library_web/live/collection_live/show.html.heex #: lib/music_library_web/live/scrobble_rules_live/index.html.heex @@ -154,11 +156,13 @@ msgid "Record updated successfully" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #, elixir-autogen, elixir-format msgid "Save" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #: lib/music_library_web/live/scrobble_rules_live/form_component.ex #, elixir-autogen, elixir-format msgid "Saving..." @@ -565,16 +569,19 @@ msgid "Similar artists" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #, elixir-autogen, elixir-format msgid "PNG, JPG, WEBP up to 8MB" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #, elixir-autogen, elixir-format msgid "Upload a file" msgstr "" #: lib/music_library_web/components/form_component.ex +#: lib/music_library_web/live/artist_live/form_component.ex #, elixir-autogen, elixir-format msgid "or drag and drop" msgstr "" @@ -1159,3 +1166,13 @@ msgstr "" #, elixir-autogen, elixir-format msgid "Search records and artists..." msgstr "" + +#: lib/music_library_web/live/artist_live/form_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Artist image" +msgstr "" + +#: lib/music_library_web/live/artist_live/form_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Artist updated successfully" +msgstr ""