🎧 Your Personal Streaming Service https://www.navidrome.org
  • Go 81.5%
  • JavaScript 15.1%
  • Rust 2.2%
  • Go Template 0.5%
  • Makefile 0.3%
  • Other 0.4%
Find a file
Deluan Quintão ffc68e29db
feat(cli): add artwork cancel to call off queued artwork work (#6006)
* feat(cli): add `artwork cancel` to call off queued artwork work

A bulk backfill had no off switch. Changing an artwork setting bumps the config
fingerprint, which enqueues every entity in the library, and the only way to stop
it was to turn agents off -- which changes the fingerprint again and enqueues a
second full backfill. The escape hatch was the trap.

`artwork cancel` deletes pending queue rows selected by --kind and/or --priority,
with the --dry-run/confirm/-y flow `reprocess` already uses. Cancelling by
priority is the point: it drops a runaway backfill while leaving the bump-priority
rows an operator queued by hand.

It only touches the queue. Resolved artwork and the item_artwork state behind
`artwork explain` are left alone, and the trace of why a cancelled item last
failed goes with its row. Preserving that trace would mean writing it to
last_failure, which `explain` prints under "Gave up after" -- reporting a
cancellation as an exhausted retry budget. The help text says the trace is
discarded instead.

Two limits the help text states, because neither is guessable: work already
dequeued is not interrupted, and an item with no artwork state yet can be queued
again by the hourly missing-artwork recheck. Cancel calls off queued work; it
does not stop the worker.

--kind validates against RefreshableKinds, not the RecheckKinds `reprocess` uses:
the queue holds media file rows, so --all has to reach them. PurgeQueued follows
the repository's naming rule -- it finds its own rows and reports how many went --
and ignores retry_at, since a row still backing off is pending work. The preview
reuses CountByKindAndPriority rather than adding a counter. reprocessConfirm
became confirmUnlessYes(yes, in, verb) now that two commands prompt.

* refactor(cli): share the artwork queue filter between the preview and the delete

Follow-up cleanup on the previous commit; no change to what the command does,
apart from --all, noted below.

The "which rows does cancel touch" predicate was written three times: once as SQL
in PurgeQueued, once in Go in cmd's matchingQueueStats, and once more in the mock.
The preview and the delete could therefore drift, and the mock would keep the
tests green while they did. persistence now has one artworkQueueFilter, shared by
PurgeQueued and a new CountQueued, and cmd does no filtering at all.

That also makes the preview cheaper. It counted the whole queue and filtered in
Go, so `artwork cancel --kind al` scanned every row of every kind to print a
handful. CountQueued pushes the filter into SQL, which the drain index serves as a
range seek. CountByKindAndPriority is gone: it is CountQueued(nil, nil).

--all now selects with an empty filter instead of enumerating RefreshableKinds.
It is what the flag help already claimed, and the enumeration was narrower than
its own documentation -- a queue row whose item_kind this build does not know
survived `--all` with no flag combination able to remove it. It also restores
SQLite's truncate path: measured with EXPLAIN QUERY PLAN, a bare DELETE plans to
nothing, while `WHERE (1=1)` -- which an empty squirrel And renders -- plans to a
full index scan. A test pins the filter's emptiness so that cannot regress
silently.

Also folded together three copies of the parse-and-dedup loop (parseAll), two
copies of the queue-stats table (printQueueStats, now shared with `artwork
status`), two copies of the stat sum (queueTotal), and four copies of the
kind-to-prefix mapping (model.KindPrefixes). The PurgeQueued specs became one
DescribeTable that asserts count and delete agree on every selection.

* docs(cli): say when `artwork cancel` evaluates its selection

The help text covered the two limits that surprise an operator after the fact, but
not the one that bites during the prompt: the count is a preview, and the filters
run again on confirm. A scan or a manual refresh landing in between is cancelled
without ever appearing in the table the operator agreed to.

Deleting only the previewed rows was considered and rejected. The exposure is one
item re-resolving on next view instead of immediately: clearing an item's artwork
state is what every recovery path selects on, so a lost Bump row from
artwork.Refresh comes back at the same priority via provisional() on the next
request, and otherwise within the hour via EnqueueAllMissing. Buying a guarantee
against that costs the truncate path on --all, the flag that exists for a
29k-item backfill.

* refactor(cli): share one set of flag targets across the artwork subcommands

reprocess and cancel each declared their own kinds/all/dry-run/yes variables, but
cobra only ever parses the one subcommand being run, so the two sets could never
hold values at the same time. backup.go already binds one backupDir across two
subcommands and one force across two more; this follows that.

Ten package-level variables become six. Each command keeps its own help string
and its own valid-kind list, so --kind still reports RecheckKinds for reprocess
and RefreshableKinds for cancel, and --source and --priority stay registered only
on the command that has them.

The priority lookup table is now knownPriorities, freeing the artworkPriorities
name for the flag. The new name also reads better against priorityName's fallback
for a value it does not know.
2026-08-21 14:03:59 -04:00
.devcontainer chore(deps): upgrade Go to 1.26 (#5361) 2026-04-14 19:31:01 -04:00
.github ci: pull base images through mirror.gcr.io instead of ECR Public (#5997) 2026-08-20 10:02:04 -04:00
adapters feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
cmd feat(cli): add artwork cancel to call off queued artwork work (#6006) 2026-08-21 14:03:59 -04:00
conf feat(artwork): make the artwork image size cap configurable (#5931) 2026-08-11 10:42:39 -04:00
consts feat(artwork): make the artwork image size cap configurable (#5931) 2026-08-11 10:42:39 -04:00
contrib fix(contrib): added missing hyphen in OpenRC script that caused crashes on startup (#5906) 2026-08-08 15:40:05 -04:00
core feat(artwork): store the resolution trace so artwork explain works offline (#5980) 2026-08-21 10:24:01 -04:00
db feat(artwork): store the resolution trace so artwork explain works offline (#5980) 2026-08-21 10:24:01 -04:00
git feat(plugins): experimental support for plugins (#3998) 2025-06-22 20:45:38 -04:00
log feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
model feat(cli): add artwork cancel to call off queued artwork work (#6006) 2026-08-21 14:03:59 -04:00
persistence feat(cli): add artwork cancel to call off queued artwork work (#6006) 2026-08-21 14:03:59 -04:00
plugins docs(plugins): document how a metadata agent signals "not found" (#6001) 2026-08-20 22:48:26 -04:00
release ci: pull base images through mirror.gcr.io instead of ECR Public (#5997) 2026-08-20 10:02:04 -04:00
resources feat(scrobbler): add per-user scrobble filter (#5964) 2026-08-15 16:10:53 -04:00
scanner fix(scanner): detect in-place playlist edits via the folder content hash (#5914) 2026-08-19 13:11:00 -04:00
scheduler fix(log): change debug log level to trace to reduce log noise from cron 2026-08-06 00:40:58 -04:00
scripts build(worktree): add script for setting up git worktrees 2026-03-17 21:34:00 -04:00
server test: unskip path-separator tests on Windows (#5381) (#5916) 2026-08-19 11:50:32 -04:00
tests feat(cli): add artwork cancel to call off queued artwork work (#6006) 2026-08-21 14:03:59 -04:00
ui fix(playlist): block track edits on synced playlists across all APIs (#5984) 2026-08-19 08:47:53 -04:00
utils fix(cache): write the completion marker before closing the cache writer (#5927) 2026-08-10 14:10:05 -04:00
.dockerignore fix: add music.old to .dockerignore and .gitignore 2026-02-06 07:40:05 -05:00
.git-blame-ignore-revs Move project to Navidrome GitHub organization 2021-02-06 21:47:19 -05:00
.gitignore perf(persistence): use *_artists join tables for artist participant filters (#5930) 2026-08-10 11:42:27 -04:00
.golangci.yml refactor: replace md5 with xxh3 for faster and more efficient hashing 2026-08-19 09:28:25 -04:00
.nvmrc chore(deps): update all dependencies (#4618) 2025-10-25 17:05:16 -04:00
CODE_OF_CONDUCT.md Use Contributor Covenant v2.0 2020-07-21 14:40:21 -04:00
context7.json Add context7.json with URL and public key 2026-04-14 19:19:42 -04:00
CONTRIBUTING.md docs: update commit message format in CONTRIBUTING.md 2026-02-20 11:00:34 -05:00
Dockerfile ci: pull base images through mirror.gcr.io instead of ECR Public (#5997) 2026-08-20 10:02:04 -04:00
go.mod chore(deps): update module dependencies in go.mod and go.sum to latest versions 2026-08-17 20:36:13 -04:00
go.sum chore(deps): update module dependencies in go.mod and go.sum to latest versions 2026-08-17 20:36:13 -04:00
LICENSE Change license to GPLv3 2020-01-22 14:48:38 -05:00
main.go feat(server): implement FTS5-based full-text search (#5079) 2026-02-21 17:52:42 -05:00
Makefile fix(build): derive version from reachable git tag (#5711) 2026-07-05 00:15:59 -04:00
Procfile.dev chore(deps): upgrade to Go 1.24.1 (#3851) 2025-03-17 21:08:10 -04:00
README.md feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
reflex.conf feat(server): implement FTS5-based full-text search (#5079) 2026-02-21 17:52:42 -05:00

Navidrome logo

Navidrome Music Server  Tweet

Last Release Build Downloads Docker Pulls Dev Chat Subreddit Contributor Covenant Gurubase

Navidrome is an open source web-based music collection server and streamer. It gives you freedom to listen to your music collection from any browser or mobile device. It's like your personal Spotify!

Note: The master branch may be in an unstable or even broken state during development. Please use releases instead of the master branch in order to get a stable set of binaries.

Check out our Live Demo!

Any feedback is welcome! If you need/want a new feature, find a bug or think of any way to improve Navidrome, please file a GitHub issue or join the discussion in our Subreddit. If you want to contribute to the project in any other way (ui/backend dev, translations, themes), please join the chat in our Discord server.

Installation

See instructions on the project's website

Cloud Hosting

PikaPods has partnered with us to offer you an officially supported, cloud-hosted solution. A share of the revenue helps fund the development of Navidrome at no additional cost for you.

PikaPods

Features

  • Handles very large music collections
  • Streams virtually any audio format available
  • Reads and uses all your beautifully curated metadata
  • Great support for compilations (Various Artists albums) and box sets (multi-disc albums)
  • Multi-user, each user has their own play counts, playlists, favourites, etc...
  • Very low resource usage
  • Multi-platform, runs on macOS, Linux and Windows. Docker images are also provided
  • Ready to use binaries for all major platforms, including Raspberry Pi
  • Automatically monitors your library for changes, importing new files and reloading new metadata
  • Supports lyrics from sidecar .ttml, .yaml/.yml Lyricsfile, .elrc, .lrc, .srt, .txt files and embedded TTML, Enhanced LRC, LRC, SRT, and plain-text tags (via lyricspriority)
  • Themeable, modern and responsive Web interface based on Material UI
  • Compatible with all Subsonic/Madsonic/Airsonic clients
  • Transcoding on the fly. Can be set per user/player. Opus encoding is supported
  • Translated to various languages

Translations

Navidrome uses POEditor for translations, and we are always looking for more contributors

Documentation

All documentation can be found in the project's website: https://www.navidrome.org/docs. Here are some useful direct links:

Screenshots