Integrate refresh of Last.fm data (both per artist and batch)

This commit is contained in:
Claudio Ortolina
2026-03-01 09:46:27 +00:00
parent 84323e5732
commit 6836cf5c9e
8 changed files with 146 additions and 6 deletions
+6
View File
@@ -22,4 +22,10 @@ defmodule MusicLibrary.Artists.Batch do
Artists.refresh_wikipedia_data_async(artist_info)
end)
end
def refresh_lastfm_data do
Batch.run_on_all(from(r in ArtistInfo), "artist_info", fn artist_info ->
Artists.fetch_lastfm_data_async(artist_info.id)
end)
end
end
@@ -6,6 +6,8 @@ defmodule MusicLibrary.Worker.FetchArtistInfo do
with {:ok, _artist_info} <- MusicLibrary.Artists.fetch_artist_info(artist_id),
{:ok, _artist_info} <- MusicLibrary.Artists.fetch_wikipedia_data(artist_id),
{:ok, _artist_info} <- MusicLibrary.Artists.fetch_image(artist_id) do
# fetch_lastfm_data returns {:ok, _} even on API errors, so it won't block embeddings
MusicLibrary.Artists.fetch_lastfm_data(artist_id)
regenerate_record_embeddings(artist_id)
end
end
@@ -1,11 +1,16 @@
defmodule MusicLibrary.Worker.FetchArtistLastFmData do
use Oban.Worker, queue: :default, max_attempts: 3
use Oban.Worker, queue: :last_fm, max_attempts: 3
@impl Oban.Worker
def perform(%Oban.Job{args: %{"id" => artist_id}}) do
case MusicLibrary.Artists.fetch_lastfm_data(artist_id) do
{:ok, _artist_info} -> :ok
error -> error
end
result =
case MusicLibrary.Artists.fetch_lastfm_data(artist_id) do
{:ok, _artist_info} -> :ok
error -> error
end
Process.sleep(500)
result
end
end
@@ -198,6 +198,18 @@ defmodule MusicLibraryWeb.ArtistLive.Show do
/>
{gettext("Refresh Wikipedia")}
</.dropdown_link>
<.dropdown_link
id={"actions-#{@artist.musicbrainz_id}-refresh-lastfm"}
phx-click={JS.push("refresh_lastfm_data", value: %{id: @artist.musicbrainz_id})}
>
<.icon
name="hero-arrow-path"
class="h-4 w-4 mr-1 phx-click-loading:animate-spin"
aria-hidden="true"
data-slot="icon"
/>
{gettext("Refresh Last.fm")}
</.dropdown_link>
</.focus_wrap>
</.dropdown>
</.button_group>
@@ -444,6 +456,14 @@ defmodule MusicLibraryWeb.ArtistLive.Show do
data: @artist_info.wikipedia_data,
type: :json
}
),
if(@artist_info.lastfm_data != %{},
do: %{
name: "lastfm",
title: gettext("Last.fm"),
data: @artist_info.lastfm_data,
type: :json
}
)
],
& &1
@@ -538,6 +558,28 @@ defmodule MusicLibraryWeb.ArtistLive.Show do
end
end
def handle_event("refresh_lastfm_data", %{"id" => id}, socket) do
case Artists.fetch_lastfm_data(id) do
{:ok, artist_info} ->
id
|> Records.get_artist_records()
|> Enum.each(&Records.generate_embedding_async/1)
{:noreply,
socket
|> assign(:artist_info, artist_info)
|> put_toast(:info, gettext("Last.fm data refreshed successfully"))}
{:error, reason} ->
{:noreply,
socket
|> put_toast(
:error,
gettext("Error refreshing Last.fm data") <> "," <> inspect(reason)
)}
end
end
def handle_event("refresh_artist_image", %{"id" => id}, socket) do
case Artists.fetch_image(id) do
{:ok, artist_info} ->
@@ -110,6 +110,19 @@ defmodule MusicLibraryWeb.MaintenanceLive.Index do
<.loading :if={@refresh_artists_wikipedia_jobs > 0} class="size-4" />
{gettext("Refresh Wikipedia data")}
</.button>
<.button
type="button"
phx-click="refresh_artists_lastfm_data"
disabled={@refresh_artists_lastfm_jobs > 0}
data-confirm={
gettext(
"Are you sure you want to refresh Last.fm data for all artists? This operation can take a long time to complete."
)
}
>
<.loading :if={@refresh_artists_lastfm_jobs > 0} class="size-4" />
{gettext("Refresh Last.fm data")}
</.button>
</.button_group>
</li>
</ul>
@@ -191,6 +204,10 @@ defmodule MusicLibraryWeb.MaintenanceLive.Index do
:refresh_artists_wikipedia_jobs,
count_jobs("MusicLibrary.Worker.ArtistRefreshWikipediaData")
)
|> assign(
:refresh_artists_lastfm_jobs,
count_jobs("MusicLibrary.Worker.FetchArtistLastFmData")
)
end
defp count_jobs(worker) do
@@ -244,6 +261,14 @@ defmodule MusicLibraryWeb.MaintenanceLive.Index do
|> put_toast(:info, gettext("Operation started in the background."))}
end
def handle_event("refresh_artists_lastfm_data", _params, socket) do
Artists.Batch.refresh_lastfm_data()
{:noreply,
socket
|> put_toast(:info, gettext("Operation started in the background."))}
end
def handle_event("db_vacuum", _params, socket) do
case Repo.vacuum() do
{:ok, _result} ->