Files
music_library/lib/music_library/artists/artist_info.ex
T
Claudio Ortolina 77aae3be38 Document casting of id param
Closes #136
2026-03-25 12:03:05 +00:00

138 lines
3.8 KiB
Elixir

defmodule MusicLibrary.Artists.ArtistInfo do
use Ecto.Schema
import Ecto.Changeset
alias MusicBrainz.ExternalLink
alias MusicLibrary.Notes.Note
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "artist_infos" do
field :musicbrainz_data, :map, default: %{}
field :discogs_data, :map, default: %{}
field :wikipedia_data, :map, default: %{}
field :lastfm_data, :map, default: %{}
field :image_data_hash, :string
has_one :note, Note, foreign_key: :musicbrainz_id
timestamps(type: :utc_datetime)
end
@type t :: %__MODULE__{}
@spec changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t()
def changeset(artist_info, attrs) do
# :id is cast because it's set from MusicBrainz, not autogenerated
artist_info
|> cast(attrs, [
:id,
:musicbrainz_data,
:discogs_data,
:wikipedia_data,
:lastfm_data,
:image_data_hash
])
|> validate_required([:musicbrainz_data])
end
@spec country(t()) :: %{name: String.t(), code: String.t()}
def country(artist_info) do
area = Map.get(artist_info.musicbrainz_data, "area", %{})
country_code =
case area["iso-3166-1-codes"] || area["iso-3166-2-codes"] do
[code | _rest] -> code
nil -> artist_info.musicbrainz_data["country"]
end
%{name: area["name"] || "World", code: country_code || "XW"}
end
@spec extract_image(t()) ::
{:ok, %{url: String.t(), width: integer()}}
| {:error, :no_discogs_data | :image_not_found}
def extract_image(artist_info) when is_nil(artist_info.discogs_data) do
{:error, :no_discogs_data}
end
def extract_image(artist_info) do
image =
extract_image(artist_info.discogs_data, "primary") ||
extract_image(artist_info.discogs_data, "secondary")
if image do
{:ok, %{url: image["resource_url"], width: image["width"]}}
else
{:error, :image_not_found}
end
end
defp extract_image(discogs_data, type) do
Enum.find(discogs_data["images"] || [], fn image ->
image["type"] == type
end)
end
@external_link_patterns %{
"ProgArchives" => "progarchives.com"
}
@spec external_links(t()) :: [map()]
def external_links(artist_info),
do: ExternalLink.external_links(artist_info.musicbrainz_data, @external_link_patterns)
@spec discogs_id(t()) :: integer() | nil
def discogs_id(artist_info) do
case artist_info.discogs_data do
%{"id" => discogs_id} -> discogs_id
_ -> nil
end
end
@spec wikidata_id(t()) :: String.t() | nil
def wikidata_id(artist_info) do
relations = get_in(artist_info.musicbrainz_data, ["relations"]) || []
Enum.find_value(relations, fn
%{"type" => "wikidata", "url" => %{"resource" => "https://www.wikidata.org/wiki/" <> id}} ->
id
_ ->
nil
end)
end
@spec wikipedia_bio(t()) :: String.t() | nil
def wikipedia_bio(artist_info) do
get_in(artist_info.wikipedia_data, ["intro_html"]) ||
get_in(artist_info.wikipedia_data, ["extract_html"])
end
@spec wikipedia_summary(t()) :: String.t() | nil
def wikipedia_summary(artist_info) do
get_in(artist_info.wikipedia_data, ["extract"])
end
@spec wikipedia_url(t()) :: String.t() | nil
def wikipedia_url(artist_info) do
get_in(artist_info.wikipedia_data, ["content_urls", "desktop", "page"])
end
@spec wikipedia_description(t()) :: String.t() | nil
def wikipedia_description(artist_info) do
get_in(artist_info.wikipedia_data, ["description"])
end
@spec lastfm_tags(t()) :: [map()]
def lastfm_tags(artist_info) do
get_in(artist_info.lastfm_data, ["tags"]) || []
end
@spec lastfm_similar_artists(t()) :: [map()]
def lastfm_similar_artists(artist_info) do
get_in(artist_info.lastfm_data, ["similar_artists"]) || []
end
end