How to use Vifm as your music player
If you have mpc then with some scripting you can use Vifm as your music player!
Requirements and Reccomendations[edit]
- The most recent version of mpc, preferably compiled from github.
- dmenu or fzf. You should have one of these two.
- Python 3 with the python-mpd2 package installed. (OPTIONAL)
You can use older versions of mpc and still get 99% of the functionality, but certain mappings will have to be adjusted for displaying the current status. The python dependency is only for if you want to sort or edit your playlist with a terminal-based text editor on-the-fly.
Vifm mappings and Commands[edit]
Simply copy and paste this into your vifmrc:
NOTE Make sure that you set the shell variables to their appropriate locations on the harddrive. |
" {{{1 MUSIC PLAYER
if v:session == "music"
let $SONG_FORMAT="[[[%album%]|[%artist%]] - %title%]|[%file%]"
let $MUSIC_DIR=expand("/mnt/vids/music")
" The only supported programs are fzf and dmenu. I couln't get it to work
" on fzy. You'll have to write in support for anything else.
let $FUZZY="dmenu"
let $FUZZY_ARGS="-i -l 20"
cd $MUSIC_DIR
nunmap a
nunmap an
nunmap al
nunmap av
nunmap cc
nunmap cw
nunmap cW
nunmap s
" Unfortunately much of this code is not posix complient. The correct
" solution to this is to have vifm use lua, but that would be against its
" original design goal and requres a massive rewrite. The use of some
" external mechanism to do it is NOT desirable, so you'll need the gnu
" realpath to run this. BSD users get shafted once again!
" {{{ mappings
nnoremap + :let $IGNORE = system('mpc volume +2')<cr>:echo system('mpc status "Volume is:%volume%"')<cr>
nnoremap - :let $IGNORE = system('mpc volume -2')<cr>:echo system('mpc status "Volume is:%volume%"')<cr>
nnoremap < :let $IGNORE = system('mpc prev')<cr>:ShowSong<cr>
nnoremap q :mpcStatus<cr>
nnoremap L :mpcls<cr>
nnoremap <c-g> :GotoSong<cr>
nnoremap > :let $IGNORE = system('mpc next')<cr>:ShowSong<cr>
nnoremap A :!echo "%f:p" | sed -e 's/\([^\]\) /\1\n/g' -e 's/\\//g' | xargs -d '\n' -L 1 realpath -s --relative-to=$MUSIC_DIR -- %M | sort -nr | xargs -d '\n' -L 1 mpc add<cr>:mpcplay<cr>:ShowSong<cr>
nnoremap D :Delcurrent<cr>:ShowSong<cr>
nnoremap E :mpcEdit<cr>
nnoremap I :!mpc seek +5 %i<cr>:mpcStat 2<cr>
nnoremap K :!mpc seek -5 %i<cr>:mpcStat 2<cr>
nnoremap P :PlayListfind<cr>:!sleep 0.2 %i<cr>:Playfind<cr>
nnoremap gS :let $IGNORE = system('mpc stop')<cr>
nnoremap S :let $IGNORE = system('mpc single')<cr>:echo system('mpc status "Single is %single%"')<cr>
nnoremap o :mpcStat 2<cr>
nnoremap X :let $IGNORE = system('mpc random')<cr>:echo system('mpc status "Random is %random%"')<cr>
nnoremap a :mpcadd<cr>:!echo Song %f:t added. %S<cr>
nnoremap c :!mpc crop %i<cr>:echo 'Playlist cropped'<cr>
nnoremap C :!mpc clear %i<cr>:echo 'Playlist cleared'<cr>
nnoremap dp :Delfind<cr>
nnoremap j :Playfind<cr>
nnoremap p :!mpc toggle %i<cr>:mpcStat 2<cr>
nnoremap R :let $IGNORE = system('mpc repeat')<cr>:echo system('mpc status "Repeat is %repeat%"')<cr>
nnoremap s1 :let $IGNORE = system('mpc seek 10%')<cr>:let $IGNORE = system('mpc play')<cr>:mpcStat 2<cr>
nnoremap s2 :let $IGNORE = system('mpc seek 20%')<cr>:let $IGNORE = system('mpc play')<cr>:mpcStat 2<cr>
nnoremap s3 :let $IGNORE = system('mpc seek 30%')<cr>:let $IGNORE = system('mpc play')<cr>:mpcStat 2<cr>
nnoremap s4 :let $IGNORE = system('mpc seek 40%')<cr>:let $IGNORE = system('mpc play')<cr>:mpcStat 2<cr>
nnoremap s5 :let $IGNORE = system('mpc seek 50%')<cr>:let $IGNORE = system('mpc play')<cr>:mpcStat 2<cr>
nnoremap s6 :let $IGNORE = system('mpc seek 60%')<cr>:let $IGNORE = system('mpc play')<cr>:mpcStat 2<cr>
nnoremap s7 :let $IGNORE = system('mpc seek 70%')<cr>:let $IGNORE = system('mpc play')<cr>:mpcStat 2<cr>
nnoremap s8 :let $IGNORE = system('mpc seek 80%')<cr>:let $IGNORE = system('mpc play')<cr>:mpcStat 2<cr>
nnoremap s9 :let $IGNORE = system('mpc seek 90%')<cr>:let $IGNORE = system('mpc play')<cr>:mpcStat 2<cr>
nnoremap sg :!mpc pause %i<cr>:let $IGNORE = system('mpc seek 0%')<cr>:mpcStat 1<cr>
nnoremap w :ShowSong<cr>
nnoremap x:!mpc shuffle %i<cr>:echo 'Playlist shuffled'<cr>
" }}}
" {{{ COMMANDS
command! Playfind :exe '!mpc play "'.system('mpc playlist | nl -s " " -n "rz" | grep -Fv "$(mpc | sed -ne "1p" --)" | $FUZZY $FUZZY_ARGS 2>/dev/tty | cut -d " " -f 1 | tr -d " "').'" %IU &>/dev/null &' | redraw
command Delcurrent mpc del $(mpc status %%songpos%%) %i
command ShowSong mpc -f "$SONG_FORMAT" current %S
command GotoSong :exe 'goto "$MUSIC_DIR/'.system('mpc -f "%%file%%" | sed -ne "1p" --').'"'
command mpcinsert :let $IGNORE = system(expand('realpath -s --relative-to=$MUSIC_DIR %%f | mpc insert'))
command mpcadd :let $IGNORE = system(expand('realpath -s --relative-to=$MUSIC_DIR %%f | mpc add'))
command pls mpc rm %a ; mpc save %a | echo "Playlist saved." %i
command pll mpc load %a %i
command mpcStat :echo system('mpc | sed -ne '.%a.'p --')
command mpcls mpc -f "$SONG_FORMAT" playlist | sed -e "s%%$(mpc -f "$SONG_FORMAT" | sed -ne "s/\[/\\\[/g" -e "1p")%%* &%%" -- %m
command mpcStatus mpc status "vol:%%volume%% rep %%repeat%% rand %%random%% single %%single%%" %S
command mpcEdit mpc_edit.py %s
command mpcplay :if system('mpc | sed -ne 2p -- | cut -d " " -f 1') == '' | execute '!mpc toggle &' | else | execute '!mpc next &' | endif
" }}}
endif
" }}}
Playlist Editor Script[edit]
Copy and paste the following into your $VIFM/scripts
folder or somewhere in your $PATH
as mpc_edit.py
:
#!/usr/bin/env python3
import os
import tempfile
from mpd import MPDClient
# Options
editor = os.environ['EDITOR']
# Amount (in seconds) to rewind when reloading the playlist
setback = 4
# Startup
client = MPDClient()
client.connect("localhost", 6600)
with tempfile.NamedTemporaryFile('w+') as tmpf:
tmpf.writelines((song['file']+'\n' for song in client.playlistid()))
tmpf.seek(0)
old_playlist = [song.strip() for song in tmpf.readlines()]
# Ensure that the file has been written to disk
tmpf.flush()
os.system("{} {}".format(editor, tmpf.name))
tmpf.seek(0)
new_playlist = [song.strip() for song in tmpf.readlines()]
if new_playlist != old_playlist:
# Save the status right before we change things:
current_song = client.currentsong()['file']
elapsed = float(client.status()['elapsed'])
client.clear()
for song in new_playlist:
client.add(song)
# Check if the song we were listing to is in the new playlist
try:
new_pos = [song['file'] for song in
client.playlistid()].index(current_song)
except ValueError:
pass
else:
client.seek(new_pos, elapsed-setback if elapsed>setback else 0)
This allows you to edit the current playlist by mapping a mapping to call the script.
What's nice about it is that if the new, edited playlist contains the same song you were just listening to, then it will immediately jump to were you left off and continue playing.
A video showcasing most of the features is here.