From b68a7ac7346a60188c6cc14e0063d72bfaf54beb Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Tue, 10 Dec 2024 16:45:29 +0300 Subject: [PATCH] Can fetch genres from OpenAI --- lib/music_library/records.ex | 21 +++++ lib/music_library/records/record.ex | 4 + .../live/collection_live/show.ex | 20 +++++ .../live/collection_live/show.html.heex | 12 +++ .../live/wishlist_live/show.ex | 20 +++++ .../live/wishlist_live/show.html.heex | 12 +++ experiment.exs => lib/open_ai.ex | 36 ++++---- priv/gettext/default.pot | 82 ++++++++++++------- 8 files changed, 156 insertions(+), 51 deletions(-) rename experiment.exs => lib/open_ai.ex (71%) diff --git a/lib/music_library/records.ex b/lib/music_library/records.ex index 376a844f..d69261cb 100644 --- a/lib/music_library/records.ex +++ b/lib/music_library/records.ex @@ -185,6 +185,27 @@ defmodule MusicLibrary.Records do end end + def populate_genres(record) do + artists = + record.artists + |> Enum.map(fn a -> a.name end) + |> Enum.join(",") + + prompt = """ + Provide a list of music genres applicable to the album "#{record.title}" by #{artists}. + + Limit the list to 5 genres, ordered by decreasing specificity, all lowercase. + + Return a valid JSON list. + """ + + {:ok, response} = OpenAI.gpt(prompt) + + record + |> Record.add_genres(response["genres"]) + |> Repo.update() + end + defp get_cover_art_or_default(musicbrainz_id) do case music_brainz_config().api.get_cover_art( {:musicbrainz_id, musicbrainz_id}, diff --git a/lib/music_library/records/record.ex b/lib/music_library/records/record.ex index c8410c9f..d367554e 100644 --- a/lib/music_library/records/record.ex +++ b/lib/music_library/records/record.ex @@ -84,6 +84,10 @@ defmodule MusicLibrary.Records.Record do |> put_embed(:artists, artists_attrs) end + def add_genres(record, genres) do + change(record, genres: genres) + end + def add_cover_data(record, cover_data) do record |> change(cover_data: cover_data) diff --git a/lib/music_library_web/live/collection_live/show.ex b/lib/music_library_web/live/collection_live/show.ex index 97913734..471e2156 100644 --- a/lib/music_library_web/live/collection_live/show.ex +++ b/lib/music_library_web/live/collection_live/show.ex @@ -63,6 +63,26 @@ defmodule MusicLibraryWeb.CollectionLive.Show do end end + def handle_event("populate_genres", %{"id" => id}, socket) do + record = Records.get_record!(id) + + case Records.populate_genres(record) do + {:ok, updated_record} -> + {:noreply, + socket + |> put_flash(:info, gettext("Genres populated successfully")) + |> assign(:record, updated_record)} + + {:error, reason} -> + {:noreply, + socket + |> put_flash( + :error, + gettext("Error populating genres") <> "," <> inspect(reason) + )} + end + end + def handle_event("refresh_cover", %{"id" => id}, socket) do record = Records.get_record!(id) diff --git a/lib/music_library_web/live/collection_live/show.html.heex b/lib/music_library_web/live/collection_live/show.html.heex index 26995b61..f7203138 100644 --- a/lib/music_library_web/live/collection_live/show.html.heex +++ b/lib/music_library_web/live/collection_live/show.html.heex @@ -56,6 +56,18 @@ {gettext("MB Data")} + <.link phx-click={JS.push("populate_genres", value: %{id: @record.id})}> + <.button type="button" class="relative -ml-px inline-flex items-center rounded-none"> + {gettext("Populate")} + <.icon + name="hero-sparkles" + class="h-4 w-4 mr-1 phx-click-loading:animate-shake" + aria-hidden="true" + data-slot="icon" + /> + {gettext("Genres")} + + <.link phx-click={JS.push("delete", value: %{id: @record.id})} data-confirm={gettext("Are you sure?")} diff --git a/lib/music_library_web/live/wishlist_live/show.ex b/lib/music_library_web/live/wishlist_live/show.ex index 4d9359ba..a6314d2b 100644 --- a/lib/music_library_web/live/wishlist_live/show.ex +++ b/lib/music_library_web/live/wishlist_live/show.ex @@ -83,6 +83,26 @@ defmodule MusicLibraryWeb.WishlistLive.Show do end end + def handle_event("populate_genres", %{"id" => id}, socket) do + record = Records.get_record!(id) + + case Records.populate_genres(record) do + {:ok, updated_record} -> + {:noreply, + socket + |> put_flash(:info, gettext("Genres populated successfully")) + |> assign(:record, updated_record)} + + {:error, reason} -> + {:noreply, + socket + |> put_flash( + :error, + gettext("Error populating genres") <> "," <> inspect(reason) + )} + end + end + @impl true def handle_info({MusicLibraryWeb.RecordLive.FormComponent, {:saved, record}}, socket) do {:noreply, assign(socket, :record, record)} diff --git a/lib/music_library_web/live/wishlist_live/show.html.heex b/lib/music_library_web/live/wishlist_live/show.html.heex index e7ab1715..e8a86ee3 100644 --- a/lib/music_library_web/live/wishlist_live/show.html.heex +++ b/lib/music_library_web/live/wishlist_live/show.html.heex @@ -56,6 +56,18 @@ {gettext("MB Data")} + <.link phx-click={JS.push("populate_genres", value: %{id: @record.id})}> + <.button type="button" class="relative -ml-px inline-flex items-center rounded-none"> + {gettext("Populate")} + <.icon + name="hero-sparkles" + class="h-4 w-4 mr-1 phx-click-loading:animate-shake" + aria-hidden="true" + data-slot="icon" + /> + {gettext("Genres")} + + <.link phx-click={JS.push("delete", value: %{id: @record.id})} data-confirm={gettext("Are you sure?")} diff --git a/experiment.exs b/lib/open_ai.ex similarity index 71% rename from experiment.exs rename to lib/open_ai.ex index 37912593..5aac8e2c 100644 --- a/experiment.exs +++ b/lib/open_ai.ex @@ -1,5 +1,20 @@ defmodule OpenAI do - def gpt_stream(prompt, cb) do + def gpt(prompt) do + {:ok, collector} = Agent.start_link(fn -> "" end) + + gpt_stream(prompt, fn data -> + case get_in(data, ["choices", Access.at(0), "delta", "content"]) do + nil -> :ok + data -> Agent.update(collector, fn current -> current <> data end) + end + end) + + result = Agent.get(collector, & &1) |> Jason.decode!() + Agent.stop(collector) + {:ok, result} + end + + defp gpt_stream(prompt, cb) do fun = fn request, finch_request, finch_name, finch_options -> fun = fn {:status, status}, response -> @@ -47,22 +62,3 @@ defmodule OpenAI do defp decode_body("[DONE]", _), do: :ok defp decode_body(json, cb), do: cb.(Jason.decode!(json)) end - -{:ok, collector} = Agent.start_link(fn -> "" end) - -prompt = """ -Provide a list of music genres applicable to the album "Stupid Things that Mean the World" by Tim Bowness. - -Limit the list to 5 genres, ordered by decreasing specificity, all lowercase. - -Return a valid JSON list. -""" - -OpenAI.gpt_stream(prompt, fn data -> - case get_in(data, ["choices", Access.at(0), "delta", "content"]) do - nil -> :ok - data -> Agent.update(collector, fn current -> current <> data end) - end -end) - -Agent.get(collector, & &1) |> Jason.decode!() |> IO.inspect() diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 5b7e5a70..c8029c96 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -17,9 +17,9 @@ msgid "Actions" msgstr "" #: lib/music_library_web/live/collection_live/index.html.heex:186 -#: lib/music_library_web/live/collection_live/show.html.heex:61 +#: lib/music_library_web/live/collection_live/show.html.heex:73 #: lib/music_library_web/live/wishlist_live/index.html.heex:196 -#: lib/music_library_web/live/wishlist_live/show.html.heex:61 +#: lib/music_library_web/live/wishlist_live/show.html.heex:73 #, elixir-autogen, elixir-format msgid "Are you sure?" msgstr "" @@ -29,12 +29,12 @@ msgstr "" msgid "Attempting to reconnect" msgstr "" -#: lib/music_library_web/live/collection_live/show.html.heex:161 +#: lib/music_library_web/live/collection_live/show.html.heex:173 #, elixir-autogen, elixir-format msgid "Back to records" msgstr "" -#: lib/music_library_web/live/wishlist_live/show.html.heex:154 +#: lib/music_library_web/live/wishlist_live/show.html.heex:166 #, elixir-autogen, elixir-format msgid "Back to wishlist" msgstr "" @@ -53,8 +53,8 @@ msgstr "" #: lib/music_library_web/components/layouts/app.html.heex:14 #: lib/music_library_web/live/artist_live/show.html.heex:61 #: lib/music_library_web/live/collection_live/index.ex:77 -#: lib/music_library_web/live/collection_live/show.ex:102 -#: lib/music_library_web/live/collection_live/show.ex:119 +#: lib/music_library_web/live/collection_live/show.ex:122 +#: lib/music_library_web/live/collection_live/show.ex:139 #, elixir-autogen, elixir-format msgid "Collection" msgstr "" @@ -65,18 +65,18 @@ msgid "Cover art" msgstr "" #: lib/music_library_web/live/collection_live/index.html.heex:188 -#: lib/music_library_web/live/collection_live/show.html.heex:67 +#: lib/music_library_web/live/collection_live/show.html.heex:79 #: lib/music_library_web/live/wishlist_live/index.html.heex:198 -#: lib/music_library_web/live/wishlist_live/show.html.heex:67 +#: lib/music_library_web/live/wishlist_live/show.html.heex:79 #, elixir-autogen, elixir-format msgid "Delete" msgstr "" #: lib/music_library_web/live/collection_live/index.html.heex:177 -#: lib/music_library_web/live/collection_live/show.ex:126 +#: lib/music_library_web/live/collection_live/show.ex:146 #: lib/music_library_web/live/collection_live/show.html.heex:32 #: lib/music_library_web/live/wishlist_live/index.html.heex:177 -#: lib/music_library_web/live/wishlist_live/show.ex:109 +#: lib/music_library_web/live/wishlist_live/show.ex:129 #: lib/music_library_web/live/wishlist_live/show.html.heex:32 #, elixir-autogen, elixir-format msgid "Edit" @@ -105,8 +105,10 @@ msgstr "" msgid "Formats" msgstr "" -#: lib/music_library_web/live/collection_live/show.html.heex:77 -#: lib/music_library_web/live/wishlist_live/show.html.heex:78 +#: lib/music_library_web/live/collection_live/show.html.heex:68 +#: lib/music_library_web/live/collection_live/show.html.heex:89 +#: lib/music_library_web/live/wishlist_live/show.html.heex:68 +#: lib/music_library_web/live/wishlist_live/show.html.heex:90 #, elixir-autogen, elixir-format msgid "Genres" msgstr "" @@ -122,8 +124,8 @@ msgstr "" msgid "Import" msgstr "" -#: lib/music_library_web/live/collection_live/show.html.heex:136 -#: lib/music_library_web/live/wishlist_live/show.html.heex:129 +#: lib/music_library_web/live/collection_live/show.html.heex:148 +#: lib/music_library_web/live/wishlist_live/show.html.heex:141 #, elixir-autogen, elixir-format msgid "Inserted at" msgstr "" @@ -149,9 +151,9 @@ msgstr "" msgid "Logout" msgstr "" -#: lib/music_library_web/live/collection_live/show.html.heex:91 +#: lib/music_library_web/live/collection_live/show.html.heex:103 #: lib/music_library_web/live/record_live/form_component.ex:47 -#: lib/music_library_web/live/wishlist_live/show.html.heex:92 +#: lib/music_library_web/live/wishlist_live/show.html.heex:104 #, elixir-autogen, elixir-format msgid "MusicBrainz ID" msgstr "" @@ -199,7 +201,7 @@ msgstr "" msgid "Purchased at" msgstr "" -#: lib/music_library_web/live/collection_live/show.html.heex:113 +#: lib/music_library_web/live/collection_live/show.html.heex:125 #, elixir-autogen, elixir-format msgid "Purchased on" msgstr "" @@ -248,9 +250,9 @@ msgid "Search for records" msgstr "" #: lib/music_library_web/live/collection_live/index.html.heex:157 -#: lib/music_library_web/live/collection_live/show.ex:125 +#: lib/music_library_web/live/collection_live/show.ex:145 #: lib/music_library_web/live/wishlist_live/index.html.heex:157 -#: lib/music_library_web/live/wishlist_live/show.ex:108 +#: lib/music_library_web/live/wishlist_live/show.ex:128 #, elixir-autogen, elixir-format msgid "Show" msgstr "" @@ -305,8 +307,8 @@ msgstr "" msgid "Types" msgstr "" -#: lib/music_library_web/live/collection_live/show.html.heex:144 -#: lib/music_library_web/live/wishlist_live/show.html.heex:137 +#: lib/music_library_web/live/collection_live/show.html.heex:156 +#: lib/music_library_web/live/wishlist_live/show.html.heex:149 #, elixir-autogen, elixir-format msgid "Updated at" msgstr "" @@ -330,7 +332,7 @@ msgstr "" #: lib/music_library_web/components/layouts/app.html.heex:20 #: lib/music_library_web/live/artist_live/show.html.heex:100 #: lib/music_library_web/live/wishlist_live/index.ex:75 -#: lib/music_library_web/live/wishlist_live/show.ex:102 +#: lib/music_library_web/live/wishlist_live/show.ex:122 #, elixir-autogen, elixir-format msgid "Wishlist" msgstr "" @@ -346,14 +348,14 @@ msgstr "" msgid "close" msgstr "" -#: lib/music_library_web/live/collection_live/show.html.heex:101 -#: lib/music_library_web/live/wishlist_live/show.html.heex:102 +#: lib/music_library_web/live/collection_live/show.html.heex:113 +#: lib/music_library_web/live/wishlist_live/show.html.heex:114 #, elixir-autogen, elixir-format msgid "Copy MusicBrainz ID to clipboard" msgstr "" -#: lib/music_library_web/live/collection_live/show.html.heex:155 -#: lib/music_library_web/live/wishlist_live/show.html.heex:148 +#: lib/music_library_web/live/collection_live/show.html.heex:167 +#: lib/music_library_web/live/wishlist_live/show.html.heex:160 #, elixir-autogen, elixir-format msgid "MusicBrainz data" msgstr "" @@ -387,8 +389,8 @@ msgstr "" msgid "Number of included records" msgstr "" -#: lib/music_library_web/live/collection_live/show.html.heex:124 -#: lib/music_library_web/live/wishlist_live/show.html.heex:117 +#: lib/music_library_web/live/collection_live/show.html.heex:136 +#: lib/music_library_web/live/wishlist_live/show.html.heex:129 #, elixir-autogen, elixir-format msgid "Includes" msgstr "" @@ -410,7 +412,7 @@ msgstr "" msgid "Refresh LastFm Feed" msgstr "" -#: lib/music_library_web/live/collection_live/show.ex:73 +#: lib/music_library_web/live/collection_live/show.ex:93 #: lib/music_library_web/live/wishlist_live/show.ex:73 #, elixir-autogen, elixir-format msgid "Cover refreshed successfully" @@ -441,7 +443,7 @@ msgstr "" msgid "Error refreshing Cover" msgstr "" -#: lib/music_library_web/live/collection_live/show.ex:81 +#: lib/music_library_web/live/collection_live/show.ex:101 #, elixir-autogen, elixir-format msgid "Error refreshing cover" msgstr "" @@ -452,7 +454,7 @@ msgid "No MB ID" msgstr "" #: lib/music_library_web/live/artist_live/show.ex:38 -#: lib/music_library_web/live/collection_live/show.ex:100 +#: lib/music_library_web/live/collection_live/show.ex:120 #, elixir-autogen, elixir-format msgid "Details" msgstr "" @@ -512,3 +514,21 @@ msgstr "" #, elixir-autogen, elixir-format msgid "There was an error loading the play count" msgstr "" + +#: lib/music_library_web/live/collection_live/show.ex:81 +#: lib/music_library_web/live/wishlist_live/show.ex:101 +#, elixir-autogen, elixir-format +msgid "Error populating genres" +msgstr "" + +#: lib/music_library_web/live/collection_live/show.ex:73 +#: lib/music_library_web/live/wishlist_live/show.ex:93 +#, elixir-autogen, elixir-format +msgid "Genres populated successfully" +msgstr "" + +#: lib/music_library_web/live/collection_live/show.html.heex:61 +#: lib/music_library_web/live/wishlist_live/show.html.heex:61 +#, elixir-autogen, elixir-format +msgid "Populate" +msgstr ""