Last active
May 15, 2021 05:15
-
-
Save zoqaeski/364c247d15dbc4441344a60b974f16c1 to your computer and use it in GitHub Desktop.
MPV single instance controller
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Desktop Entry] | |
Type=Application | |
Name=mpv Media Player (Single-instance mode) | |
GenericName=Multimedia player | |
Comment=Play movies and songs without starting new instance of multimedia player | |
Icon=mpv | |
TryExec=mpvctl | |
Exec=mpvctl add -- %U | |
Terminal=false | |
Categories=AudioVideo;Audio;Video;Player;TV; | |
MimeType=application/ogg;application/x-ogg;application/mxf;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/vnd.dolby.heaac.1;audio/vnd.dolby.heaac.2;audio/aiff;audio/x-aiff;audio/m4a;audio/x-m4a;application/x-extension-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/mpeg2;audio/mpeg3;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/musepack;audio/x-musepack;audio/ogg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg2;video/x-mpeg3;video/mp4v-es;video/x-m4v;video/mp4;application/x-extension-mp4;video/divx;video/vnd.divx;video/msvideo;video/x-msvideo;video/ogg;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;audio/x-ms-asf;application/vnd.ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/avi;video/x-flic;video/fli;video/x-flc;video/flv;video/x-flv;video/x-theora;video/x-theora+ogg;video/x-matroska;video/mkv;audio/x-matroska;application/x-matroska;video/webm;audio/webm;audio/vorbis;audio/x-vorbis;audio/x-vorbis+ogg;video/x-ogm;video/x-ogm+ogg;application/x-ogm;application/x-ogm-audio;application/x-ogm-video;application/x-shorten;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;audio/eac3;audio/amr-wb;video/mp2t;audio/flac;audio/mp4;application/x-mpegurl;video/vnd.mpegurl;application/vnd.apple.mpegurl;audio/x-pn-au;video/3gp;video/3gpp;video/3gpp2;audio/3gpp;audio/3gpp2;video/dv;audio/dv;audio/opus;audio/vnd.dts;audio/vnd.dts.hd;audio/x-adpcm;application/x-cue;audio/m3u; | |
X-KDE-Protocols=ftp,http,https,mms,rtmp,rtsp,sftp,smb |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env zsh | |
# This script requires: | |
# - that the directory $XDG_RUNTIME_DIR exist | |
# - that the programs socat and jq be installed for writing to and reading from | |
# the MPV JSON IPC pipe | |
# Parts of this script were adapted from https://github.com/Duncaen/mpvctl, but | |
# I'm intending to add a few more features to my version. | |
# The socket is put in the XDG_RUNTIME_DIR so it won't be accessible to anyone else | |
readonly socket="$XDG_RUNTIME_DIR/mpv.socket" | |
readonly progname=$0 | |
readonly urlregex='^(http|https|ytdl|smb|bd|bluray|dvd|dvdnav|dvdread|tv|pvr|dvb|mf|cdda|lavf|av|file|fd|fdclose|edl)://' | |
# Options | |
local -a debug playlist | |
zparseopts -D d=debug -debug=debug \ | |
l=playlist -list=playlist \ | |
# We need to check if an mpv instance with the socket open is running | |
local mpvpid=$(ps -x -o pid,command | grep $socket | grep -v grep | awk '{print $1}') | |
usage() { | |
cat <<EOFUSAGE | |
$progname [options] <cmd> [ARGS...] | |
OPTIONS: | |
-l, --list Show playlist after performing COMMAND | |
-d, --debug Debug this script | |
COMMANDS: | |
play [index] Start playing | |
pause Pause current file | |
stop Stop and quit | |
next Play next file | |
prev(ious) Play previous file | |
seek <seconds> Seek <seconds> in file | |
move <index1> <index2> Move <index1> to <index2> in playlist | |
remove [index] Remove current file or item <index> from playlist | |
clear Clear all but currently playing files | |
ls, list List the playlist | |
add <filenames...> Add files to playlist | |
replace <filenames...> Replace playlist with files | |
prop [...] Get properties | |
EOFUSAGE | |
} | |
error() { | |
printf "Error: %s\n" "$@" | |
exit 1 | |
} | |
local ipc | |
local errmsg | |
ipc-send() { | |
# Cycle through command and arguments | |
cmd="$1" | |
args="" | |
idx=0 | |
shift | |
for a in "$@"; do | |
[[ $(( idx=idx+1 )) -le "$#" ]] && args="$args, " | |
case "${a}" in | |
true|false|[0-9]*) args="${args}${a}" ;; | |
*) args="${args}\"${a}\"" | |
esac | |
done | |
# Send the command to mpv, and store the output in $ipc | |
ipc=$(printf '{ "command": [ "%s" %s ] }\n' "$cmd" "$args" | socat - $socket | jq) | |
errmsg=$(echo $ipc | jq -r 'if .error != "success" then .error else empty end') | |
[[ -n $errmsg ]] && error $errmsg | |
[[ -n $debug ]] && echo $ipc | |
} | |
list() { | |
ipc-send 'get_property' 'playlist' | |
echo $ipc | jq -r '.data | ([keys[] as $k | .[$k] | .index=$k]) | map((.index | tostring) + (if .playing == true then " ▶ " else " " end) + .filename + "") | .[]' | |
} | |
# Basic sanity check. | |
# If the argument is a URL that mpv recognises, pass it through, otherwise | |
# use the full path of the file if it is readable. It won't stop you from | |
# sending mpv files or URLs it doesn't know how to play, but it will ensure | |
# those files can be passed | |
local sanefilename | |
check() { | |
if [[ $1 =~ $urlregex ]]; then | |
sanefilename="$1" | |
elif [[ -r $1:a ]] ; then | |
sanefilename="$1:a" | |
fi | |
} | |
append() { | |
opt="$1" | |
[ -n "$opt" ] && shift | |
# Basic sanity check of each file passed through | |
local -a files | |
for f in $argv ; do | |
check $f | |
files+=($sanefilename) | |
unset sanefilename | |
done | |
for f in $files ; do | |
ipc-send 'loadfile' "$f" "$opt" | |
done | |
} | |
main() { | |
cmd="$1" | |
[ -n "$cmd" ] && shift | |
if [[ -z $mpvpid ]] && [[ ! $cmd = "add" ]] ; then | |
error "MPV is not running!" | |
fi | |
# Initiate mpv if it is not already running | |
# TODO: Fix this so that it sleeps until the socket and mpv instance are | |
# listening, because sometimes on a first run the file doesn't get added | |
if [[ -z $mpvpid ]] ; then | |
echo "Starting new instance…" | |
mpv --no-terminal --force-window --input-ipc-server=$socket --playlist-start=0 --idle=once & mpvpid=$! | |
sleep 1 | |
fi | |
case $cmd in | |
# Start playing | |
# $1 skips to index on playlist | |
play) | |
if [[ "$1" ]] ; then | |
ipc-send 'set_property' 'playlist-pos' "$1" | |
fi | |
ipc-send 'set_property' 'pause' 'false' | |
;; | |
# Pause | |
pause) | |
ipc-send 'set_property' 'pause' 'true' | |
;; | |
# Exit mpv | |
stop) | |
ipc-send 'quit' | |
;; | |
# Play next item in playlist | |
next) | |
ipc-send 'playlist-next' | |
;; | |
# Play previous item in playlist | |
prev|previous) | |
ipc-send 'playlist-prev' | |
;; | |
# Seek by $1 seconds | |
seek) | |
ipc-send 'seek' "$1" "$2" | |
;; | |
# Switch playlist items | |
move) | |
ipc-send 'playlist-move' "$1" "$2" | |
;; | |
# Remove item | |
# With no argument, removes current item from playlist | |
remove) | |
if [[ -z "$1" ]] ; then | |
ipc-send 'playlist-remove' 'current' | |
else | |
ipc-send 'playlist-remove' "$1" | |
fi | |
;; | |
# Remove all but current item | |
clear) | |
ipc-send 'playlist-clear' | |
;; | |
# Show current playlist | |
ls|list) | |
unset playlist | |
list | |
;; | |
# Add item(s) to playlist | |
add|append) | |
append "append-play" $argv | |
;; | |
# Replace currently playing items | |
rep|replace) | |
append "replace" $1 | |
shift | |
for file in $argv ; do | |
append "append" "$file" | |
done | |
;; | |
# Show properties | |
prop) | |
for p in $argv ; do | |
ipc-send "get_property" "$p" | |
echo $ipc | jq '.data' | |
done | |
;; | |
*) | |
usage | |
exit 1 | |
;; | |
esac | |
# Print playlist if started with -l or --list | |
if [[ -n $playlist[1] ]] ; then | |
list | |
fi | |
} | |
if [[ -n $debug[1] ]] ; then | |
setopt xtrace | |
fi | |
main "$@" | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment