Initial Bash mpv remote
This commit is contained in:
commit
14c46fc5f2
1 changed files with 359 additions and 0 deletions
359
mpvremote2.sh
Executable file
359
mpvremote2.sh
Executable file
|
|
@ -0,0 +1,359 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# ==============================================================================
|
||||
# GPLv3: You better join us now and share the software, motherfucker.
|
||||
# Keep the code free as in freedom.
|
||||
# If you're gonna sell it, give 'em the code too.
|
||||
#
|
||||
# WTF IS THIS?
|
||||
# A quick-and-dirty "TV style" remote control for mpv over a UNIX socket.
|
||||
#
|
||||
# ASSUMPTIONS:
|
||||
# 1. mpv is already running.
|
||||
# 2. mpv was launched with:
|
||||
# --input-ipc-server=/tmp/mpv-socket
|
||||
# or the socket is configured in mpv.conf.
|
||||
# ==============================================================================
|
||||
|
||||
SOCKET="/tmp/mpv-socket"
|
||||
MEDIA_DIR="$HOME/GDrive/Videos"
|
||||
|
||||
if [[ ! -S "$SOCKET" ]]; then
|
||||
echo "Error: Socket $SOCKET not found or mpv is not running."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Send an mpv IPC command.
|
||||
#
|
||||
# Usage:
|
||||
# mpv-cmd "cycle" "pause"
|
||||
# mpv-cmd "seek" "10"
|
||||
# mpv-cmd "loadfile" "/path/to/file" "append-play"
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
mpv-cmd() {
|
||||
local json_args=""
|
||||
local arg
|
||||
|
||||
for arg in "$@"; do
|
||||
# Escape JSON string characters.
|
||||
arg="${arg//\\/\\\\}"
|
||||
arg="${arg//\"/\\\"}"
|
||||
arg="${arg//$'\n'/\\n}"
|
||||
arg="${arg//$'\r'/\\r}"
|
||||
arg="${arg//$'\t'/\\t}"
|
||||
|
||||
if [[ -n "$json_args" ]]; then
|
||||
json_args+=","
|
||||
fi
|
||||
|
||||
json_args+="\"$arg\""
|
||||
done
|
||||
|
||||
printf '{"command":[%s]}\n' "$json_args" |
|
||||
socat -t 1 - "$SOCKET" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Get an mpv property.
|
||||
#
|
||||
# Requires jq. This avoids trying to parse JSON with sed.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
mpv-get() {
|
||||
local prop="$1"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "Error: jq is required for property queries." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '{"command":["get_property","%s"]}\n' "$prop" |
|
||||
socat -t 1 - "$SOCKET" 2>/dev/null |
|
||||
jq -r '.data // empty'
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Queue a video.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
queue_picker() {
|
||||
echo
|
||||
echo "--- Queue Media from $MEDIA_DIR ---"
|
||||
|
||||
if [[ ! -d "$MEDIA_DIR" ]]; then
|
||||
echo "Directory not found: $MEDIA_DIR"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local clean_dir="${MEDIA_DIR%/}"
|
||||
declare -A video_map
|
||||
|
||||
local exts=(
|
||||
"mp4"
|
||||
"mkv"
|
||||
"avi"
|
||||
"webm"
|
||||
)
|
||||
|
||||
local find_args=()
|
||||
|
||||
for ext in "${exts[@]}"; do
|
||||
if [[ ${#find_args[@]} -gt 0 ]]; then
|
||||
find_args+=("-o")
|
||||
fi
|
||||
|
||||
find_args+=("-iname" "*.$ext")
|
||||
done
|
||||
|
||||
while IFS= read -r -d '' file; do
|
||||
local rel_name="${file#"${clean_dir}/"}"
|
||||
video_map["$rel_name"]="$file"
|
||||
done < <(
|
||||
find "$clean_dir" \
|
||||
-type f \
|
||||
\( "${find_args[@]}" \) \
|
||||
-print0
|
||||
)
|
||||
|
||||
if [[ ${#video_map[@]} -eq 0 ]]; then
|
||||
echo "No video files found."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local selected_key=""
|
||||
|
||||
if command -v fzf >/dev/null 2>&1; then
|
||||
selected_key=$(
|
||||
printf '%s\n' "${!video_map[@]}" |
|
||||
sort |
|
||||
fzf --prompt="Select video to queue: "
|
||||
)
|
||||
else
|
||||
echo "Choose a file to queue (or Cancel):"
|
||||
|
||||
PS3="Enter number: "
|
||||
|
||||
local keys=("${!video_map[@]}")
|
||||
|
||||
select choice in "${keys[@]}" "Cancel"; do
|
||||
if [[ "$choice" == "Cancel" || -z "$choice" ]]; then
|
||||
echo "Cancelled."
|
||||
return 0
|
||||
fi
|
||||
|
||||
selected_key="$choice"
|
||||
break
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -n "$selected_key" && -n "${video_map[$selected_key]:-}" ]]; then
|
||||
local abs_path="${video_map[$selected_key]}"
|
||||
|
||||
echo "Queuing: $(basename "$abs_path")"
|
||||
|
||||
mpv-cmd "loadfile" "$abs_path" "append-play"
|
||||
mpv-cmd "set_property" "pause" "no"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${old_stty:-}" ]]; then
|
||||
stty "$old_stty" 2>/dev/null
|
||||
else
|
||||
stty sane 2>/dev/null
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Edit the current playlist via fzf.
|
||||
#
|
||||
# Enter -> skip to selected item
|
||||
# Del -> remove selected item
|
||||
# q/Esc/Left -> return to remote
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
edit_playlist() {
|
||||
local playlist_json fzf_input selected key_pressed idx
|
||||
|
||||
while true; do
|
||||
playlist_json=$(mpv-get "playlist")
|
||||
|
||||
if [[ -z "$playlist_json" || "$playlist_json" == "[]" ]]; then
|
||||
echo "Playlist is empty."
|
||||
return
|
||||
fi
|
||||
|
||||
fzf_input=$(printf '%s' "$playlist_json" | jq -r '
|
||||
to_entries[] |
|
||||
(if .value.current == true then "▶ " else "" end) as $prefix |
|
||||
"\(.key)|\($prefix)\(.value.title // (.value.filename | split("/") | last))"
|
||||
')
|
||||
|
||||
if [[ -z "$fzf_input" ]]; then
|
||||
echo "Playlist is empty."
|
||||
return
|
||||
fi
|
||||
|
||||
readarray -t result < <(
|
||||
printf '%s\n' "$fzf_input" | fzf \
|
||||
--delimiter='|' \
|
||||
--with-nth=2 \
|
||||
--prompt="Playlist (Enter=skip, Del=remove, Esc/q/←=back): " \
|
||||
--expect=del,left,esc,q \
|
||||
--header="Current Playlist"
|
||||
)
|
||||
|
||||
key_pressed="${result[0]:-}"
|
||||
selected="${result[1]:-}"
|
||||
|
||||
if [[ -z "$selected" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
idx="${selected%%|*}"
|
||||
|
||||
if [[ -z "$key_pressed" ]]; then
|
||||
# Enter pressed - skip to index
|
||||
mpv-cmd "set_property" "playlist-pos" "$idx"
|
||||
return
|
||||
elif [[ "$key_pressed" == "del" ]]; then
|
||||
mpv-cmd "playlist-remove" "$idx"
|
||||
# Refresh loop
|
||||
continue
|
||||
else
|
||||
# q, esc, left
|
||||
return
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
remote() {
|
||||
local old_stty
|
||||
old_stty=$(stty -g)
|
||||
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
trap cleanup EXIT
|
||||
|
||||
stty -echo -icanon min 1 time 0
|
||||
|
||||
echo "=== MPV Terminal Remote ==="
|
||||
echo "[Space] Pause | [h/l] Seek -10s/+10s | [j/k] Vol -/+ | [s] Subtitle"
|
||||
echo "[o] Open/Queue Video | [e] Edit Playlist | [n] Next Track | [p] Prev Track"
|
||||
echo "[r] Repeat Current Vid | [R] Repeat Playlist | [c] Controls | [q] Quit"
|
||||
echo "Listening..."
|
||||
|
||||
while IFS= read -r -n1 key; do
|
||||
case "$key" in
|
||||
' ')
|
||||
mpv-cmd "cycle" "pause"
|
||||
is_paused=$(mpv-get "pause")
|
||||
|
||||
if [[ "$is_paused" == "true" ]]; then
|
||||
echo ">> Video Paused..."
|
||||
else
|
||||
echo ">> Video Unpaused..."
|
||||
fi
|
||||
;;
|
||||
|
||||
c)
|
||||
echo "=== MPV Terminal Remote ==="
|
||||
echo "[Space] Pause | [h/l] Seek -10s/+10s | [j/k] Vol -/+ | [s] Subtitle"
|
||||
echo "[o] Open/Queue Video | [e] Edit Playlist | [n] Next Track | [p] Prev Track"
|
||||
echo "[r] Repeat Current Vid | [R] Repeat Playlist | [c] Controls | [q] Quit"
|
||||
;;
|
||||
h)
|
||||
mpv-cmd "seek" "-10"
|
||||
echo ">> Seeked -10s"
|
||||
;;
|
||||
|
||||
l)
|
||||
mpv-cmd "seek" "10"
|
||||
echo ">> Seeked +10s"
|
||||
;;
|
||||
|
||||
k)
|
||||
mpv-cmd "add" "volume" "5"
|
||||
vol=$(mpv-get "volume")
|
||||
echo ">> Volume Level: ${vol%.*}"
|
||||
;;
|
||||
|
||||
j)
|
||||
mpv-cmd "add" "volume" "-5"
|
||||
vol=$(mpv-get "volume")
|
||||
echo ">> Volume Level: ${vol%.*}"
|
||||
;;
|
||||
|
||||
s)
|
||||
mpv-cmd "cycle" "sub"
|
||||
sub_state=$(mpv-get "sub")
|
||||
|
||||
if [[ "$sub_state" == "false" ||
|
||||
"$sub_state" == "no" ||
|
||||
-z "$sub_state" ]]; then
|
||||
echo ">> Subtitles Disabled..."
|
||||
else
|
||||
echo ">> Subtitles Enabled... ($sub_state)"
|
||||
fi
|
||||
;;
|
||||
|
||||
n)
|
||||
mpv-cmd "playlist-next"
|
||||
echo ">> Next Track"
|
||||
;;
|
||||
|
||||
p)
|
||||
mpv-cmd "playlist-prev"
|
||||
echo ">> Prev Track"
|
||||
;;
|
||||
r)
|
||||
f_loop=$(mpv-get "loop-file")
|
||||
|
||||
if [[ "$f_loop" == "no" || "$f_loop" == "false" || -z "$f_loop" ]]; then
|
||||
mpv-cmd "set_property" "loop-file" "inf"
|
||||
echo ">> Loop Current Vid: yes (inf)"
|
||||
else
|
||||
mpv-cmd "set_property" "loop-file" "no"
|
||||
echo ">> Loop Current Vid: no"
|
||||
fi
|
||||
;;
|
||||
|
||||
R)
|
||||
p_loop=$(mpv-get "loop-playlist")
|
||||
|
||||
if [[ "$p_loop" == "no" || "$p_loop" == "false" || -z "$p_loop" ]]; then
|
||||
mpv-cmd "set_property" "loop-playlist" "inf"
|
||||
echo ">> Loop Current Playlist: yes (inf)"
|
||||
else
|
||||
mpv-cmd "set_property" "loop-playlist" "no"
|
||||
echo ">> Loop Current Playlist: no"
|
||||
fi
|
||||
;;
|
||||
|
||||
e)
|
||||
stty "$old_stty"
|
||||
edit_playlist
|
||||
stty -echo -icanon min 1 time 0
|
||||
echo "Listening..."
|
||||
;;
|
||||
|
||||
o)
|
||||
stty "$old_stty"
|
||||
queue_picker
|
||||
stty -echo -icanon min 1 time 0
|
||||
echo "Listening..."
|
||||
;;
|
||||
|
||||
q)
|
||||
echo
|
||||
echo "Exiting remote."
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
remote "$@"
|
||||
Loading…
Add table
Add a link
Reference in a new issue