Files
music_library/lib/last_fm.ex
T
Claudio Ortolina 9e1dbfd530 Re-enable Credo ModuleDoc check
Closes #108
2026-03-13 10:52:01 +00:00

115 lines
3.6 KiB
Elixir

defmodule LastFm do
@moduledoc """
Last.fm API facade for scrobbling and listening history.
"""
alias LastFm.{API, Feed, Refresh, Scrobble, Track, Worker}
alias MusicLibrary.{BackgroundRepo, Repo}
@spec subscribe_to_feed() :: :ok
def subscribe_to_feed, do: Feed.subscribe()
@spec refresh_scrobbled_tracks() :: :ok | {:error, term()}
def refresh_scrobbled_tracks, do: Refresh.refresh()
@spec get_tracks(keyword()) :: {:ok, [Track.t()]} | {:error, term()}
def get_tracks(opts) do
last_fm_config = last_fm_config()
API.get_recent_tracks(opts, last_fm_config)
end
@spec lowest_scrobbled_at_uts() :: integer() | nil
def lowest_scrobbled_at_uts do
Repo.aggregate(Track, :min, :scrobbled_at_uts)
end
@spec backfill_scrobbled_tracks() :: {:ok, Oban.Job.t()} | {:error, Ecto.Changeset.t()}
def backfill_scrobbled_tracks do
to_uts = lowest_scrobbled_at_uts()
%{"to_uts" => to_uts}
|> Worker.BackfillScrobbledTracks.new()
|> BackgroundRepo.insert()
end
@spec get_artist_info(String.t(), String.t()) :: {:ok, LastFm.Artist.t()} | {:error, term()}
def get_artist_info(musicbrainz_id, name) do
last_fm_config = last_fm_config()
case API.get_artist_info({:musicbrainz_id, musicbrainz_id}, last_fm_config) do
{:ok, info} ->
{:ok, info}
{:error, :invalid_parameters} ->
# Sometimes the artist info cannot be identified with the MusicBrainz ID,
# because Last.fm doesn't have that information. In that case, we try again with the artist name.
API.get_artist_info({:name, name}, last_fm_config)
error ->
error
end
end
@spec get_similar_artists(String.t(), String.t()) ::
{:ok, [LastFm.Artist.t()]} | {:error, term()}
def get_similar_artists(musicbrainz_id, name) do
last_fm_config = last_fm_config()
case API.get_similar_artists({:musicbrainz_id, musicbrainz_id}, last_fm_config) do
{:ok, info} ->
{:ok, info}
{:error, :invalid_parameters} ->
# Sometimes the artist info cannot be identified with the MusicBrainz ID,
# because Last.fm doesn't have that information. In that case, we try again with the artist name.
API.get_similar_artists({:name, name}, last_fm_config)
error ->
error
end
end
@spec get_artist_tags(String.t(), String.t()) ::
{:ok, [{String.t(), integer()}]} | {:error, term()}
def get_artist_tags(musicbrainz_id, name) do
last_fm_config = last_fm_config()
case API.get_artist_tags({:musicbrainz_id, musicbrainz_id}, last_fm_config) do
{:ok, tags} ->
{:ok, tags}
{:error, :invalid_parameters} ->
# Sometimes the artist cannot be identified with the MusicBrainz ID,
# because Last.fm doesn't have that information. In that case, we try again with the artist name.
API.get_artist_tags({:name, name}, last_fm_config)
error ->
error
end
end
@spec get_session(String.t()) :: {:ok, LastFm.Session.t()} | {:error, term()}
def get_session(token) do
last_fm_config = last_fm_config()
API.get_session(token, last_fm_config)
end
@spec scrobble([Scrobble.t()], String.t()) :: {:ok, map()} | {:error, term()}
def scrobble(scrobbles, session_key) do
last_fm_config = last_fm_config()
scrobbles
|> Enum.map(&Scrobble.encode/1)
|> API.scrobble(session_key, last_fm_config)
end
@spec auth_url() :: String.t()
def auth_url do
last_fm_config = last_fm_config()
"https://www.last.fm/api/auth/?api_key=" <> last_fm_config.api_key
end
defp last_fm_config, do: LastFm.Config.resolve(:music_library)
end