Commit Graph

1939 Commits

Author SHA1 Message Date
Claudio Ortolina b557fe3eea Fix record picker search results clearing on add
The update/2 callback unconditionally reset the query and results to empty
on every parent re-render. Changed to assign_new/3 so search state persists
when the parent updates the record set after adding a record.
2026-05-09 20:11:51 +01:00
Claudio Ortolina be7a9d5990 Start PubSub before Oban to prevent registry errors
Phoenix.PubSub must be initialized (creating its internal Registry)
before Oban starts processing jobs. With one_for_one strategy,
children start sequentially; previously Oban was started first,
causing 'unknown registry: MusicLibrary.PubSub' errors when a
job called broadcast before PubSub was ready (error #3891).
2026-05-08 14:10:56 +01:00
Claudio Ortolina ed6032bd15 Slow down shine animation, make it left to right only
Avoids dark edge at the bottom
2026-05-07 22:42:04 +01:00
Claudio Ortolina 621b35d824 Open universal search modal instantly via client-side JS
Replace phx-click server event with Fluxon.open_dialog for zero-latency
modal opening. The modal DOM is now always present (no :if conditional) so
it can be shown instantly; the server syncs state in the background.

Fix test selectors that now match the always-present modal form by scoping
to forms without phx-target (which the modal form always has).
2026-05-07 20:43:15 +01:00
Claudio Ortolina 10a24da49c Improve shine animations 2026-05-07 14:19:42 +01:00
Claudio Ortolina 5f240fe573 Fix stale PubSub subscriptions on record navigation
Subscription management was only done in mount/3, meaning
navigating to a different record via handle_params/3 left the
old subscription active. A background update for the old record
would then overwrite the currently displayed record.

Fix: manage subscriptions in handle_params/3 (unsubscribe old,
subscribe new) via shared RecordActions.manage_subscription/2,
and guard handle_info({:update,...}) against mismatched IDs.
2026-05-07 10:14:10 +01:00
Claudio Ortolina 0a9fafd35e Add micro_cover_url 2026-05-05 23:22:37 +01:00
Claudio Ortolina be76d0f214 Provide mini covers (150px) 2026-05-05 16:33:43 +01:00
Claudio Ortolina cccd28a9f9 ML-165: implementation 2026-05-05 13:43:18 +01:00
Claudio Ortolina ba2566828c Fix FTS5 crashes when searching with special characters
Always wrap FTS5 query terms in phrase double-quotes instead of
checking a hardcoded list of special characters. Characters like
|, ;, +, #, @ were missing from the list, causing FTS5 syntax
errors (production errors #1 and #1398). Phrase-quoting every term
produces identical results for normal ASCII search (verified via
SQL) while safely handling any character FTS5 chokes on.

Add regression tests for bare special characters and special
characters mixed with normal words.
2026-05-05 12:05:12 +01:00
Claudio Ortolina 7c6760d7db ML-158: force single-line log output in production
Three-layer architecture:
- Logster v2 handles HTTP request and LiveView socket telemetry in logfmt format
- Custom formatter as safety net escaping any remaining embedded newlines
- Config flag (single_line_logging) gating Logster attachment to prod only

Dev environment retains multi-line format for readability.
2026-05-04 15:01:10 +01:00
Claudio Ortolina 1ddef07bcc fix(api): handle non-integer error ids, clamp negative pagination params
- show/2: use case instead of bare Integer.parse match so
  /api/v1/errors/not-an-id returns 404 instead of crashing
- index/2: clamp limit to >= 1 and offset to >= 0 to prevent
  SQLite from treating negative values as unlimited
- Add 3 tests: non-integer id 404, negative limit/offset clamping
2026-05-04 14:17:45 +01:00
Claudio Ortolina 93ac86f748 fix(errors): LIKE wildcard escaping, memory-efficient counts, context tests
- Escape LIKE wildcards (% and _) in search queries using fragment ESCAPE
  to prevent accidental pattern expansion and potential DoS vectors
- Use Repo.aggregate for occurrence_count instead of loading all
  occurrences into memory in get_error/1
- Use Repo.aggregate(:min, :inserted_at) for first_occurrence_at instead
  of fragile List.last/1 that depends on query ordering
- Add MusicLibrary.ErrorsTest with 15 tests covering list_errors/1
  (default, status/muted/search filters, wildcard escaping, pagination,
  empty results) and get_error/1 (with/without occurrences, not_found)
2026-05-04 14:11:46 +01:00
Claudio Ortolina 51bd24d5b4 ML-162: enable /api/v1/errors endpoint 2026-05-04 13:14:40 +01:00
Claudio Ortolina 84012cbd1c Update to error_tracker 0.9
Includes amendments resulting from new events, and proper handling of
the muted attribute
2026-05-03 14:46:40 +01:00
Claudio Ortolina db7e50d68b Move flash messages to the left
As the application makes heavy use of right-hand sheets, flash messages
are often lost.
2026-05-03 08:52:26 +01:00
Claudio Ortolina 3600bb204b Set explicit max_frame_size on websocket 2026-05-02 07:12:20 +01:00
Claudio Ortolina 0b78f24fed ML-155: Compact collection summary and add stats preamble 2026-05-02 07:11:18 +01:00
Claudio Ortolina 450114100b Fix dimensions of camera button and preview 2026-05-01 22:09:44 +01:00
Claudio Ortolina ca5b13ad8d Remove some uses of List.last 2026-05-01 17:03:42 +01:00
Claudio Ortolina c11009f3f9 Update dependencies
ex_slop 0.3.1 => 0.4.0
2026-05-01 17:03:14 +01:00
Claudio Ortolina 3b558044b4 refactor: extract Records sub-contexts (Search, Import, Enrichment)
Split the 450+ line Records context into focused modules:

- Records.Search — FTS5 search, SearchParser integration, genre listing
- Records.Import — MusicBrainz release/group import, release status
- Records.Enrichment — genre population, cover refresh, color extraction,
  MusicBrainz data refresh
- Records.Query — shared helpers (essential_fields, order_alphabetically macro)

Records module is now a facade with defdelegate for compile-time safety.
CRUD and PubSub stay directly in Records. Tests moved alongside each
sub-module. Zero callers changed, 886 tests pass.
2026-04-30 17:20:37 +01:00
Claudio Ortolina 3849b338f3 Improve performance of job counting in maintenance
- Use a single query for all workers
- Replace the manual Ecto query against Oban.Job with Oban.Job.query/1
2026-04-30 14:54:56 +01:00
Claudio Ortolina 08fcd70a8c ML-154: add early platform detection for SQLite extensions 2026-04-30 13:39:39 +01:00
Claudio Ortolina 13068aa0ac ML-153: move MusicBrainz data transforms from Record 2026-04-30 13:21:18 +01:00
Claudio Ortolina b8b3b24ffa ML-151: document Assets.Cache TTL and invalidation strategy
Expand @moduledoc with cache key structure, 7-day TTL via
@one_week_seconds, periodic pruning by PruneAssetCache (every 12h),
and rationale for TTL-only invalidation (content-addressable immutable
assets). Update architecture.md entry accordingly.
2026-04-30 13:07:06 +01:00
Claudio Ortolina c489fde1cc ML-152: add /api/v1/ version prefix to all API routes 2026-04-30 11:57:43 +01:00
Claudio Ortolina f8a52993c1 ML-149: Fix selected bar not showing track data in Safari 2026-04-30 07:21:25 +01:00
Claudio Ortolina 597cc89821 ML-147: Upgrade to ServerSentEvents 1.0.0 2026-04-27 22:02:45 +01:00
Claudio Ortolina f10a932aea Re-enable medium header + checkbox for releases with 1 medium 2026-04-27 11:47:32 +01:00
Claudio Ortolina 4075ec45ac Make sure creating artist infos returns all fields 2026-04-26 19:24:56 +01:00
Claudio Ortolina b64ab98189 Extract to pipe 2026-04-26 19:08:21 +01:00
Claudio Ortolina eb9b1866a8 Streamline function implementation 2026-04-26 19:05:27 +01:00
Claudio Ortolina d6e154b7b3 Remove redundant case 2026-04-26 19:01:44 +01:00
Claudio Ortolina 5b94d6bdcf Avoid intermediate variables, and use consistent names 2026-04-26 07:57:40 +01:00
Claudio Ortolina 4fb1bf5079 Remove duplication in implementation 2026-04-25 21:34:11 +01:00
Claudio Ortolina d4132d8851 Uniform interface of lastfm refresh async function 2026-04-25 21:30:06 +01:00
Claudio Ortolina 9f8ef6c329 Use larger image for Last.fm artists 2026-04-25 21:22:18 +01:00
Claudio Ortolina 9113df5879 ML-146: honour API retry headers 2026-04-25 07:39:49 +01:00
Claudio Ortolina ab4eaa8890 Use if instead of case -> nil 2026-04-25 06:37:02 +01:00
Claudio Ortolina 4418fbc377 Remove unnecessary specs
Function is private, and AsyncResult doesn't expose `t()`
2026-04-24 14:13:04 +01:00
Claudio Ortolina a1c665b490 ML-21: Classify API errors as transient vs permanent 2026-04-24 14:02:15 +01:00
Claudio Ortolina 2e38412a84 ML-10: break compile cycle via Artists → Collection edge 2026-04-24 10:49:47 +01:00
Claudio Ortolina 252caf31ce ML-11: Improve performance of Telemetry.Storage with in-memory buffer 2026-04-24 10:30:28 +01:00
Claudio Ortolina 90db113845 ML-5: document Last.fm OAuth callback trust boundary
Closes GitHub issue #178.
2026-04-24 08:05:07 +01:00
Claudio Ortolina 0784fc5e0c Fix page title for scrobble anything release page 2026-04-23 21:06:32 +01:00
Claudio Ortolina 1bd5707cb4 Fix page title for scrobble anything index and release group route 2026-04-23 21:06:12 +01:00
Claudio Ortolina d03e288353 Make sure top_by_period components have a proper ID 2026-04-23 16:41:18 +01:00
Claudio Ortolina 62586c9ae5 Restore flex context for sheet selection bar 2026-04-23 15:12:58 +01:00
Claudio Ortolina ca9cc7687c Hide release-actions dropdown when empty 2026-04-23 15:06:45 +01:00