Commit Graph

44 Commits

Author SHA1 Message Date
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 f8628cc2aa Update tailwind from 4.1.10 to 4.1.11 2025-06-29 08:37:39 +01:00
Claudio Ortolina 0739e12ad3 Polyfill scrobbled tracks every hour 2025-06-20 22:43:04 +03:00
Claudio Ortolina 98d0fe8ee2 Update tailwind from 4.1.9 to 4.1.10 2025-06-12 16:00:09 +03:00
Claudio Ortolina df00a7270f Update tailwind from 4.1.8 to 4.1.9 2025-06-11 19:32:57 +03:00
Claudio Ortolina 5207aa2773 Update tailwind from 4.1.7 to 4.1.8 2025-05-28 19:07:28 +01:00
Claudio Ortolina a1baa10e88 Update esbuild from 0.17.11 to 0.25.5 2025-05-27 14:43:29 +01:00
Claudio Ortolina ee808fb041 Add ability to refresh musicbrainz data async 2025-05-24 18:04:20 +01:00
Claudio Ortolina 112e2e455a Update tailwind from 4.1.6 to 4.1.7 2025-05-15 16:10:40 +01:00
Claudio Ortolina 755af1d97e Update tailwind from 4.1.5 to 4.1.6 2025-05-09 15:05:31 +01:00
Claudio Ortolina 51b423c9fb Add default Last.fm shared secret 2025-05-06 16:05:30 +01:00
Claudio Ortolina 8be0144cc7 Update tailwind from 4.1.4 to 4.1.5 2025-04-30 21:45:18 +01:00
Claudio Ortolina d603bc2ffd Define a queue for heavy writes 2025-04-27 21:23:13 +01:00
Claudio Ortolina 2c57f9bf65 Add Discogs module 2025-04-25 18:09:43 +01:00
Claudio Ortolina 8fcb5ad46b Update tailwind from 4.1.3 to 4.1.4 2025-04-15 08:46:25 +01:00
Claudio Ortolina c44fcc7321 Install Oban with dedicated repo 2025-04-13 18:09:53 +01:00
Claudio Ortolina 32c11f86c9 Make timezone configurable 2025-04-10 18:34:05 +01:00
Claudio Ortolina 099c895c8f Use localized scrobbled_at label 2025-04-10 12:49:23 +01:00
Claudio Ortolina 896450fb0a Update tailwind from 4.1.2 to 4.1.3 2025-04-05 07:55:11 +01:00
Claudio Ortolina a6c7c67a96 Update tailwind from 4.1.1 to 4.1.2 2025-04-03 18:19:20 +01:00
Claudio Ortolina f0703caa19 Update tailwind from 4.1.0 to 4.1.1 2025-04-03 08:01:31 +01:00
Claudio Ortolina 086705b7b3 Update tailwind from 4.0.17 to 4.1.0 2025-04-01 19:11:13 +01:00
Claudio Ortolina c113202640 Update tailwind from 4.0.16 to 4.0.17 2025-03-26 18:25:32 +00:00
Claudio Ortolina 8ff37b4903 Update tailwind from 4.0.15 to 4.0.16 2025-03-25 18:08:16 +00:00
Claudio Ortolina 1f43e1d993 Use JSON instead of Jason where possible
In some places, we need pretty printing so it's not possible to remove
the dependency.
2025-03-23 08:34:25 +00:00
Claudio Ortolina 8fbb7f5a01 Update Tailwind from 4.0.14 to 4.0.15 2025-03-20 19:26:19 +00:00
Claudio Ortolina 6c9a504c5e Update tailwind from 4.0.13 to 4.0.14 2025-03-13 14:59:59 +00:00
Claudio Ortolina 1b1222df06 Update tailwind from 4.0.12 to 4.0.13 2025-03-11 20:22:14 +00:00
Claudio Ortolina 6ef014b02a Update tailwind from 4.0.11 to 4.0.12 2025-03-09 08:11:25 +00:00
Claudio Ortolina 95f9832d00 Update tailwind from 4.0.9 to 4.0.11 2025-03-06 14:51:00 +00:00
Claudio Ortolina d9e87d6c80 Migrate to Tailwind v4 2025-02-28 20:46:53 +00:00
Claudio Ortolina a218942110 Remove mox in favour of built-in Req stubs 2025-02-28 16:41:16 +00:00
Claudio Ortolina 7ac2f7a815 Remove LastFm.APIBehaviour 2025-02-27 13:43:49 +00:00
Claudio Ortolina eeaaf8b542 Move ErrorTracker to its own repo and file
Note that the SQlite adapter supports version 2 onwards.
2025-01-23 10:40:36 +00:00
Claudio Ortolina 5cd57fdde4 Install Error Tracker to track production issues 2025-01-01 17:11:17 +00:00
Claudio Ortolina d35cb8f05d Update Tailwind version
3.4.3 => 3.4.17
2024-12-23 23:38:11 +00:00
Claudio Ortolina f1af38606a Protect API behind a bearer token 2024-12-22 18:32:04 +00:00
Claudio Ortolina 53e8d88b6a Rename auth_password -> login_password 2024-12-21 14:57:44 +00:00
Claudio Ortolina 7575e5978a Extract config for MusicBrainz 2024-12-04 10:33:41 +00:00
Claudio Ortolina 14518870a0 Make LastFm user agent configurable 2024-12-04 10:07:25 +00:00
Claudio Ortolina 61cda5a62c Explicitly set auto-refresh config for LastFm refresh 2024-12-03 19:58:17 +00:00
Claudio Ortolina 18ae8b7866 Refactor last_fm configuration
Move all configuration outside of the LastFm module, so that it's not
polluted with references to the parent music_library application.
2024-11-06 12:09:38 +00:00
Claudio Ortolina 214d12f6cb Use password from environment 2024-10-21 08:54:46 +01:00
Claudio Ortolina d547ee6f8d Generate project
Using `mix phx.new music_library --database sqlite3 --binary-id --no-mailer`
2024-09-12 14:05:08 +01:00