Skip to content
Project-ColonyPublic

About

Desktop music player for the music you already own: your folder is the library. Built with Rust and iced.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Grape

A desktop music player for the music you already own.

License: GPL-3.0-or-later Colony app Platforms

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.

Why Grape

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/Track works, 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.

What it does

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 shared ARTIST.
  • 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, album as .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+← and Ctrl+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.

Installation

Via Colony (recommended)

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.

Direct binary download

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.

Running the binaries

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-x86 on Intel Macs. If you skip the xattr step, 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.

Build from source

git clone https://github.com/Project-Colony/Grape
cd Grape
cargo build --release

Requires 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/library

With 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 -- --ignored

Documentation

Full documentation is in docs/. Start at docs/README.md.

Code signing policy

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 line

How releases are built, signed and published: design/releases.md.

Privacy

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.

Last.fm

  • 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.

License

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.

About

Desktop music player for the music you already own: your folder is the library. Built with Rust and iced.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages