Increase mpv audio buffer to 2 seconds to fix stuttering on WSLg. Ref: https://github.com/microsoft/wslg/issues/1257
237 lines
6.5 KiB
Markdown
237 lines
6.5 KiB
Markdown
# CM Player - Music and Video TUI
|
|
|
|
## Overview
|
|
|
|
A high-performance Rust-based TUI player for playing music and video files. Built to use mpv.
|
|
|
|
## Key features
|
|
|
|
- Scan and cache file structure
|
|
- Made for use on low bandwidh vpn connections
|
|
- Use same TUI layout/theme as cm-dashboard
|
|
- Left panel is file structure
|
|
- Right panel is media status
|
|
|
|
## Architecture
|
|
|
|
### State Management
|
|
|
|
**CRITICAL:** Player state must be derived from MPV, not maintained separately.
|
|
|
|
**Single Source of Truth:** MPV properties via IPC
|
|
- `idle-active` (bool) - No file loaded or file ended
|
|
- `pause` (bool) - Playback is paused
|
|
|
|
**Derive PlayerState:**
|
|
```rust
|
|
if player.is_idle → PlayerState::Stopped
|
|
if !player.is_idle && player.is_paused → PlayerState::Paused
|
|
if !player.is_idle && !player.is_paused → PlayerState::Playing
|
|
```
|
|
|
|
**Rationale:**
|
|
- Eliminates state synchronization bugs
|
|
- MPV is always the authoritative source
|
|
- No need to update state in multiple places
|
|
- Simpler auto-play logic
|
|
|
|
**Anti-pattern:** DO NOT maintain `state.player_state` that can desync from MPV
|
|
|
|
### Cache-Only Operation
|
|
|
|
**CRITICAL:** Left panel shows ONLY cached data. Never browse filesystem directly during operation.
|
|
|
|
**Configuration:** `~/.config/cm-player/config.toml`
|
|
```toml
|
|
[scan_paths]
|
|
paths = [
|
|
"/media/music",
|
|
"/media/videos"
|
|
]
|
|
```
|
|
|
|
**Cache Structure:** `~/.cache/cm-player/`
|
|
- `file_tree.json` - Full cached file structure
|
|
- `metadata.json` - File metadata (duration, codec, size)
|
|
|
|
**Workflow:**
|
|
1. User configures paths in config.toml
|
|
2. Scanner scans configured paths → builds cache
|
|
3. Left panel displays cached tree (instant, no filesystem access)
|
|
4. Playback uses cached file paths
|
|
5. Rescan command/keybinding to refresh cache
|
|
|
|
## Features
|
|
|
|
### Core Functionality
|
|
- **Cache-Only Operation** - Browse media from cached file structure, no direct filesystem access
|
|
- **MPV Integration** - Playback via MPV IPC (subprocess), no library linking required
|
|
- **Playlist Management** - Play single files, entire directories, or marked files
|
|
- **File Marking** - Mark multiple files to create custom playlists
|
|
- **Tree Navigation** - Vim-style keybindings for browsing collapsible directory structure
|
|
- **Manual Library Refresh** - User-controlled scanning with 'r' key
|
|
|
|
### UI Layout (cm-dashboard style)
|
|
- **Title Bar** - Shows player state (Playing/Paused/Stopped/Refreshing)
|
|
- **Left Panel** - Cached media file tree with vim navigation
|
|
- **Right Panel** - Player info (progress, volume) + Playlist view
|
|
- **Status Bar** - Centered help text with all keybindings
|
|
|
|
### Keybindings
|
|
- `j/k` or `↑↓` - Navigate files
|
|
- `h/l` - Collapse/expand directories
|
|
- `t` - Mark/unmark file
|
|
- `c` - Clear all marks
|
|
- `Enter` - Play (priority: marked files > directory > single file)
|
|
- `Space` - Pause/resume
|
|
- `← →` - Seek backward/forward 10 seconds
|
|
- `+ -` - Volume up/down by 5%
|
|
- `n/p` - Next/previous track in playlist
|
|
- `r` - Rescan library (manual refresh)
|
|
- `q` - Quit
|
|
|
|
## API for OS-Wide Shortcuts
|
|
|
|
cm-player provides a Unix socket API for external control, allowing integration with OS-wide media keys and custom shortcuts.
|
|
|
|
### Architecture
|
|
|
|
- **Single Binary**: `cm-player` acts as both TUI server and CLI client
|
|
- **IPC**: Unix socket at `$XDG_RUNTIME_DIR/cm-player.sock` (or `/tmp/cm-player.sock`)
|
|
- **Protocol**: JSON commands over Unix socket
|
|
|
|
### Usage
|
|
|
|
```bash
|
|
# Start TUI (server mode)
|
|
cm-player
|
|
|
|
# Send commands to running instance (client mode)
|
|
cm-player play-pause
|
|
cm-player next
|
|
cm-player prev
|
|
cm-player stop
|
|
cm-player volume-up
|
|
cm-player volume-down
|
|
cm-player volume 50
|
|
cm-player seek-forward 30
|
|
cm-player seek-backward 10
|
|
cm-player quit
|
|
```
|
|
|
|
### OS-Wide Keyboard Shortcuts
|
|
|
|
**i3/sway config:**
|
|
```
|
|
bindsym XF86AudioPlay exec cm-player play-pause
|
|
bindsym XF86AudioNext exec cm-player next
|
|
bindsym XF86AudioPrev exec cm-player prev
|
|
bindsym XF86AudioStop exec cm-player stop
|
|
```
|
|
|
|
**KDE/GNOME:**
|
|
Add custom shortcuts pointing to `cm-player <command>`
|
|
|
|
### JSON Protocol
|
|
|
|
Commands are JSON objects with a `command` field:
|
|
|
|
```json
|
|
{"command": "play-pause"}
|
|
{"command": "next"}
|
|
{"command": "prev"}
|
|
{"command": "stop"}
|
|
{"command": "volume-up"}
|
|
{"command": "volume-down"}
|
|
{"command": "volume-set", "volume": 50}
|
|
{"command": "seek-forward", "seconds": 30}
|
|
{"command": "seek-backward", "seconds": 10}
|
|
{"command": "get-status"}
|
|
{"command": "quit"}
|
|
```
|
|
|
|
Responses:
|
|
```json
|
|
{"success": true, "message": null, "data": null}
|
|
{"success": false, "message": "error details", "data": null}
|
|
```
|
|
|
|
### Technical Details
|
|
- **MPV IPC** - Communicates with mpv via Unix socket and JSON protocol
|
|
- **No Version Lock** - Uses mpv binary, not libmpv library (avoids version mismatch)
|
|
- **Logging** - Redirected to `/tmp/cm-player.log` to avoid TUI interference
|
|
- **Auto-advance** - Automatically plays next track when current ends
|
|
|
|
## Automated Binary Release System
|
|
|
|
CM Player uses automated binary releases instead of source builds.
|
|
|
|
### Creating New Releases
|
|
|
|
```bash
|
|
cd ~/projects/cm-player
|
|
git tag v0.1.X
|
|
git push origin v0.1.X
|
|
```
|
|
|
|
This automatically:
|
|
|
|
- Builds static binaries with `RUSTFLAGS="-C target-feature=+crt-static"`
|
|
- Creates GitHub-style release with tarball
|
|
- Uploads binaries via Gitea API
|
|
|
|
### NixOS Configuration Updates
|
|
|
|
Edit `~/projects/nixosbox/hosts/services/cm-player.nix`:
|
|
|
|
```nix
|
|
version = "v0.1.X";
|
|
src = pkgs.fetchurl {
|
|
url = "https://gitea.cmtec.se/cm/cm-player/releases/download/${version}/cm-player-linux-x86_64.tar.gz";
|
|
sha256 = "sha256-NEW_HASH_HERE";
|
|
};
|
|
```
|
|
|
|
## Important Communication Guidelines
|
|
|
|
Keep responses concise and focused. Avoid extensive implementation summaries unless requested.
|
|
|
|
## Commit Message Guidelines
|
|
|
|
**NEVER mention:**
|
|
|
|
- Claude or any AI assistant names
|
|
- Automation or AI-generated content
|
|
- Any reference to automated code generation
|
|
|
|
**ALWAYS:**
|
|
|
|
- Focus purely on technical changes and their purpose
|
|
- Use standard software development commit message format
|
|
- Describe what was changed and why, not how it was created
|
|
- Write from the perspective of a human developer
|
|
|
|
**Examples:**
|
|
|
|
- ❌ "Generated with Claude Code"
|
|
- ❌ "AI-assisted implementation"
|
|
- ❌ "Automated refactoring"
|
|
- ✅ "Implement maintenance mode for backup operations"
|
|
- ✅ "Restructure storage widget with improved layout"
|
|
- ✅ "Update CPU thresholds to production values"
|
|
|
|
## Implementation Rules
|
|
|
|
**NEVER:**
|
|
|
|
- Copy/paste ANY code from legacy implementations
|
|
- Create files unless absolutely necessary for achieving goals
|
|
- Create documentation files unless explicitly requested
|
|
|
|
**ALWAYS:**
|
|
|
|
- Prefer editing existing files to creating new ones
|
|
- Follow existing code conventions and patterns
|
|
- Use existing libraries and utilities
|
|
- Follow security best practices
|