Commit Graph

264 Commits

Author SHA1 Message Date
Claudio Ortolina 2aeae28cd5 First pass at manual editing of dominant colors 2025-07-11 08:54:34 +01:00
Claudio Ortolina 9ecbd470af Add button to copy selected release ID 2025-07-07 13:48:00 +01:00
Claudio Ortolina ad74560cf9 Enable Ctrl/Meta+K to trigger universal search 2025-07-07 11:36:41 +01:00
Claudio Ortolina f57485e509 Allow manual upload of artist image 2025-07-06 16:57:30 +01:00
Claudio Ortolina 4b27699d47 Remove copy about unsupported keyboard shortcuts 2025-07-06 10:19:31 +01:00
Claudio Ortolina 3787736e39 Remove custom loading state logic 2025-07-06 09:44:42 +01:00
Claudio Ortolina f5092e32c5 Use Fluxon elements for universal search 2025-07-05 19:28:57 +01:00
Claudio Ortolina 543cbe39b7 Misc universal search improvements
- Use zinc instead of gray
- Translate all strings, and remove redundant ones
- Add format, type and release year to record results
- Use existing helpers instead of hand-rolled variants
2025-07-05 19:14:42 +01:00
Claudio Ortolina 1688fa3663 Improve main action button group in scrobble rules page 2025-07-04 15:45:21 +01:00
Claudio Ortolina 29ea381167 Render scrobble rule status with badge 2025-07-04 14:55:46 +01:00
Claudio Ortolina 40346d9123 Render scrobble rule type with badge 2025-07-04 14:53:27 +01:00
Claudio Ortolina 81f0ec8830 Render scrobble rules list with Fluxon table 2025-07-04 14:07:35 +01:00
Claudio Ortolina bb6c838d6a Remove unnecessary explanation text 2025-07-04 13:36:29 +01:00
Claudio Ortolina 93e10bc5d9 Remove excessively defensive coding 2025-07-03 21:18:21 +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 e2c2c6d547 Add tooltips for scrobble activity debug info 2025-06-21 15:20:24 +03:00
Claudio Ortolina f3425d8cb9 Add top artist and top albums of all time
Executed by Claude
2025-06-19 17:17:51 +03:00
Claudio Ortolina 9b20db762d Show top artists in stats page 2025-06-16 16:47:35 +03:00
Claudio Ortolina 389c268344 Add another feed refresh button 2025-06-15 11:51:45 +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 109913cbfd Extract top albums to components 2025-06-14 09:44:47 +03:00
Claudio Ortolina 268aff9918 Add top albums to the stats page
Authored with Claude.

> I'd like to have a new section in the Stats page. In this section I
want to see the top albums I listened to in the past 30,
  90, 365 days. Use the data in `scrobbled_tracks` to determine which
albums need to be displayed.

> This all looks great as a starter. I'd like to make some
modifications.

  1. Invert the position of the artist name and the album title (so that
it's consistent with the Scrobble Activity)
  2. Remove the border and shadow from around each album
  3. Rename the "Last 365 days" section to "Last Year"
2025-06-14 09:38:44 +03:00
Claudio Ortolina c23578725e Fix error when deleting a record from the artist page 2025-06-11 17:29:04 +03:00
Claudio Ortolina 4da5a8d59b Fix error handling for adding a record to the collection from artist page 2025-06-09 18:32:27 +01:00
Claudio Ortolina 2cc1d7ca8f Pick color extraction strategy from dropdown 2025-06-08 20:06:54 +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 c23260d03d Show last listen at for each collected record 2025-06-03 15:57:12 +01:00
Claudio Ortolina 9100c6c04a Extract actions_menu 2025-05-29 22:15:34 +01:00
Claudio Ortolina b72bee709e Fix error when purchasing a record from the artist page 2025-05-29 17:19:40 +01:00
Claudio Ortolina 423d1e7764 Can sort wishlisted records by insertion 2025-05-26 23:12:34 +01:00
Claudio Ortolina f8b8816af0 Rename 'Digital Download' -> 'Download' for brevity 2025-05-25 20:11:07 +01:00
Claudio Ortolina 6e19ab410d Account for VHS and Unknown formats 2025-05-25 10:32:11 +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 45204c4b3f Show musicbrainz data and discogs data on artist page 2025-05-24 12:58:13 +01:00
Claudio Ortolina 7dcc792214 Show similar artists on a grid 2025-05-21 21:56:19 +01:00
Claudio Ortolina 80ca186ba8 Render artist bio in a left-side sheet 2025-05-21 21:24:15 +01:00
Claudio Ortolina ee27b7a252 Can refresh artist info and image from UI 2025-05-20 09:41:44 +01:00
Claudio Ortolina 051c6195b5 Harden release label display 2025-05-18 20:57:17 +01:00
Claudio Ortolina 7e994389e7 Extract play_count and on_tour_link components 2025-05-15 19:33:48 +01:00
Claudio Ortolina cc3cf02490 Show artist MusicBrainz ID (with link and copy button) 2025-05-15 16:59:12 +01:00
Claudio Ortolina ddca7be870 Use dropdown for "more" link in the nav 2025-05-14 08:09:14 +01:00
Claudio Ortolina bb4fce61c7 Extract StaticAssets hook with a single default live_session 2025-05-13 21:03:59 +01:00
Claudio Ortolina bb6fffca38 Support warning flash messages 2025-05-13 21:02:51 +01:00
Claudio Ortolina d0a3fa1eff Remove unused components 2025-05-13 15:39:06 +01:00
Claudio Ortolina 6751a071ab Remove pagination record counter
I don't need it anymore, and it gets in the way of the UI
2025-05-12 17:37:47 +01:00
Claudio Ortolina b831a2e1d7 Can scrobble individual discs 2025-05-10 09:46:07 +01:00