Commit Graph

244 Commits

Author SHA1 Message Date
Claudio Ortolina 95414f5d5c Improve styling of no results add record state 2025-07-04 15:56:46 +01:00
Claudio Ortolina 57223821d4 Remove obsolete TODO 2025-07-04 15:50:39 +01:00
Claudio Ortolina 6518a4b10e Use Fluxon from HTML helpers 2025-07-04 13:41:15 +01:00
Claudio Ortolina 714dc0b8cc Use textarea for description 2025-07-04 13:31:16 +01:00
Claudio Ortolina d91685b646 First rough implementation of scrobble rules (Claude)
This plan outlines the implementation of functionality to manage
arbitrary rules for fixing data in the `scrobbled_tracks` table. The
system will support two types of transformations:

1. **Album Rule**: Update the album MusicBrainz ID of all tracks
belonging to a specific album (identified by album title)
2. **Artist Rule**: Update the artist MusicBrainz ID of all tracks
belonging to a specific artist (identified by artist name)

- `scrobbled_tracks` table exists with the following structure:
  - `scrobbled_at_uts` (integer, primary key)
  - `musicbrainz_id` (string) - track MusicBrainz ID
  - `title` (string) - track title
  - `cover_url` (string)
  - `scrobbled_at_label` (string)
  - `artist` (map) - embedded artist data with `musicbrainz_id` and
`name`
  - `album` (map) - embedded album data with `musicbrainz_id` and
`title`
  - `last_fm_data` (map) - raw Last.fm API response

- `LastFm.Track` - Schema for scrobbled tracks
- `LastFm.Artist` - Embedded artist schema
- `LastFm.Album` - Embedded album schema

- Main navigation has Stats, Collection, Wishlist sections
- "More" dropdown contains development tools and utilities
- Uses Phoenix LiveView for interactive components

**File**:
`priv/repo/migrations/YYYYMMDDHHMMSS_create_scrobble_rules.exs`

Create `scrobble_rules` table with:

- `id` (primary key)
- `type` (string) - "album" or "artist"
- `match_value` (string) - album title or artist name to match
- `target_musicbrainz_id` (string) - MusicBrainz ID to set
- `enabled` (boolean) - whether rule is active
- `description` (text) - optional description for the rule
- `inserted_at` (timestamp)
- `updated_at` (timestamp)

Indexes:

- `type, match_value` (compound index for efficient matching)
- `enabled` (for filtering active rules)

**File**: `lib/music_library/scrobble_rules/scrobble_rule.ex`

Create Ecto schema with:

- Field definitions matching migration
- Validations:
  - `type` must be "album" or "artist"
  - `match_value` required and non-empty
  - `target_musicbrainz_id` required and non-empty
  - `enabled` defaults to true
- Changeset functions for create/update operations

**File**: `lib/music_library/scrobble_rules.ex`

Implement context functions:

- `list_scrobble_rules(opts \\ [])` - list all rules with optional
filtering
- `get_scrobble_rule!(id)` - get specific rule by ID
- `create_scrobble_rule(attrs)` - create new rule
- `update_scrobble_rule(rule, attrs)` - update existing rule
- `delete_scrobble_rule(rule)` - delete rule
- `change_scrobble_rule(rule, attrs \\ %{})` - create changeset
- `list_enabled_rules()` - get only enabled rules for worker
- `apply_album_rule(rule)` - apply album transformation
- `apply_artist_rule(rule)` - apply artist transformation
- `apply_all_rules()` - apply all enabled rules

**File**: `lib/music_library_web/components/layouts/app.html.heex`

Add new navigation link in the "More" dropdown:

- Icon: `hero-adjustments-horizontal`
- Label: "Scrobble Rules"
- Route: `/scrobble-rules`
- Position: After "Oban" link, before separator

**File**: `lib/music_library_web/router.ex`

Add new LiveView routes in the authenticated scope:

- `/scrobble-rules` - index page
- `/scrobble-rules/new` - create new rule
- `/scrobble-rules/:id/edit` - edit existing rule

**File**: `lib/music_library_web/live/scrobble_rules_live/index.ex`

Implement LiveView with:

- List view showing all rules with status indicators
- Create/Edit forms with validation
- Delete confirmation
- Filter controls (by type, enabled/disabled)
- Manual rule application triggers
- Real-time updates via PubSub

**Template**:
`lib/music_library_web/live/scrobble_rules_live/index.html.heex`

- Table layout with rule details
- Action buttons (Edit, Delete, Apply)
- Form components for create/edit
- Status indicators for enabled/disabled rules

**File**:
`lib/music_library_web/live/scrobble_rules_live/form_component.ex`

