Smooth video playing with mpv and ranger
Marriages aren’t easy
Ranger and mpv are among my favourite open-souce software projects. They are both such a delight to use. I have always had - however - the impression that the defaults for these two to dance together nicely are not exactly great.
Once you open a new video from ranger, it will run it with mpv via rifle. After the video is finished, the mpv process is terminated, and the window is gone. I would rather have the mpv window stay where it already is, so I don’t have to move it again with my window manager.
The list goes on. For instance, new videos added to an mpv playlist won’t play automatically after they’ve been added if something is already playing. Did the video finish before you added the new video? Great, now you also have to press play!
You can see this by tracing the system calls mpv does:
lstrace -e trace=exit mpv your_video_file.mp4
● Video --vid=1 (av1 3840x2160 23.976 fps) [default]
● Audio --aid=1 --alang=eng (aac 2ch 44100 Hz 128 kbps) [default]
AO: [pipewire] 44100Hz stereo 2ch floatp
VO: [gpu] 3840x2160 yuv420p
AV: 01:04:11 / 01:04:11 (100%) A-V: 0.000
Exiting... (End of file)
exit_group(0) = ?
+++ exited with 0 +++
Here’s what happens when the video reaches 100%: It exits.
But don’t despair lads, mpv has more options than days are there in a year, so lets get to it:
MPV
Configuring mpv
There’s a couple of options I need to change in order for mpv to behave the way I want it to. I will be adding the following to the mpv config file, which is at ~/.config/mpv/mpv.conf.
keep-open=yes
keep-open-pause=no
input-ipc-server=/var/run/user/1000/mpv.sock
Now lets see what they mean:
keep-openprevents thempvprocess/window terminating after a video is finished.keep-open-pauseIf set to no, instead of pausing whenkeep-openis active, just stop at end of file and continue playing forward when you seek backwards until end where it stops again. (In other words, with this option set toyesyou don’t have to play the video manually if you add the new video in the playlist AFTER the previous video has stopped).input-ipc-serverallows us to send JSON IPC messages to anmpvinstance (we will need this to send the new videos to mpv later).
The socket can be whatever you have in $XDG_RUNTIME_DIR.
I have seen people saving the socket file in /tmp, but I think its a bit safer to have it on $XDG_RUNTIME_DIR. You get this created automatically (I think by… systemd?) and the right permissions 0700/drwx------.
You have all these options and more in the mpv manual [1].
Since we will be using the sock path in the ranger/rifle configuration we can export a variable in our .zshrc or .bashrc:
export MPV_SOCKET=$XDG_RUNTIME_DIR/mpv.sock
Messaging mpv
You can send messages to mpv using the JSON IPC with the socket we configured above.
Here’s the message pattern:
{"command": ["command_name", "param1", "param2"]}
Now you can send commands to the mpv instance that is playing your video. You can for example request video properties. They are listed in the mpv manual in the section “Property list”.
Let’s ask mpv to let us know the video duration using socat.
The socket should be already there if you reconfigured mpv like above and started mpv:
stat /var/run/user/1000/mpv.sock
File: /var/run/user/1000/mpv.sock
Size: 0 Blocks: 0 IO Block: 4096 socket
Device: 0,44 Inode: 4034 Links: 1
Access: (0600/srw-------) Uid: ( 1000/ twfox) Gid: ( 1000/ twfox)
Access: 2025-08-09 23:18:12.269760532 +0200
Modify: 2025-08-09 23:18:12.269760532 +0200
Change: 2025-08-09 23:18:12.269760532 +0200
Birth: 2025-08-09 23:18:12.269760532 +0200
echo '{"command": ["get_property", "file-size"]}' | socat - /var/run/user/1000/mpv.sock
Or, if you created the environmental variable MPV_SOCKET you can also use:
echo '{"command": ["get_property", "file-size"]}' | socat - $MPV_SOCKET
{"data":5632598817,"request_id":0,"error":"success"}
The command we used is get-property. The whole list of input commands are available in the mpv manual on the “Input Commands” section.
For playing the videos from ranger and sending them to mpv we will be using the loadfile, append-play and playlist-next commands:
loadfileLoad the given file or URL and play it.playlist-nextGo to the next entry on the playlist.append-playAppend the file, and if nothing is currently playing, start playback.
And now that we know how to talk with mpv let’s mess around with ranger/rifle.
Rifle
Rifle is ranger’s file executor. It has a lot of defaults for common applications, but you change the actions if you want for each file type. The config file is at ~/.config/ranger/rifle.conf
mime ^video, has mpv, X, flag f = if pgrep -x mpv; then echo "{ \"command\": [ \"loadfile\", \"$1\", \"append-play\" ] }" | socat - $MPV_SOCKET && echo "{ \"command\": [ \"playlist-next\", \"force\" ] }" | socat - $MPV_SOCKET; else mpv "$1"; fi
Pretty complex for a one liner, isn’t it? Let’s go by parts, said Jack:
mime ^video, has mpv, X, flag f
Rifle matches the different files (such as images, videos etc.) with mime types [2]. Then it checks mpv is installed and in our PATH, and finally checks if we are running inside X11. We can use different flags, but we use the most common f:
ffork program to background.rrun program as root, using sudo.trun program in a separate terminal, as specified by $TERMCM.
Now, for the program we are running, you may have already notice an if statement based wether mpv is already running or not.
if pgrep -x mpv; then echo "{ \"command\": [ \"loadfile\", \"$1\", \"append-play\" ] }" | socat - $MPV_SOCKET && echo "{ \"command\": [ \"playlist-next\", \"force\" ] }" | socat - $MPV_SOCKET; else mpv "$1"; fi
$1 is the filename of the file we open with ranger.
The pgrep command is what I use to check if mpv is running.
The status code and the if statement value will be:
- If
TRUE,mpvis running, the status code is0:
$ pgrep -x mpv
387911
$ echo $?
0
- If
FALSE,mpvis not running the status code is1:
$ pgrep -x mpv
$ echo $?
1
-
So, when its
TRUE, we already have anmpvinstance, so we can issue theloadfilecommand to load the new video, andappend-playto add it to the playlist. If you don’t want a playlist you can just useloadfileinstead. What I do then is also optional and depending on your preference you may want to:- Append the video to the playlist, but don’t start playing it interrupting the current video.
append-playis enough. - That you want to start the new video as soon as its added, in which case we also need to use
playlist-nextso the playlist moves on to the video we just added.
- Append the video to the playlist, but don’t start playing it interrupting the current video.
echo "{ \"command\": [ \"loadfile\", \"$1\", \"append-play\" ] }" | socat - $MPV_SOCKET && echo "{ \"command\": [ \"playlist-next\", \"force\" ] }" | socat - $MPV_SOCKET
- When its
FALSE,mpvis not running, we can’t just messages to it, there’s nothing listening. We start mpv instead.
mpv "$1"
Wrapper
If the one-liner is too much for you, you can always create a wrapper and replace that monstruosity we created earlier:
#!/bin/bash
if pgrep -x mpv > /dev/null; then
# mpv is running, send via IPC
echo '{"command": ["loadfile", "'$1'","append-play"]}' | socat - "$MPV_SOCKET"
echo '{"command": ["playlist-next","force"]}' | socat - "$MPV_SOCKET"
else
# mpv not running, start it
mpv "$1"
fi
Make sure you create this somewhere inside your PATH and give it execute permission:
chmod +x ~/local/bin/mpv-ranger
Now reconfigure rifle:
mime ^video, has mpv, X, flag f = mpv-ranger "$1"
Troubleshooting
If something’s not working as expected you can always try to configure mpv logging level to debug.
Logging to a file in case you have to troubleshoot something that work when running in the terminal but not through ranger/rifle is also a good idea you can explore.
msg-level=all=debug
log-file=/tmp/mpv.log
I do recommend that you try all the commands manually before you start moving them to rifle.conf so you know wether they work or not.
Have fun & good luck!