Last active
March 27, 2025 23:20
-
-
Save ernstki/93bc8653556053e51c41cc1ba0c24255 to your computer and use it in GitHub Desktop.
Enqueue items (like YouTube playlists) with mpv
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 bash | |
# | |
# Enqueue the named file(s) / URL(s) mpv's JSON IPC; starts an instance of mpv | |
# if one isn't already found in the process list | |
# | |
# Author: Kevin Ernst (ernstki -at- mail.uc.edu) | |
# Date: 03 July 2019; updated 12 November 2021 | |
# Source: https://gist.github.com/ernstki/93bc8653556053e51c41cc1ba0c24255 | |
# | |
# Requirements: | |
# 1. mpv: https://mpv.io/ | |
# - available in MacPorts on macOS; may need to 'pip install youtube-dl' | |
# 2. jq: https://stedolan.github.io/jq/ | |
# - available in most package managers under that name | |
# 3. 'xclip' (if on Linux) | |
# 4. 'socat' | |
# - also available in MacPorts on macOS | |
# | |
(( TRACE )) && set -x | |
set -u | |
SOCKET=/tmp/mpvsocket | |
# wait this long after startup before enqueuing any media | |
STARTUPDELAY=5 | |
MPVOPTS=( | |
# (any options unrecognized by this script are also passed through) | |
--geometry='+5%-10%' | |
--autofit='25%x25%' | |
--idle=once | |
--input-ipc-server=$SOCKET | |
# 'pip install yt-dlp' and uncomment this if the video stutters a lot | |
--script-opts=ytdl_hook-ytdl_path=yt-dlp | |
# ref: https://www.reddit.com/r/mpv/comments/q759xc | |
# console output from `mpv` usually has useful information when yt-dlp | |
# fails, so don't uncomment unless you're *sure* yt-dlp is working | |
#--really-quiet | |
) | |
if [[ -t 1 ]]; then | |
UL=$(tput sgr 0 1) | |
BOLD=$(tput bold) | |
RED=$(tput setaf 1) | |
RESET=$(tput sgr0) | |
else | |
UL=; BOLD=; RED=; RESET= | |
fi | |
ME=$(basename ${BASH_SOURCE[0]}) | |
USAGE=" | |
$ME - enqueue named file(s) or clipboard contents with mpv | |
${UL}usage$RESET | |
$ME [-h|--help] [-s|--show[-playlist|-queue]] [MEDIA [MEDIA...]] | |
$ME {-c|--command} JSONIPC | |
${UL}where$RESET | |
-h, --help shows this help | |
-s, --show-playlist, shows the current queue/playlist | |
--show-queue | |
-p, --pop pop the last entry off the queue | |
-c, --command CMD send arbitrary JSON IPC command (for testing) | |
MEDIA URL or filename(s) to enqueue with mpv | |
(default: read from stdin, then clipboard) | |
${UL}default mpv options$RESET | |
$(echo ${MPVOPTS[*]} | fold -s -w 78 | sed 's/^/ &/') | |
(options unrecognized by this script are also passed through) | |
" | |
if [[ $(uname -s) != Darwin ]]; then | |
# requires 'xclip' to be installed | |
pbpaste() { xclip -o -sel clipboard; } | |
fi | |
_ipc() { | |
local output quiet= data= args=() | |
while (( $# )); do | |
case $1 in | |
-q|--quiet) quiet=1;; | |
-d|--data) data=1;; | |
*) args+=("\"$1\"");; | |
esac | |
shift | |
done | |
# echo "{\"command\": [ $(IFS=,; echo "${args[*]}") ]}" | |
output=$( | |
socat - $SOCKET <<<"{\"command\": [ $(IFS=,; echo "${args[*]}") ]}" | |
) | |
(( $? )) && | |
bail "Error sending IPC command '${args[0]}'" | |
error=$( jq -r .error <<<"$output" ) | |
[[ $error == success ]] || | |
bail "Error '$error' sending IPC command '${args[0]}'" | |
if (( !quiet )); then | |
if (( data )); then | |
echo "$output" | jq -r .data | |
else | |
echo "$output" | |
fi | |
fi | |
} | |
show_playlist() { | |
echo | |
echo " Contents of playlist" | |
echo " --------------------" | |
_ipc --data get_property playlist | jq -r '" - " + .[].filename' | |
echo | |
echo " Now playing" | |
echo " -----------" | |
_ipc --data get_property media-title \ | |
| fold -s -w 78 \ | |
| sed 's/^/ &/' | |
echo | |
} | |
send_command() { | |
local pid=$(pgrep mpv) | |
_ipc --data "$@" || | |
bail "Error sending IPC command '$*' (mpv pid: $pid)" | |
} | |
enqueue_item() { | |
local pid=$(pgrep mpv) | |
local item=$1 | |
if _ipc -q loadfile "$item" append-play; then | |
echo "Enqueued '$item' (mpv pid: $pid)" >&2 | |
else | |
bail "Error enqueueing '$item' (mpv pid: $pid)" | |
fi | |
} | |
pop_item() { | |
local pid=$(pgrep mpv) | |
local count=$(_ipc --data get_property playlist-count) | |
if _ipc --quiet playlist-remove $(( count - 1 )); then | |
echo "Popped last entry from playlist (mpv pid: $pid)" >&2 | |
else | |
bail "Error popping from playlist (mpv pid: $pid)" | |
fi | |
} | |
bail() { | |
echo >&2 | |
echo " $RED${BOLD}OH NOES!${RESET} $*" >&2 | |
echo >&2 | |
exit 1 | |
} | |
queue=() | |
mpvopts=("${MPVOPTS[@]}") | |
output= | |
pid= | |
ret= | |
showplaylist= | |
# parse input arguments | |
while (( $# )); do | |
case $1 in | |
-h|--h*) | |
echo "$USAGE" | |
exit | |
;; | |
-s|--show*) | |
show_playlist | |
exit | |
;; | |
-p|--pop) | |
pop_item | |
exit | |
;; | |
-c|--command) | |
shift | |
send_command "$@" | |
exit | |
;; | |
-*) | |
mpvopts+=("$1") | |
;; | |
*) | |
queue+=("$1") | |
esac | |
shift | |
done | |
# if no queue arguments given on command line: | |
# - if reading from a pipe/redirection, read that in | |
# - else try the clipboard | |
if [[ ${#queue[@]} -eq 0 ]]; then | |
if [[ ! -t 0 ]]; then | |
echo "Reading from stdin..." >&2 | |
readarray -t stdin | |
queue+=("${stdin[@]}") | |
else | |
echo "Checking clipboard..." >&2 | |
readarray -t clipboard < <(pbpaste) | |
queue+=("${clipboard[@]}") | |
fi | |
fi | |
if [[ ${#queue[@]} -eq 0 ]]; then | |
echo >&2 | |
echo "$USAGE" >&2 | |
bail "Queue cannot be empty; try passing filenames or URLs" | |
fi | |
# start mpv with IPC service enabled, if not running | |
pid=$(pgrep mpv) | |
ret=$? | |
if (( $ret != 0 )); then | |
nohup mpv "${mpvopts[@]}" 2>&1 & | |
pid=$! | |
echo "Started mpv (pid: $pid)" >&2 | |
sleep $STARTUPDELAY | |
fi | |
# spool up the rest by sending messages to the socket | |
for item in "${queue[@]}"; do | |
enqueue_item "$item" | |
done |
FIXME
The script-opts=ytdl_hook-ytdl_path=yt-dlp
key isn't supported until v0.33.0 (I think), and Ubuntu 20.04 only has 0.32.0. See this Reddit thread and mpv-player/mpv@93f84b5.
With 0.32.0, you just get the error message
[ytdl_hook] script-opts: unknown key ytdl_path, ignoring
which is silent, and so mpv
just quits, no indication of what went wrong in nohup.out
, I think because of the --no-terminal
option (which I might want to reconsider).
Workaround
pipx install yt-dlp
cd ~/.local/bin
ln -s yt-dlp youtube-dl
And then comment out the line in the script that says
--script-opts=ytdl_hook-ytdl_path=yt-dlp
As a general practice, you're always better off installing yt-dlp with pip or pipx. LTS distros' repositories may include a yt-dlp package, but it's almost guaranteed to be too old to actually work with YouTube. It's a constant arms race.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Needs to check for presence of
socat
and bail if it's not found.