Create reusable form component:

- Type selection (album/artist)
- Match value input with validation
- Target MusicBrainz ID input
- Description textarea
- Enable/disable toggle
- Save/Cancel actions

Where possible, use the components available at
<https://docs.fluxonui.com/1.1.4/overview.html>.

**File**: `lib/music_library/scrobble_rules/worker.ex`

Implement Oban worker:

- Scheduled to run periodically (e.g., every 30 minutes)
- Fetches all enabled rules
- Applies each rule in sequence
- Logs application results
- Handles errors gracefully
- Tracks last application time

**File**: `test/music_library/scrobble_rules_test.exs`

- Test all context functions
- Test rule application logic
- Test edge cases and error handling

**File**: `test/music_library/scrobble_rules/scrobble_rule_test.exs`

- Test schema validations
- Test changeset functions
- Test constraint validations

**File**: `test/music_library_web/live/scrobble_rules_live_test.exs`

- Test LiveView interactions
- Test form submissions
- Test real-time updates
- Test navigation and routing

**File**: `test/music_library/scrobble_rules/worker_test.exs`

- Test job execution
- Test rule application
- Test error handling
- Test scheduling

- Ensure atomic operations when applying rules
- Validate MusicBrainz IDs before applying
- Log all transformations for audit trail

- Index optimization for rule matching
- Batch processing for large datasets
- Rate limiting for external API calls

- Clear feedback on rule application
- Preview mode to show what would change
- Bulk operations for managing multiple rules
- Export/import functionality for rule sets

- Input validation and sanitization
- Rate limiting on rule creation
- Audit logging for all changes

```sql
-- scrobble_rules table
CREATE TABLE scrobble_rules (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  type TEXT NOT NULL CHECK (type IN ('album', 'artist')),
  match_value TEXT NOT NULL,
  target_musicbrainz_id TEXT NOT NULL,
  enabled BOOLEAN NOT NULL DEFAULT TRUE,
  description TEXT,
  inserted_at TIMESTAMP NOT NULL,
  updated_at TIMESTAMP NOT NULL
);

CREATE INDEX idx_scrobble_rules_type_match ON scrobble_rules(type,
match_value);
CREATE INDEX idx_scrobble_rules_enabled ON scrobble_rules(enabled);
```

```
lib/music_library/
├── scrobble_rules/
│   ├── scrobble_rule.ex          # Schema
│   └── worker.ex                 # Oban worker
└── scrobble_rules.ex             # Context

lib/music_library_web/
└── live/
    └── scrobble_rules_live/
        ├── index.ex              # LiveView
        ├── index.html.heex       # Template
        └── form_component.ex     # Form component

priv/repo/migrations/
└── YYYYMMDDHHMMSS_create_scrobble_rules.exs

test/
├── music_library/
│   ├── scrobble_rules_test.exs
│   └── scrobble_rules/
│       ├── scrobble_rule_test.exs
│       └── worker_test.exs
└── music_library_web/
    └── live/
        └── scrobble_rules_live_test.exs
```

1. Database migration and schema
2. Context module with basic CRUD operations
3. LiveView interface for rule management
4. Navigation and routing updates
5. Periodic worker implementation
6. Comprehensive testing
7. Documentation and deployment

