Remove LastFm.Feed, LastFm.Refresh, and LastFm.Supervisor

This commit is contained in:
Claudio Ortolina
2026-03-27 23:17:25 +00:00
parent 61dd3f45c7
commit fbcf40b4b3
4 changed files with 0 additions and 219 deletions
-48
View File
@@ -1,48 +0,0 @@
defmodule LastFm.Feed do
@moduledoc """
Persists scrobbled tracks in `MusicLibrary.Repo` and publishes feed updates.
Tracks are inserted into the `scrobbled_tracks` table with conflict handling on
`[:scrobbled_at_uts, :title]` to avoid duplicates, then scrobble rules are applied
to newly inserted rows.
"""
@insertable_fields [
:musicbrainz_id,
:title,
:artist,
:album,
:cover_url,
:scrobbled_at_uts,
:scrobbled_at_label,
:last_fm_data
]
@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} =
MusicLibrary.Repo.insert_all(LastFm.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(LastFm.PubSub, "feed:update", %{track_count: count})
{:ok, count}
end
@spec subscribe() :: :ok
def subscribe do
Phoenix.PubSub.subscribe(LastFm.PubSub, "feed:update")
end
end
-100
View File
@@ -1,100 +0,0 @@
defmodule LastFm.Refresh do
@moduledoc """
A GenServer that manages periodic refreshing of Last.fm scrobbled tracks.
This module is responsible for:
- Fetching recent tracks from Last.fm at configurable intervals
- Updating an in-memory feed with the latest tracks
- Supporting both automatic and manual refresh modes
## Configuration
The server accepts a `LastFm.Config` struct with the following options:
- `auto_refresh`: When true, automatically starts refreshing on init
- `refresh_interval`: Time in milliseconds between refresh attempts
## Operation Modes
1. Automatic Mode (`auto_refresh: true`):
- Starts refreshing immediately on initialization
- Continues to refresh at the configured interval
- Handles failures gracefully by continuing to retry
2. Manual Mode (`auto_refresh: false`):
- Server remains dormant on initialization
- Refreshes only occur via explicit `refresh/0` calls
- Useful for testing or controlled refresh scenarios
## Usage
# Manual refresh
LastFm.Refresh.refresh()
The module uses `LastFm.Feed` to store and broadcast track updates to subscribers.
"""
use GenServer
alias LastFm.{API, Config, Feed}
@type config :: Config.t()
@spec start_link(config) :: GenServer.on_start()
def start_link(config) do
GenServer.start_link(__MODULE__, config, name: __MODULE__)
end
@spec refresh() :: :ok
def refresh do
GenServer.call(__MODULE__, :refresh, 10_000)
end
@impl true
@spec init(config) :: {:ok, config, {:continue, :refresh}} | :ignore
def init(config) do
if config.auto_refresh do
{:ok, config, {:continue, :refresh}}
else
:ignore
end
end
@impl true
@spec handle_call(:refresh, GenServer.from(), config) ::
{:reply, :ok | {:error, term()}, config, pos_integer()}
def handle_call(:refresh, _from, config) do
case API.get_recent_tracks(config) do
{:ok, tracks} ->
Feed.update(tracks)
{:reply, :ok, config, config.refresh_interval}
error ->
{:reply, error, config, config.refresh_interval}
end
end
@impl true
@spec handle_continue(atom(), config) :: {:noreply, config, pos_integer()}
def handle_continue(:refresh, config), do: refresh(config)
@impl true
@spec handle_info(atom(), config) :: {:noreply, config, pos_integer()}
def handle_info(:refresh, config), do: refresh(config)
def handle_info(:timeout, config), do: refresh(config)
defp refresh(config) do
case API.get_recent_tracks(config) do
{:ok, tracks} ->
Feed.update(tracks)
{:noreply, config, config.refresh_interval}
{:error, error} ->
if API.ErrorResponse.retryable_error?(error) do
{:noreply, config, API.ErrorResponse.retry_delay(error)}
else
{:stop, error, config}
end
end
end
end
-19
View File
@@ -1,19 +0,0 @@
defmodule LastFm.Supervisor do
@moduledoc false
use Supervisor
def start_link(config) do
Supervisor.start_link(__MODULE__, config, name: __MODULE__)
end
@impl true
def init(config) do
children = [
{Phoenix.PubSub, name: LastFm.PubSub},
{LastFm.Refresh, config}
]
Supervisor.init(children, strategy: :one_for_one)
end
end
-52
View File
@@ -1,52 +0,0 @@
defmodule LastFm.FeedTest do
use MusicLibrary.DataCase
alias LastFm.{Album, Artist, Track}
alias MusicLibrary.ListeningStats
@track_one %Track{
musicbrainz_id: "5689211e-9afa-3c3e-8e34-63dc0de45ef1",
title: "The Flow",
artist: %Artist{
musicbrainz_id: "0cf0af1f-20ca-4863-9b24-5f52772f7715",
name: "Anekdoten"
},
album: %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: %Artist{
musicbrainz_id: "0cf0af1f-20ca-4863-9b24-5f52772f7715",
name: "Anekdoten"
},
album: %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: %{}
}
describe "update and broadcast" do
test "stores the track and broadcasts the updated track count" do
:ok = ListeningStats.subscribe()
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
end