Files
music_library/priv/gettext/default.pot
T
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

1125 lines
31 KiB
Plaintext

## This file is a PO Template file.
##
## "msgid"s here are often extracted from source code.
## Add new messages manually only if they're dynamic
## messages that can't be statically extracted.
##
## Run "mix gettext.extract" to bring this file up to
## date. Leave "msgstr"s empty as changing them here has no
## effect: edit them in PO (.po) files instead.
#
msgid ""
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Are you sure?"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#: lib/music_library_web/live/artist_live/show.html.heex
#: lib/music_library_web/live/collection_live/index.ex
#: lib/music_library_web/live/collection_live/show.ex
#, elixir-autogen, elixir-format
msgid "Collection"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "Cover art"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Delete"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Edit"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#: lib/music_library_web/live/collection_live/index.ex
#, elixir-autogen, elixir-format
msgid "Error importing record"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "Format"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Formats"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Genres"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Inserted at"
msgstr ""
#: lib/music_library_web/controllers/session_controller.ex
#, elixir-autogen, elixir-format
msgid "Invalid password"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Latest purchase"
msgstr ""
#: lib/music_library_web/controllers/session_controller.ex
#: lib/music_library_web/controllers/session_html/new.html.heex
#, elixir-autogen, elixir-format
msgid "Login"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#, elixir-autogen, elixir-format
msgid "Logout"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#: lib/music_library_web/live/artist_live/show.html.heex
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "MusicBrainz ID"
msgstr ""
#: lib/music_library_web/components/pagination_component.ex
#, elixir-autogen, elixir-format
msgid "Next"
msgstr ""
#: lib/music_library_web/components/add_record_component.ex
#, elixir-autogen, elixir-format
msgid "No results"
msgstr ""
#: lib/music_library_web/controllers/session_html/new.html.heex
#, elixir-autogen, elixir-format
msgid "Password"
msgstr ""
#: lib/music_library_web/components/pagination_component.ex
#, elixir-autogen, elixir-format
msgid "Previous"
msgstr ""
#: lib/music_library_web/live/collection_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Purchase"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "Purchased at"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/collection_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Purchased on"
msgstr ""
#: lib/music_library_web/live/collection_live/index.ex
#, elixir-autogen, elixir-format
msgid "Record imported successfully"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "Record updated successfully"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "Save"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Saving..."
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Search"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Show"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#: lib/music_library_web/live/stats_live/index.ex
#, elixir-autogen, elixir-format
msgid "Stats"
msgstr ""
#: lib/music_library_web/hooks/static_assets.ex
#, elixir-autogen, elixir-format
msgid "The application has been updated, please reload."
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Total collection"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Total wishlist"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Type"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Types"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Updated at"
msgstr ""
#: lib/music_library_web/controllers/session_html/new.html.heex
#, elixir-autogen, elixir-format
msgid "Welcome to your Music Library"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#: lib/music_library_web/live/artist_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/index.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Wishlist"
msgstr ""
#: lib/music_library_web/auth.ex
#, elixir-autogen, elixir-format
msgid "You must be logged in to access this page"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Copy MusicBrainz ID to clipboard"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "MusicBrainz data"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Scrobble activity"
msgstr ""
#: lib/music_library_web/components/add_record_component.ex
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Choose which format to import"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Collected"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Wishlisted"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Includes"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Error refreshing MusicBrainz data"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "MusicBrainz data refreshed successfully"
msgstr ""
#: lib/music_library_web/components/stats_components.ex
#, elixir-autogen, elixir-format
msgid "Refresh LastFm Feed"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Cover refreshed successfully"
msgstr ""
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Error refreshing Cover"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#, elixir-autogen, elixir-format
msgid "Error refreshing cover"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "No MB ID"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#: lib/music_library_web/live/collection_live/show.ex
#, elixir-autogen, elixir-format
msgid "Details"
msgstr ""
#: lib/music_library_web/components/layouts/root.html.heex
#, elixir-autogen, elixir-format
msgid "Made by"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "View details"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Loading biography"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Loading play count"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Error populating genres"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Genres populated successfully"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Populate genres"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Refresh MB data"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Refresh cover"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Records"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Error loading biography"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Error loading play count"
msgstr ""
#: lib/music_library_web/live/collection_live/index.html.heex
#: lib/music_library_web/live/wishlist_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "A->Z"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#, elixir-autogen, elixir-format
msgid "Errors"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "On Tour"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "1 record"
msgid_plural "%{count} records"
msgstr[0] ""
msgstr[1] ""
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "1 scrobble"
msgid_plural "%{count} scrobbles"
msgstr[0] ""
msgstr[1] ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Copy record ID to clipboard"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "ID"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Unreleased"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Album"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Backup"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Blu-ray"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "CD"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Comp"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "DVD"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "EP"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Live"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Multi"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Other"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Single"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Vinyl"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Purchased"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#: lib/music_library_web/live/wishlist_live/index.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Record added to the collection"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Published releases"
msgstr ""
#: lib/music_library_web/live/collection_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Scan"
msgstr ""
#: lib/music_library_web/live/collection_live/index.ex
#, elixir-autogen, elixir-format
msgid "Scan barcodes · Collection"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#, elixir-autogen, elixir-format
msgid "Scan one or more barcodes"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#, elixir-autogen, elixir-format
msgid "Open camera"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#, elixir-autogen, elixir-format
msgid "Failed to search release for barcode %{number}"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#, elixir-autogen, elixir-format
msgid "Records imported successfully"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#, elixir-autogen, elixir-format
msgid "New"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Error loading similar artists"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Loading similar artists"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Similar artists"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "PNG, JPG, WEBP up to 8MB"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "Upload a file"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "or drag and drop"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#, elixir-autogen, elixir-format
msgid "Live Dashboard"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#, elixir-autogen, elixir-format
msgid "Barcode not found"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#, elixir-autogen, elixir-format
msgid "Some records could not be imported: %{summary}"
msgstr ""
#: lib/music_library_web/live/collection_live/index.html.heex
#: lib/music_library_web/live/wishlist_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Add"
msgstr ""
#: lib/music_library_web/live/collection_live/index.ex
#, elixir-autogen, elixir-format
msgid "Add new Record · Collection"
msgstr ""
#: lib/music_library_web/live/wishlist_live/index.ex
#, elixir-autogen, elixir-format
msgid "Add new Record · Wishlist"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#, elixir-autogen, elixir-format
msgid "Add releases"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex
#, elixir-autogen, elixir-format
msgid "Adding..."
msgstr ""
#: lib/music_library_web/components/add_record_component.ex
#, elixir-autogen, elixir-format
msgid "Search for a record"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Albums"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Tracks"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Add more"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Add more · Artist"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#: lib/music_library_web/live/stats_live/index.ex
#: lib/music_library_web/live/wishlist_live/index.ex
#, elixir-autogen, elixir-format
msgid "Error wishlisting record"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#: lib/music_library_web/live/stats_live/index.ex
#: lib/music_library_web/live/wishlist_live/index.ex
#, elixir-autogen, elixir-format
msgid "Record wishlisted successfully"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "No scrobbles"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Meta"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Record updated in the background"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#, elixir-autogen, elixir-format
msgid "Oban"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Top %{n} Artists"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Top %{n} Genres"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "Release Date"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Collected release"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "No release selected"
msgstr ""
#: lib/music_library_web/components/form_component.ex
#, elixir-autogen, elixir-format
msgid "Selected Release"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Error loading tracks"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Loading release with tracks"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Show Tracks"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Disc %{no}"
msgstr ""
#: lib/music_library_web/controllers/last_fm_controller.ex
#, elixir-autogen, elixir-format
msgid "Successfully connected your Last.fm account"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Error scrobbling release"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Release scrobbled successfully"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Connect your Last.fm account"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Scrobbling..."
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Scrobble release"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Disc scrobbled successfully"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Error scrobbling disc"
msgstr ""
#: lib/music_library_web/components/release_component.ex
#, elixir-autogen, elixir-format
msgid "Scrobble disc"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#, elixir-autogen, elixir-format
msgid "More"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Artist image refreshed successfully"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Artist info refreshed successfully"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Error refreshing artist image"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Error refreshing artist info"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Refresh image"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Refresh info"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Biography"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Read more"
msgstr ""
#: lib/music_library_web/live/artist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Discogs data"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Unknown"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "VHS"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Download"
msgstr ""
#: lib/music_library_web/live/wishlist_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Insertion"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Last listened at"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Error"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Extract colors (fast)"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Extract colors (slow)"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/wishlist_live/show.ex
#, elixir-autogen, elixir-format
msgid "In progress - record will update automatically"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex
#, elixir-autogen, elixir-format
msgid "Record deleted"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Last 30 days"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Last 90 days"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Top Albums"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Last year"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#, elixir-autogen, elixir-format
msgid "Wishlisted on"
msgstr ""
#: lib/music_library_web/components/record_components.ex
#: lib/music_library_web/live/artist_live/show.html.heex
#: lib/music_library_web/live/collection_live/show.html.heex
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#: lib/music_library_web/live/wishlist_live/show.html.heex
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Top Artists"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "All time"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Album ID:"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Artist ID:"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Copy album MusicBrainz ID to clipboard"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Copy artist MusicBrainz ID to clipboard"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Copy track MusicBrainz ID to clipboard"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Track ID:"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Add a description to help identify this rule"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Album Title"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.ex
#, elixir-autogen, elixir-format
msgid "All rules applied successfully. Updated %{count} tracks total."
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Apply All Rules"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this rule?"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Artist"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Artist Name"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Create a new rule to transform scrobbled track data"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Created"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Description"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Description (optional)"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.ex
#, elixir-autogen, elixir-format
msgid "Edit Scrobble Rule"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Edit the rule to change how it transforms data"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Enable this rule"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Enter the value to match"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.ex
#, elixir-autogen, elixir-format
msgid "Error applying rule: %{reason}"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.ex
#, elixir-autogen, elixir-format
msgid "Error applying rules: %{reason}"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Manage rules to fix data in scrobbled tracks"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Match Value"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "New Rule"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.ex
#, elixir-autogen, elixir-format
msgid "New Scrobble Rule"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Rule Type"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.ex
#, elixir-autogen, elixir-format
msgid "Rule applied successfully. Updated %{count} tracks."
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Rules are applied automatically by the background worker, or you can apply them manually."
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Save Rule"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex
#: lib/music_library_web/live/scrobble_rules_live/index.ex
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Scrobble Rules"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Scrobble rule created successfully"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Scrobble rule updated successfully"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Select a rule type"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Status"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#: lib/music_library_web/live/scrobble_rules_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Target MusicBrainz ID"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "e.g. Pink Floyd"
msgstr ""
#: lib/music_library_web/live/scrobble_rules_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "e.g. The Dark Side of the Moon"
msgstr ""