Skip to content

Instantly share code, notes, and snippets.

@fearphage
Created January 24, 2020 16:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fearphage/7ddfbecf80e26ea33825898b41ba93c7 to your computer and use it in GitHub Desktop.
Save fearphage/7ddfbecf80e26ea33825898b41ba93c7 to your computer and use it in GitHub Desktop.
Command Line Audio Playlist
#!/bin/bash
# Requirements: dialog mpv
# Optional: youtube-dl wget hq
# Arguments: none
#
# Changelog:
# > + from function to standalone script
# + menu stack for saving previous menu command
# + choices stack for saving previous menu choice
# + "require" function
# + "niceinfo" function
# + back to menu on player close
# + autofetch for http://musicforprogramming.net
# + automatic requirements check
#
# > + dialog menu entries via associative array
# + key array support for keeping custom order
# + nice "addentry" function
# + selection title shows up before playing
# + internal functions unsetting before close
declare next back out err keys label defitem
declare stackmenu=("quit c") stackchoice=(-)
declare -A options
clearOptions() {
keys=()
options=()
}
addentry() {
keys+=("${1:?Missing key}")
options["$1"]="$2"
}
print() {
printf '%b\n' "${@}"
}
niceinfo() {
local text="${1:?Missing info text.}"
if [[ "${text: -1}" != $'\n' ]]; then
text+=$'\n'
fi
if ! which dialog &>/dev/null; then
echo "$text"
return
fi
local width height
IFS=' ' read -r height width <<< $(wc -l -L <<< "$text")
dialog --infobox "$text" $((height+1)) $((width+4))
}
nicemenu() {
local oldnext="$next"
next="${1:?Missing next cmd}"
back="$3"
local title="$2"
shift 3
local list=()
for k in "${keys[@]}"; do
list+=("$k" "${options[$k]}")
done
out=$(dialog ${defitem:+--default-item "$defitem"} --menu "$title" 0 0 0 "${list[@]}" 3>&2 2>&1 1>&3)
local err=$?
defitem=""
if let "${#out} == 0"; then
err=1
fi
if let "!err"; then
stackmenu+=("$oldnext")
stackchoice+=("$out")
label="${options[$out]}"
fi
clearOptions
next="${next} '$out'"
return $err
}
require() {
local notfound=()
for pkg in "$@"; do
if ! which "$pkg" &>/dev/null; then
notfound+=("$pkg")
fi
done
if let "${#notfound[@]} > 0"; then
local msg="Unable to find these required executables:"
local wait=0
for missing in "${notfound[@]}"; do
msg+=$'\n'" - $missing"
let 'wait++'
done
niceinfo "$msg"
sleep "$wait.7"
return 1
fi
return 0
}
mmenu() {
addentry ytvideo "Youtube"
addentry mfp "musicforprogramming.net"
addentry direct "Direct links"
nicemenu 'eval' "Select source"
}
ytvideo() {
if ! require youtube-dl; then return 1; fi
addentry nxWxC0DffG8 "Power Outage During Thunderstorm ASMR Ambience"
addentry mlQmCfjXApM "Mario Kart Wii - Main Menu Medley (Extended)"
addentry ebhoaxFyDuM "Relaxation music 12 hours - Vol 3"
addentry zT_zEAVFrNo "Study and DayDream #1 | Retrowave - Synthwave - Chillwave Mix 2019"
addentry j5edzGidnPA "Best of Relaxing Synthwave Mix"
nicemenu 'play yt' "Select video"
}
mfp_error() {
niceinfo "Unable to fetch website content."
sleep 1.7
return 1
}
mfp() {
if ! require hq wget; then return 1; fi
niceinfo "Fetching website content..."
local page=$(mktemp)
if ! wget -q -O "$page" https://musicforprogramming.net/?about &>/dev/null; then
mfp_error
rm -f "$page"
return
fi
while read -r entry; do
IFS=$'\t' read -r url title <<< "$entry"
addentry "$url" "$title"
done <<< "$(paste \
<(hq -f "$page" '#episodes > a' attr href) \
<(hq -f "$page" '#episodes > a' text) \
| tac
)"
rm -f "$page"
nicemenu 'mfp_entry' "musicforprogramming.net"
}
mfp_entry() {
niceinfo "Fetching website content..."
local page=$(mktemp)
if ! wget -q -O "$page" \
"https://musicforprogramming.net/${1:?Missing url part.}" \
&>/dev/null; then
mfp_error
rm -f "$page"
return
fi
local url
url=$(hq -f "$page" '#player' attr src)
next="play - ${url@Q}"
rm -f "$page"
}
direct() {
addentry http://radio.plaza.one/opus_64 "Plaza One"
nicemenu 'play -' "Select link"
}
play() {
clear
print "\e[1mTitle: \e[36m$label\e[0m"
echo "(Please wait)"
local args=()
case "$1" in
yt) args+=(
"--ytdl-format=bestaudio[ext=m4a]/best[ext=mp4]/best"
"https://www.youtube.com/watch?v=$2"
);;
*) args+=("$2")
esac
mpv --no-video --hwdec=yes --volume=50 "${args[@]}"
if let 'err'; then
sleep 1.7
fi
return 1
}
quit() {
${1:+clear}
exit
}
signal() { :; }
if ! require dialog mpv; then return 1; fi
trap signal SIGINT
next=mmenu
while true; do
if ! eval "$next"; then
if [[ "$back" != "" ]]; then
next="$back"
else
next="${stackmenu[-1]}"
defitem="${stackchoice[-1]}"
unset stackmenu[-1]
unset stackchoice[-1]
fi
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment