No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-25 21:11:18 -04:00
src Add keyboard seeking and filter clear shortcut 2026-10-25 21:11:18 -04:00
tests/fixtures Report decoder failures and detect truncated or corrupt audio 2026-10-25 20:31:48 -04:00
.gitignore initial 2026-10-25 20:08:28 -04:00
AGENTS.md Record dependencies and planned MPRIS2 playerctl support 2026-10-25 20:16:36 -04:00
Cargo.lock Add session queue and MPRIS2 transport controls 2026-10-25 20:50:12 -04:00
Cargo.toml Add session queue and MPRIS2 transport controls 2026-10-25 20:50:12 -04:00
README.md Add keyboard seeking and filter clear shortcut 2026-10-25 21:11:18 -04:00

lp

An offline terminal music player in development, using Rust and Ratatui.

Run

Run cargo run -- /path/to/music in an interactive terminal with an available audio output device. Linux builds require ALSA and D-Bus development libraries.

Recursively discover FLAC and MP3 files without changing the library. File extensions are matched case-insensitively; symlinks are skipped, including an explicitly supplied symlink root. Scan errors appear with a warning count and the most recent diagnostic. Results are sorted by path, preserving the selected file as new results arrive.

  • j / k, arrows: select; Home / End: first / last
  • Enter: play selection, capturing the currently filtered library order
  • Space: pause / resume; start the current target or first queued entry when stopped
  • s: stop
  • n / p: next / previous, without wrapping
  • h / l: seek backward / forward 5 seconds
  • + / -: volume
  • a / A in the library: append selection / all filtered tracks to the session queue
  • Tab: switch library / queue
  • Enter in the queue: play the selected queue entry
  • J / K in the queue: move the selected entry down / up
  • d / Delete in the queue: remove selection; C: clear queue
  • /: edit a case-insensitive title, artist, album, or relative-path filter
  • Enter while filtering: accept; Esc: cancel editing or clear an accepted filter
  • Ctrl-L: clear the filter in either view or while editing
  • ?: help
  • q: quit outside filtering; close help when help is open
  • Ctrl-C: quit in any mode

> marks selection; * marks the current playback target. Filtering and navigation do not change active playback or its captured order. Playback advances automatically and stops at the end of that order. A failed track stops playback with a diagnostic rather than being silently skipped. Next/previous preserve paused or stopped state and do nothing at the order's boundaries. Natural completion stops at the end.

Seeking preserves the current track and paused/playing state, clamps at zero and known duration, and does nothing while stopped. At the end, the track stays selected until you seek back or explicitly resume to allow completion.

Session queue

Build a queue with a or A, then press Tab and Enter to play from it. Adding tracks does not start playback. Duplicate paths are distinct queue entries. Reordering the playing entry preserves its identity and playback position; subsequent advancement uses the edited order. Removing the current queue entry, or clearing its queue, stops playback without choosing another.

Library playback and queue construction are separate: pressing Enter in the library captures the filtered library order and leaves the constructed queue available. Editing or clearing an inactive queue does not interrupt library playback. Queue contents are discarded when you exit; no library files are written.

playerctl / MPRIS2

With a Linux session D-Bus available, use playerctl --player=lp to control the same transport as the terminal. First choose a track or build a queue in lp.

playerctl --player=lp play-pause
playerctl --player=lp pause
playerctl --player=lp play
playerctl --player=lp next
playerctl --player=lp previous
playerctl --player=lp volume 0.5
playerctl --player=lp stop
playerctl --player=lp status
playerctl --player=lp metadata
playerctl --player=lp position

The bus name is org.mpris.MediaPlayer2.lp; additional instances use org.mpris.MediaPlayer2.lp.instance<PID>. Status, source position, known duration, metadata, volume, and transport capabilities reflect the playback worker. Unknown metadata fields are omitted. Remote seeking and opening URIs are not implemented; repeat and shuffle report off. Terminal seeks emit Seeked updates. If the session bus is unavailable, lp displays a warning and you can use terminal playback normally.

Metadata and playback

The table displays available title, artist, album, and duration metadata, with filename fallbacks and explicit unknown values. Elapsed time comes from the audio backend's source position, not a wall-clock estimate. Unknown total durations appear as --:--. Scanning and audio each use a separate worker; audio output and decoding do not run in the terminal event loop.

Header metadata reads are capped at 1 MiB. Oversized or invalid metadata produces a warning and a filename fallback; the candidate can still be played. Trailing-only metadata such as ID3v1 is not scanned. MP3 durations without a reliable frame-count header are unknown rather than bitrate estimates. Symphonia performs decoding, with Rodio handling output. Decoder and read errors, FLAC checksum failures, and mismatches against declared audio frame counts stop playback with a path-bearing diagnostic. Tracks without a frame count or checksum cannot be checked for every missing frame; some MP3 damage may also be hidden by the demuxer's resynchronization. After a nonzero seek, skipped PCM prevents full-stream checksum and frame-count verification; packet and decode errors are still reported. Seeking to zero restores full-stream verification.

Development

Run cargo fmt --check, cargo test, and cargo clippy --all-targets. Ordinary tests do not require audio hardware. To exercise real output, provide three-second local fixtures named a.flac and b.mp3 and run:

LP_AUDIO_TEST_DIR=/path/to/fixtures cargo test playback::tests::real_output -- --ignored --nocapture

This test produces sound and exercises transport transitions and completion. Verify actual output independently, for example by recording the output sink's monitor, rather than treating successful decoder construction as proof of sound.

The isolated D-Bus integration test uses the real service and transport without opening an audio device:

dbus-run-session -- cargo test mpris::tests::session_bus -- --ignored

Codec fixtures and regeneration commands are in tests/fixtures/README.md.