Backlog cleanup

This commit is contained in:
Claudio Ortolina
2026-05-03 15:40:20 +01:00
parent b7666168dc
commit a6ef5996ed
@@ -0,0 +1,86 @@
---
id: ML-150
title: Extract Records sub-contexts to reduce module size
status: Done
assignee: []
created_date: '2026-04-30 10:47'
updated_date: '2026-04-30 16:08'
labels:
- refactor
- records
dependencies: []
references:
- lib/music_library/records.ex
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
The `Records` context module (450+ lines in `lib/music_library/records.ex`) handles CRUD, FTS5 search, MusicBrainz import, cover management, genre population, color extraction, PubSub notifications, and similarity embedding dispatch — too many responsibilities for a single module.
Extract focused sub-contexts:
- `Records.Search` — FTS5 search, `SearchParser` integration, search result formatting
- `Records.Import` — MusicBrainz release/group import, barcode scan integration
- `Records.Enrichment` — genre population, color extraction, cover management, embedding dispatch
Keep the public `Records` module as a facade that re-exports key functions for backward compatibility with all existing callers (LiveViews, workers, controllers).
The `SearchIndex` schema, `Record` schema, `Similarity` module, `TracklistPdf`, and `Batch` sub-modules stay as-is. Only `records.ex` itself is being split.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 `Records.Search`, `Records.Import`, and `Records.Enrichment` modules exist with focused responsibilities
- [x] #2 Public `Records` module re-exports all previously-public functions through delegation
- [x] #3 All callers (LiveViews, workers, controllers, tests) continue to work without changes to their import/alias lines
- [x] #4 Full test suite passes with no regressions
- [x] #5 `@moduledoc` for each new module explains its responsibility
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
## Implementation Summary
### New modules created
**`Records.Search`** (`lib/music_library/records/search.ex`):
- `essential_fields/0`, `search_records/3`, `search_records_count/2`, `list_genres/0`
- Private: `build_search/3`, `fts_escape/1`, `fts_query_escape/1`
- Imports `order_alphabetically` macro from `Records` (one-direction dependency, no cycle)
**`Records.Import`** (`lib/music_library/records/import.ex`):
- `get_release_status/2`, `get_artist_records/1`
- `import_from_musicbrainz_release/2`, `import_from_musicbrainz_release_group/2`
- Private: `get_cover_art_or_default/1`, `build_record_attrs/2`
- Calls `Records.create_record/1` and `Records.Search.essential_fields/0`
**`Records.Enrichment`** (`lib/music_library/records/enrichment.ex`):
- `populate_genres/1`, `populate_genres_async/1`
- `refresh_cover/1`, `refresh_cover_async/1`
- `extract_colors/1`, `resize_cover/1`
- `refresh_musicbrainz_data/1`, `refresh_musicbrainz_data_async/1`
- `best_effort_extract_colors/1` (public, called from `Records.create_record/1`)
- Private: `maybe_extract_colors/1`, `enqueue_worker/3`, `record_meta/1`
- Calls `Records.update_record/2`
### Facade (`lib/music_library/records.ex`)
- 15 `defdelegate` calls covering all moved functions (compile-time safety)
- `order_alphabetically` macro stays in `Records` for backward compatibility
- CRUD (`get_record`, `create_record`, `update_record`, `delete_record`, `change_record`) and PubSub (`subscribe`, `notify_update`) stay directly in `Records`
### Dependency graph (one direction only):
```
Records.Search → imports macro from Records
Records.Import → calls Records, Records.Search
Records.Enrichment → calls Records
Records → delegates to Search, Import, Enrichment (compile-time: defdelegate only, no runtime cycle)
```
### Tests
- `test/music_library/records_test.exs` — CRUD tests (6 tests)
- `test/music_library/records/search_test.exs` — search tests (13 tests)
- `test/music_library/records/import_test.exs` — import tests (3 tests)
- `test/music_library/records/enrichment_test.exs` — enrichment tests (3 tests)
<!-- SECTION:NOTES:END -->