All checks were successful
Build and Release / build-and-release (push) Successful in 54s
Change position update logic to only trigger redraw when the displayed value (rounded to seconds) changes, not when the raw float value changes. This eliminates jumpy time display and reduces unnecessary redraws.
cm-player
A high-performance terminal UI music and video player written in Rust, designed for low-bandwidth VPN environments with cache-only operation.
Features
- Cache-only architecture: Scans media directories once and operates from cache, ideal for low-bandwidth VPN connections
- Vim-style navigation: Familiar keybindings (j/k/h/l) for efficient navigation
- Incremental fuzzy search: Real-time search with Tab completion, similar to vim's command-line search
- Directory tree browsing: Collapsible folder structure with lazy scrolling
- Playlist management: Mark multiple files (vim-style 'v' key) to create custom playlists
- MPV integration: Video and audio playback via MPV subprocess with IPC control
- Dual-panel layout: File browser on left, playlist on right
- Auto-advance: Automatically plays next track when current track ends
Architecture
Core Components
State Management (src/state/mod.rs)
- Maintains flattened file tree from cache for efficient rendering
- Handles directory expansion/collapse state
- Manages playlist and playback position
- Implements fuzzy search with folder priority boost
MPV IPC Integration (src/player/mod.rs)
- Spawns MPV as subprocess with Unix socket IPC
- Automatic process respawn when MPV exits (user closes window)
- Non-blocking socket communication with JSON protocol
- Tracks playback state, position, duration via property observation
Cache System (src/cache/mod.rs)
- XDG-compliant cache storage:
~/.cache/cm-player/ - Serializes directory tree structure with serde_json
- Supports manual refresh with 'r' key
- Scans multiple configured paths
UI Rendering (src/ui/mod.rs)
- Three-section layout: title bar (1 line) | content | status bar (1 line)
- Ratatui framework with crossterm backend
- Theme matching cm-dashboard color scheme
- Real-time playback progress in title bar
Technical Details
Fuzzy Search Algorithm
// Scoring system:
// - Characters must appear in order (not necessarily consecutive)
// - Consecutive matches: +10 bonus per character
// - Word boundary matches: +15 bonus
// - Gap penalty: -1 per character between matches
// - Folder priority: +50 score boost
// - Length bonus: 100 - text_length
MPV Communication
- IPC socket:
/tmp/cm-player-{pid}.sock - Commands:
{ "command": ["loadfile", "path"] } - Property observation:
time-pos,duration,pause,idle-active - Process lifecycle: spawn -> connect -> command -> observe -> respawn on exit
Lazy Scrolling
- Selection stays in place until reaching viewport edge
- Scroll offset only updates when necessary
- Page navigation with Ctrl-D/U (half-page jumps)
Installation
Pre-built Binary (NixOS)
# hosts/common/cm-player.nix is automatically included
# mpv is installed as dependency
From Release
curl -L https://gitea.cmtec.se/cm/cm-player/releases/latest/download/cm-player-linux-x86_64 -o cm-player
chmod +x cm-player
sudo mv cm-player /usr/local/bin/
Build from Source
git clone https://gitea.cmtec.se/cm/cm-player.git
cd cm-player
cargo build --release
./target/release/cm-player
Static linking (for portability):
export RUSTFLAGS="-C target-feature=+crt-static"
cargo build --release --target x86_64-unknown-linux-gnu
Configuration
Config File
Location: ~/.config/cm-player/config.toml
[scan_paths]
paths = [
"/mnt/music",
"/mnt/movie",
"/mnt/tv"
]
First Run
- Create config file with media paths
- Launch cm-player
- Press 'r' to scan directories and build cache
- Cache stored at
~/.cache/cm-player/cache.json
Keybindings
Navigation
j/k/↓/↑- Move selection down/uph- Collapse directoryl- Expand directoryCtrl-D- Page down (half page)Ctrl-U- Page up (half page)
Search
/- Enter search mode (fuzzy find)Tab- Next search resultShift-Tab- Previous search resultEnter- Execute search and enable n/N cyclingn- Next search match (after Enter) or next trackN- Previous search matchEsc- Clear search results
Playlist
v- Mark/unmark file or directorya- Add marked files to playlistc- Clear playlist
Playback
Enter- Play selected file/folder (uses marks if any)Space- Pause/Resumes- Stop playbackp- Previous trackn- Next track (when not in search results)←/→- Seek backward/forward 10 seconds+/=- Increase volume-- Decrease volume
System
r- Rescan media directories and rebuild cacheq- Quit
Development
Project Structure
cm-player/
├── src/
│ ├── main.rs # Event loop and key handling
│ ├── state/mod.rs # App state and search logic
│ ├── player/mod.rs # MPV IPC integration
│ ├── ui/
│ │ ├── mod.rs # TUI rendering
│ │ └── theme.rs # Color scheme
│ ├── cache/mod.rs # Cache serialization
│ ├── scanner/mod.rs # Directory scanning
│ └── config/mod.rs # Config file handling
├── .gitea/workflows/
│ └── release.yml # CI/CD pipeline
└── Cargo.toml
Dependencies
- ratatui - Terminal UI framework
- crossterm - Terminal backend
- tokio - Async runtime for event loop
- serde/serde_json - Cache serialization
- walkdir - Directory traversal
- anyhow/thiserror - Error handling
Logging
Logs written to /tmp/cm-player.log to avoid interfering with TUI:
tracing::info!("Playing: {:?}", path);
tail -f /tmp/cm-player.log
Release Process
Automated CI/CD
Releases are fully automated via Gitea Actions:
# Update version in Cargo.toml
vim Cargo.toml
# Commit and tag
git add Cargo.toml
git commit -m "Bump version to v0.1.X"
git push
git tag v0.1.X
git push origin v0.1.X
Workflow steps:
- Build static binary with
RUSTFLAGS="-C target-feature=+crt-static" - Create GitHub-style release on Gitea
- Upload binary and tarball as release assets
- Calculate SHA256 hash from local tarball
- Clone nixosbox repository
- Update
hosts/common/cm-player.nixwith new version and hash - Commit and push to nixosbox
Manual NixOS Update
If workflow fails, update manually:
cd ~/projects/nixosbox
# Download and hash the tarball
curl -L https://gitea.cmtec.se/cm/cm-player/releases/download/v0.1.X/cm-player-linux-x86_64.tar.gz -o /tmp/cm-player.tar.gz
sha256sum /tmp/cm-player.tar.gz | cut -d' ' -f1
# Convert hex to base64: python3 -c "import base64, binascii; print(base64.b64encode(binascii.unhexlify('HEX_HASH')).decode())"
# Edit hosts/common/cm-player.nix
vim hosts/common/cm-player.nix
# Update version and sha256
git add hosts/common/cm-player.nix
git commit -m "Update cm-player to v0.1.X"
git push
MPV Configuration
Default MPV flags:
--idle- Keep MPV running between tracks--no-terminal- Disable MPV's terminal output--profile=fast- Use fast decoding profile--audio-display=no- Disable cover art for audio files--input-ipc-server=/tmp/cm-player-{pid}.sock- Enable IPC
Troubleshooting
MPV window closes immediately
Check MPV installation: mpv --version
Scan shows no files
- Verify paths in
~/.config/cm-player/config.toml - Press 'r' to rescan
- Check logs:
tail -f /tmp/cm-player.log
Hash mismatch on NixOS rebuild
- Wait for workflow to complete before rebuilding
- Workflow calculates hash from local tarball, not downloaded
- Check release assets are fully uploaded: https://gitea.cmtec.se/cm/cm-player/releases
Video playback issues
MPV process respawns automatically if closed. If issues persist:
- Stop playback with 's'
- Restart cm-player
- Check
/tmp/cm-player.logfor errors
License
Copyright (c) 2025 CM Tech
Description
cm-player v0.1.18
Latest
Languages
Rust
99.8%
Nix
0.2%