diff --git a/lib/music_library/listening_stats.ex b/lib/music_library/listening_stats.ex index 18308900..a1d285bc 100644 --- a/lib/music_library/listening_stats.ex +++ b/lib/music_library/listening_stats.ex @@ -14,6 +14,17 @@ defmodule MusicLibrary.ListeningStats do @pagination Application.compile_env!(:music_library, :pagination) + @insertable_fields [ + :musicbrainz_id, + :title, + :artist, + :album, + :cover_url, + :scrobbled_at_uts, + :scrobbled_at_label, + :last_fm_data + ] + @type period_opts :: [ period: atom(), limit: non_neg_integer(), @@ -26,6 +37,34 @@ defmodule MusicLibrary.ListeningStats do Repo.aggregate(Track, :count, :scrobbled_at_uts) end + @spec update([LastFm.Track.t()]) :: {:ok, non_neg_integer()} | no_return + def update(tracks) do + track_params = + tracks + |> Enum.map(fn t -> Map.take(t, @insertable_fields) end) + |> Enum.map(&Map.to_list/1) + + {count, tracks} = + Repo.insert_all(Track, track_params, + on_conflict: :nothing, + conflict_target: [:scrobbled_at_uts, :title], + returning: true + ) + + tracks + |> MusicLibrary.ScrobbleRules.apply_all_rules() + |> MusicLibrary.ScrobbleRules.log_apply_results() + + Phoenix.PubSub.broadcast(MusicLibrary.PubSub, "listening_stats:update", %{track_count: count}) + + {:ok, count} + end + + @spec subscribe() :: :ok + def subscribe do + Phoenix.PubSub.subscribe(MusicLibrary.PubSub, "listening_stats:update") + end + @spec artist_play_count(String.t()) :: non_neg_integer() def artist_play_count(artist_musicbrainz_id) do from(t in Track, diff --git a/test/music_library/listening_stats_test.exs b/test/music_library/listening_stats_test.exs index 707b1d30..fa635db5 100644 --- a/test/music_library/listening_stats_test.exs +++ b/test/music_library/listening_stats_test.exs @@ -9,6 +9,55 @@ defmodule MusicLibrary.ListeningStatsTest do alias MusicLibrary.ListeningStats alias MusicLibrary.Records.Record + describe "update/1 and subscribe/0" do + test "stores tracks and broadcasts the updated track count" do + :ok = ListeningStats.subscribe() + + track_one = %Track{ + musicbrainz_id: "5689211e-9afa-3c3e-8e34-63dc0de45ef1", + title: "The Flow", + artist: %LastFm.Artist{ + musicbrainz_id: "0cf0af1f-20ca-4863-9b24-5f52772f7715", + name: "Anekdoten" + }, + album: %LastFm.Album{ + musicbrainz_id: "08237599-8fdf-4e2b-a7c9-eb5336f60346", + title: "Vemod" + }, + cover_url: + "https://lastfm.freetls.fastly.net/i/u/64s/9741e297b9884a4294624f0f90e14749.jpg", + scrobbled_at_uts: 1_731_318_211, + scrobbled_at_label: "11 Nov 2024, 09:43", + last_fm_data: %{} + } + + track_two = %Track{ + musicbrainz_id: "619cb295-b155-3e35-b65a-396a7cd1fc47", + title: "Wheel", + artist: %LastFm.Artist{ + musicbrainz_id: "0cf0af1f-20ca-4863-9b24-5f52772f7715", + name: "Anekdoten" + }, + album: %LastFm.Album{ + musicbrainz_id: "08237599-8fdf-4e2b-a7c9-eb5336f60346", + title: "Vemod" + }, + cover_url: + "https://lastfm.freetls.fastly.net/i/u/64s/9741e297b9884a4294624f0f90e14749.jpg", + scrobbled_at_uts: 1_731_318_945, + scrobbled_at_label: "11 Nov 2024, 09:55", + last_fm_data: %{} + } + + assert {:ok, 2} == ListeningStats.update([track_two, track_one]) + assert_receive %{track_count: 2} + + # Tracks have already been inserted, count of new tracks is 0 + assert {:ok, 0} == ListeningStats.update([track_two, track_one]) + assert_receive %{track_count: 0} + end + end + describe "scrobble_count/0" do test "returns correct count" do initial_count = ListeningStats.scrobble_count()