A desktop music player for the music you already own.
Point Grape at a folder of music. It walks the tree, reads the tags, caches what it found, and gives you artists, albums, genres and folders to browse through: no account, no server, no network call unless you ask for one. It is a single binary built on iced and rodio, distributed through Colony.
Status: the scan → cache → browse → play loop is what Grape is used for daily, and it is the part covered by tests: scanning, tag reading, album-artist inference, the library cache round trip, Last.fm parsing and backoff, playlist manipulation, and preference clamping and migration. The UI browsing, search, filters, tabs and FR/EN strings still need interactive testing. CI builds and runs tests on Linux, Windows and macOS; tests requiring a real audio device are explicitly ignored there. Native media sessions have state tests and an opt-in MPRIS round-trip test on an isolated D-Bus session. Linux is the platform exercised locally; Windows system controls still need interactive validation on Windows. Opus, AIFF and WMA files are scanned into the library and then fail to decode.
Most local players ask you to import your music into their world first. Grape treats the folder on disk as the truth and everything else as a cache you can delete.
- Your folder layout is the library.
Artist/Album/Trackworks, albums sitting at the root work, loose files at the root work. No music file is moved, renamed or rewritten, and by default Grape writes nothing into the music folder: its cache lives in the Colony cache directory. Delete the cache, or point it elsewhere in preferences, and nothing is lost but a rescan. - Nothing leaves the machine by default. The only network call is Last.fm album enrichment, and it stays off until you paste in your own API key.
- Rescans are cheap. Every track is cached under a size + mtime signature, so a second scan only touches what changed.
- Tags lose to you. A genre or year you set by hand on an album outranks both the file's tags and anything Last.fm returns.
┌──────────────────────────────────────────────────────────────┐
│ Grape Artists Genres Albums Folders [search] │
├───────────────┬──────────────────┬───────────────────────────┤
│ │ │ │
│ Artists │ Albums │ Tracks │
│ │ │ │
├───────────────┴──────────────────┴───────────────────────────┤
│ ◀◀ ▶ ▶▶ ──────●──────────── 1:42 / 4:05 ♪ │
└──────────────────────────────────────────────────────────────┘
The Grape logo in the corner opens the menu that holds Library, Playlist, Queue, Preferences and the four search toggles.
Library
- Scans a root folder given on the command line, or the one saved in
preferences (default
~/Music). - Reads tags with lofty: title, artist, album artist, compilation flag, duration, bitrate, codec, year, genre, embedded cover art.
- Works out the album artist for root-level albums: a compilation flag gives
Various Artists, otherwise a shared
ALBUMARTIST, otherwise a sharedARTIST. - Caches a track index, per-folder entries, cover art and metadata in the Colony cache directory, outside the music folder, and invalidates per track on size and modification time.
- Picks cover art from the album folder first (
cover,folder,front,artwork,albumas.jpg,.jpeg,.png,.webp), then the embedded picture, then whatever the last scan cached. - Optionally fills in missing album genre and year from Last.fm, with your own API key, a TTL cache and backoff when the API pushes back.
Playback
- Play, pause, seek, next, previous, shuffle, repeat, playback speed from 0.5× to 2×.
- Gapless playback: the next track is queued into the output half a second before the current one ends.
- A 3- or 5-band equalizer with Flat, Bass, Treble, Vocal and Custom presets, clamped to ±12 dB.
- Quiet / Normal / Loud output levels, and a configurable sample rate that falls back to the system default, with a visible notice, when the device refuses it.
Browsing
- Four tabs (Artists, Genres, Albums, Folders) over a three-column layout, with a player bar along the bottom.
- Search with accent-insensitive matching, widened on demand to also match genre, year, duration or codec. The ordering is fixed (by album, then track number): the four sort modes exist in the code but no control reaches them.
- A full-screen queue split into now playing and up next, and playlists you can create, rename, delete and reorder.
- A mini-player mode that collapses the window down to the player bar alone.
- Keyboard control inside the window (arrows,
Tab,Enter,Space,n,p,+/-,m) and three global hotkeys,Ctrl+Alt+P,Ctrl+Alt+←andCtrl+Alt+→, once Advanced shortcuts is enabled in Preferences.
Around the edges
- Opt-in desktop notifications, a tray icon and autostart, each one disabling itself when the platform says it is unavailable.
- Native system media controls: MPRIS 2 on Linux (including DMS) and SMTC on Windows. The system panel displays the current track, artist, album, cover and timeline, and can play, pause, stop, skip and seek. Linux also exposes volume and playback rate; Windows supports rate requests when offered by the system client. This works independently of tray and notification preferences.
- French and English interface, following the system language on Linux, Windows and macOS, with English for any other language.
- Session resume: track, position, tab and queue index come back on the next launch.
- Themes from Colony's shared palette catalog, defaulting to Catppuccin Mocha, and Colony's typography: one text scale, and a dyslexia-friendly font that swaps every text at once.
Media sessions follow the track actually playing, even while browsing another album. Commands remain available while paused without an extra polling timer. Metadata and cover references update only when the track changes; regular Linux position updates do not broadcast D-Bus signals. Covers stay local. If the OS media service is unavailable, Grape logs the failure and keeps playing normally. The exact controls shown depend on the desktop or Windows client. macOS system media controls are not implemented.
Files that actually decode: MP3, FLAC, WAV, OGG Vorbis, M4A/AAC. The scanner
also accepts .opus, .aif, .aiff and .wma; those appear in the library and
fail at playback, because the decoders compiled in do not cover them.
Install Colony, open the Multimedia category and install Grape. Updates arrive through the launcher, and because releases are signed, Colony verifies the signature before installing.
Grab the asset for your platform from the
latest release. Each
one ships with a matching .sig, .meta and .meta.sig.
| Platform | Asset |
|---|---|
| Linux (x86_64) | grape-linux |
| Windows (x86_64) | grape-windows.exe |
| macOS (Apple Silicon) | grape-macos |
| macOS (Intel) | grape-macos-x86 |
There is no installer: download the asset and run it.
The .sig and .meta.sig files are Colony's own ed25519 signatures. The
binaries are not signed by Apple or Microsoft, so macOS and Windows warn on
first launch.
-
Linux:
chmod +x grape-linux && ./grape-linux -
macOS: make it executable and clear the download quarantine flag, then run it:
chmod +x grape-macos xattr -d com.apple.quarantine grape-macos ./grape-macos
Use
grape-macos-x86on Intel Macs. If you skip thexattrstep, macOS 15 and later block the first launch; allow it under System Settings > Privacy & Security > Open Anyway. -
Windows: if SmartScreen shows "Windows protected your PC", click More info > Run anyway.
Releases are cut by release-please when its release pull request is merged, not
on every merge to main.
git clone https://github.com/Project-Colony/Grape
cd Grape
cargo build --releaseRequires Rust 1.90 or newer. On Linux you also need libasound2-dev and
pkg-config for the audio backend, plus clang and lld: the repository pins
the Linux linker to clang -fuse-ld=lld, and without them the build fails at
link time in a way that looks like a Rust problem and is not.
Run it against a specific folder:
cargo run --release -- /path/to/libraryWith no argument, Grape uses the library folder from preferences.
Run the regular suite with cargo test. On Linux, the native media checks can
also exercise a private session bus and a real audio output device:
dbus-run-session -- cargo test --lib mpris_round_trip -- --ignored
cargo test --lib player::tests -- --ignored
cargo test --lib preloaded_track -- --ignoredFull documentation is in docs/. Start at docs/README.md.
Every release asset is signed by the Project Colony organisation in CI, never on a developer machine. Next to each asset on the release page:
| File | What it is |
|---|---|
<asset>.sig |
an ed25519 signature over the asset, made with the organisation's release key |
<asset>.meta |
three lines binding the asset to its file name, its sha256 and the release version |
<asset>.meta.sig |
an ed25519 signature over the .meta |
The private key is an organisation secret, used only by the shared sign-and-publish workflow in a job that builds nothing; the jobs that compile Grape never see it. Colony checks all three files before it installs or updates Grape, and refuses a release older than the one installed. To check a download yourself with OpenSSL 3 (the same commands work for every asset):
cat > colony-release.pub <<'EOF'
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEARNjg3Nn8H6/aBg1unwGjkUTcrdTxERNefVaqU8cFu0s=
-----END PUBLIC KEY-----
EOF
a=grape-linux
openssl pkeyutl -verify -pubin -inkey colony-release.pub -rawin -in "$a" -sigfile "$a.sig"
openssl pkeyutl -verify -pubin -inkey colony-release.pub -rawin -in "$a.meta" -sigfile "$a.meta.sig"
cat "$a.meta" # version=<tag>, asset=<file name>, sha256=<digest>
sha256sum "$a" # the digest must equal the sha256 lineHow releases are built, signed and published: design/releases.md.
Grape sends no telemetry, no analytics and no crash reports. It has no account and no update check.
| Data | Stored or sent | Where, and why |
|---|---|---|
| Settings | stored | preferences.json in Colony/Grape/ of this machine's config directory; on Linux and macOS only your user account can read it |
| Credentials | stored | your Last.fm API key, only if you add one, in preferences.json (metadata_api_key); it is empty by default |
| Playlists and the album details you edit | stored | playlist.json and metadata-overrides/, in the same config directory |
| Listening history and where playback stopped | stored | history.json and session.json in Colony/Grape/ of the data directory |
| Exported playlists | stored | exports/ in the data directory, only when you export one |
| Start at login | stored | only while Launch Grape at system startup is on in Preferences, an autostart entry outside Grape's folders: grape.desktop in ~/.config/autostart/ (or $XDG_CONFIG_HOME/autostart/) on Linux, ~/Library/LaunchAgents/com.colony.grape.plist on macOS, a Grape value under HKCU\Software\Microsoft\Windows\CurrentVersion\Run on Windows; turning the option off removes it |
| Library index, cover art and Last.fm answers | stored | the library cache: libraries/ in Grape's Colony cache folder, or a grape-cache/ folder inside the location you set in Preferences |
| Your music folder | read | scanned for tracks, tags and cover art; Grape writes into it only if you set the cache location inside it |
| Now playing: title, artist, album and cover, plus the file path on Linux | shared on this machine | the system media controls (MPRIS on Linux, SMTC on Windows), so media keys and the system media panel work; desktop notifications, if you turn them on, show the title, artist and album. Other programs on this machine can read these, and none of it leaves the machine |
| An album's artist and title, with your API key | sent | Last.fm, only once you have added your own API key, as described below |
Grape connects to no server except the ones in this table, and only for the action named there. The exact folders on each platform are in docs/guide/install.md.
- What turns it on. Putting your own Last.fm API key into
preferences.json(metadata_api_key). The key is empty by default and there is no field for it in Preferences; with no key, Grape makes no network request at all. - When. With a key set, Grape asks Last.fm's
album.getInfo(https://ws.audioscrobbler.com/2.0/) for the album you select, directly or through its artist, genre or folder, unless it already holds an answer for that album younger than the cache lifetime (24 hours by default), and again when you press Enrich on an album. - What is sent. Your API key, the album's artist and title, and a
Grape/<version>user agent. Like any HTTPS request, it also shows Last.fm your IP address. Last.fm's answer (genre and year) is cached on your machine. Last.fm's own privacy policy: https://www.last.fm/legal/privacy.
GPL-3.0-or-later © 2026 MotherSphere
You may redistribute and modify Grape under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
The fonts, JetBrains Mono Nerd Font, OpenDyslexic and Font Awesome Free, are licensed under the SIL Open Font License 1.1. Grape does not bundle them itself: they ship inside colony-ui, with their license texts.