Commit Graph

55 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 0aa0a19a00 Use live_toast instead of built-in flash
Precursor for building better notifications
2025-06-07 08:27:25 +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 f36b829377 Rename nav_section -> current_section 2025-05-14 10:49:12 +01:00
Claudio Ortolina dda355e764 Refactor to create .nav_link component 2025-05-14 10:47:01 +01:00
Claudio Ortolina 029bcf5bca Improve styling of the "more" dropdown 2025-05-14 10:03:13 +01:00
Claudio Ortolina ddca7be870 Use dropdown for "more" link in the nav 2025-05-14 08:09:14 +01:00
Claudio Ortolina 7d536d0871 Add live_debugger for development 2025-05-07 16:11:01 +01:00
Claudio Ortolina 8501a48a06 Tighten design of navbar 2025-04-16 11:04:08 +01:00
Claudio Ortolina 7c0cee4732 Mount oban_web 2025-04-14 17:14:39 +01:00
Claudio Ortolina 4c25bd63ff Rename "Dev dashboard" -> "Live Dashboard" 2025-03-05 17:46:05 +00:00
Claudio Ortolina a5ec76833c Add navbar icons except on smallest viewport 2025-03-05 17:02:34 +00:00
Claudio Ortolina bcfe2a7f3c Hide backup link on mobile 2025-03-04 07:27:55 +00:00
Claudio Ortolina eb1a75d2d0 Add aria-label to main navigation 2025-02-11 10:05:05 +00:00
Claudio Ortolina af77095e6c Add subpixel antialiasing to application
Subjective - makes it slightly more pleasing
2025-02-10 10:36:00 +00:00
Claudio Ortolina b47f282223 Add database backup functionality 2025-01-30 09:39:45 +00:00
Claudio Ortolina 441bfcb9ef Add logout icon and tighten navbar on mobile 2025-01-30 09:39:33 +00:00
Claudio Ortolina 5cd57fdde4 Install Error Tracker to track production issues 2025-01-01 17:11:17 +00:00
Claudio Ortolina fe05efb06d Prevent pinch to zoom
I wouldn't normally do this, but I'm the only user and I'm ok with it.
2024-12-23 17:04:31 +00:00
Claudio Ortolina 7e31c95839 Smooth scroll to top when paginating records 2024-12-08 15:18:14 +01:00
Claudio Ortolina 24319b1bff Annotate templates with TODOs for markup replacement
To open source the project down the line, it makes sense to have either
everything custom or using OSS components in order to keep licensing as
simple as possible.
2024-12-04 14:43:07 +00:00
Claudio Ortolina ccefc6e697 Update to new interpolation syntax
Includes a fix to a brittle test that failed due to changes in the
number of linebreaks.
2024-12-03 00:08:32 +00:00
Claudio Ortolina df9d18a21f Add nav link to dev dashboard
Invisible on mobile portrait layout - visible if device is turned to
landscape. Reasoning is that the dev dashboard is pretty unusable in
portrait mode anyway.
2024-11-29 08:33:29 +00:00
Claudio Ortolina 95e11d45d5 Tighten navigation menu on mobile 2024-11-29 08:29:21 +00:00
Claudio Ortolina 60fef06dc1 Uniform nav and footer dividers 2024-11-28 08:55:57 +00:00
Claudio Ortolina 3a813f9b4a Add personal website link in footer 2024-11-28 08:48:49 +00:00
Claudio Ortolina 7701ef9bc5 Add missing translation 2024-11-27 23:45:57 +00:00
Claudio Ortolina 452a6a3f5b Fix footer on mobile 2024-11-27 23:45:47 +00:00
Claudio Ortolina 9f6791909d Add a footer 2024-11-27 23:41:11 +00:00
Claudio Ortolina 09115a635f Reduce vertical margins and paddings 2024-11-21 10:42:33 +00:00
Claudio Ortolina 5b3aab51ed Change routes from "/records/*" to "/collection/*" 2024-11-20 15:43:25 +00:00
Claudio Ortolina 3cbfde1940 Fix layout max width glitches between different views 2024-11-18 16:51:02 +00:00
Claudio Ortolina 5c4c87e5c1 Make dark mode backgrounds darker 2024-11-07 19:12:26 +00:00
Claudio Ortolina 01798b343c Use live navigation between sections 2024-10-29 16:43:10 +00:00
Claudio Ortolina 1ca9663bb6 Track favicon assets
So that if they change, the UI can notify the user for a reload
2024-10-27 17:30:20 +00:00
Claudio Ortolina 793dbcabb8 Add favicons 2024-10-27 17:28:01 +00:00
Claudio Ortolina eba1e2eca8 Fix border colours for navigation
Remove drop shadow as it doesn't work in dark mode
2024-10-23 20:21:44 +01:00
Claudio Ortolina c45ba43c46 Update colors in navigation bar 2024-10-23 10:28:37 +01:00
Claudio Ortolina 9b305ff06a Add wishlist section with paginated list 2024-10-21 18:25:56 +01:00
Claudio Ortolina d78bc1715f Extract app layout translations 2024-10-21 11:33:33 +01:00
Claudio Ortolina 221bca0e1a Add logout button 2024-10-21 10:08:21 +01:00
Claudio Ortolina b5bceb92e0 Add login route with hardcoded password validation 2024-10-20 19:05:41 +01:00
Claudio Ortolina 82c23c2dfa Fix positioning of top navigation 2024-10-17 22:08:23 +01:00
Claudio Ortolina 34e74c7342 Fix page-wide margin inconsistencies 2024-10-17 22:08:22 +01:00
Claudio Ortolina 69e6cc67a2 Make page titles consistent with application structure
- All existing pages get a nice title
- Suffix is "Music Library"
- Fallback is current request path
2024-10-15 17:32:59 +01:00
Claudio Ortolina 6c9d1775d7 Rename application sections 2024-10-13 21:58:12 +01:00
Claudio Ortolina e0b19707bf Use Inter font (without variable) 2024-10-10 17:33:45 +01:00
Claudio Ortolina 764c4cebb3 Fix mobile spacing issues with links 2024-10-07 14:49:00 +01:00
Claudio Ortolina d191ff50fd Set default font to Inter 2024-10-03 23:18:32 +01:00