This plan ensures a robust, maintainable solution that integrates
seamlessly with the existing Music Library application architecture.
2025-07-03 16:47:08 +01:00
Claudio Ortolina 82e9d3c5a7 Extract markup from stats live to separate set of components 2025-06-29 08:49:12 +01:00
Claudio Ortolina 0c84adde59 Use tabs for top artists and top albums 2025-06-25 11:43:58 +03:00
Claudio Ortolina e2c2c6d547 Add tooltips for scrobble activity debug info 2025-06-21 15:20:24 +03:00
Claudio Ortolina 1910265aea Improve styling of record edit form title and artists 2025-06-15 09:54:36 +03:00
Claudio Ortolina ac3cda8790 Removed unused dropdown links 2025-06-15 09:46:06 +03:00
Claudio Ortolina 860682403b Replace actions_menu with dropdown component
Authored with Claude, refined by hand
2025-06-15 09:29:49 +03:00
Claudio Ortolina 2df5e7ee1d Show wishlisted date for wishlisted records 2025-06-14 12:58:47 +03:00
Claudio Ortolina 39564be894 Remove x axis labels and lines from charts
Makes the chart more minimal and readable
2025-06-12 21:40:24 +03:00
Claudio Ortolina 275e7160d3 Display record cover in edit form 2025-06-11 20:54:47 +03:00
Claudio Ortolina d14587c120 Revert "Show confetti when adding a record"
Shows up too often
2025-06-11 08:42:41 +03:00
Claudio Ortolina a31d92bc9f Show confetti when adding a record 2025-06-09 18:40:03 +01:00
Claudio Ortolina be8b7ead5f Load dominant colors from record 2025-06-07 20:41:39 +01:00
Claudio Ortolina 7efbbe0683 Show dominant colors in wishlisted record details page 2025-06-07 19:41:32 +01:00
Claudio Ortolina 0aa0a19a00 Use live_toast instead of built-in flash
Precursor for building better notifications
2025-06-07 08:27:25 +01:00
Claudio Ortolina a005a3c203 Make sure release sheet tracks are correctly aligned 2025-06-06 21:24:25 +01:00
Claudio Ortolina 167f1df8cb Extract artist_links/1 component 2025-06-04 17:42:10 +01:00
Claudio Ortolina fd997e853f Update artists when refreshing MusicBrainz data 2025-06-04 15:57:25 +01:00
Claudio Ortolina 35bca44c9b Prevent multiple scrobbles for 3 seconds 2025-06-02 20:42:19 +01:00
Claudio Ortolina d7060697b2 Format dates in release summary and label 2025-06-01 16:58:04 +01:00
Claudio Ortolina 9100c6c04a Extract actions_menu 2025-05-29 22:15:34 +01:00
Claudio Ortolina 1f64c71efa Apply quokka blocks rule 2025-05-28 19:51:54 +01:00
Claudio Ortolina 676c2e4290 Enable quokka with :module_directives rule 2025-05-28 19:41:57 +01:00
Claudio Ortolina 681db7686b Add record menu dropdown to records in artist page 2025-05-28 17:41:50 +01:00
Claudio Ortolina cc40b5dab7 No unnecessary pipes 2025-05-27 15:13:09 +01:00
Claudio Ortolina d954a3f196 Move format_duration/1 inside the only component where it's used 2025-05-27 15:08:38 +01:00
Claudio Ortolina f8b8816af0 Rename 'Digital Download' -> 'Download' for brevity 2025-05-25 20:11:07 +01:00
Claudio Ortolina cd5253cdbc Constrain display of release summary 2025-05-25 20:10:42 +01:00
Claudio Ortolina d94ebe3095 Remove search from release dropdown
Not using it, and it breaks styles
2025-05-25 18:33:42 +01:00
Claudio Ortolina efdc7ca7f1 Improve styling of release dropdown 2025-05-25 18:33:18 +01:00
Claudio Ortolina 6e19ab410d Account for VHS and Unknown formats 2025-05-25 10:32:11 +01:00
Claudio Ortolina c2943174a2 Make display of selected release option consistent with rich display 2025-05-25 08:56:45 +01:00
Claudio Ortolina 1298d711a3 Remove unnecessary selected_release_label/1 2025-05-25 08:56:24 +01:00
Claudio Ortolina b6c554af34 Display rich release summary in form and details 2025-05-25 08:45:38 +01:00
Claudio Ortolina d666765d18 Include catalog number in selected release label 2025-05-24 19:01:43 +01:00
Claudio Ortolina 9a95625a90 Revert "Enable webapp support"
This reverts commit 0cb1ddfa50.

While technically working, the iOS webapp switches back to the browser
whenever the page URL changes.
2025-05-24 15:04:51 +01:00
Claudio Ortolina 0cb1ddfa50 Enable webapp support 2025-05-24 14:59:38 +01:00
Claudio Ortolina c09438fe5e Extract json_viewer component 2025-05-24 09:39:12 +01:00
Claudio Ortolina c11e4662eb Enforce minimum width for release sheet 2025-05-22 21:16:19 +01:00
Claudio Ortolina d38fcd6fee Account for no artist country or area 2025-05-21 22:35:45 +01:00
Claudio Ortolina 81ffe184ab Make artist records larger 2025-05-21 21:56:31 +01:00
Claudio Ortolina 051c6195b5 Harden release label display 2025-05-18 20:57:17 +01:00
Claudio Ortolina 8340a8d51f When possible, use track number falling back to position
So that it can display vinyl numbers like A1, B2, etc.
2025-05-17 08:48:16 +01:00
Claudio Ortolina 0c5acde4fa Add internal scrolling to add record component 2025-05-16 09:19:03 +01:00
Claudio Ortolina bd74809092 Workaround for incorrect detection of viewport bottom
See https://gist.github.com/cloud8421/80c1a89be9f5e4f5db13d798cdba0071
2025-05-16 08:32:42 +01:00
Claudio Ortolina f36b829377 Rename nav_section -> current_section 2025-05-14 10:49:12 +01:00