Skip to content

Repository files navigation

galdr

Galdr gopher wearing headphones

Galdr is a fast, keyboard-first terminal music player for local libraries. It is written in Go and designed for Linux terminals, with first-class support for Arch Linux and Omarchy.

It keeps music playback local, starts without mandatory configuration, and uses libmpv to work with the audio output already available on the system.

Galdr showing the music library, track list, playback queue and now-playing controls

Features

  • Recursive local-library scanning with metadata and filename fallbacks.
  • MP3, WAV and FLAC playback through libmpv.
  • Gapless transitions between compatible adjacent tracks using one libmpv playlist and its conservative audio-device reuse mode.
  • Missing durations appear progressively while playback is stopped, without delaying startup or competing with the active audio engine.
  • Responsive three-panel interface for browsing the library, tracks and queue.
  • Local album covers from cover.jpg, cover.jpeg or cover.png; artwork is never downloaded.
  • Automatic theme that follows Omarchy when available, then the terminal's ANSI palette; also usable in basic 16-color terminals and without color.
  • Play / pause / stop, next / previous, volume up / down, mute.
  • Optional track or album ReplayGain normalization with clipping protection.
  • Seek: ±5s with ←/→, jump to start / end with home/end.
  • Queue composition, reordering and removal, including add-to-end and play-next.
  • Persistent, human-readable M3U8 playlists with explicit save and overwrite.
  • Shuffle, repeat and manual library rescans.
  • Automatic Linux MPRIS controls and metadata for playerctl, desktop media keys, status bars and lock screens; D-Bus remains runtime-optional.
  • Volume and last-track state persisted between sessions.
  • Incremental search (/) over title, artist, album — case-insensitive substring match; the footer shows the active filter and matching count.
  • Minimal TOML config file (optional — sensible defaults out of the box).
  • Embedded English, French, Spanish and German interface translations.
  • No accounts, network calls, telemetry or background daemon.

The footer shows duration-loading progress such as Durations 42/118, then briefly reports how many files were unavailable. Durations are kept for the current session and recalculated the next time Galdr starts; no library database or cache file is created.

Requirements

  • Go 1.26 or newer.
  • Linux with a PipeWire, PulseAudio or ALSA-compatible audio stack.
  • The system mpv package, which provides libmpv.so. On Arch / Omarchy:
    sudo pacman -S mpv

Galdr is built with CGO_ENABLED=0, but it is not a standalone binary: go-mpv loads a compatible system libmpv.so dynamically at startup. If the library is missing or its SONAME is incompatible, Galdr cannot start.

A user D-Bus session is optional. When present, Galdr automatically exposes org.mpris.MediaPlayer2.galdr; when absent or already owned by another Galdr instance, the terminal player continues normally after one diagnostic.

Desktop media controls

MPRIS clients can control play, pause, stop, next, previous, seek, shuffle, repeat and Galdr's internal volume. They also receive playback state, position, queue capabilities, track metadata and escaped local cover-art URIs.

playerctl -p galdr play-pause
playerctl -p galdr next
playerctl -p galdr previous
playerctl -p galdr metadata

On Omarchy, the existing play/pause/next/previous media-key bindings use playerctl and work automatically while Galdr is running. System volume keys remain system-volume controls.

Install and run

Distribution packages

Tagged releases provide packages for x86-64 and ARM64 Linux systems. Download the package for your architecture from the GitHub release, then install it with the distribution package manager.

On Arch Linux, Omarchy or Manjaro:

sudo pacman -U ./galdr-*.pkg.tar.zst

On Debian:

sudo apt install ./galdr_*.deb

The Arch package installs mpv as a runtime dependency. The Debian package installs libmpv2. Both provide the dynamically loaded libmpv.so required by Galdr.

From source

git clone https://github.com/kvitrvn/galdr.git
cd galdr

# Build ./bin/galdr
make build

# Run from source
make run

# Run the already-built binary
./bin/galdr

Configuration

The config file is optional. If absent, galdr uses these defaults:

  • music_dir = ~/Music
  • playlist_dir = <music_dir>/Playlists
  • volume = 100
  • theme = auto (auto | light | dark)
  • language = auto (auto | en | fr | es | de)
  • audio.replaygain = off (off | track | album)

theme = auto reads the active Omarchy palette from ~/.config/omarchy/current/theme/colors.toml without modifying it. Outside Omarchy, or if that file is unavailable or invalid, Galdr inherits the terminal's default foreground/background and ANSI palette. The explicit light and dark modes keep their fixed palettes as configurable fallbacks. Restart Galdr after changing the Omarchy theme to load the new palette.

