diff --git a/lib/music_library/scrobble_activity.ex b/lib/music_library/scrobble_activity.ex index 2d3bc348..541d7d08 100644 --- a/lib/music_library/scrobble_activity.ex +++ b/lib/music_library/scrobble_activity.ex @@ -240,6 +240,40 @@ defmodule MusicLibrary.ScrobbleActivity do Repo.all(query) end + @doc """ + Gets the top artists by scrobble count for the given number of days. + Returns a list of maps with artist information and play counts. + """ + def get_top_artists_by_days(days, opts) do + limit = Keyword.get(opts, :limit, 10) + current_time = Keyword.get_lazy(opts, :current_time, &DateTime.utc_now/0) + timezone = Keyword.get(opts, :timezone, &resolve_timezone!/0) + + cutoff_timestamp = + current_time + |> DateTime.add(-days, :day) + |> NaiveDateTime.beginning_of_day() + |> DateTime.from_naive!(timezone) + |> DateTime.to_unix() + + query = + from t in Track, + where: t.scrobbled_at_uts >= ^cutoff_timestamp, + group_by: [ + fragment("json_extract(artist, '$.name')"), + fragment("json_extract(artist, '$.musicbrainz_id')") + ], + select: %{ + artist_name: fragment("json_extract(artist, '$.name')"), + artist_musicbrainz_id: fragment("json_extract(artist, '$.musicbrainz_id')"), + play_count: count(t.scrobbled_at_uts) + }, + order_by: [desc: count(t.scrobbled_at_uts)], + limit: ^limit + + Repo.all(query) + end + @doc """ Gets top albums for multiple time periods (30, 90, 365 days). Returns a map with the results for each period, along with collected and @@ -268,6 +302,22 @@ defmodule MusicLibrary.ScrobbleActivity do } end + @doc """ + Gets top artists for multiple time periods (30, 90, 365 days). + Returns a map with the results for each period. + """ + def get_top_artists_by_periods(opts) do + last_30_days = get_top_artists_by_days(30, opts) + last_90_days = get_top_artists_by_days(90, opts) + last_365_days = get_top_artists_by_days(365, opts) + + %{ + last_30_days: last_30_days, + last_90_days: last_90_days, + last_365_days: last_365_days + } + end + defp resolve_timezone! do Application.get_env(:music_library, MusicLibraryWeb) |> Keyword.fetch!(:timezone) diff --git a/lib/music_library_web/live/stats_live/index.ex b/lib/music_library_web/live/stats_live/index.ex index ae11c0a7..b07d9f1c 100644 --- a/lib/music_library_web/live/stats_live/index.ex +++ b/lib/music_library_web/live/stats_live/index.ex @@ -150,6 +150,54 @@ defmodule MusicLibraryWeb.StatsLive.Index do """ end + attr :artists, :list, required: true + attr :title, :string, required: true + + def top_artists_by_period(assigns) do + ~H""" +
+

+ {@title} +

+
+
+ {artist.artist_name} ~p"/images/cover-not-found.png" <> "';"} + /> +
+ <.icon name="hero-user" class="w-6 h-6 text-zinc-400" /> +
+
+ <.link + :if={artist.artist_musicbrainz_id != ""} + class="text-sm font-medium text-zinc-900 dark:text-zinc-300 hover:text-zinc-700 dark:hover:text-zinc-400 truncate" + navigate={~p"/artists/#{artist.artist_musicbrainz_id}"} + > + {artist.artist_name} + +

+ {artist.artist_name} +

+
+ <.badge> + {artist.play_count} + +
+
+
+ """ + end + defp refresh_lastfm_feed_button(assigns) do ~H"""