Skip to content

Instantly share code, notes, and snippets.

@DimitriGilbert
Last active March 5, 2024 15:43
Show Gist options
  • Select an option

  • Save DimitriGilbert/bf78c35208ae848c757b0e13fd39342c to your computer and use it in GitHub Desktop.

Select an option

Save DimitriGilbert/bf78c35208ae848c757b0e13fd39342c to your computer and use it in GitHub Desktop.
a simple bash program to play content from YT using mpv. It has a playlist like feature using --next "url". I use it on my RPI homelab from SSH.
#!/bin/bash# @parseArger-begin
# @parseArger-help "I send an SOS to the world" --option "help" --short-option "h"
# @parseArger-verbose --option "verbose" --level "0" --quiet-option "quiet"
# @parseArger-declarations
# @parseArger pos vid "video id"
# @parseArger opt base-url "service url" --default-value "https://www.youtube.com/watch?v="
# @parseArger opt next "play next" --short n --repeat --alias playlist
# @parseArger opt player-command "player command" --short p --default-value "mpv" --repeat --alias player
# @parseArger opt set-display "set display id" --default-value "0"
# @parseArger opt pulse-out "pulseaudio output sink id" --default-value "2"
# @parseArger-declarations-end
# @parseArger-utils
_helpHasBeenPrinted=1;
_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)";
# @parseArger-utils-end
# @parseArger-parsing
__cli_arg_count=$#;
die()
{
local _ret="${2:-1}"
test "${_PRINT_HELP:-no}" = yes && print_help >&2
log "$1" -3 >&2
exit "${_ret}"
}
begins_with_short_option()
{
local first_option all_short_options=''
first_option="${1:0:1}"
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}
# POSITIONALS ARGUMENTS
_positionals=();
_optional_positionals=();
_arg_vid="";
# OPTIONALS ARGUMENTS
_arg_base_url="https://www.youtube.com/watch?v="
_arg_next=()
_arg_player_command=("mpv" --vid=no)
_arg_set_display="0"
_arg_pulse_out="2"
# FLAGS
# NESTED
_verbose_level="0";
print_help()
{
_triggerSCHelp=1;
if [[ "$_helpHasBeenPrinted" == "1" ]]; then
_helpHasBeenPrinted=0;
echo -e "I send an SOS to the world:"
echo -e " vid: video id"
echo -e " --base-url <base-url>: service url [default: ' https://www.youtube.com/watch?v= ']"
echo -e " -n, --next|--playlist <next>: play next, repeatable"
echo -e " -p, --player-command|--player <player-command>: player command, repeatable [default: '( mpv )']"
echo -e " --set-display <set-display>: set display id [default: ' 0 ']"
echo -e " --pulse-out <pulse-out>: pulseaudio output sink id [default: ' 2 ']"
echo -e "Usage :
$0 <vid> [--base-url <value>] [--next <value>] [--player-command <value>] [--set-display <value>] [--pulse-out <value>]";
fi
}
log() {
local _arg_msg="${1}";
local _arg_level="${2:-0}";
if [ "${_arg_level}" -le "${_verbose_level}" ]; then
case "$_arg_level" in
-3)
_arg_COLOR="\033[0;31m";
;;
-2)
_arg_COLOR="\033[0;33m";
;;
-1)
_arg_COLOR="\033[1;33m";
;;
1)
_arg_COLOR="\033[0;32m";
;;
2)
_arg_COLOR="\033[1;36m";
;;
3)
_arg_COLOR="\033[0;36m";
;;
*)
_arg_COLOR="\033[0m";
;;
esac
echo -e "${_arg_COLOR}${_arg_msg}\033[0m";
fi
}
parse_commandline()
{
_positionals_count=0
while test $# -gt 0
do
_key="$1"
case "$_key" in
--base-url)
test $# -lt 2 && die "Missing value for the option: '$_key'" 1
_arg_base_url="$2"
shift
;;
--base-url=*)
_arg_base_url="${_key##--base-url=}"
;;
-n|--playlist|--next)
test $# -lt 2 && die "Missing value for the option: '$_key'" 1
_arg_next+=("$2")
shift
;;
--next=*)
_arg_next+=("${_key##--next=}")
;;
--playlist=*)
_arg_next+=("${_key##--playlist=}")
;;
-n*)
_arg_next+=("${_key##-n}")
;;
-p|--player|--player-command)
test $# -lt 2 && die "Missing value for the option: '$_key'" 1
_arg_player_command+=("$2")
shift
;;
--player-command=*)
_arg_player_command+=("${_key##--player-command=}")
;;
--player=*)
_arg_player_command+=("${_key##--player=}")
;;
-p*)
_arg_player_command+=("${_key##-p}")
;;
--set-display)
test $# -lt 2 && die "Missing value for the option: '$_key'" 1
_arg_set_display="$2"
shift
;;
--set-display=*)
_arg_set_display="${_key##--set-display=}"
;;
--pulse-out)
test $# -lt 2 && die "Missing value for the option: '$_key'" 1
_arg_pulse_out="$2"
shift
;;
--pulse-out=*)
_arg_pulse_out="${_key##--pulse-out=}"
;;
-h|--help)
print_help;
exit 0;
;;
-h*)
print_help;
exit 0;
;;
--verbose)
if [ $# -lt 2 ];then
_verbose_level="$((_verbose_level + 1))";
else
_verbose_level="$2";
shift;
fi
;;
--quiet)
if [ $# -lt 2 ];then
_verbose_level="$((_verbose_level - 1))";
else
_verbose_level="-$2";
shift;
fi
;;
*)
_last_positional="$1"
_positionals+=("$_last_positional")
_positionals_count=$((_positionals_count + 1))
;;
esac
shift
done
}
handle_passed_args_count()
{
local _required_args_string="vid"
if [ "${_positionals_count}" -gt 1 ] && [ "$_helpHasBeenPrinted" == "1" ];then
_PRINT_HELP=yes die "FATAL ERROR: There were spurious positional arguments --- we expect at most 1 (namely: $_required_args_string), but got ${_positionals_count} (the last one was: '${_last_positional}').\n\t${_positionals[*]}" 1
fi
if [ "${_positionals_count}" -lt 1 ] && [ "$_helpHasBeenPrinted" == "1" ];then
_PRINT_HELP=yes die "FATAL ERROR: Not enough positional arguments - we require at least 1 (namely: $_required_args_string), but got only ${_positionals_count}.
${_positionals[*]}" 1;
fi
}
assign_positional_args()
{
local _positional_name _shift_for=$1;
_positional_names="_arg_vid ";
shift "$_shift_for"
for _positional_name in ${_positional_names};do
test $# -gt 0 || break;
eval "if [ \"\$_one_of${_positional_name}\" != \"\" ];then [[ \"\${_one_of${_positional_name}[*]}\" =~ \"\${1}\" ]];fi" || die "${_positional_name} must be one of: $(eval "echo \"\${_one_of${_positional_name}[*]}\"")" 1;
eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an ParseArger bug." 1;
shift;
done
}
print_debug()
{
print_help
# shellcheck disable=SC2145
echo "DEBUG: $0 $@";
echo -e " vid: ${_arg_vid}";
echo -e " base-url: ${_arg_base_url}";
echo -e " next: ${_arg_next[*]}";
echo -e " player-command: ${_arg_player_command[*]}";
echo -e " set-display: ${_arg_set_display}";
echo -e " pulse-out: ${_arg_pulse_out}";
}
on_interrupt() {
die Process aborted! 130;
}
parse_commandline "$@";
handle_passed_args_count;
assign_positional_args 1 "${_positionals[@]}";
trap on_interrupt INT;
# @parseArger-parsing-end
# print_debug "$@"
# @parseArger-end
if [[ $_arg_vid =~ ^https?:// ]] || [[ $_arg_vid =~ ^ftp:// ]] || [[ $_arg_vid =~ ^file:// ]]; then
_topl="${_arg_vid}";
else
_topl="${_arg_base_url}${_arg_vid}";
fi
# DISPLAY=:${_arg_set_display} PULSE_SINK=${_arg_pulse_out} mpv --vid=no "$_topl";
export DISPLAY=:${_arg_set_display};
export PULSE_SINK=${_arg_pulse_out};
_plCmd=("${_arg_player_command[@]}" "$_topl");
"${_plCmd[@]}";
if [ "${#_arg_next[@]}" -ge 0 ]; then
_nx=${_arg_next[0]};
_nxCmd=("$0" "$_nx" --base-url "${_arg_base_url}");
_nxArr=("${_arg_next[@]:1}");
if [ "${#_nxArr[@]}" -gt 0 ]; then
for i in "${_nxArr[@]}"; do
_nxCmd+=("--next" "$i");
done
fi
"${_nxCmd[@]}";
fi
@DimitriGilbert
Copy link
Author

# set display 1
yt-mpv cVXMmGUBw34 --set-display 1

# set audio interface 0 (headphone jack on the RPI)
yt-mpv cVXMmGUBw34 --pulse-out 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment