A lightweight, dark, keyboard-driven music sorter for triaging large libraries. Point it at a folder, then shuffle, audition, delete to trash, and quick-move tracks into target folders without ever lifting your hands off the keyboard. Built as a native GTK4 / GStreamer application - one small binary, no Electron, no embedded browser.
The palette is deliberately warm and very dark: easy on the eyes at night, dim enough to leave running while you fall asleep (as I tend to do this before bed, lol), but every control stays legible. Everything - colours, settings, keybindings, quick-move slots - is editable inside the app and in a single persistent config file, in the spirit of mpv.
- Load a library via the folder picker or a path argument (recurses into subfolders by default).
- Shuffle the whole library, keeping the current track playing.
- Delete → OS trash, never a permanent unlink. Press
D; undo withU. - Quick-move slots - pin a target folder to each slot, then move the playing
file there instantly with the number keys. Configurable from 1 to 9 slots;
assign a folder with
Ctrl+number or the pencil button on each slot. - Right-click "Move to…" - right-click the player or any track in the list to move it to a quick slot, a recent destination, or any folder you browse to. Recent destinations are remembered automatically (the count is configurable).
- Multi-level undo for deletes and moves (
U/Ctrl+Z). Trashed files are pulled back out of the freedesktop trash; moved files are returned. - Waveform + scrubbing - peaks are extracted with GStreamer and drawn with
Cairo; click or drag anywhere on the wave to seek.
J/Lseek back/forward. - Auto-advance to the next track when one ends (toggleable).
- Optional visualizer with several muted themes (
V), fed by GStreamer'sspectrumelement. - Metadata (ID3/tag) editing - tags are read always; editing is off by
default and enabled with a settings toggle (
E). Powered byloftyfor audio formats andmidlyfor MIDI (title maps to the MIDI TrackName meta-event). Works across formats, including.mid/.midi. - MIDI support -
.midand.midifiles load and play back via GStreamer's MIDI synth pipeline. Format, track count, BPM, time signature, key, and instrument names appear in the player subtitle when present in the file. The waveform display shows a note density graph so you can still scrub through the track. - Everything is configurable - General / Keybindings / Quick slots / Theme tabs, plus a raw Config file editor for total control. Rebind any action to any key; codes are physical-key based, so layout and numpad keys behave predictably.
- Handles all common audio formats (mp3, flac, ogg, opus, wav, m4a/aac,
aiff, wma, …) plus MIDI (
.mid/.midi). Playback depends on the installed GStreamer plugins; file operations work regardless.
tunesort is a plain GTK4 / libadwaita / GStreamer application, so it runs on
any Linux desktop - GNOME, KDE Plasma (X11 or Wayland), Cinnamon, XFCE, … - as
long as those libraries are present. The icons it uses are compiled into the
binary, so it looks the same under every icon theme, and its desktop entry and
app icon are named after its application id (io.github.theelderemo.tunesort),
which is what Wayland compositors such as KWin use to match windows to
launchers.
Each release includes a .deb. It declares every runtime
dependency, including the GStreamer plugin sets needed for playback, so this
is all it takes:
sudo apt install ./tunesort_*_amd64.debEach release also includes a tarball with the tunesort
binary, its desktop entry and its icon, built on GitHub's current
ubuntu-latest runner. Check it against SHA256SUMS.txt, then:
tar -xzf tunesort-*-x86_64-linux.tar.gz
install -Dm755 tunesort ~/.local/bin/tunesort
install -Dm644 io.github.theelderemo.tunesort.desktop ~/.local/share/applications/io.github.theelderemo.tunesort.desktop
install -Dm644 io.github.theelderemo.tunesort.png ~/.local/share/icons/hicolor/512x512/apps/io.github.theelderemo.tunesort.pngThe binary links GTK4, libadwaita and GStreamer dynamically, so the runtime
packages from Dependencies must be installed, and
~/.local/bin has to be on your PATH for the launcher entry to work. It is
built against one distro's library versions; if it refuses to start on an
older or very different system, build from source instead.
You need the Rust toolchain plus the build packages from
Dependencies (glib-compile-resources, used to bundle the
icons, ships with the GLib development package they pull in). Then:
cargo build --release
install -Dm755 target/release/tunesort ~/.local/bin/tunesort
install -Dm644 assets/io.github.theelderemo.tunesort.desktop ~/.local/share/applications/io.github.theelderemo.tunesort.desktop
install -Dm644 assets/io.github.theelderemo.tunesort.png ~/.local/share/icons/hicolor/512x512/apps/io.github.theelderemo.tunesort.pngGStreamer plugins are loaded at runtime: -base (playbin) and -good
(audio sinks, spectrum) are required, -bad provides the MIDI synth,
-ugly and libav cover the remaining codecs, and a General MIDI soundfont is
needed for .mid / .midi playback.
| Distro | Build packages | GStreamer plugins + soundfont |
|---|---|---|
| Ubuntu / Debian | build-essential libgtk-4-dev libadwaita-1-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev |
gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-pipewire fluid-soundfont-gm |
| Fedora | gtk4-devel libadwaita-devel gstreamer1-devel gstreamer1-plugins-base-devel |
gstreamer1-plugins-base gstreamer1-plugins-good gstreamer1-plugins-bad-free gstreamer1-plugins-bad-free-fluidsynth gstreamer1-plugins-ugly-free gstreamer1-plugin-libav pipewire-gstreamer (patent-encumbered codecs: gstreamer1-plugins-ugly from RPM Fusion) |
| Arch | gtk4 libadwaita gstreamer gst-plugins-base |
gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav gst-plugin-pipewire soundfont-fluid |
| openSUSE | gtk4-devel libadwaita-devel gstreamer-devel gstreamer-plugins-base-devel |
gstreamer-plugins-base gstreamer-plugins-good gstreamer-plugins-bad gstreamer-plugins-bad-fluidsynth gstreamer-plugins-ugly gstreamer-plugins-libav gstreamer-plugin-pipewire fluid-soundfont-gm |
The prebuilt binary only needs the runtime libraries (libgtk-4-1 libadwaita-1-0 on Ubuntu / Debian, otherwise the non--devel packages the
build packages depend on) plus the plugin column.
tunesort # open the app
tunesort ~/Music # load a library on start| Key | Action | Key | Action |
|---|---|---|---|
Space / K |
Play / pause | D |
Delete → trash |
→ |
Next track | S |
Shuffle |
← |
Previous track | U / Ctrl+Z |
Undo last delete/move |
↑ / ↓ |
Volume up / down | O |
Open library |
L / J |
Seek forward / back | , |
Settings |
M |
Mute | V |
Toggle visualizer |
1–9 / numpad |
Quick-move to slot | E |
Toggle metadata editor |
Ctrl+1–9 (numpad) |
Assign folder to slot |
All of these are remappable in Settings → Keybindings.
The config lives at ~/.config/tunesort/config.json (or $XDG_CONFIG_HOME).
It is written whenever you change something in the app, and re-read on launch.
You can edit it by hand, or from Settings → Config file - invalid JSON is
rejected, and unknown or missing keys are merged against the defaults so
upgrades never wipe your edits.
Top-level keys: library_path, settings, theme, quickslots,
recent_destinations, keybindings. The number of active quick slots
(quickslot_count) and how many recent destinations to remember
(max_recent_destinations) live under settings. See src/config.rs for the
full default set.
src/config.rs- load / save / merge of the JSON config.src/library.rs- scanning, shuffle, trash / move with a multi-level undo stack.src/metadata.rs-loftytag read / write for audio;midlytag read / write for MIDI (reads TrackName, Tempo, KeySignature and more for display; writes TrackName for the editable title field).src/player.rs- a GStreamerplaybinwrapper for playback, async waveform peak extraction, MIDI note-density waveform fallback, and thespectrum-fed visualizer.src/ui.rs- the GTK4 UI, the action registry, and keyboard dispatch.src/main.rs- entry point and application bootstrap.build.rs- compiles the icons inassets/iconsinto a GResource that is embedded in the binary.
Apache-2.0 — see LICENSE.