Path: ~/.config/galdr/config.toml.

Example:

music_dir = "~/Music"
# Optional. Defaults to a Playlists directory inside music_dir.
playlist_dir = "~/Music/Playlists"
volume = 80
theme = "dark"
language = "auto"

[audio]
# Optional loudness normalization; disabled by default.
replaygain = "off"

[ui]
# Preferred side-panel widths in wide terminals.
left_width = 22
right_width = 22
min_width = 48
min_height = 14

A leading ~ in music_dir or playlist_dir is expanded against the current user's home directory. Keeping playlist_dir inside the music library makes the relative M3U8 entries portable with that library. Set it to an XDG data directory instead when the music library is read-only.

language = "auto" selects the interface language once at startup from LC_ALL, then LC_MESSAGES, then LANG, with English as the fallback. Regional tags and encodings such as fr_FR.UTF-8, es_MX and de-DE are recognized; C and POSIX select English. Set en, fr, es or de to override the environment. English and French translations are stable; Spanish and German are currently beta and welcome native-speaker review. Restart Galdr after changing the setting or the process locale.

ReplayGain uses loudness tags already stored in local files. track normalizes each song independently, which is useful for a mixed queue. album preserves the intended loudness differences between songs on an album and falls back to track gain when album gain is unavailable. Files without ReplayGain tags play at their original level, and Galdr asks mpv to lower normalization gain when needed to prevent clipping. ReplayGain does not change the configured or saved user volume.

If the config file is missing, Galdr starts with the defaults. A malformed file or invalid value prevents startup and is reported with the configuration path so it can be corrected.

Keybindings

The TUI adapts to the terminal: all three panels are visible from 110 columns, Library plus Tracks or Queue from 72 to 109 columns, and one focused panel from 48 to 71 columns. Tab / shift+Tab cycles focus; 1, 2, and 3 jump directly to Library, Tracks, and Queue. Focus is shown with a symbol, text weight, and color.

Key Library (focused) Tracks / Queue (focused)
↑ / k previous row previous track
↓ / j next row next track
← / h collapse artist / go to parent seek -5s
→ / l expand artist / drill in seek +5s
enter select artist or album play selected track
A add the artist or album to a playlist add the track to a playlist
tab cycle focus forward cycle focus forward
shift+tab cycle focus backward cycle focus backward

Global keys (work in any panel):

Key Action
space Toggle play / pause
x Stop playback
n Next track (shuffle-aware, scope + filter)
p Previous track (shuffle-aware, scope + filter)
home / end Seek to start / end of current track
+ / = Volume up (5)
- / _ Volume down (5)
m Toggle mute
r Rescan the music directory
s Toggle shuffle
R Cycle repeat (off → all → one → off)
P Open the playlist browser
/ Enter search (filter by title / artist / album)
esc Clear filter (or exit search input)
ctrl+l Clear filter
? Toggle help overlay
q / ctrl+c Quit

Tracks panel (when focused):

Key Action
a Add the selected track to the queue tail
A Add the selected track to a playlist
N Insert the selected track after the current track
enter Replace the queue with the visible scope and play

Queue panel (when focused):

Key Action
↑ / k Move cursor up
↓ / j Move cursor down
K / shift+↑ Move the highlighted track up in the queue
J / shift+↓ Move the highlighted track down in the queue
d Remove the highlighted track (except playing)
c Clear the queue (keep the playing track)
enter Play the highlighted track immediately

Playlist browser:

Key Action
P / esc Close the browser
↑ / k Select the previous playlist
↓ / j Select the next playlist
enter Load the selected playlist into the queue
S Save the current queue under a new name

Playlist tracks (centre panel):

Key Action
enter Play the selected playlist occurrence
a Add the selected occurrence to the queue tail
A Add the selected occurrence to another playlist
N Insert the selected occurrence after the current track
d Remove only the selected occurrence from the playlist

Playlists

The active queue is temporary working state. A saved playlist is changed only through an explicit playlist action; editing the queue after loading one never rewrites its file. Loading replaces the queue in file order but does not start playback. If the currently playing occurrence also exists in the loaded list, playback continues; otherwise Galdr stops it safely. Loading also disables shuffle so the queue visibly follows the stored order; shuffle can be enabled again afterwards.

Each playlist is one UTF-8 .m3u8 file. Entries are relative to the playlist file, remain confined to music_dir, retain duplicates and can be edited or backed up with ordinary filesystem tools. Missing, unsupported or escaping entries are skipped and summarized while valid entries still load. Saves use a same-directory temporary file and atomic rename, and existing playlists require an explicit overwrite confirmation.

