70 KiB
Shanty — Project Issues
This file contains all planned issues for the Shanty project. Each issue is self-contained and can be copy-pasted into Gitea as a separate issue. Issues are numbered for cross-referencing but your Gitea numbering may differ — adjust dependency references accordingly.
Issue #1: Initialize Cargo workspace and project scaffolding
Labels: mvp, scaffolding, priority:high
Description
Shanty is a modular music management application built in Rust. The project is organized as a Cargo workspace where each component (indexing, tagging, organization, watching, downloading, searching, web interface, etc.) lives in its own crate. Each crate serves as both a library (for integration into the larger app) and a standalone binary with its own CLI.
This issue covers the initial project scaffolding:
- Create a top-level
Cargo.tomldefining the workspace and listing all planned member crates - Create stub crates for each planned component:
shanty-index— music file indexing and metadata extractionshanty-tag— metadata tagging via online databasesshanty-org— file organization and renamingshanty-watch— library watchlist managementshanty-dl— music downloadingshanty-search— online music searchshanty-notify— notifications (post-MVP, but scaffold now)shanty-playlist— playlist generation (post-MVP, but scaffold now)shanty-serve— music serving/streaming (post-MVP, but scaffold now)shanty-play— built-in playback (post-MVP, but scaffold now)shanty-web— web interface backend (Actix)shanty-db— shared database schema and access layer
- Each crate should have a minimal
lib.rsandmain.rs(where applicable) with placeholder content - Create a top-level
README.mdexplaining the project structure - Add a
.gitignoreappropriate for Rust projects - Ensure
cargo buildandcargo testsucceed on the empty workspace
Acceptance Criteria
Cargo.tomlat the workspace root lists all member crates- Each crate directory exists with its own
Cargo.toml,src/lib.rs, and (where applicable)src/main.rs cargo build --workspacesucceedscargo test --workspacesucceeds (even if no tests yet).gitignoreexcludestarget/,.env, and other standard Rust artifactsREADME.mdexists with a brief project description and crate listing
Dependencies
None — this is the first issue.
Issue #2: Design and implement the shared database schema crate (shanty-db)
Labels: mvp, database, priority:high, indexing
Description
Shanty uses a shared database to store all music metadata, library state, watchlist entries, and more. Multiple crates need to read from and write to this database. To avoid tight coupling and schema drift, there should be a dedicated shanty-db crate that owns the schema, migrations, and provides a typed access layer.
This crate should:
- Choose and integrate an ORM/query builder — recommend
dieselorsea-ormwith SQLite as the default backend (SQLite is lightweight, file-based, and appropriate for a self-hosted music app). The choice should support migrations and be ergonomic for other crates to use. - Define the core schema covering at minimum:
tracks— file path, title, artist, album, album artist, track number, disc number, duration, genre, year, codec/format, bitrate, file size, fingerprint (nullable), added_at, updated_atalbums— name, album artist, year, genre, cover art path (nullable), musicbrainz_id (nullable)artists— name, musicbrainz_id (nullable), monitored (bool), added_atwatch_items— references to artist/album/track the user wants to monitor, with status (wanted/available/downloaded)download_queue— items pending download, with status, source URL, error infosearch_cache— optional cache for online search results to avoid excessive API calls
- Provide migrations so the schema can evolve over time without losing data
- Expose a connection pool and typed query functions that other crates import
Design Considerations
- The schema must be versioned and migratable. Other crates depend on
shanty-dbbut should not define their own tables. - Consider using
r2d2or the ORM's built-in connection pooling. - Keep the schema normalized but pragmatic — e.g., a track belongs to an album which belongs to an artist, but allow nullable foreign keys for partially-tagged files.
- The crate should re-export relevant types (models, enums) so other crates don't need to depend on the ORM directly for basic type usage.
Acceptance Criteria
shanty-dbcrate compiles and is usable as a dependency by other workspace crates- SQLite database is created automatically if it doesn't exist
- Migrations can be run programmatically (and via CLI if using diesel)
- Core tables (
tracks,albums,artists,watch_items,download_queue) exist after migration - Typed Rust structs exist for each table (insertable and queryable variants)
- Connection pooling is set up
- At least basic integration tests exist — create a DB, run migrations, insert a track, query it back
- Schema version is tracked so future migrations can be applied incrementally
Dependencies
- Issue #1 (workspace scaffolding)
Issue #3: Implement music file scanning in shanty-index
Labels: mvp, indexing, priority:high
Description
The shanty-index crate is responsible for scanning a directory tree of music files and extracting metadata from them. This is the foundation of Shanty — before anything else can happen, the app needs to know what music the user already has.
This issue covers:
- Directory scanning — recursively walk a given directory path and identify music files by extension (
.mp3,.flac,.ogg,.opus,.m4a,.wav,.wma,.aac,.alacat minimum). Use a crate likewalkdirfor efficient traversal. - Metadata extraction — for each music file found, extract embedded metadata (ID3 tags for MP3, Vorbis comments for FLAC/OGG, etc.). Use a crate like
loftyorsymphoniathat handles multiple formats uniformly. Extract at minimum: title, artist, album, album artist, track number, disc number, year/date, genre, duration, codec, bitrate. - Database insertion — insert (or update) the extracted metadata into the
shanty-dbdatabase. Handle the case where a file has already been indexed (update if file mtime changed, skip if unchanged). Create artist and album records as needed based on the metadata found. - CLI interface — the
shanty-indexbinary should accept:- A path to scan (required)
- A path to the database file (optional, with a sensible default)
- A
--dry-runflag that reports what would be indexed without writing to DB - Verbosity flags
- Concurrency — scanning and metadata extraction should be parallelized. Use
rayonortokiotasks to process multiple files concurrently. This is important because large libraries can have tens of thousands of files.
Design Considerations
- Files with missing metadata should still be indexed — store whatever is available and leave the rest as NULL. The tagging crate will fill in gaps later.
- Track file modification time so re-indexing can be incremental (only process changed files).
- Consider emitting structured progress information (e.g., files scanned, files indexed, errors) that the web UI can consume later.
- The library API should be usable without the CLI — other crates (especially the web backend) will call indexing functions directly.
Acceptance Criteria
- Given a directory containing music files,
shanty-indexscans recursively and finds all supported formats - Metadata is extracted from each file and stored in the database via
shanty-db - Re-running the scan on the same directory is incremental — unchanged files are skipped
- The CLI binary works with
shanty-index /path/to/music --dry-runmode works and outputs what would be indexed- Scanning is parallelized (measurably faster than sequential on large directories)
- Files with partial/missing metadata are still indexed (with NULLs for missing fields)
- Artist and album records are created/linked based on extracted metadata
- Errors on individual files (corrupt, unreadable) are logged but don't halt the scan
- Unit tests exist for metadata extraction logic
- Integration test: create temp dir with test audio files, scan it, verify DB contents
Dependencies
- Issue #1 (workspace scaffolding)
- Issue #2 (shared database schema)
Issue #4: Implement online metadata lookup in shanty-tag
Labels: mvp, tagging, priority:high
Description
The shanty-tag crate is responsible for filling in missing or incorrect metadata on music files. The MVP approach is "look online first" — query online databases (primarily MusicBrainz) using whatever partial metadata is available (artist + title, album name, etc.) to find the correct tags.
This issue covers:
- MusicBrainz client — implement a client that queries the MusicBrainz API to look up track/album/artist metadata. MusicBrainz has a free API with rate limiting (1 request/second for unauthenticated). The client should:
- Search by artist + title to find a matching recording
- Search by album name + artist to find a matching release
- Retrieve full metadata for a matched recording/release (title, artist, album, track number, year, genre, cover art URL via Cover Art Archive, MusicBrainz IDs)
- Respect rate limits (implement a rate limiter / request queue)
- Handle API errors gracefully
- Tag matching logic — given a track from the database (which may have partial metadata), attempt to find a match online:
- If artist + title are available, search for the recording
- If only a filename is available, attempt to parse artist/title from the filename (common patterns like "Artist - Title.mp3")
- Score potential matches by similarity to existing metadata (fuzzy string matching)
- Allow a configurable confidence threshold — only apply tags if the match confidence is above the threshold
- Database update — when a match is found and accepted, update the track (and album/artist) records in
shanty-dbwith the new metadata. Also update the MusicBrainz IDs for future reference. - File tag writing — optionally write the updated metadata back to the actual music file's embedded tags (ID3, Vorbis comments, etc.). This should be an opt-in behavior since some users may not want their files modified.
- CLI interface — the
shanty-tagbinary should accept:- A path to the database (optional, with default)
--allto tag all untagged/partially-tagged tracks in the database--track <id>to tag a specific track--dry-runto show what would be changed without applying--write-tagsto enable writing tags back to files--confidence <0.0-1.0>to set the match threshold (default ~0.8)
Design Considerations
- The data backend should be trait-based so that alternative providers (Last.fm, Discogs, etc.) can be added later without changing the core logic. Define a
MetadataProvidertrait with methods likesearch_recording,search_release,get_recording_details, etc. - MusicBrainz requires a descriptive User-Agent header — use something like
Shanty/0.1.0 (https://github.com/your-repo). - Batch operations should be parallelized where possible, but respect API rate limits.
- Store which provider supplied the metadata so the user knows the source.
Acceptance Criteria
- MusicBrainz API client is implemented with proper rate limiting
- Given a track with artist + title, the tagger finds a matching recording and retrieves full metadata
- Fuzzy matching works — minor spelling differences don't prevent matches
- Database records are updated with new metadata and MusicBrainz IDs
--write-tagsactually writes metadata back into the music file--dry-runshows proposed changes without applying them- Confidence threshold filtering works
MetadataProvidertrait exists, and MusicBrainz is the first implementation- CLI interface works as specified
- Errors from the API (rate limits, network issues, no results) are handled gracefully
- Tests exist for matching logic (unit tests with mocked API responses)
Dependencies
- Issue #1 (workspace scaffolding)
- Issue #2 (shared database schema)
- Issue #3 (music indexing — so there are tracks in the DB to tag)
Issue #5: Implement music file organization in shanty-org
Labels: mvp, organization, priority:medium
Description
The shanty-org crate is responsible for organizing music files into a clean directory structure and renaming them according to a configurable format. Many music libraries are a mess of random filenames and flat folders — this crate fixes that.
This issue covers:
-
Directory structure generation — given a target root directory and a format template, move/copy music files into the correct structure. The default structure should be:
{root}/{artist}/{album}/{track_number} - {title}.{ext}For example:
Music/Pink Floyd/The Dark Side of the Moon/03 - Time.flac -
Format templates — support a simple template syntax for both directory structure and filename. Available variables should include at minimum:
{artist}— track or album artist{album_artist}— album artist (falls back to artist){album}— album name{title}— track title{track_number}— zero-padded track number (e.g., "03"){disc_number}— disc number{year}— release year{genre}— genre{ext}— file extension
-
Safe file operations — this crate moves actual files, so it must be very careful:
- Sanitize filenames (remove/replace characters invalid on the target filesystem)
- Handle conflicts (what if two files resolve to the same target path?)
- Support dry-run mode to preview changes before executing
- Log all file moves so the operation could theoretically be reversed
- Update the file path in the
shanty-dbdatabase after moving
-
CLI interface — the
shanty-orgbinary should accept:--source <dir>— source directory (or use DB paths)--target <dir>— target root directory--format <template>— format template (with a sensible default)--dry-run— preview changes without moving files--copyvs--move— whether to copy or move files (default: move)--from-db— organize all tracks known to the database
Design Considerations
- This crate can operate independently of the database — if someone just has a directory of music files with embedded tags, it should be able to read those tags and organize the files. But when used with the database, it should read metadata from there (faster than re-reading tags) and update paths afterward.
- Handle the case where metadata is incomplete — if there's no album, put the file in an "Unknown Album" folder; if no artist, use "Unknown Artist"; etc.
- Empty directories left behind after moving files should be cleaned up.
- On failure mid-operation, the state should be recoverable (already-moved files should still be tracked).
Acceptance Criteria
- Files are moved/copied into the correct directory structure based on metadata
- Format templates work with all specified variables
--dry-runshows planned moves without executing them- Filenames are sanitized for filesystem safety
- Conflicts are detected and handled (e.g., appending a number)
- Database file paths are updated after moving (when using
--from-db) - Works standalone (reading embedded tags) without requiring the database
- Empty source directories are cleaned up after moves
- CLI interface works as specified
- Integration test: create temp files with metadata, organize them, verify the resulting directory structure
Dependencies
- Issue #1 (workspace scaffolding)
- Issue #2 (shared database schema)
- Issue #3 (music indexing — beneficial but not strictly required)
Issue #6: Implement library watchlist management in shanty-watch
Labels: mvp, watching, priority:medium
Description
The shanty-watch crate manages the user's "library" — the collection of artists, albums, and individual songs they want to have. This is what makes Shanty more than just a tagger/organizer — it tracks what the user wants and compares it against what they have. Unlike Lidarr, Shanty supports monitoring at the individual song level, not just artist/album.
This issue covers the MVP watchlist functionality:
-
Adding items to the library — the user should be able to add:
- An artist (meaning "I want all releases by this artist")
- A specific album (meaning "I want this particular album")
- A specific track (meaning "I want this particular song")
Each item can be in one of these states:
wanted,available(found online but not downloaded),downloaded,owned(already in the local library)
-
Matching against existing library — when an item is added to the watchlist, check if it already exists in the database (from indexing). If so, mark it as
owned. This cross-referencing should use fuzzy matching (artist name + title similarity) since local files may not match online metadata exactly. -
Listing/querying the watchlist — provide functions to:
- List all monitored artists/albums/tracks
- Filter by status (show only "wanted" items, for example)
- Show a summary (e.g., "Artist X: 8/12 albums owned, 3 wanted, 1 available")
-
CLI interface — the
shanty-watchbinary should accept:add artist <name>— add an artist to the watchlistadd album <artist> <album>— add a specific albumadd track <artist> <title>— add a specific tracklist [--status <status>] [--artist <name>]— list watchlist itemsremove <id>— remove an item from the watchliststatus— show a summary of the library state
Design Considerations
- The watchlist items should reference MusicBrainz IDs where available (from the tagging step) for more reliable matching than string comparison.
- This crate doesn't do downloading or new-release checking in the MVP — it just manages the list. The downloading crate will look at "wanted" items, and new-release checking will be a post-MVP enhancement.
- Think about how the web interface will interact with this — the web UI will be the primary way users add items (via search results from
shanty-search), so the library API should be clean and JSON-serializable.
Acceptance Criteria
- Users can add artists, albums, and individual tracks to the watchlist
- Each watchlist item has a status (
wanted,available,downloaded,owned) - Adding an item that already exists in the local library auto-marks it as
owned - Watchlist can be queried/filtered by status and artist
- Summary statistics work (X of Y albums owned, etc.)
- Items can be removed from the watchlist
- CLI interface works as specified
- Watchlist data is stored in
shanty-db - Unit tests for status transitions and matching logic
- Fuzzy matching between watchlist items and local library entries works
Dependencies
- Issue #1 (workspace scaffolding)
- Issue #2 (shared database schema)
- Issue #3 (music indexing — for cross-referencing against local library)
Issue #7: Implement music downloading via yt-dlp in shanty-dl
Labels: mvp, downloading, priority:high
Description
The shanty-dl crate is responsible for actually downloading music files. For the MVP, the only backend is yt-dlp, which is capable of downloading audio from YouTube, SoundCloud, Bandcamp, and hundreds of other sites. The user provides a specific song (or list of songs), and the crate handles downloading, converting to the desired format, and storing the result.
This issue covers:
-
yt-dlp integration — shell out to
yt-dlp(assumed to be installed on the system) to download audio. The crate should:- Accept a search query (e.g., "Pink Floyd Time") or a direct URL
- Use yt-dlp's search feature (
ytsearch:) to find the best match when given a query - Download as the best available audio quality
- Convert to a target format (default: MP3 320kbps or OPUS, configurable)
- Save to a configurable output directory
- Embed available metadata (title, artist) into the downloaded file
-
Download queue — integrate with the
download_queuetable inshanty-db:- Items can be added to the queue (manually or from the watchlist's "wanted" items)
- The downloader processes the queue, updating status as it goes (
pending→downloading→completed/failed) - Failed downloads include error information for debugging
- Support retrying failed downloads
-
Post-download integration — after a successful download:
- Add the file to the database via
shanty-index(or at minimum insert a record) - If the download was for a watchlist item, update that item's status to
downloaded - Optionally run the organizer (
shanty-org) to move the file to the right location
- Add the file to the database via
-
CLI interface — the
shanty-dlbinary should accept:download <query_or_url>— download a single itemdownload --from-queue— process all pending items in the download queue--format <mp3|opus|flac|best>— output format--output <dir>— output directory--dry-run— show what would be downloaded without doing itqueue add <query>— add an item to the download queuequeue list— show the current queuequeue retry— retry all failed downloads
Design Considerations
- The download backend should be trait-based (
DownloadBackendtrait) so that future backends (torrents viatransmission-rpc, Soulseek, NZBGet, etc.) can be added. The trait should define methods likesearch,download,supports_url, etc. - yt-dlp output parsing can be tricky — use
--print-jsonor--dump-jsonfor structured output. - Consider concurrent downloads (configurable limit, e.g., 3 simultaneous) for queue processing.
- yt-dlp must be installed separately — the crate should check for its presence and give a clear error if missing.
- Be mindful of YouTube rate limiting — don't hammer the API.
Acceptance Criteria
- Given a search query, the downloader finds and downloads the correct song via yt-dlp
- Given a direct URL, the downloader downloads the audio from that URL
- Downloaded files are converted to the requested format
- Downloads are recorded in the database
- The download queue works — items can be added, processed, and retried
- Failed downloads are logged with error details
DownloadBackendtrait exists and yt-dlp is the first implementation- CLI interface works as specified
- The crate checks for yt-dlp availability and errors clearly if missing
- Concurrent queue processing works with a configurable limit
- Integration test: download a short public-domain audio clip, verify file exists and is valid
Dependencies
- Issue #1 (workspace scaffolding)
- Issue #2 (shared database schema)
- External:
yt-dlpmust be installed on the system
Issue #8: Implement online music search in shanty-search
Labels: mvp, searching, priority:medium
Description
The shanty-search crate provides the ability to search for music online — finding artists, albums, and tracks that can then be added to the user's library (watchlist). This is the "discovery" piece that feeds into shanty-watch. It wraps online music databases and returns structured results.
This issue covers:
-
MusicBrainz search — implement search functionality against the MusicBrainz API:
- Search for artists by name → returns list of matching artists with MusicBrainz IDs, disambiguation, and basic info
- Search for albums (releases) by name and/or artist → returns matching releases with track listings
- Search for tracks (recordings) by name and/or artist → returns matching recordings
- Get an artist's discography (all releases) given a MusicBrainz artist ID
-
Result types — define clean, serializable result types:
ArtistSearchResult— name, MBID, disambiguation, country, type (person/group)AlbumSearchResult— title, artist, MBID, year, track count, cover art URLTrackSearchResult— title, artist, album, MBID, duration, track numberDiscography— list of releases grouped by type (album, single, EP, compilation)
-
Search provider trait — define a
SearchProvidertrait so that alternative backends (Last.fm, Spotify API for metadata, Discogs, etc.) can be added later. The trait should cover:search_artist(query) -> Vec<ArtistSearchResult>search_album(query, artist_hint) -> Vec<AlbumSearchResult>search_track(query, artist_hint) -> Vec<TrackSearchResult>get_artist_discography(artist_id) -> Discography
-
Caching — optionally cache search results in
shanty-dbto reduce API calls for repeated searches. Cache entries should have a TTL (e.g., 24 hours). -
CLI interface — the
shanty-searchbinary should accept:artist <query>— search for an artistalbum <query> [--artist <artist>]— search for an albumtrack <query> [--artist <artist>]— search for a trackdiscography <artist_name_or_mbid>— show an artist's discography--json— output results as JSON (useful for piping to other tools)--limit <n>— max number of results (default 10)
Design Considerations
- This crate shares the MusicBrainz client with
shanty-tag. Consider whether the MB client should live in a shared utility crate (e.g.,shanty-mb) or inshanty-db, or whether each crate has its own thin client. A shared client avoids duplication of rate-limiting logic. - The web interface will be the primary consumer of this crate — search results will be displayed in the UI and the user will click to add items to their watchlist. So the result types should be JSON-serializable and contain enough info for display.
- MusicBrainz rate limits apply here too — reuse the rate limiter.
Acceptance Criteria
- Artist search returns relevant results from MusicBrainz
- Album search works, optionally filtered by artist
- Track search works, optionally filtered by artist
- Artist discography retrieval works and groups releases by type
- Result types are well-defined and JSON-serializable
SearchProvidertrait exists with MusicBrainz as the first implementation- CLI interface works as specified
--jsonoutput is valid JSON- Rate limiting is respected
- Optional caching works and respects TTL
- Tests exist with mocked API responses
Dependencies
- Issue #1 (workspace scaffolding)
- Issue #2 (shared database schema — for caching)
- Consider: shared MusicBrainz client with Issue #4 (tagging)
Issue #9: Implement the Actix web backend for shanty-web
Labels: mvp, web, backend, priority:high
Description
The shanty-web crate is the Actix-web backend that ties all the other crates together and exposes a REST API for the Elm frontend. This is the "brain" of Shanty when used as a web application — it orchestrates indexing, tagging, downloading, and everything else through HTTP endpoints.
This issue covers the backend API. The frontend (Elm) is a separate issue.
-
Core API endpoints:
Library / Watchlist
GET /api/artists— list all artists in the library (with filter/pagination)GET /api/artists/:id— get artist details including albums and watchlist statusPOST /api/artists— add an artist to the watchlistDELETE /api/artists/:id— remove an artist from the watchlistGET /api/albums— list all albums (with filter/pagination)GET /api/albums/:id— get album details including tracksPOST /api/albums— add an album to the watchlistGET /api/tracks— list all tracks (with filter/pagination)GET /api/tracks/:id— get track details
Search
GET /api/search/artist?q=<query>— search for artists onlineGET /api/search/album?q=<query>&artist=<artist>— search for albums onlineGET /api/search/track?q=<query>&artist=<artist>— search for tracks onlineGET /api/search/discography/:artist_id— get an artist's full discography
Downloads
POST /api/downloads— trigger a download (by query or URL)GET /api/downloads/queue— get the current download queuePOST /api/downloads/retry/:id— retry a failed downloadDELETE /api/downloads/:id— cancel/remove a download
System
GET /api/status— system status (DB stats, queue length, currently running tasks)POST /api/index— trigger a library re-scanPOST /api/tag— trigger auto-tagging for untagged tracksGET /api/config— get current configurationPUT /api/config— update configuration
-
Background task system — long-running operations (indexing, tagging, bulk downloads) should run as background tasks. The API should:
- Return immediately with a task ID
- Provide a
GET /api/tasks/:idendpoint to check task progress - Support WebSocket or SSE for real-time progress updates to the frontend
-
Configuration — the web app needs a configuration system:
- Music library root directory
- Database path
- Download output directory
- Organization format template
- Auto-tagging settings
- Port/bind address
- Use a TOML or YAML config file, with environment variable overrides
-
Static file serving — serve the compiled Elm frontend from a static directory
-
Error handling — consistent JSON error responses with appropriate HTTP status codes
Design Considerations
- Use
actix-webas specified in the design doc. - Each crate's library API should be called directly — don't shell out to the CLI binaries.
- Consider using
actix-rtfor spawning background tasks. - API responses should use consistent pagination (offset/limit or cursor-based).
- Consider CORS configuration for development (Elm dev server on a different port).
Acceptance Criteria
- Actix-web server starts and serves the API on a configurable port
- All listed API endpoints are implemented and return appropriate JSON responses
- Background tasks work for long-running operations (indexing, tagging)
- Task progress can be queried
- Configuration is loaded from a TOML file with env var overrides
- Static file serving works for the frontend
- Error responses are consistent JSON with proper status codes
- Pagination works on list endpoints
- CORS is configured for development
- Integration tests exist for the key API endpoints
- The server gracefully shuts down (completes in-progress tasks)
Dependencies
- Issue #1 (workspace scaffolding)
- Issue #2 (shared database schema)
- Issue #3 (music indexing)
- Issue #4 (music tagging)
- Issue #6 (watchlist management)
- Issue #7 (music downloading)
- Issue #8 (music searching)
Issue #10: Implement the Elm frontend for shanty-web
Labels: mvp, web, frontend, priority:high
Description
The Elm frontend is the "face" of Shanty — the primary way users interact with the application. It communicates with the Actix backend via the REST API defined in Issue #9. The philosophy is that the user should be able to add artists/albums/songs to their library and have the app handle everything else automatically.
This issue covers the MVP frontend:
-
Project setup:
- Initialize an Elm project in a subdirectory (e.g.,
shanty-web/frontend/or a top-levelfrontend/directory) - Set up a build pipeline (e.g.,
elm makewith an npm script or a Makefile target) that compiles to a JS bundle served by the Actix backend - Basic HTML shell with the Elm app mounted
- Initialize an Elm project in a subdirectory (e.g.,
-
Core pages/views:
Dashboard
- Overview of library stats (total artists, albums, tracks)
- Recent additions
- Current download queue status
- Any running background tasks
Library Browser
- Browse artists → albums → tracks hierarchy
- Show watchlist status per item (wanted/downloaded/owned)
- Filter and sort options
- Click into an artist to see their albums, click into an album to see its tracks
Search & Add
- Search bar that queries the backend's search API
- Display search results (artists, albums, tracks) in a clear layout
- "Add to Library" button on each result that adds it to the watchlist
- When adding an artist, show their discography and let the user select which albums to monitor
Downloads
- View the download queue (pending, in-progress, completed, failed)
- Trigger manual downloads by entering a search query or URL
- Retry failed downloads
- Cancel pending downloads
Settings
- View and edit configuration (library path, download directory, format template, etc.)
- Trigger manual actions (re-index, re-tag)
-
Styling:
- Use a clean, dark-theme design (fitting the "high seas" aesthetic)
- Responsive layout that works on desktop and tablet
- No CSS framework required — keep it simple with custom CSS or a minimal utility approach
-
Real-time updates:
- Use WebSocket or SSE to receive progress updates from background tasks
- Update the UI live when downloads complete or indexing finishes
Design Considerations
- Elm enforces a clean architecture (Model-View-Update) — lean into this. Define clear message types for each action.
- Use
elm/httpfor API calls andelm/jsonfor decoding responses. - Consider using
elm/urlfor client-side routing (SPA with browser history). - Keep the UI functional over pretty for MVP — polish can come later.
- All API types should have corresponding Elm decoders/encoders.
Acceptance Criteria
- Elm project compiles and the frontend loads in a browser when served by the Actix backend
- Dashboard shows library stats, recent additions, and queue status
- Library browser allows navigating artists → albums → tracks
- Search works and displays results from MusicBrainz
- Users can add artists/albums/tracks to their library from search results
- Download queue is viewable and manageable (retry, cancel)
- Manual download by query/URL works
- Settings page allows viewing and editing configuration
- UI updates in real-time for background tasks
- Client-side routing works (SPA navigation)
- Layout is responsive and uses a dark theme
- No runtime errors in the Elm app (Elm's type system should help here)
Dependencies
- Issue #9 (Actix web backend — the API must exist for the frontend to consume)
Issue #11: Implement configuration management and app startup orchestration
Labels: mvp, configuration, priority:medium
Description
Shanty needs a unified configuration system that all crates can use, plus a top-level orchestrator that starts the web server and any background services. Currently each crate accepts its own CLI args, but when running as a unified web app, there needs to be one config file and one startup command.
This issue covers:
-
Configuration crate or module — create a shared configuration type (either in
shanty-dbor a newshanty-configcrate) that includes:library_path— root directory for organized music filesimport_paths— list of directories to scan for new musicdatabase_path— path to the SQLite database filedownload_path— temporary directory for downloads before organizationorganization_format— template string for file organizationweb.port— HTTP port (default 8085)web.bind_address— bind address (default 0.0.0.0)tagging.auto_tag— whether to auto-tag new files after indexingtagging.write_tags— whether to write tags back to filestagging.confidence_threshold— match confidence thresholddownload.concurrent_limit— max simultaneous downloadsdownload.default_format— default audio format- Load from a TOML file (default:
~/.config/shanty/config.tomlor./shanty.toml) - Override any value with environment variables (e.g.,
SHANTY_WEB_PORT=9090)
-
Top-level binary — create a
shantybinary (the main entry point) that:- Loads configuration
- Initializes the database (runs migrations)
- Starts the web server
- Optionally runs an initial library scan on startup
- Handles graceful shutdown (SIGTERM/SIGINT)
-
Logging — set up structured logging (using
tracingcrate) across all crates:- Configurable log level via config file and env var (
SHANTY_LOGorRUST_LOG) - Log to stdout and optionally to a file
- Include context (crate name, operation) in log entries
- Configurable log level via config file and env var (
Acceptance Criteria
- A single config file controls all crate behavior
- Config loads from TOML with environment variable overrides
- Default config is generated if no config file exists
shantybinary starts the web server and initializes the database- Graceful shutdown works on SIGTERM/SIGINT
- Structured logging is configured and works across all crates
- Log level is configurable
- Documentation/comments explain each config option
Dependencies
- Issue #1 (workspace scaffolding)
- Issue #2 (shared database schema)
- Issue #9 (web backend)
Issue #12: Add error handling strategy and shared error types
Labels: mvp, quality, priority:medium
Description
As a multi-crate workspace, Shanty needs a consistent error handling strategy. Each crate will have its own error conditions, but they need to compose cleanly — especially when the web backend calls into library crates and needs to translate errors into HTTP responses.
This issue covers:
-
Shared error types — create an error module (in
shanty-dbor a newshanty-corecrate) that defines:- A base error enum that other crates can extend or wrap
- Common error variants:
DatabaseError,IoError,NetworkError,ConfigError,NotFoundError,ExternalToolError(for yt-dlp, etc.) - Implement
std::error::Error,Display, andFromconversions for common types - Consider using
thiserrorfor ergonomic error type derivation
-
Per-crate errors — each crate should define its own error type that wraps the shared errors plus crate-specific variants. For example,
shanty-tagmight haveTagError::NoMatchFound,TagError::ApiRateLimited, etc. -
Web error mapping — in
shanty-web, implementactix_web::ResponseErrorfor the error types so they automatically convert to appropriate HTTP responses (404 for NotFound, 500 for internal errors, 429 for rate limits, etc.) -
Result type alias — define a
Result<T> = std::result::Result<T, ShantyError>alias for convenience
Acceptance Criteria
- Shared error types exist and are usable by all crates
thiserror(or equivalent) is used for clean error type definitions- Each crate has its own error type wrapping the shared base
- Web errors map to correct HTTP status codes
- Error messages are user-friendly (not raw debug output)
Fromimplementations allow easy error propagation with?- No unwrap/expect calls in library code (only in tests and CLI
main)
Dependencies
- Issue #1 (workspace scaffolding)
Issue #13: Add CI pipeline and basic test infrastructure
Labels: mvp, ci, quality, priority:medium
Description
Set up a CI pipeline (for Gitea Actions or whatever CI system is available) and establish testing patterns that all crates should follow.
This issue covers:
-
CI pipeline — create a CI configuration that runs on every push/PR:
cargo fmt --check— enforce consistent formattingcargo clippy --workspace -- -D warnings— catch common mistakescargo build --workspace— verify everything compilescargo test --workspace— run all tests- Optional:
cargo audit— check for known vulnerabilities in dependencies
-
Test infrastructure:
- Set up a test helper module/crate with utilities for creating temporary databases, test audio files, etc.
- Define integration test patterns: each crate's
tests/directory should have at least one integration test - Consider using
assert_cmdfor testing CLI binaries - Provide a small set of test audio files (public domain / Creative Commons) or a script to generate them (e.g., using
soxto create short silent audio files with metadata)
-
Development tooling:
Makefileorjustfilewith common commands:build,test,lint,fmt,runrust-toolchain.tomlto pin the Rust version
Acceptance Criteria
- CI pipeline runs on push and verifies fmt, clippy, build, and test
- CI configuration file exists in the repo
rust-toolchain.tomlpins the Rust edition/version- Test helper utilities exist for common test setup (temp DB, test files)
- A
Makefileorjustfileexists with common development commands - At least a placeholder integration test exists in each MVP crate
Dependencies
- Issue #1 (workspace scaffolding)
Issue #14: Implement shared MusicBrainz client crate
Labels: mvp, api, priority:medium
Description
Both shanty-tag (Issue #4) and shanty-search (Issue #8) need to communicate with the MusicBrainz API. Rather than duplicating the client, rate-limiting logic, and type definitions, extract a shared MusicBrainz client.
This could be a standalone crate (e.g., shanty-mb) or a module within shanty-db. It should provide:
-
HTTP client — configured with:
- Proper User-Agent header (
Shanty/<version> (<contact>)) as required by MusicBrainz - Rate limiting (max 1 request/second for unauthenticated use)
- Retry logic with exponential backoff for transient failures
- Timeout configuration
- Proper User-Agent header (
-
API methods:
search_artists(query, limit)→ artist resultssearch_releases(query, artist_hint, limit)→ release resultssearch_recordings(query, artist_hint, limit)→ recording resultsget_artist(mbid)→ full artist detailsget_release(mbid)→ full release details with track listingget_recording(mbid)→ full recording detailsget_artist_releases(mbid)→ artist's discographyget_cover_art(release_mbid)→ cover art URL from Cover Art Archive
-
Response types — well-typed Rust structs for all API responses, with
serdedeserialization -
Testing — mock server tests (using
mockitoorwiremock) to test client behavior without hitting the real API
Acceptance Criteria
- Shared MusicBrainz client crate exists and is usable by
shanty-tagandshanty-search - Rate limiting enforces max 1 req/sec
- All listed API methods are implemented
- Response types are well-defined and deserializable
- User-Agent header is set correctly
- Retry logic handles transient failures
- Tests exist using a mock HTTP server
- The client is configurable (timeout, rate limit, base URL for testing)
Dependencies
- Issue #1 (workspace scaffolding)
Issue #15: Implement Docker packaging and deployment
Labels: mvp, deployment, priority:low
Description
Shanty should be easy to self-host. A Docker image is the most common way people deploy self-hosted media apps. This issue covers creating a Docker setup for Shanty.
-
Dockerfile — multi-stage build:
- Builder stage: compile the Rust workspace and Elm frontend
- Runtime stage: minimal image (e.g.,
debian-slimoralpine) with just the compiled binary, frontend assets, and runtime dependencies (yt-dlp,ffmpeg) - Include
yt-dlpandffmpegin the runtime image (needed for downloading/conversion)
-
docker-compose.yml — example compose file with:
- The Shanty service
- Volume mounts for music library, config, and database
- Environment variable configuration
- Example port mapping
-
Documentation — a section in the README or a dedicated
DOCKER.mdexplaining:- How to build and run with Docker
- Volume mount requirements
- Environment variable configuration
- Example docker-compose setup
Acceptance Criteria
docker buildproduces a working image- The container starts and the web UI is accessible
- Music library directory can be mounted as a volume
- Database persists across container restarts (via volume)
- Config can be provided via volume mount or environment variables
yt-dlpandffmpegare available inside the containerdocker-compose.ymlworks out of the box for basic usage- Image size is reasonable (under 500MB)
Dependencies
- Issue #9 (web backend)
- Issue #10 (Elm frontend)
- Issue #11 (configuration and startup)
Issue #16: [Post-MVP] Implement acoustic fingerprinting fallback in shanty-tag
Labels: post-mvp, tagging, enhancement
Description
When online metadata lookup fails (Issue #4), fall back to acoustic fingerprinting to identify tracks. This uses the Chromaprint/AcoustID system to generate a fingerprint from the audio data and look it up in the AcoustID database, which links back to MusicBrainz.
- Chromaprint integration — use the
chromaprintlibrary (via FFI bindings or a Rust crate if available) to generate acoustic fingerprints from audio files - AcoustID lookup — submit the fingerprint + duration to the AcoustID API to get matching MusicBrainz recording IDs
- Integration with existing tag flow — modify the tagging pipeline: try online text search first → if no match, generate fingerprint → look up via AcoustID → retrieve metadata from MusicBrainz using the matched recording ID
- Store fingerprints — save generated fingerprints in the database so they don't need to be recomputed
Acceptance Criteria
- Chromaprint fingerprints can be generated from audio files
- AcoustID API lookup returns matching MusicBrainz IDs
- Fingerprinting is used as a fallback when text-based search fails
- Generated fingerprints are cached in the database
- CLI flag
--fingerprintforces fingerprint-based lookup - Works for common audio formats (MP3, FLAC, OGG, OPUS)
Dependencies
- Issue #4 (online metadata lookup — this extends it)
- External:
chromaprintlibrary must be available on the system (or bundled)
Issue #17: [Post-MVP] Add torrent download backend to shanty-dl
Labels: post-mvp, downloading, enhancement
Description
Add a torrent-based download backend to shanty-dl as an alternative to yt-dlp. This would allow users to download music from torrent sources, which often have higher quality (lossless FLAC) than YouTube.
-
Transmission RPC client — integrate with Transmission (a popular torrent client) via its RPC API:
- Add a torrent by URL or magnet link
- Monitor download progress
- Get the downloaded file path when complete
- Configure download directory
-
Torrent search — this is trickier and more legally sensitive. Options:
- Accept magnet links / .torrent URLs directly (user provides the source)
- Integrate with Jackett/Prowlarr (torrent indexer aggregators that many self-hosters already run) to search multiple indexers
- Start with direct magnet/URL input only, add Jackett integration later
-
Backend registration — implement the
DownloadBackendtrait from Issue #7 for the torrent backend. The download manager should be able to route requests to the appropriate backend. -
Post-download processing — torrents may download entire albums. After download:
- Identify which files are audio files
- Index them into the database
- Organize them according to the user's format template
Acceptance Criteria
- Torrent backend implements the
DownloadBackendtrait - Can add torrents via magnet link or .torrent URL through Transmission RPC
- Download progress is tracked and queryable
- Downloaded audio files are identified and indexed
- Configuration for Transmission connection (host, port, auth) exists
- Falls back gracefully if Transmission is not available
Dependencies
- Issue #7 (download system with trait-based backends)
- External: Transmission daemon must be running
Issue #18: [Post-MVP] Add Soulseek download backend to shanty-dl
Labels: post-mvp, downloading, enhancement
Description
Add a Soulseek-based download backend. Soulseek is a peer-to-peer network popular among music collectors, often having rare and high-quality files. Integration would likely use nicotine+ (a Soulseek client) or a direct protocol implementation.
-
Soulseek client integration — options:
- Use
slskd(a modern Soulseek client with a REST API) as the backend — similar to how we use Transmission for torrents - Direct protocol implementation (complex, probably not worth it for MVP+1)
- Use
-
Search and download — search Soulseek for a track, present results (with quality/format info), and download the best match
-
Implement
DownloadBackendtrait for the Soulseek backend
Acceptance Criteria
- Soulseek backend implements the
DownloadBackendtrait - Can search for tracks on Soulseek via slskd API
- Can download files from Soulseek
- Download progress is tracked
- Quality/format info is available in search results for choosing the best source
- Configuration for slskd connection exists
Dependencies
- Issue #7 (download system with trait-based backends)
- External:
slskdinstance must be running
Issue #19: [Post-MVP] Implement new release monitoring in shanty-watch
Labels: post-mvp, watching, enhancement
Description
Extend shanty-watch to periodically check for new releases from monitored artists. When a new release is detected, it should be added to the watchlist as "available" and optionally trigger a notification or automatic download.
-
Release checking — periodically query MusicBrainz (or other configured provider) for each monitored artist's releases. Compare against what's already known in the database. New releases get added to the watchlist with status
available. -
Scheduling — the check should run on a configurable schedule (e.g., daily, every 12 hours). Use a simple scheduler or cron-like system within the app.
-
Auto-download option — if enabled, automatically add new releases to the download queue when detected.
-
Notification hook — when a new release is detected, emit an event that
shanty-notifycan consume (or directly trigger a notification if the notify crate is available). -
Handling rate limits — if the user monitors hundreds of artists, checking all of them requires many API calls. Spread checks over time to stay within rate limits. Track when each artist was last checked to avoid redundant queries.
Acceptance Criteria
- New releases are detected for monitored artists
- Detected releases are added to the watchlist as
available - Checking runs on a configurable schedule
- Rate limits are respected even with many monitored artists
- Auto-download option works when enabled
- A notification event is emitted for new releases
- Last-checked timestamp is stored per artist
- Can trigger a manual check for a specific artist or all artists
Dependencies
- Issue #6 (watchlist management)
- Issue #14 (shared MusicBrainz client)
Issue #20: [Post-MVP] Implement notification system in shanty-notify
Labels: post-mvp, notifications, enhancement
Description
The shanty-notify crate sends notifications to the user when events occur (new releases detected, downloads completed, errors requiring attention, etc.).
-
Notification backends — implement a trait-based notification system supporting:
- Apprise — a notification aggregator that supports 80+ services (Discord, Slack, Telegram, email, Pushover, Gotify, etc.). By integrating with Apprise, Shanty gets all those services for free.
- Webhook — simple HTTP POST to a user-configured URL with a JSON payload
- Log only — just log the notification (useful as a default/fallback)
-
Event types:
NewReleaseDetected— a monitored artist has a new releaseDownloadCompleted— a download finished successfullyDownloadFailed— a download failed (after retries)IndexingCompleted— a library scan finishedTaggingCompleted— auto-tagging finished
-
Configuration — per-event-type notification settings (e.g., notify on new releases via Discord but not on download completion)
Acceptance Criteria
- Notification trait exists with multiple backend implementations
- Apprise integration works (via shelling out to
appriseCLI or its API) - Webhook backend sends proper HTTP POST with JSON payload
- Notifications are triggered by the appropriate events
- Per-event-type configuration works
- CLI:
shanty-notify testsends a test notification
Dependencies
- Issue #1 (workspace scaffolding)
- Issue #19 (new release monitoring — primary trigger for notifications)
Issue #21: [Post-MVP] Implement playlist generation in shanty-playlist
Labels: post-mvp, playlisting, enhancement
Description
The shanty-playlist crate generates playlists based on the user's indexed music library. Some of this work has been done in a prior project called "drift" and the approach should be adapted for Shanty's database and architecture.
-
Playlist generation strategies:
- Similar artists — given a seed artist, find similar artists (via Last.fm or MusicBrainz) that exist in the library and build a playlist from their top tracks
- Genre-based — build a playlist from tracks matching a given genre or set of genres
- Random/shuffle — weighted random selection with configurable rules (e.g., no two tracks by the same artist in a row, prefer recently added, etc.)
- Smart playlists — rule-based playlists (e.g., "all tracks added in the last 30 days with genre 'rock'")
-
Output formats:
- M3U playlist file
- JSON (for API consumption)
- Direct integration with the music server (if
shanty-serveis available)
-
CLI interface:
shanty-playlist similar <artist>— generate a similar-artists playlistshanty-playlist genre <genre>— generate a genre playlistshanty-playlist random [--count <n>]— random playlistshanty-playlist smart <rules>— rule-based playlist--format <m3u|json>— output format--count <n>— number of tracks (default 50)
-
Web API integration — expose playlist generation through the web API so the frontend can generate and display playlists
Acceptance Criteria
- Similar-artist playlist generation works
- Genre-based playlist generation works
- Random playlist with rules works
- M3U output format is valid and plays in standard media players
- JSON output is well-structured
- CLI interface works as specified
- Web API endpoint for generating playlists exists
- Playlists only include tracks that exist in the local library
Dependencies
- Issue #2 (database — needs indexed tracks to build playlists from)
- Issue #3 (music indexing)
- Issue #14 (MusicBrainz client — for similar artist lookups)
Issue #22: [Post-MVP] Implement music serving/streaming in shanty-serve
Labels: post-mvp, serving, enhancement
Description
The shanty-serve crate serves music files for streaming playback. The initial approach is to implement a Subsonic-compatible API, which would allow users to use existing Subsonic-compatible clients (DSub, Ultrasonic, Sonixd, Sublime Music, etc.) to stream their library.
-
Subsonic API compatibility — implement the core Subsonic REST API endpoints:
ping— server statusgetLicense— return a valid license (always "valid" for self-hosted)getMusicFolders— return configured library pathsgetArtists/getArtist— artist listing and detailsgetAlbum— album details with tracksgetSong— track detailsstream— stream the actual audio file (with optional transcoding)download— download the original filegetCoverArt— serve album cover artsearch3— search the librarygetPlaylists/getPlaylist— playlist supportscrobble— track play history
-
Transcoding — optionally transcode to a lower bitrate on-the-fly (using ffmpeg) for bandwidth-limited clients. Support common output formats (MP3, OPUS, AAC).
-
Authentication — the Subsonic API uses a simple username/password scheme. Implement basic auth that works with existing clients.
Design Considerations
- The Subsonic API is XML-based by default but most clients support JSON. Support both.
- This could be a separate Actix server on a different port, or additional routes on the main web server.
- Consider starting with the minimum viable set of Subsonic endpoints that popular clients need to function.
Acceptance Criteria
- Subsonic-compatible API serves music from the library
- At least one popular Subsonic client (e.g., Sonixd, DSub) can connect and browse/play
- Audio streaming works for all supported formats
- Cover art serving works
- Basic search works
- Authentication works
- Optional transcoding works via ffmpeg
Dependencies
- Issue #2 (database)
- Issue #3 (music indexing — library must be populated)
- External:
ffmpegfor transcoding
Issue #23: [Post-MVP] Implement built-in web player in the Elm frontend
Labels: post-mvp, frontend, playback, enhancement
Description
Add a built-in music player to the Elm web UI so users can listen to their library directly in the browser without needing an external Subsonic client.
-
Audio player component:
- HTML5
<audio>element integration in Elm - Play/pause, next/previous, seek bar, volume control
- Track info display (title, artist, album, cover art)
- Persistent player bar at the bottom of the UI (doesn't disappear when navigating)
- HTML5
-
Queue management:
- "Play now" adds a track and starts playing
- "Add to queue" appends to the play queue
- "Play album" / "Play artist" loads all tracks into the queue
- Queue is viewable, reorderable, and clearable
- Shuffle and repeat modes
-
Integration with library browser:
- Play buttons on tracks, albums, and artists in the library view
- "Now playing" indicator in the library browser
- Clicking a track in album view plays that track and queues the rest of the album
Acceptance Criteria
- Audio playback works in the browser for all supported formats
- Player controls (play, pause, seek, volume, next, previous) work
- Track info and cover art display while playing
- Play queue is functional (add, remove, reorder, clear)
- Shuffle and repeat modes work
- Player persists across page navigation
- Playing a track from the library browser works seamlessly
Dependencies
- Issue #10 (Elm frontend)
- Issue #22 (music serving — or alternatively, serve files directly from the Actix backend)
Issue #24: [Post-MVP] Configurable organization templates and advanced renaming in shanty-org
Labels: post-mvp, organization, enhancement
Description
Extend the organization system (Issue #5) with more powerful and configurable templates.
-
Advanced template variables:
{original_filename}— the original filename without extension{quality}— e.g., "FLAC", "MP3 320", "MP3 V0"{album_type}— album, single, EP, compilation{first_letter}— first letter of artist (for A-Z directory grouping)- Conditional blocks:
{if disc_count > 1}Disc {disc_number}/{endif}
-
Multiple organization profiles — allow different templates for different use cases (e.g., one for lossless, one for lossy, one for singles vs. albums)
-
Conflict resolution strategies — configurable behavior when target path already exists:
- Skip
- Overwrite
- Rename with incrementing number
- Keep higher quality version
-
Reverse organization — given the current organized structure and the format template, allow "flattening" or reorganizing to a different template
Acceptance Criteria
- All new template variables work
- Conditional blocks in templates work
- Multiple organization profiles can be configured
- Each conflict resolution strategy works correctly
- Re-organization from one template to another works
- Existing tests still pass
Dependencies
- Issue #5 (basic organization)
Issue #25: [Post-MVP] Add Last.fm scrobbling and integration
Labels: post-mvp, integration, enhancement
Description
Integrate with Last.fm for two purposes: (1) scrobbling played tracks (if using the built-in player or Subsonic server), and (2) using Last.fm's data for better recommendations and similar-artist lookups.
- Last.fm authentication — implement the Last.fm auth flow (API key + user authentication)
- Scrobbling — when a track is played (via web player or Subsonic), scrobble it to Last.fm
- Similar artists API — use Last.fm's
artist.getSimilarfor playlist generation and discovery - Top tracks — use Last.fm's
artist.getTopTracksfor suggesting which tracks to download for a monitored artist SearchProviderimplementation — implement the search trait from Issue #8 using Last.fm as a backend
Acceptance Criteria
- Last.fm authentication works
- Track scrobbling works during playback
- Similar artist lookups return relevant results
- Top tracks for an artist can be retrieved
- Last.fm works as an alternative search provider
- API key configuration is documented
Dependencies
- Issue #8 (search provider trait)
- Issue #22 or #23 (playback — for scrobbling)
Issue #26: [Post-MVP] Add import from existing Lidarr database
Labels: post-mvp, migration, enhancement
Description
Many potential Shanty users will be migrating from Lidarr. Provide a migration tool that imports their existing Lidarr library (artists, albums, wanted items, quality profiles) into Shanty.
-
Lidarr database reading — Lidarr uses a SQLite database. Read the relevant tables:
- Artists (monitored status, metadata, quality profile)
- Albums (monitored status, whether it's been downloaded)
- Root folders
-
Mapping — convert Lidarr's data model to Shanty's:
- Lidarr artists → Shanty artists (with MusicBrainz IDs)
- Lidarr monitored albums → Shanty watchlist items
- Lidarr quality profiles → approximate Shanty download format preferences
-
CLI tool:
shanty import lidarr --db /path/to/lidarr.db--dry-runto preview what would be imported- Report: X artists, Y albums, Z items to be monitored
Acceptance Criteria
- Can read a Lidarr SQLite database
- Artists and their monitored status are imported correctly
- Albums and their wanted/downloaded status are imported
- MusicBrainz IDs are preserved
--dry-runworks- Import summary is displayed
- Duplicate detection — don't import artists/albums that already exist in Shanty
Dependencies
- Issue #2 (database schema)
- Issue #6 (watchlist management)
Issue #27: [Post-MVP] Add cover art management
Labels: post-mvp, metadata, enhancement
Description
Manage album cover art — download, store, and serve cover art images for albums in the library.
-
Cover art downloading:
- Download from Cover Art Archive (MusicBrainz) using release MBID
- Fall back to embedded cover art in audio files
- Store in a configurable directory (e.g.,
covers/{mbid}.jpgor alongside the album)
-
Embedded art extraction — extract cover art embedded in audio file tags and save as a standalone file
-
Art serving — serve cover art via the web API for display in the frontend and Subsonic clients
-
Art embedding — optionally embed cover art back into audio files that lack it
Acceptance Criteria
- Cover art is downloaded from Cover Art Archive when available
- Embedded cover art is extracted as a fallback
- Cover art is served via the API
- Cover art displays in the web frontend
- Cover art is available via the Subsonic API
- Optionally embed art into audio files
Dependencies
- Issue #4 (tagging — provides MusicBrainz IDs for Cover Art Archive)
- Issue #9 (web backend — for serving)
Issue #28: [Post-MVP] Add multi-user support
Labels: post-mvp, enhancement, web
Description
Add basic multi-user support so multiple people in a household can share a Shanty instance with their own watchlists and preferences.
- User accounts — basic username/password authentication
- Per-user watchlists — each user has their own monitored artists/albums
- Shared library — the actual music library is shared, but watchlists are personal
- Admin user — one user is admin and can manage settings, trigger system operations, manage other users
- Session management — JWT or cookie-based sessions for the web UI
Acceptance Criteria
- Users can register and log in
- Each user has their own watchlist
- The music library is shared across users
- Admin can access settings and manage users
- Non-admin users can only manage their own watchlist
- Session management works in the web UI
- Subsonic auth maps to Shanty users
Dependencies
- Issue #9 (web backend)
- Issue #10 (Elm frontend)
- Issue #6 (watchlist — needs to become per-user)
Issue #29: Add comprehensive documentation
Labels: mvp, documentation, priority:low
Description
Write user-facing and developer-facing documentation for Shanty.
-
User documentation:
- Installation guide (from source, Docker, binary releases)
- Configuration reference (all config options explained)
- Quick start guide (get from zero to playing music)
- FAQ
-
Developer documentation:
- Architecture overview (crate structure, data flow)
- Contributing guide (how to set up the dev environment, coding standards, PR process)
- API documentation (REST API reference for the web backend)
-
Inline documentation:
- Rustdoc comments on all public types and functions
cargo doc --workspaceshould produce useful documentation
Acceptance Criteria
- README covers installation, quick start, and basic usage
- Configuration reference documents all options
- Architecture doc explains the crate structure and how they interact
- Contributing guide exists
- REST API is documented (consider using OpenAPI/Swagger)
cargo docproduces clean documentation with no warnings
Dependencies
- Most other MVP issues (documentation should reflect implemented features)
Issue #30: [Post-MVP] Add NZBGet/SABnzbd download backend to shanty-dl
Labels: post-mvp, downloading, enhancement
Description
Add a Usenet-based download backend using NZBGet or SABnzbd as the download client. This is similar in approach to the torrent backend (Issue #17) — integrate with the existing download client's API.
-
NZBGet/SABnzbd API client — communicate with the download client to:
- Submit NZB files for download
- Monitor download progress
- Retrieve completed downloads
-
NZB source integration — integrate with NZB indexers (via Prowlarr/NZBHydra2, or direct indexer API) to search for music
-
Implement
DownloadBackendtrait
Acceptance Criteria
- Can submit NZBs to NZBGet or SABnzbd
- Download progress is tracked
- Completed downloads are detected and processed
- Implements the
DownloadBackendtrait - Configuration for the Usenet client connection exists
Dependencies
- Issue #7 (download system with trait-based backends)
Issue #31: [Post-MVP] Implement quality profile system
Labels: post-mvp, enhancement, downloading
Description
Add a quality profile system that lets users define their preferred audio quality and format, similar to Lidarr's quality profiles. This guides the download system in choosing the best available source.
-
Quality profiles — define named profiles like:
- "Lossless" — prefer FLAC > ALAC > WAV, minimum bitrate 800kbps
- "High Quality" — prefer FLAC > MP3 320 > OPUS 256
- "Standard" — MP3 320 > MP3 V0 > OPUS 128
- "Any" — accept anything
-
Per-item quality assignment — watchlist items can be assigned a quality profile (default to a global setting)
-
Download source scoring — when multiple download sources are available, score them against the quality profile and pick the best match
-
Upgrade detection — if a user has an MP3 version but their profile prefers FLAC, flag it as "upgradeable"
Acceptance Criteria
- Quality profiles can be defined with format and bitrate preferences
- Profiles can be assigned to watchlist items
- Download source selection respects the quality profile
- Upgrade detection works (lower quality → can be upgraded)
- Default profile is configurable
- At least 3 built-in profiles exist
Dependencies
- Issue #6 (watchlist)
- Issue #7 (downloading)
Issue #32: [Post-MVP] Add TUI interface
Labels: post-mvp, enhancement, tui
Description
Several crates were designed to have TUI (terminal user interface) capability. Implement a unified TUI using ratatui that provides an interactive terminal experience for managing Shanty without a web browser.
- Library browser — navigate artists/albums/tracks in the terminal
- Search — search for music and add to watchlist
- Download queue — view and manage downloads
- System status — overview of running tasks, library stats
- Key bindings — vim-style navigation
This is particularly useful for headless servers where users SSH in, or for users who prefer terminal workflows.
Acceptance Criteria
- TUI launches with
shanty tuior as the default for individual crate binaries - Library browsing works with keyboard navigation
- Search and add-to-library flow works
- Download queue is viewable and manageable
- System status is displayed
- Responsive to terminal size changes
Dependencies
- Issue #2 (database)
- Issue #9 (may share the backend API, or call library crates directly)