- Go 99.1%
- Dockerfile 0.9%
| .forgejo/workflows | ||
| cmd/likesync | ||
| internal | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
likesync
Mirrors your Navidrome starred songs to ListenBrainz recording loves on a schedule.
One-way: Navidrome stars -> ListenBrainz love feedback. Un-starring a track in
Navidrome removes the love on ListenBrainz — but only for loves this tool created
(tracked in a local SQLite DB). Loves you made elsewhere are never touched.
How matching works
Each starred song is resolved to a MusicBrainz recording MBID:
- The
musicBrainzIdreturned by Navidrome (present when your library is tagged). - If absent and
MBID_LOOKUP_ENABLED=true, the ListenBrainz metadata lookup is queried by artist + title (+ album). Results are cached. - Otherwise the track is skipped and logged.
On a large, poorly-tagged library the first run issues one ListenBrainz metadata
lookup per uncached track and can hit rate limits — expect a burst of resolve failed logs that clear on later cycles (results, including misses, are cached).
To avoid this, tag your library with MusicBrainz IDs (e.g. via Picard) so the
musicBrainzId is present, or set MBID_LOOKUP_ENABLED=false.
Configuration
| Var | Default | Notes |
|---|---|---|
NAVIDROME_URL |
— | required, e.g. https://music.example.com |
NAVIDROME_USERNAME |
— | required |
NAVIDROME_PASSWORD |
— | required |
LISTENBRAINZ_TOKEN |
— | required, from https://listenbrainz.org/settings/ |
LISTENBRAINZ_URL |
https://api.listenbrainz.org |
override for self-hosted |
SYNC_INTERVAL |
1h |
Go duration (e.g. 30m, 6h) |
RUN_ON_START |
true |
run a sync immediately on boot |
MBID_LOOKUP_ENABLED |
true |
metadata-lookup fallback toggle |
MBID_CACHE_TTL |
720h |
how long lookup results (incl. misses) are cached; 0 = never expire |
STATE_PATH |
/data/likesync.db |
SQLite file path |
LOG_LEVEL |
info |
debug/info/warn/error |
DRY_RUN |
false |
log intended changes, write nothing |
Run
cp .env.example .env # fill in your values
docker compose up -d --build
docker compose logs -f
Do a DRY_RUN=true pass first to preview what would change.