+27
-4
@@ -4,11 +4,34 @@
|
||||
name: "default",
|
||||
checks: %{
|
||||
extra: [
|
||||
{Credo.Check.Refactor.Nesting, max_nesting: 3}
|
||||
{Credo.Check.Refactor.Nesting, max_nesting: 3},
|
||||
{Credo.Check.Readability.ModuleDoc,
|
||||
ignore_names: [
|
||||
# Credo defaults
|
||||
~r/(\.\w+Controller|\.Endpoint|\.\w+Live(\.\w+)?|\.Repo|\.Router|\.\w+Socket|\.\w+View|\.\w+HTML|\.\w+JSON|\.Telemetry|\.Layouts|\.Mailer)$/,
|
||||
# Custom: external API internals follow a three-module pattern
|
||||
# where only the facade needs documentation
|
||||
~r/\.API(\..*)?$/,
|
||||
~r/\.Config$/,
|
||||
# Custom: test support modules
|
||||
~r/Fixtures/,
|
||||
# Custom: mix tasks
|
||||
~r/^Mix\.Tasks\./
|
||||
],
|
||||
ignore_modules_using: [
|
||||
# Credo defaults
|
||||
Credo.Check,
|
||||
Ecto.Schema,
|
||||
Phoenix.LiveView,
|
||||
~r/\.Web$/,
|
||||
# Custom: LiveComponents and Oban workers are self-documenting
|
||||
Oban.Worker,
|
||||
# Matches `use MusicLibraryWeb, :live_component` etc.
|
||||
MusicLibraryWeb
|
||||
]},
|
||||
{Credo.Check.Refactor.CyclomaticComplexity, max_complexity: 12}
|
||||
],
|
||||
disabled: [
|
||||
{Credo.Check.Readability.ModuleDoc, []}
|
||||
]
|
||||
disabled: []
|
||||
}
|
||||
# files etc.
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule BraveSearch do
|
||||
@moduledoc """
|
||||
Brave Search API facade for cover art and artist image search.
|
||||
"""
|
||||
|
||||
alias BraveSearch.API
|
||||
|
||||
@spec search_images(String.t(), keyword()) :: {:ok, [map()]} | {:error, term()}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule Discogs do
|
||||
@moduledoc """
|
||||
Discogs API facade for artist profiles and images.
|
||||
"""
|
||||
|
||||
alias Discogs.API
|
||||
|
||||
@spec get_artist(integer() | String.t()) :: {:ok, map()} | {:error, term()}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
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}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule LastFm.Import do
|
||||
@moduledoc false
|
||||
|
||||
@spec batch(keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
|
||||
def batch(opts) do
|
||||
with {:ok, tracks} <- LastFm.get_tracks(opts) do
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule LastFm.Scrobble do
|
||||
@moduledoc false
|
||||
|
||||
defstruct [:track, :artist, :timestamp, :album, :album_artist, :mbid]
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule LastFm.Session do
|
||||
@moduledoc false
|
||||
|
||||
require Record
|
||||
|
||||
defstruct [:name, :key, :pro]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule LastFm.Supervisor do
|
||||
@moduledoc false
|
||||
|
||||
use Supervisor
|
||||
|
||||
def start_link(config) do
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicBrainz do
|
||||
@moduledoc """
|
||||
MusicBrainz API facade for release and artist metadata search.
|
||||
"""
|
||||
|
||||
alias MusicBrainz.API
|
||||
|
||||
@type search_opts :: [limit: non_neg_integer(), offset: non_neg_integer()]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicBrainz.Artist do
|
||||
@moduledoc false
|
||||
|
||||
@enforce_keys [:id, :name, :sort_name]
|
||||
defstruct [:id, :name, :sort_name, :country, :relations, :musicbrainz_data]
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicBrainz.ExternalLink do
|
||||
@moduledoc false
|
||||
|
||||
defstruct [:name, :url]
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicBrainz.Release do
|
||||
@moduledoc false
|
||||
|
||||
@enforce_keys [
|
||||
:id,
|
||||
:title,
|
||||
@@ -38,6 +40,8 @@ defmodule MusicBrainz.Release do
|
||||
}
|
||||
|
||||
defmodule Artist do
|
||||
@moduledoc false
|
||||
|
||||
@enforce_keys [:id, :name, :sort_name]
|
||||
defstruct [:id, :name, :sort_name]
|
||||
|
||||
@@ -49,6 +53,8 @@ defmodule MusicBrainz.Release do
|
||||
end
|
||||
|
||||
defmodule Medium do
|
||||
@moduledoc false
|
||||
|
||||
@enforce_keys [:title, :format, :number, :track_count, :tracks]
|
||||
defstruct [:title, :format, :number, :track_count, :tracks]
|
||||
|
||||
@@ -62,6 +68,8 @@ defmodule MusicBrainz.Release do
|
||||
end
|
||||
|
||||
defmodule Track do
|
||||
@moduledoc false
|
||||
|
||||
@enforce_keys [:id, :title, :artists, :length, :number, :position]
|
||||
defstruct [:id, :title, :artists, :length, :number, :position]
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicBrainz.ReleaseGroup do
|
||||
@moduledoc false
|
||||
|
||||
alias MusicBrainz.ReleaseGroupSearchResult
|
||||
|
||||
@spec included_release_groups(map()) :: [ReleaseGroupSearchResult.t()]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicBrainz.ReleaseGroupSearchResult do
|
||||
@moduledoc false
|
||||
|
||||
alias MusicBrainz.ReleaseGroup
|
||||
|
||||
@enforce_keys [:id, :type, :title, :artists, :release_date]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicBrainz.ReleaseSearchResult do
|
||||
@moduledoc false
|
||||
|
||||
alias MusicBrainz.ReleaseGroup
|
||||
|
||||
@enforce_keys [:id, :title, :release_group, :artists, :date, :barcode, :media]
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.ArtistChat do
|
||||
@moduledoc """
|
||||
Chat implementation for artists using OpenAI streaming with Wikipedia context.
|
||||
"""
|
||||
|
||||
@behaviour MusicLibrary.Chat
|
||||
|
||||
alias MusicLibrary.Artists.ArtistInfo
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Artists do
|
||||
@moduledoc """
|
||||
Artist metadata management from MusicBrainz, Discogs, Wikipedia, and Last.fm.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
|
||||
alias MusicLibrary.Artists.ArtistInfo
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Artists.Batch do
|
||||
@moduledoc """
|
||||
Batch operations for artists: refresh MusicBrainz, Discogs, Wikipedia, and Last.fm data.
|
||||
"""
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias MusicLibrary.Artists
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Assets do
|
||||
@moduledoc """
|
||||
Binary asset storage and retrieval for covers and artist images.
|
||||
"""
|
||||
|
||||
alias MusicLibrary.Assets.{Asset, Cache}
|
||||
alias MusicLibrary.Repo
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Assets.Cache do
|
||||
@moduledoc """
|
||||
ETS-based asset cache with TTL for serving frequently accessed images.
|
||||
"""
|
||||
|
||||
@spec new() :: :ets.table()
|
||||
def new do
|
||||
:ets.new(__MODULE__, [:named_table, :public, :compressed, read_concurrency: true])
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Assets.Image do
|
||||
@moduledoc """
|
||||
Image processing via Vix (libvips) for covers and artist images.
|
||||
"""
|
||||
|
||||
alias Vix.Vips.{Image, Operation}
|
||||
|
||||
fallback_path = Application.app_dir(:music_library, ["priv", "image-not-found.jpg"])
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Assets.Transform do
|
||||
@moduledoc """
|
||||
Represents an image transformation (hash + target width) for asset serving.
|
||||
"""
|
||||
|
||||
@derive JSON.Encoder
|
||||
defstruct [:hash, :width]
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicLibrary.BackgroundRepo do
|
||||
@moduledoc false
|
||||
|
||||
use Ecto.Repo,
|
||||
otp_app: :music_library,
|
||||
adapter: Ecto.Adapters.SQLite3
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.BarcodeScan do
|
||||
@moduledoc """
|
||||
Barcode-to-MusicBrainz lookup workflow.
|
||||
"""
|
||||
|
||||
alias MusicLibrary.BarcodeScan.Result
|
||||
alias MusicLibrary.Records
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicLibrary.BarcodeScan.Result do
|
||||
@moduledoc false
|
||||
|
||||
defstruct [:status, :number, :record_id, :release]
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Batch do
|
||||
@moduledoc """
|
||||
Generic batch runner: streams records through a transaction with error accumulation.
|
||||
"""
|
||||
|
||||
alias MusicLibrary.Repo
|
||||
|
||||
require Logger
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Chat do
|
||||
@moduledoc """
|
||||
Behaviour for streaming AI chat with entity-specific context.
|
||||
"""
|
||||
|
||||
@callback stream_response(
|
||||
messages :: list(map()),
|
||||
context :: term(),
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Collection do
|
||||
@moduledoc """
|
||||
Queries for collected records (where `purchased_at` is set).
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
import MusicLibrary.Records, only: [order_alphabetically: 0]
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
defmodule MusicLibrary.Encrypted.Binary do
|
||||
@moduledoc false
|
||||
use Cloak.Ecto.Binary, vault: MusicLibrary.Vault
|
||||
end
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Notes do
|
||||
@moduledoc """
|
||||
Free-text notes attached to records and artists.
|
||||
"""
|
||||
|
||||
alias MusicLibrary.Notes.Note
|
||||
alias MusicLibrary.Repo
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.RecordChat do
|
||||
@moduledoc """
|
||||
Chat implementation for records using OpenAI streaming with web search.
|
||||
"""
|
||||
|
||||
@behaviour MusicLibrary.Chat
|
||||
|
||||
alias MusicLibrary.Records.Record
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.RecordSets do
|
||||
@moduledoc """
|
||||
User-curated record groupings with ordered items.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
|
||||
alias MusicLibrary.RecordSets.{RecordSet, RecordSetItem}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Records.Batch do
|
||||
@moduledoc """
|
||||
Batch operations for records: refresh MusicBrainz data and generate embeddings.
|
||||
"""
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias MusicLibrary.Batch
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Records.TracklistPdf do
|
||||
@moduledoc """
|
||||
Generates 120mm x 120mm PDF tracklists from record and release data via Typst.
|
||||
"""
|
||||
|
||||
alias MusicBrainz.Release
|
||||
alias MusicLibraryWeb.Duration
|
||||
alias Typst.Format
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.ScrobbleActivity do
|
||||
@moduledoc """
|
||||
Scrobbling releases, media, and tracks to Last.fm.
|
||||
"""
|
||||
|
||||
alias LastFm.Scrobble
|
||||
alias MusicBrainz.Release
|
||||
alias MusicLibrary.Secrets
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Secrets do
|
||||
@moduledoc """
|
||||
Encrypted key-value storage for API keys and credentials.
|
||||
"""
|
||||
|
||||
alias MusicLibrary.Repo
|
||||
alias MusicLibrary.Secrets.Secret
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicLibrary.TelemetryRepo do
|
||||
@moduledoc false
|
||||
|
||||
use Ecto.Repo,
|
||||
otp_app: :music_library,
|
||||
adapter: Ecto.Adapters.SQLite3
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
defmodule MusicLibrary.Vault do
|
||||
@moduledoc false
|
||||
use Cloak.Vault, otp_app: :music_library
|
||||
end
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule MusicLibrary.Wishlist do
|
||||
@moduledoc """
|
||||
Queries for wishlisted records (where `purchased_at` is nil).
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
|
||||
alias MusicLibrary.Records
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicLibraryWeb.Auth do
|
||||
@moduledoc false
|
||||
|
||||
use Gettext, backend: MusicLibraryWeb.Gettext
|
||||
|
||||
import LiveToast, only: [put_toast: 3]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicLibraryWeb.Hooks.ShowToast do
|
||||
@moduledoc false
|
||||
|
||||
import Phoenix.LiveView
|
||||
import LiveToast, only: [put_toast: 3]
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicLibraryWeb.LiveHelpers.Params do
|
||||
@moduledoc false
|
||||
|
||||
@pagination Application.compile_env!(:music_library, :pagination)
|
||||
|
||||
@spec parse_page(String.t() | nil | term()) :: pos_integer()
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicLibraryWeb.Telemetry.Storage do
|
||||
@moduledoc false
|
||||
|
||||
use GenServer
|
||||
|
||||
@buffer_size Application.compile_env(
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule OpenAI do
|
||||
@moduledoc """
|
||||
OpenAI API facade for text embeddings and streaming chat.
|
||||
"""
|
||||
|
||||
alias OpenAI.API
|
||||
|
||||
@type chat_stream_opts :: [
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule OpenAI.Completion do
|
||||
@moduledoc false
|
||||
|
||||
@enforce_keys [:content]
|
||||
defstruct content: "",
|
||||
temperature: 0.2,
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
defmodule Wikipedia do
|
||||
@moduledoc """
|
||||
Wikipedia API facade for artist biographies.
|
||||
"""
|
||||
|
||||
alias Wikipedia.API
|
||||
|
||||
@spec get_artist_summary(String.t()) :: {:ok, map()} | {:error, :no_english_wikipedia | term()}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicLibrary.ColorHelpers do
|
||||
@moduledoc false
|
||||
|
||||
@doc """
|
||||
Returns true if the given string is a valid lowercase hex color code.
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
defmodule MusicLibraryWeb.LiveTestHelpers do
|
||||
@moduledoc false
|
||||
|
||||
def escape(string) do
|
||||
LazyHTML.html_escape(string)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user