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-open
prevents thempv
process/window terminating after a video is finished.keep-open-pause
If set to no, instead of pausing whenkeep-open
is 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 toyes
you 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-server
allows us to send JSON IPC messages to anmpv
instance (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:
loadfile
Load the given file or URL and play it.playlist-next
Go to the next entry on the playlist.append-play
Append 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
:
f
fork program to background.r
run program as root, using sudo.t
run 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
,mpv
is running, the status code is0
:
$ pgrep -x mpv
387911
$ echo $?
0
- If
FALSE
,mpv
is not running the status code is1
:
$ pgrep -x mpv
$ echo $?
1
-
So, when its
TRUE
, we already have anmpv
instance, so we can issue theloadfile
command to load the new video, andappend-play
to add it to the playlist. If you don’t want a playlist you can just useloadfile
instead. 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-play
is enough. - That you want to start the new video as soon as its added, in which case we also need to use
playlist-next
so 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
,mpv
is 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!