359 lines
9.3 KiB
Bash
Executable file
359 lines
9.3 KiB
Bash
Executable file
#!/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 "$@"
|