The search input is incremental: each keystroke updates the filter live and the list shrinks in every panel. enter or esc exits the input and keeps the filter active; a second esc (or ctrl+l) clears it. While the filter is active, the Library panel hides artists and albums with no matching track, and n / p / ↑↓ in the Tracks panel operate on the visible subset.

Supported formats

Format Extension Tags Duration Playback
MP3 .mp3 ID3v1, ID3v2 (all versions) loaded progressively libmpv / FFmpeg
WAV .wav RIFF INFO available from startup libmpv / FFmpeg
FLAC .flac Vorbis comments available from startup libmpv / FFmpeg

Files with unsupported extensions are skipped during the scan. Although mpv supports many more inputs, Galdr deliberately accepts only local MP3, WAV and FLAC files; URLs, video and additional formats remain out of scope.

Galdr keeps the current track and only its resolved successor in libmpv. The successor follows the visible queue order, including shuffle and repeat, and is reconciled after queue edits or a rescan. Compatible files can therefore pass without an application-created pause. A codec, sample-rate, channel-layout or output-format change may still require libmpv to reconfigure the audio device; playback continues normally when that transition cannot be seamless. Galdr keeps automatic output-device selection but asks libmpv for a fixed 48 kHz output rate, so 44.1 kHz sources are resampled before reaching PipeWire.

Tags are read from every supported file at startup. When tags are missing, the title falls back to the filename so every track remains identifiable. A manual rescan refreshes the library and restarts duration loading for newly discovered tracks.

Album covers

Galdr never downloads artwork or contacts an external cover service. To display an album cover, place a JPEG or PNG image next to the music files and name it exactly cover.jpg, cover.jpeg or cover.png.

When several supported files are present, Galdr prefers cover.jpg, then cover.jpeg, then cover.png. The image appears in the Now Playing area when the terminal is large enough; playback works normally without one.

Contributing

Issues, bug reports and focused pull requests are welcome. Before submitting a change, keep it scoped to Galdr's local, lightweight terminal-player goals and run the standard checks:

make fmt     # go fmt ./...
make vet     # go vet ./...
make test    # go test ./...
make build   # produces bin/galdr
make run     # runs the player
make tidy    # go mod tidy
make clean   # rm -rf bin/

Tests do not require audio hardware, a graphical desktop or network access. The test suite uses a fake mpv client for playback behavior. A system libmpv.so is still required when loading the binding.

Releasing

Releases are built by GoReleaser and published by GitHub Actions. Before creating a tag, validate the release configuration and build local snapshot packages. These local targets require GoReleaser v2 to be installed:

make release-check
make release-snapshot

Snapshot artifacts are written to dist/. Publish a release by pushing a semantic version tag:

git tag v0.1.0
git push origin v0.1.0

The release workflow runs make vet, make test, and make build, then publishes Arch Linux packages, Debian packages, portable tar archives, and checksums.txt for AMD64 and ARM64. Tags containing a semantic-version prerelease suffix, such as v0.2.0-rc.1, create a prerelease on GitHub.

Arch Linux notes

  • Install the runtime with sudo pacman -S mpv; no development headers or CGO toolchain are needed to build Galdr.
  • libmpv automatically selects an available PipeWire, PulseAudio or ALSA output. Galdr does not force a backend, but uses a 48 kHz output rate to keep the audio-device format stable across tracks.
  • Personal mpv.conf, input bindings, scripts, OSC and video output are disabled so user mpv configuration cannot alter Galdr playback.

Limitations

  • No streaming, cloud sync, lyrics or visualizer.
  • No library database, metadata cache or filesystem watcher (manual rescan only).
  • Enriched durations are held in memory for the current session and are recalculated on the next launch.
  • Files that libmpv cannot decode or whose duration remains unavailable keep the --:-- placeholder. VBR MP3 duration is provided by libmpv.
  • Playback capabilities depend on the installed mpv/libmpv build and its codec and audio-output dependencies.
  • The binary dynamically depends on a compatible libmpv.so SONAME and is therefore not a self-contained portable artifact. Galdr does not bundle or statically distribute libmpv.
  • Volume and last track are saved on quit and restored on next launch, but the in-queue position is not.

License

Galdr is released under the MIT License.

The go-mpv binding is also MIT licensed. The dynamically loaded libmpv is LGPL 2.1+ or GPL, depending on how the system package was compiled.

About

A fast, lightweight, keyboard-first terminal music player for local libraries, built in Go for Linux. Supports MP3, FLAC, and WAV playback through a clean, theme-adaptive TUI.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages