Git-like version control for your playlists. Track changes, sync across Spotify and YouTube, and play music with a beautiful TUI.
musicgrit init https://open.spotify.com/playlist/37i9dqef...
musicgrit search "lofi beats"
musicgrit add 4iV5W9uYEdYUVa79Axb7Rh
musicgrit commit -m "add chill vibes"
musicgrit push
musicgrit play
- Version Control - Track playlist changes with git-like commands (init, commit, push, pull, diff, log, switch)
- Multi-Provider - Supports Spotify and YouTube playlists (including Spotify albums)
- TUI Player - Beautiful terminal interface with progress bar, queue, and controls
- Synced Lyrics - Real-time lyrics display via LRCLIB (works with both Spotify and YouTube)
- Staging Area - Stage changes before committing (add, remove, move tracks)
- Offline-First - Local state with on-demand remote sync
- Live Reload - TUI updates when playlist file changes externally
- Resume Playback - Remembers last played track and resumes from there
- Device Selection - Choose Spotify playback device when multiple are available
For YouTube playback:
# mpv (media player)
sudo apt install mpv # Debian/Ubuntu
brew install mpv # macOS
# yt-dlp (YouTube audio extraction)
pip install yt-dlpFor Spotify playback:
- Spotify Premium account (for Spotify Connect)
- A Spotify device (desktop app, phone, etc.) must be active
git clone https://github.com/codewithevilxd/musicgrit
cd musicgrit
cargo build --release
# Add to PATH
cp target/release/musicgrit ~/.local/bin/
# or
sudo cp target/release/musicgrit /usr/local/bin/cargo install --git https://github.com/codewithevilxd/musicgrit# Run directly (compiles from source, includes dependencies)
nix run github:codewithevilxd/musicgrit
# Or install to profile
nix profile install github:codewithevilxd/musicgritCreate a .env file or export these:
# Spotify API (https://developer.spotify.com/dashboard)
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
# YouTube API (https://console.cloud.google.com)
YOUTUBE_CLIENT_ID=your_client_id
YOUTUBE_CLIENT_SECRET=your_client_secret# 1. Authenticate with your provider
musicgrit auth spotify
musicgrit auth youtube
# 2. Start tracking a playlist (or album)
musicgrit init https://open.spotify.com/playlist/37i9dqef1DX...
musicgrit init https://open.spotify.com/album/4LH4d3cOWNNsVw41Gqt2kv
musicgrit init https://youtube.com/playlist?list=PL...
# 3. Play your music
musicgrit play
# or
musicgrit play -l 37i9dqef1DX...| Command | Description |
|---|---|
musicgrit auth <provider> |
Authenticate with Spotify or YouTube |
musicgrit logout <provider> |
Remove stored credentials |
musicgrit whoami <provider> |
Show authenticated user info |
| Command | Alias | Description |
|---|---|---|
musicgrit init <url> |
i |
Start tracking a playlist or album |
musicgrit playlists [query] |
List all tracked playlists | |
musicgrit switch <id> |
Switch working playlist | |
musicgrit curr |
Show current working playlist info | |
musicgrit list |
ls |
List tracks in playlist |
musicgrit find <query> |
Search within playlist |
| Command | Alias | Description |
|---|---|---|
musicgrit search <query> |
s |
Search for tracks to add |
musicgrit search <query> --add |
Interactive mode to add by index | |
musicgrit add <track-id> |
a |
Stage a track for addition |
musicgrit remove <track-id> |
rm |
Stage a track for removal |
musicgrit move <track-id> <index> |
mv |
Stage a track to be moved |
musicgrit reset |
Clear all staged changes |
| Command | Alias | Description |
|---|---|---|
grit status |
st |
Show staged changes and sync status |
grit commit -m "msg" |
c |
Commit staged changes locally |
grit push |
Push local changes to remote | |
grit pull |
Pull remote changes to local | |
grit diff |
d |
Show differences (--staged or --remote) |
grit log |
Show commit history | |
grit revert [hash] |
Revert to a previous commit | |
grit apply <file> |
Apply playlist state from YAML |
| Command | Alias | Description |
|---|---|---|
grit play |
p |
Start TUI player (resumes from last track) |
grit play --shuffle |
Start with shuffle enabled |
Playback automatically resumes from where you left off. The last played track is saved when you quit.
+--------------------------------+-----------------+
| grit > playlist-name [yt] | playlist |
+--------------------------------+ |
| now playing | Track 1 |
| | > Track 2 |
| Track Name | > Track 3 < |
| Artist1, Artist2 | Track 4 |
| | ... |
| ━━━━━━━━━━━━━━━━━━ 2:34/4:12 | |
| | |
| next up | |
| Next Track - Next Artist | |
+--------------------------------+-----------------+
| [space] pause [n/p] skip [g] goto [/] search |
| [l] lyrics [s] shuffle [r] repeat [q] quit |
+--------------------------------------------------+
| Key | Action |
|---|---|
space |
Pause/Resume |
n / p |
Next/Previous track |
s |
Toggle shuffle |
r |
Cycle repeat (None -> All -> One) |
← / → |
Seek -/+ 5 seconds |
↑ / ↓ |
Select track in playlist |
enter |
Play selected track |
q |
Quit |
| Key | Action |
|---|---|
type |
Filter tracks by name/artist |
ctrl+n / ctrl+p |
Jump to next/previous match |
enter |
Play selected match |
esc |
Cancel search |
| Key | Action |
|---|---|
← / → |
Seek -/+ 5 seconds |
enter |
Confirm seek position |
esc |
Cancel |
| Key | Action |
|---|---|
↑ / ↓ |
Scroll lyrics |
a |
Toggle auto-scroll |
n / p |
Next/Previous track |
← / → |
Seek |
l |
Exit lyrics mode |
# Option 1: Interactive mode (add by index)
grit search "daft punk" --add
# Shows numbered results, then prompts: "Add tracks [1,2,3...] or 'q' to quit:"
# Enter: 1,3 to add tracks 1 and 3
# Option 2: Add by track ID
grit search "daft punk get lucky"
grit add 2Foc5Q5nqNiosCNqttzHof
# Commit and push
grit commit -m "add daft punk"
grit push# Check if remote has changes
grit status
# Pull remote changes
grit pull
# View what changed
grit log# Find current position
grit list
# Move track to new position (0-based index)
grit move 4iV5W9uYEdYUVa79Axb7Rh 0
# Commit and push
grit commit -m "move track to top"
grit push# View history
grit log
# Revert to previous commit
grit revert
# Or revert to specific commit
grit revert a1b2c3d4.grit/
├── working_playlist.json # Current playlist ID + last track index
├── credentials/ # Encrypted OAuth tokens
│ ├── spotify.json
│ └── youtube.json
├── encryption.key # AES-256-GCM key
└── playlists/
└── <playlist-id>/
├── playlist.yaml # Local snapshot
├── staged.json # Pending changes
├── journal.log # Commit history
└── snapshots/ # Historical snapshots
- Uses Spotify Connect for playback
- Requires an active Spotify device (desktop app, phone, web player)
- Premium account required for playback control
- Write access requires playlist ownership
- Prompts for device selection when multiple devices are available
- Uses mpv + yt-dlp for playback
- No premium account required
- May be slower due to audio URL extraction
- Write access requires playlist ownership
- Credentials are encrypted with AES-256-GCM
- Sensitive files use restricted permissions (0o600)
- Tokens auto-refresh before expiration
MIT
