diff --git a/lib/music_library_web/live/maintenance_live/index.ex b/lib/music_library_web/live/maintenance_live/index.ex
index 26d69eb5..006e4c66 100644
--- a/lib/music_library_web/live/maintenance_live/index.ex
+++ b/lib/music_library_web/live/maintenance_live/index.ex
@@ -1,19 +1,65 @@
defmodule MusicLibraryWeb.MaintenanceLive.Index do
use MusicLibraryWeb, :live_view
+ import Ecto.Query
+
require Logger
alias MusicLibrary.Artists
+ alias MusicLibrary.BackgroundRepo
alias MusicLibrary.Records
alias MusicLibrary.Repo
+ @poll_interval 2_000
+
def mount(_params, _session, socket) do
+ if connected?(socket) do
+ Process.send_after(self(), :update_job_counts, @poll_interval)
+ end
+
{:ok,
socket
|> assign(
page_title: gettext("Maintenance"),
current_section: :maintenance
- )}
+ )
+ |> assign_job_counts()}
+ end
+
+ def handle_info(:update_job_counts, socket) do
+ Process.send_after(self(), :update_job_counts, @poll_interval)
+
+ {:noreply, assign_job_counts(socket)}
+ end
+
+ defp assign_job_counts(socket) do
+ socket
+ |> assign(
+ :refresh_records_musicbrainz_jobs,
+ count_jobs("MusicLibrary.Worker.RecordRefreshMusicBrainzData")
+ )
+ |> assign(
+ :generate_record_embeddings_jobs,
+ count_jobs("MusicLibrary.Worker.GenerateRecordEmbedding")
+ )
+ |> assign(
+ :refresh_artists_musicbrainz_jobs,
+ count_jobs("MusicLibrary.Worker.ArtistRefreshMusicBrainzData")
+ )
+ |> assign(
+ :refresh_artists_discogs_jobs,
+ count_jobs("MusicLibrary.Worker.ArtistRefreshDiscogsData")
+ )
+ end
+
+ defp count_jobs(worker) do
+ query =
+ from j in Oban.Job,
+ where: j.worker == ^worker,
+ where: j.state in ["available", "scheduled", "executing", "retryable"],
+ select: count(j.id)
+
+ BackgroundRepo.one(query)
end
def handle_event("refresh_records_musicbrainz_data", _params, socket) do
diff --git a/lib/music_library_web/live/maintenance_live/index.html.heex b/lib/music_library_web/live/maintenance_live/index.html.heex
index 22121ff3..3e9edf1e 100644
--- a/lib/music_library_web/live/maintenance_live/index.html.heex
+++ b/lib/music_library_web/live/maintenance_live/index.html.heex
@@ -13,28 +13,34 @@
-
- <.button
- type="button"
- phx-click="refresh_records_musicbrainz_data"
- data-confirm={
- gettext(
- "Are you sure you want to refresh MusicBrainz data for all records? This operation can take a long time to complete."
- )
- }
- >
- {gettext("Refresh MusicBrainz data")}
-
- <.button
- type="button"
- phx-click="generate_record_embeddings"
- data-confirm={
- gettext(
- "Are you sure you want to regenerate embeddings for all records? This operation can take a long time to complete."
- )
- }
- >
- {gettext("Regenerate record embeddings")}
-
+ <.button_group>
+ <.button
+ type="button"
+ phx-click="refresh_records_musicbrainz_data"
+ disabled={@refresh_records_musicbrainz_jobs > 0}
+ data-confirm={
+ gettext(
+ "Are you sure you want to refresh MusicBrainz data for all records? This operation can take a long time to complete."
+ )
+ }
+ >
+ <.loading :if={@refresh_records_musicbrainz_jobs > 0} class="size-4" />
+ {gettext("Refresh MusicBrainz data")}
+
+ <.button
+ type="button"
+ phx-click="generate_record_embeddings"
+ disabled={@generate_record_embeddings_jobs > 0}
+ data-confirm={
+ gettext(
+ "Are you sure you want to regenerate embeddings for all records? This operation can take a long time to complete."
+ )
+ }
+ >
+ <.loading :if={@generate_record_embeddings_jobs > 0} class="size-4" />
+ {gettext("Regenerate record embeddings")}
+
+
@@ -47,28 +53,34 @@
-
- <.button
- type="button"
- phx-click="refresh_artists_musicbrainz_data"
- data-confirm={
- gettext(
- "Are you sure you want to refresh MusicBrainz data for all artists? This operation can take a long time to complete."
- )
- }
- >
- {gettext("Refresh MusicBrainz data")}
-
- <.button
- type="button"
- phx-click="refresh_artists_discogs_data"
- data-confirm={
- gettext(
- "Are you sure you want to refresh Discogs data for all artists? This operation can take a long time to complete."
- )
- }
- >
- {gettext("Refresh Discogs data")}
-
+ <.button_group>
+ <.button
+ type="button"
+ phx-click="refresh_artists_musicbrainz_data"
+ disabled={@refresh_artists_musicbrainz_jobs > 0}
+ data-confirm={
+ gettext(
+ "Are you sure you want to refresh MusicBrainz data for all artists? This operation can take a long time to complete."
+ )
+ }
+ >
+ <.loading :if={@refresh_artists_musicbrainz_jobs > 0} class="size-4" />
+ {gettext("Refresh MusicBrainz data")}
+
+ <.button
+ type="button"
+ phx-click="refresh_artists_discogs_data"
+ disabled={@refresh_artists_discogs_jobs > 0}
+ data-confirm={
+ gettext(
+ "Are you sure you want to refresh Discogs data for all artists? This operation can take a long time to complete."
+ )
+ }
+ >
+ <.loading :if={@refresh_artists_discogs_jobs > 0} class="size-4" />
+ {gettext("Refresh Discogs data")}
+
+