All apps · 0 apps
Canticle
Docker app from sydlexius' Repository
Overview
Readme
View on GitHub
Command line tool and webhook service to fetch synced lyrics from multiple providers -- Musixmatch, Petit Lyrics, and YouTube Music's InnerTube API -- and save them as .lrc files, falling back to .txt for unsynced lyrics and instrumental markers.
Documentation
Full documentation is published at https://sydlexius.github.io/canticle/:
- Getting Started - onboarding guide: pick a path (one-shot, directory, or daemon) and get to working lyrics.
- User Guide - webhook server, Docker/Unraid, the filesystem watcher, inspection commands.
- CLI Reference - every subcommand and flag.
- Configuration - env vars, TOML keys, token precedence, XDG paths.
- Developer Guide - build, test, the quality gate, design decisions.
Install
macOS / Linuxbrew (Homebrew):
brew install sydlexius/tap/canticle
Linux (.deb / .rpm / .apk): Download the appropriate package for your distro from the GitHub Releases page and install it with your package manager:
# Debian / Ubuntu
sudo apt install ./canticle_*.deb
# RHEL / Fedora / Rocky
sudo dnf install ./canticle_*.rpm
# Alpine
sudo apk add --allow-untrusted canticle_*.apk
The package installs the binary to /usr/local/bin/canticle, a systemd unit
(or OpenRC script on Alpine), and an example config at
/etc/mxlrcgo-svc/config.example.toml. It also creates a mxlrcgo-svc
system user and a state directory at /var/lib/mxlrcgo-svc (mode 0750) that
holds the SQLite database. The service does not start automatically on
install.
After installing, copy the example config, set your token, and start the service:
sudo cp /etc/mxlrcgo-svc/config.example.toml /etc/mxlrcgo-svc/config.toml
# edit /etc/mxlrcgo-svc/config.toml and set [api] token = "YOUR_TOKEN"
sudo systemctl enable --now mxlrcgo-svc # systemd
# or: sudo rc-update add mxlrcgo-svc default && sudo rc-service mxlrcgo-svc start # Alpine OpenRC
The state directory and system user are preserved on package removal so the database survives upgrades and reinstalls. See the User Guide for service commands, log access, and data paths.
Tarballs / macOS / Windows: Versioned archives for all platforms are also available on the GitHub Releases page.
Build from source (requires Go 1.26.4+):
# go install resolves the module path from go.mod, which declares
# github.com/sydlexius/canticle.
go install github.com/sydlexius/canticle/cmd/mxlrcgo-svc@latest
This fork starts its release line at
v1.0.0. The upstreamfashni/mxlrc-gorepository does not publish semver release tags, sov1.0.0is reserved as the firstcanticleversion.
Quickstart
# One song
canticle adele,hello
# Multiple songs into a custom output directory
canticle adele,hello "the killers,mr. brightside" -o some_directory
# Directory mode (recursive): writes each lyric file next to its audio file
canticle "Dream Theater"
# Lidarr webhook server
MUSIXMATCH_TOKEN=YOUR_TOKEN MXLRC_WEBHOOK_API_KEY=mxlrc_your_webhook_key \
canticle serve --listen 127.0.0.1:3876
Directory mode overrides -o/--outdir; the output extension is .lrc for synced lyrics and .txt for unsynced lyrics or an instrumental marker. See the CLI Reference for every flag and the User Guide for Docker, Unraid, and webhook deployment.
Renamed an audio file and left its .lrc/.txt behind? canticle realign re-attaches orphaned lyric sidecars to their audio (dry-run by default; --yes to apply), and contrib/lidarr-rename-sidecars.sh is a Lidarr Custom Script that prevents the orphan at rename time. See Realign.
Have lyrics that drift out of sync with the song? canticle revalidate re-checks synced .lrc files already on disk against the real length of their audio and, where the timing does not fit, keeps the words as .txt or sets the file aside (dry-run by default; --apply to act). Serve mode can also do this unattended: set timing_validation.enabled and timing_validation.revalidate_existing, and a background sweep drains the backlog a batch at a time, then idles. It never re-scans the library - a judged file is recorded and left alone - and it shares the CLI's remediation core, so canticle revalidate previews exactly what it will do. See Timing validation.
Token
A token is required only when the Musixmatch provider is in use, and only that provider needs one -- Petit Lyrics and the InnerTube lane are both tokenless. In serve mode with Musixmatch as the primary or a fallback lane, a token is optional: on first run Canticle obtains one automatically and stores it encrypted at rest, reusing it on every later start, so there is nothing to set up. The one-shot fetch CLI keeps no state, so it cannot store a token and still needs one supplied explicitly when Musixmatch is selected.
What counts is the effective token -- the first non-empty of the --token flag, MUSIXMATCH_TOKEN/MXLRC_API_TOKEN, the config file's api.token, the encrypted secret store, and (in serve mode) an automatically minted token. A Musixmatch fallback lane is skipped only when none of those resolves, so an empty api.token on its own does not skip the lane.
To supply your own instead, prefer canticle secrets set musixmatch_token, which reads the value from stdin and stores it encrypted at rest, keeping it out of shell history and process listings. The --token CLI flag, the MUSIXMATCH_TOKEN environment variable, and a .env/config file remain supported, in that order of precedence (CLI > env > file). A token you supply always takes precedence and is never overwritten. See Configuration for the full env-var and TOML surface, and Multi-provider orchestration for selecting a different primary provider.
Encrypted secrets
The Musixmatch token and the webhook API key can be stored encrypted at rest in the SQLite database (AES-256-GCM) instead of as plaintext in config and environment variables. It is opt-in and backward compatible: the encrypted store is the lowest-precedence source, so existing env/TOML setups are unchanged. Import the current plaintext with canticle secrets import, set one by name from stdin with canticle secrets set <name>, and list stored names (never values) with canticle secrets list. The 32-byte master key is auto-generated as a 0600 key file on first use (the universal zero-setup default on all platforms including Docker). Set MXLRC_MASTER_KEY to an optional base64-encoded override for key/data separation (recommended when the threat model includes whole-volume theft). Losing the key makes the encrypted secrets unrecoverable by design; the remedy is to re-import or re-set them with the original plaintext. See the Encrypted secrets guide.
Web UI access (serve mode)
The serve-mode browser UI is gated by a single admin account (session login, separate from the webhook API key). It is off by default; enable it with web_ui_enabled = true under [server].
First run is interactive. With no admin yet, every UI page redirects to /setup, an onboarding form that creates the admin account and (optionally) stores the Musixmatch token and webhook API key encrypted at rest. Until that admin exists, /setup is reachable to any client that can reach the port; once it exists the page closes permanently and cannot reopen. Bootstrapping no longer requires granting a trusted-network bypass -- see Web UI access for why, and for how to close the window immediately on an exposed network by bootstrapping the admin from the environment.
For headless deployments (Docker), you can skip the interactive form by setting both MXLRC_WEBAUTH_ADMIN_USER and MXLRC_WEBAUTH_ADMIN_PASSWORD in the environment. On startup, if no admin exists yet, the daemon creates one from these values (password must be at least 8 characters). It is idempotent (an existing admin is never overwritten) and the password is never logged. Treat these as bootstrap-only credentials: after first run, sign in and rotate the password, then remove the variables from the environment.
TLS
TLS for the serve listener is off by default (plain HTTP), so deployments behind a TLS-terminating reverse proxy avoid double-encryption by leaving it disabled. Enable it under [server.tls] in one of two ways:
- Bring-your-own certificate: set
cert_fileandkey_file(both required together). The listener terminates TLS itself with a TLS 1.2 minimum. Env:MXLRC_TLS_CERT_FILE,MXLRC_TLS_KEY_FILE. - Self-signed bootstrap: set
self_signed = true(mutually exclusive withcert_file/key_file). An ECDSA P-256 certificate (CNmxlrcgo-svc, ~365-day validity) is generated and persisted0600under<dir(db_path)>/tls/, and regenerated when missing or expired. Browsers show an untrusted-certificate prompt; this is intended for a LAN box, not public exposure. Env:MXLRC_TLS_SELF_SIGNED.
When TLS is on, the session cookie's Secure flag is set automatically. An optional redirect_http listen address (e.g. ":80", env MXLRC_TLS_REDIRECT_HTTP) runs a plain-HTTP listener that 301-redirects every request to the HTTPS address. A contradictory configuration (self_signed combined with a cert/key, or only one of cert_file/key_file) is a fatal startup error. ACME/Let's Encrypt is a planned follow-up.
Credits
- Spicetify Lyrics Plus
- Forked from fashni/mxlrc-go.
Legal
- Privacy Policy - what data leaves your machine during a lyrics lookup and what does not.
- Code Signing Policy - SignPath attribution, team roles, and release approval process.
License
GPL-3.0. This project is a fork of fashni/mxlrc-go, which is MIT-licensed; the original MIT copyright and permission notice are retained in NOTICE.
Media gallery
1 / 2Install Canticle on Unraid in a few clicks.
Find Canticle in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/sydlexius/canticleRuntime arguments
- Web UI
http://[IP]:[PORT:50705]/- Network
bridge- Shell
bash- Privileged
- false
Template configuration
Serves both the web dashboard and the Lidarr webhook endpoint.
- Target
- 50705
- Default
- 50705
- Value
- 50705
Stores config.toml, the SQLite database, and the encrypted secrets key.
- Target
- /config
Your music library. Lyrics are written next to each audio file, so this must be writable. Add more Path mappings for additional libraries if needed.
- Target
- /music
Leave this blank, and ideally delete this variable entirely. Canticle obtains its own Musixmatch token on first start and stores it encrypted, so no token is needed here for a normal install. Setting a value overrides that and also turns off automatic token renewal, so a token entered here will eventually go stale and stop fetching lyrics until you replace it by hand. Only fill this in if you have a specific reason to pin your own token. If you would rather manage it in the UI, add it under Settings in the web dashboard instead, where it is stored encrypted rather than as a plain env var.
- Target
- MUSIXMATCH_TOKEN
LEAVE THIS BLANK unless you know you need it. Any device in a listed CIDR reaches the web dashboard and Settings WITHOUT SIGNING IN, permanently, which includes reading your API token and minting webhook keys. Blank means only the container itself is trusted, so everyone signs in normally. First-run setup no longer needs an entry here: the setup page is open until you create your admin account, then closes for good. Add your LAN (for example 192.168.1.0/24) only for something that genuinely cannot sign in, such as a Prometheus scraper reading /metrics. Public IPs are never trusted.
- Target
- MXLRC_TRUSTED_CIDRS
Serve the browser dashboard and settings UI on the listen port. Leave true for the normal Unraid experience; set false to run headless (webhook + scheduled scans only).
- Target
- MXLRC_WEB_UI_ENABLED
- Default
- true
- Value
- true
Static API key Lidarr presents to authenticate webhook calls. Optional; you can also mint managed keys in the web UI (Settings, Webhook keys).
- Target
- MXLRC_WEBHOOK_API_KEY
Base64 of 32 random bytes (openssl rand -base64 32). Encrypts API secrets at rest. Auto-generated to a key file under /config if left blank; set an explicit value to keep the key off the data volume and preserve secrets across container recreation.
- Target
- MXLRC_MASTER_KEY
Minimum seconds between Musixmatch calls. 60 is recommended to avoid egress-IP rate limiting; the built-in default is more aggressive.
- Target
- MXLRC_API_COOLDOWN
- Default
- 60
- Value
- 60
Watch the library for changes and trigger targeted scans (in addition to the periodic scan). true or false.
- Target
- MXLRCGO_WATCH_ENABLED
- Default
- false
- Value
- false
Optional. URL of a separately-deployed YAMNet classifier sidecar (for example http://yamnet:8080) to detect instrumental tracks on provider misses. Leave blank to disable.
- Target
- MXLRC_INSTRUMENTAL_DETECTOR_CLASSIFIER_URL
Log verbosity: info, debug, warn, or error.
- Target
- MXLRC_LOG_LEVEL
- Default
- info
- Value
- info
IANA timezone, for example America/Los_Angeles. Used for dashboard timestamps.
- Target
- TZ
User ID for file permissions. Default 99 maps to nobody on Unraid.
- Default
- 99
- Value
- 99
Group ID for file permissions. Default 100 maps to users on Unraid.
- Default
- 100
- Value
- 100