A Spring Shell CLI tool for managing and migrating a Lidarr music library. Built to solve a specific problem: bulk-migrating artist paths from one base directory to another, with QNAP NAS verification and MusicBrainz integration for adding missing artists.
This is an oddly specific application — built specifically for my setup: a QNAP NAS running
Lidarr, with a music library that needed reorganizing from a flat structure into an
/artists/ subdirectory.
The problem was twofold:
- Bulk path updates are painful in Lidarr. There's no built-in way to migrate 100+ artists from one base path to another without clicking through each one individually.
- The Lidarr metadata server was down. This meant adding new artists through the normal UI wasn't possible — but the folders already existed on disk. The only way through was directly via the API using MusicBrainz IDs.
If you happen to have a QNAP NAS, use Lidarr, and find yourself in the same situation — this might save you a few hours.
- Java 21, Spring Boot 4.0.3, Spring Shell 4.0.1
- Apache HttpClient 5 (with SSL bypass for self-signed NAS certs)
- Jackson, Apache Commons Text (fuzzy matching)
Configuration lives in application.yaml (and use configure shell command at runtime for auth settings):
fixer:
lidarUrl: http://your-lidarr-host:8686
qnapUrl: https://your-qnap-host
oldBasePath: /data/media/music
newBasePath: /data/media/music/artists
fuzzyThreshold: 0.85
contactEmail: your@email.comThis is the order that was used to successfully migrate a 115-artist library:
1. Sync folders first — finds QNAP folders not yet in Lidarr and adds them:
lidarr-sync-folders -q 1 -m 1
2. Update paths — migrates all Lidarr artist paths from oldBasePath to newBasePath:
lidarr-update-paths
Note: Both commands default to dry-run mode. Pass
-d falseto apply changes.
All commands are in the lidarr group. Run help in the shell to see all available commands.
Lists all artists currently in Lidarr with their ID and path.
shell:> lidarr-list
Output columns: Artist name, Lidarr ID, current path.
Lists all quality and metadata profiles configured in Lidarr. Useful for finding profile IDs
before running lidarr-sync-folders or lidarr-add.
shell:> lidarr-profiles
Searches MusicBrainz for an artist by name and displays results sorted by match score.
shell:> lidarr-mb-search -s "Rammstein"
| Option | Short | Description |
|---|---|---|
--search |
-s |
Artist name to search for (required) |
Results show: name, disambiguation, country, score, and MBID.
Searches MusicBrainz, lets you pick an artist, and adds them to Lidarr. Optionally checks whether the artist folder already exists on QNAP.
shell:> lidarr-add -s "Parkway Drive"
shell:> lidarr-add -s "Parkway Drive" -q 1 -m 1
| Option | Short | Description |
|---|---|---|
--search |
-s |
Artist name to search (required) |
--default-quality-profile |
-q |
Quality profile ID (prompts if omitted) |
--default-metadata-profile |
-m |
Metadata profile ID (prompts if omitted) |
Flow:
- Searches MusicBrainz, shows top 10 results
- You select a result by number
- Lidarr resolves the MBID
- Profile selection (if not passed as options)
- Shows proposed path and QNAP existence check
- Confirm to add
Previews and optionally applies a path update for a single artist by Lidarr ID.
Useful for testing the update flow before running lidarr-update-paths on the full library.
Defaults to dry-run mode for safety.
shell:> lidarr-update-single -i 42
shell:> lidarr-update-single -i 42 -d false
| Option | Short | Description |
|---|---|---|
--id |
-i |
Lidarr artist ID (required) |
--dry-run |
-d |
Preview without applying (default: true) |
Flow:
- Fetches artist from Lidarr
- Shows old path → proposed new path
- Checks if new path exists on QNAP
- If not found, attempts a fuzzy match against QNAP folders
- If not dry-run, applies the update
Bulk-migrates all Lidarr artist paths from oldBasePath to newBasePath. Defaults to dry-run
mode — pass -d false to apply changes.
shell:> lidarr-update-paths # dry-run preview
shell:> lidarr-update-paths -d false # apply changes
shell:> lidarr-update-paths -d false -s # apply, skip QNAP verification
| Option | Short | Description |
|---|---|---|
--dry-run |
-d |
Preview changes without applying (default: true) |
--skip-qnap |
-s |
Skip QNAP path verification (default: false) |
Flow:
- Loads all artists from Lidarr
- Loads all folders from QNAP
newBasePath - For each artist on
oldBasePath:[OK]— exact folder found on QNAP → updates immediately[MISS]— not found → tries fuzzy match, prompts to accept/reject/enter custom path[SKIP]— already on new path or unrelated path
- Prints summary: Updated / Skipped / Failed
Cross-references QNAP artist folders against Lidarr and interactively adds any that are missing.
This is the inverse of lidarr-update-paths — it finds folders on disk that Lidarr doesn't know about.
Fuzzy matching is enabled by default to avoid false positives from minor name differences.
shell:> lidarr-sync-folders -q 1 -m 1 # dry-run preview
shell:> lidarr-sync-folders -q 1 -m 1 -d true # apply changes
shell:> lidarr-sync-folders -q 1 -m 1 -f false # disable fuzzy filtering
| Option | Short | Description |
|---|---|---|
--dry-run |
-d |
Preview missing folders without adding (default: false) |
--use-fuzzy |
-f |
Exclude near-matches from missing list using fuzzy threshold (default: true) |
--default-quality-profile |
-q |
Quality profile ID (prompts if omitted) |
--default-metadata-profile |
-m |
Metadata profile ID (prompts if omitted) |
Flow:
- Lists all folders in QNAP
newBasePath - Lists all artist folder names from Lidarr
- Computes folders missing from Lidarr (exact match, fuzzy-filtered by default)
- Shows summary and missing list
- For each missing folder:
- Searches MusicBrainz using the folder name
- Shows top 10 results
- You select a result, or
sto skip, orqto quit - Adds the artist to Lidarr using the QNAP folder name as the path
- Prints summary: Added / Skipped
Tip: Fuzzy matching is on by default to avoid being prompted for artists already in Lidarr
under a slightly different folder name (e.g. Die Antwoord vs die antwoord). Use -f false
to see all unmatched folders regardless.
- The QNAP API requires paths without a leading slash and with literal
/separators (no URL encoding). - The Lidarr PUT endpoint requires the full artist object — only the
pathfield is changed. - MusicBrainz requires a valid
User-Agentheader in the formatAppName/Version ( contact ). Requests without one will be rejected with 403. - SSL certificate validation is bypassed for QNAP (self-signed cert). Lidarr uses a standard HTTP connection.