Created
May 31, 2022 12:28
-
-
Save frabjous/264493985b0968641d99db17512f7fd8 to your computer and use it in GitHub Desktop.
Script for browsing music for mpd with mpc and kitty's icat kitten
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
#!/bin/bash | |
# Define colors | |
txtblk='\e[0;30m' # Black - Regular | |
txtred='\e[0;31m' # Red | |
txtgrn='\e[0;32m' # Green | |
txtylw='\e[0;33m' # Yellow | |
txtblu='\e[0;34m' # Blue | |
txtpur='\e[0;35m' # Purple | |
txtcyn='\e[0;36m' # Cyan | |
txtwht='\e[0;37m' # White | |
bldblk='\e[1;30m' # Black - Bold | |
bldred='\e[1;31m' # Red | |
bldgrn='\e[1;32m' # Green | |
bldylw='\e[1;33m' # Yellow | |
bldblu='\e[1;34m' # Blue | |
bldpur='\e[1;35m' # Purple | |
bldcyn='\e[1;36m' # Cyan | |
bldwht='\e[1;37m' # White | |
unkblk='\e[4;30m' # Black - Underline | |
undred='\e[4;31m' # Red | |
undgrn='\e[4;32m' # Green | |
undylw='\e[4;33m' # Yellow | |
undblu='\e[4;34m' # Blue | |
undpur='\e[4;35m' # Purple | |
undcyn='\e[4;36m' # Cyan | |
undwht='\e[4;37m' # White | |
bakblk='\e[40m' # Black - Background | |
bakred='\e[41m' # Red | |
bakgrn='\e[42m' # Green | |
bakylw='\e[43m' # Yellow | |
bakblu='\e[44m' # Blue | |
bakpur='\e[45m' # Purple | |
bakcyn='\e[46m' # Cyan | |
bakwht='\e[47m' # White | |
trublk='\e[38;2;0;0;0m' # TrueColor Black | |
txtrst='\e[0m' # Text Reset | |
function echocolor { | |
local COLOR="$1" | |
local BG="$2" | |
shift 2 | |
echo -e ${COLOR}${BG}"$@"${txtrst} | |
} | |
# echo with a color | |
function echopurple { | |
echo -e ${bldpur}"$@"${txtrst} | |
} | |
function echored { | |
echo -e ${bldred}"$@"${txtrst} | |
} | |
function echoblue { | |
echo -e ${bldblu}"$@"${txtrst} | |
} | |
function echogreen { | |
echo -e ${bldgrn}"$@"${txtrst} | |
} | |
function echocyan { | |
echo -e ${bldcyn}"$@"${txtrst} | |
} | |
function echoyellow { | |
echo -e ${bldylw}"$@"${txtrst} | |
} | |
function echowhite { | |
echo -e ${bldwht}"$@"${txtrst} | |
} | |
function echoblack { | |
echo -e ${bldblk}"$@"${txtrst} | |
} | |
# echo with color; no newline | |
function nechopurple { | |
echo -n -e ${bldpur}"$@"${txtrst} | |
} | |
function nechored { | |
echo -n -e ${bldred}"$@"${txtrst} | |
} | |
function nechoblue { | |
echo -n -e ${bldblu}"$@"${txtrst} | |
} | |
function nechogreen { | |
echo -n -e ${bldgrn}"$@"${txtrst} | |
} | |
function nechocyan { | |
echo -n -e ${bldcyn}"$@"${txtrst} | |
} | |
function nechoyellow { | |
echo -n -e ${bldylw}"$@"${txtrst} | |
} | |
function nechowhite { | |
echo -n -e ${bldwht}"$@"${txtrst} | |
} | |
function nechoblack { | |
echo -n -e ${bldblk}"$@"${txtrst} | |
} | |
# echodim with a color | |
function echodimpurple { | |
echo -e ${txtpur}"$@"${txtrst} | |
} | |
function echodimred { | |
echo -e ${txtred}"$@"${txtrst} | |
} | |
function echodimblue { | |
echo -e ${txtblu}"$@"${txtrst} | |
} | |
function echodimgreen { | |
echo -e ${txtgrn}"$@"${txtrst} | |
} | |
function echodimcyan { | |
echo -e ${txtcyn}"$@"${txtrst} | |
} | |
function echodimyellow { | |
echo -e ${txtylw}"$@"${txtrst} | |
} | |
function echodimwhite { | |
echo -e ${txtwht}"$@"${txtrst} | |
} | |
function echodimblack { | |
echo -e ${txtblk}"$@"${txtrst} | |
} | |
# echodim with color; no newline | |
function nechodimpurple { | |
echo -n -e ${txtpur}"$@"${txtrst} | |
} | |
function nechodimred { | |
echo -n -e ${txtred}"$@"${txtrst} | |
} | |
function nechodimblue { | |
echo -n -e ${txtblu}"$@"${txtrst} | |
} | |
function nechodimgreen { | |
echo -n -e ${txtgrn}"$@"${txtrst} | |
} | |
function nechodimcyan { | |
echo -n -e ${txtcyn}"$@"${txtrst} | |
} | |
function nechodimyellow { | |
echo -n -e ${txtylw}"$@"${txtrst} | |
} | |
function nechodimwhite { | |
echo -n -e ${txtwht}"$@"${txtrst} | |
} | |
function nechodimblack { | |
echo -n -e ${txtblk}"$@"${txtrst} | |
} | |
# echo with bg, black text | |
function echobgpurple { | |
echo -e ${bldblk}${bakpur}"$@"${txtrst} | |
} | |
function echobgred { | |
echo -e ${bldblk}${bakred}"$@"${txtrst} | |
} | |
function echobgblue { | |
echo -e ${bldblk}${bakblu}"$@"${txtrst} | |
} | |
function echobggreen { | |
echo -e ${bldblk}${bakgrn}"$@"${txtrst} | |
} | |
function echobgcyan { | |
echo -e ${bldblk}${bakcyn}"$@"${txtrst} | |
} | |
function echobgyellow { | |
echo -e ${bldblk}${bakylw}"$@"${txtrst} | |
} | |
function echobgwhite { | |
echo -e ${bldblk}${bakwht}"$@"${txtrst} | |
} | |
function echobgblack { | |
echo -e ${txtwht}${bakblk}"$@"${txtrst} | |
} | |
# echo with bg, black text; no new line | |
function nechobgpurple { | |
echo -e -n ${trublk}${bakpur}"$@"${txtrst} | |
} | |
function nechobgred { | |
echo -e -n ${trublk}${bakred}"$@"${txtrst} | |
} | |
function nechobgblue { | |
echo -e -n ${trublk}${bakblu}"$@"${txtrst} | |
} | |
function nechobggreen { | |
echo -e -n ${trublk}${bakgrn}"$@"${txtrst} | |
} | |
function nechobgcyan { | |
echo -e -n ${trublk}${bakcyn}"$@"${txtrst} | |
} | |
function nechobgyellow { | |
echo -e -n ${trublk}${bakylw}"$@"${txtrst} | |
} | |
function nechobgwhite { | |
echo -e -n ${trublk}${bakwht}"$@"${txtrst} | |
} | |
function nechobgblack { | |
echo -e -n ${txtwht}${bakblk}"$@"${txtrst} | |
} | |
# spit noticeable error | |
function echoerr { | |
echo -e ${bakwht}${bldred}"$@"${txtrst} >&2 | |
} | |
function exitwitherror { | |
echo -e ${bakwht}${bldred}"$2"${txtrst} >&2 | |
exit $1 | |
} |
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
#!/bin/bash | |
# cover art should be named cover.jpg in each folder | |
# give the terminal time to resize | |
sleep 0.3 | |
source "$HOME/bin/color.sh" | |
# settings | |
MUSICFOLDER="$HOME/music" | |
DEFAULTART="$HOME/music/unknownartist.jpg" | |
# get size of terminal | |
COLS="$(tput cols)" | |
LINES="$(tput lines)" | |
MENULINES="$LINES" | |
let MENULINES=LINES-5 | |
# art width, height, menusizes | |
declare -i COVERWIDTH=15 | |
declare -i COVERHEIGHT=8 | |
declare -i ITEMHEIGHT=0 | |
let ITEMHEIGHT=COVERHEIGHT+2 | |
declare -i ITEMSPERLINE=0 | |
let ITEMSPERLINE=COLS/COVERWIDTH | |
declare -i ROWSTOFIT=0 | |
let ROWSTOFIT=MENULINES/ITEMHEIGHT | |
declare -i MENUSIZE=0 | |
let MENUSIZE=ROWSTOFIT*ITEMSPERLINE | |
MAXALBWIDTH=$((COVERWIDTH - 3)) | |
#check if kitty | |
if [[ "$TERM" == "xterm-kitty" ]] ; then | |
KITTY="yes" | |
else | |
KITTY="no" | |
fi | |
# variable declaration | |
declare -a MENULIST=() | |
declare -a PLIST=() | |
declare -a SONGLIST=() | |
declare -a SONGLISTREFS=() | |
declare -i MENUOFFSET=0 | |
MENUTYPE="main" | |
LPROMPTCHOICE="" | |
RANDOMTOKEN="Rr=A_ndD-OMm" | |
# AAORA = albumartist or artist | |
AAORA="albumartist" | |
#################### | |
# FUNCTIONS | |
#################### | |
# add a song from a song listing to the queue | |
# argument is 1-based number from song list | |
function addsong { | |
local ARG="$1" | |
# adjust to 0-based number | |
local POS=$((ARG - 1)) | |
mpc add "${SONGLISTREFS[$POS]}" | |
} | |
# load a collection of albums by searching ARG | |
function albums { | |
local ARG="$1" | |
# if no argument given, ask for starting letter | |
if [[ -z "$ARG" ]] ; then | |
letter_prompt | |
if [[ "$LPROMPTCHOICE" == '#' ]] ; then | |
ARG='^[0-9]' | |
elif [[ "$LPROMPTCHOICE" == '?' ]] ; then | |
ARG="$RANDOMTOKEN" | |
else | |
ARG="^$LPROMPTCHOICE" | |
fi | |
fi | |
# populate list of albums for the menu | |
MENULIST=() | |
if [[ "$ARG" == "$RANDOMTOKEN" ]] ; then | |
# random list | |
mapfile -t MENULIST < <(mpc -f "%album%=-=%${AAORA}%" listall | sort | uniq | shuf | head -n "$MENUSIZE") | |
else | |
# use argument to search | |
mapfile -t MENULIST < <(mpc search -f "%album%=-=%${AAORA}%" "(( album =~ \"$ARG\" ))" | sort | uniq ) | |
fi | |
# count number of albums loaded | |
MCOUNT="${#MENULIST[@]}" | |
if [[ "$MCOUNT" -eq 0 ]] ; then | |
echo "No albums found." | |
return | |
fi | |
# if there is only one, jump right to it | |
if [[ "$MCOUNT" -eq 1 ]] ; then | |
load_album "${MENULIST[0]}" | |
return | |
fi | |
# display menu with coverart | |
MENUTYPE="albums" | |
MENUOFFSET=0 | |
art_menu | |
} | |
# load a collection of artists by searching for them | |
function artists { | |
local ARG="$1" | |
# turn into artists mode for finer grained albums | |
AAORA="artist" | |
# prompt for letter if search parameter is empty | |
if [[ -z "$ARG" ]] ; then | |
letter_prompt | |
if [[ "$LPROMPTCHOICE" == '#' ]] ; then | |
ARG='^[0-9]' | |
elif [[ "$LPROMPTCHOICE" == '?' ]] ; then | |
ARG="$RANDOMTOKEN" | |
else | |
ARG="^$LPROMPTCHOICE" | |
fi | |
fi | |
# populate list of artists | |
MENULIST=() | |
if [[ "$ARG" == "$RANDOMTOKEN" ]] ; then | |
mapfile -t MENULIST < <(mpc -f "%artist%" listall | sort | uniq | shuf | head -n "$MENUSIZE") | |
else | |
mapfile -t MENULIST < <(mpc search -f "%artist%" "(( artist =~ \"$ARG\" ))" | sort | uniq ) | |
fi | |
# count size of list; if zero, return | |
MCOUNT="${#MENULIST[@]}" | |
if [[ "$MCOUNT" -eq 0 ]] ; then | |
echo "No artists found." | |
return | |
fi | |
# if one artist matching search, jump right to it | |
if [[ "$MCOUNT" -eq 1 ]] ; then | |
load_artist "${MENULIST[0]}" | |
return | |
fi | |
# display cover art menu | |
MENUTYPE="artists" | |
MENUOFFSET=0 | |
art_menu | |
} | |
# function to display menu with cover art, either albums or artists | |
function art_menu { | |
clear # clear the screen | |
local ROWSDONE=0 # count how many rows filled in | |
# loop over rows (Y), then columns (X) | |
for ((Y=0;Y<ROWSTOFIT;Y++)) ; do | |
for ((X=0;X<ITEMSPERLINE;X++)) ; do | |
# find spot in array | |
local SPOT=0 | |
let SPOT="MENUOFFSET+(ITEMSPERLINE*Y)+X" | |
# break if we are past the end of the list | |
if [[ "$SPOT" -ge ${#MENULIST[@]} ]] ; then | |
break | |
fi | |
# if we haven't broken, then add 1 to rows on first item | |
if [[ "$X" -eq 0 ]] ; then | |
let ROWSDONE=ROWSDONE+1 | |
fi | |
# read info to display from the menulist | |
if [[ "$MENUTYPE" == "artists" ]] ; then | |
# in artists mode, we just have name of artist | |
local ARTIST="${MENULIST[$SPOT]}" | |
local ALBUM='' | |
else | |
# in albums mode, we have both (album-)artist and album | |
local FULLITEM="${MENULIST[$SPOT]}" | |
local ALBUM="${FULLITEM%=-=*}" | |
local ARTIST="${FULLITEM#*=-=}" | |
fi | |
# insert cover art | |
if [[ "$KITTY" == "yes" ]] ; then | |
local ART="$(get_menu_art "${MENULIST[$SPOT]}")" | |
kitty +kitten icat --place ${COVERWIDTH}x${COVERHEIGHT}@$((X * $COVERWIDTH))x$((Y * ITEMHEIGHT)) --transfer-mode file "$ART" | |
fi | |
# put cursor under art | |
tput cup $(( (Y * ITEMHEIGHT) + COVERHEIGHT )) $(( (X * $COVERWIDTH) )) | |
# item number | |
nechocyan "$((SPOT + 1))" | |
nechowhite ") " | |
# is album not blank, put it on first line | |
if [[ -n "$ALBUM" ]] ; then | |
nechoblue "${ALBUM:0:$MAXALBWIDTH}" | |
fi | |
# move to next line, insert artist or albumartist name | |
tput cup $(( (Y * ITEMHEIGHT) + COVERHEIGHT + 1 )) $(( (X * $COVERWIDTH) )) | |
nechoyellow "${ARTIST:0:$COVERWIDTH}" | |
done | |
done | |
# insert cursor below menu based on rowsdone | |
tput cup $(( ROWSDONE*ITEMHEIGHT )) 0 | |
# input suggestions for this kind of menu | |
nechowhite "[" | |
nechocyan "#" | |
nechowhite "] " | |
nechogreen 'choose # ' | |
# only insert back suggestion if we're not at beginning | |
if [[ "$MENUOFFSET" -gt 0 ]] ; then | |
nechowhite "[" | |
nechocyan "<" | |
nechowhite "] " | |
nechogreen "back " | |
fi | |
# only insert next suggestion if there are more to see | |
if [[ "$((SPOT + 1))" -lt "${#MENULIST[@]}" ]] ; then | |
nechowhite "[" | |
nechocyan ">" | |
nechowhite "] " | |
nechogreen "more " | |
fi | |
nechowhite "[" | |
nechocyan "m" | |
nechowhite "] " | |
echogreen "main menu" | |
} | |
# deletes items from play queue, or clears entire queue | |
function clearq { | |
local ARG="$1" | |
# if no argument given, or "all" given, clear everything | |
if [[ -z "$ARG" ]] || [[ "$ARG" =~ [Aa][Ll][Ll] ]] ; then | |
mpc clear | |
return | |
fi | |
# if there is a comma in the argument, break off first part | |
if [[ "$ARG" =~ , ]] ; then | |
local FIRSTARG="${ARG%%,*}" | |
local REMAINDER="${ARG#*,}" | |
# do first part | |
clearq "$FIRSTARG" | |
# then do remainder, which may have additional commas | |
clearq "$REMAINDER" | |
return | |
fi | |
# if there is a hyphen, then do the range | |
if [[ "$ARG" =~ - ]] ; then | |
local STARTPOS="${ARG%%-*}" | |
local ENDPOS="${ARG##*-}" | |
for ((I=ENDPOS;I>=STARTPOS;I=I-1)) ; do | |
mpc del "$I" | |
done | |
return | |
fi | |
# if we're here, we should have a single number | |
mpc del "$ARG" | |
} | |
# add items to the play queue | |
function enqueue { | |
local ARG="$1" | |
# if argument is blank, or contains "all", add entire songlist | |
if [[ -z "$ARG" ]] || [[ "$ARG" =~ [Aa][Ll][Ll] ]] ; then | |
ARG="1-${#SONGLIST[@]}" | |
fi | |
# break up comma-ed arguments | |
if [[ "$ARG" =~ , ]] ; then | |
local FIRSTARG="${ARG%%,*}" | |
local REMAINDER="${ARG#*,}" | |
# do part before first comma | |
enqueue "$FIRSTARG" | |
# then do the rest (may end up back here) | |
enqueue "$REMAINDER" | |
return | |
fi | |
# if there is a hyphen, apply to range | |
if [[ "$ARG" =~ - ]] ; then | |
local STARTPOS="${ARG%%-*}" | |
local ENDPOS="${ARG##*-}" | |
for ((I=STARTPOS;I<=ENDPOS;I++)) ; do | |
addsong "$I" | |
done | |
echo "Added to queue." | |
return | |
fi | |
# if we are here, then argument should be single number | |
addsong "$ARG" | |
echo "Added to queue." | |
} | |
# determines art location by file, which should just be cover.jpg in same | |
# folder; otherwise, return default art | |
function get_art_for_file { | |
local FIRST="$1" | |
local DIR="$(dirname "$FIRST")" | |
POSSCOVER="$MUSICFOLDER/$DIR/cover.jpg" | |
if [[ -e "$POSSCOVER" ]] ; then | |
echo "$POSSCOVER" | |
return | |
fi | |
# return default | |
echo "$DEFAULTART" | |
return | |
} | |
# determines art for artist or artist/album-albumartist | |
function get_menu_art { | |
local ARG="$1" | |
# look in folder for artist or albumartist/album if it exists | |
if [[ "$MENUTYPE" == "artists" ]] ; then | |
local AUND="$ARG" | |
else | |
local ALBUM="${ARG%=-=*}" | |
local ARTIST="${ARG#*=-=}" | |
local AUND="$ARTIST/$ALBUM" | |
fi | |
AUND="${AUND// /_}" | |
local POSSCOVER="$MUSICFOLDER/$AUND/cover.jpg" | |
if [[ -e "$POSSCOVER" ]] ; then | |
echo "$POSSCOVER" | |
return | |
fi | |
# if not found yet, look for first search hit | |
if [[ "$MENUTYPE" == "artists" ]] ; then | |
local FIRST="$(mpc search "(( $AAORA == \"$ARG\" ))" | head -n 1)" #" | |
else | |
local FIRST="$(mpc search "(( $AAORA == \"$ARTIST\" ) AND ( album == \"$ALBUM\"))" | head -n 1)" #" | |
fi | |
# look for art in folder of first hit | |
local DIR="$(dirname "$FIRST")" | |
POSSCOVER="$MUSICFOLDER/$DIR/cover.jpg" | |
if [[ -e "$POSSCOVER" ]] ; then | |
echo "$POSSCOVER" | |
return | |
fi | |
# if not found yet, return default | |
echo "$DEFAULTART" | |
return | |
} | |
# play a certain song in current playlist | |
function jump { | |
mpc play "$1" | |
viewqueue | |
} | |
# prompt for a letter, or # for numbers, or ? for random | |
function letter_prompt { | |
LPROMPTCHOICE='' | |
echo | |
echogreen '# A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ?' | |
echo -n 'Starting with: ' | |
while [[ -z "$LPROMPTCHOICE" ]] ; do | |
read -r LPROMPTCHOICE | |
done | |
} | |
# load a given album; argument with be [album]=-=[(album)artist] | |
function load_album { | |
ARG="$1" | |
# break up argument into album and (album)artist | |
local ALBUM="${ARG%=-=*}" | |
local ARTIST="${ARG#*=-=}" | |
# populate lists | |
# songlist is displayable list of songs; songlistrefs are filenames of | |
# same songs | |
SONGLIST=() | |
SONGLISTREFS=() | |
mapfile -t SONGLIST < <(mpc search -f '%artist% - %title%' "(( $AAORA == \"$ARTIST\" ) AND ( album == \"$ALBUM\"))" ) | |
mapfile -t SONGLISTREFS < <(mpc search "(( $AAORA == \"$ARTIST\" ) AND ( album == \"$ALBUM\"))" ) | |
# display list of songs | |
song_menu | |
} | |
# load a given artist, argument is name of artist | |
function load_artist { | |
local ARG="$1" | |
# populate list of albums for that artist | |
MENULIST=() | |
mapfile -t MENULIST < <(mpc search -f '%album%=-=%artist%' "(( artist == \"$ARG\" ))" | sort | uniq) | |
# count size of list; if empty, return | |
MCOUNT="${#MENULIST[@]}" | |
if [[ "$MCOUNT" -eq 0 ]] ; then | |
echo "No albums found for that artist." | |
return | |
fi | |
# if one item in album list, jump right to it | |
if [[ "$MCOUNT" -eq 1 ]] ; then | |
load_album "${MENULIST[0]}" | |
return | |
fi | |
# display cover art menu of albums for that artist | |
MENUTYPE="albums" | |
MENUOFFSET=0 | |
art_menu | |
} | |
# create songlist for a given playlist; argument is name of playlist | |
function load_playlist { | |
local ARG="$1" | |
# songlist is displayablelist, songlistrefs is list of filenames | |
SONGLIST=() | |
SONGLISTREFS=() | |
mapfile -t SONGLIST < <(mpc playlist "$ARG") | |
mapfile -t SONGLISTREFS < <(mpc -f '%file%' playlist "$ARG") | |
# display list of songs | |
song_menu | |
} | |
# main menu gives main options | |
function main_menu { | |
clear # clear the screen | |
echo "Choose an option:" | |
# queue | |
nechowhite " [" | |
nechocyan "q" | |
nechowhite "]" | |
echogreen " View/edit queue" | |
# artists | |
nechowhite " [" | |
nechocyan "a" | |
nechowhite "]" | |
nechogreen " Select by artist " | |
echodimwhite "(with optional search parameter)" | |
# albums | |
nechowhite " [" | |
nechocyan "l" | |
nechowhite "]" | |
nechogreen " Select by album " | |
echodimwhite "(with optional search parameter)" | |
# songs | |
nechowhite " [" | |
nechocyan "s" | |
nechowhite "]" | |
nechogreen " Select by song title " | |
echodimwhite "(with optional search parameter)" | |
# playlists | |
nechowhite " [" | |
nechocyan "p" | |
nechowhite "]" | |
nechogreen " Select playlist " | |
echodimwhite "(with optional search parameter)" | |
# random | |
nechowhite " [" | |
nechocyan "r" | |
nechowhite "]" | |
nechogreen " Random " | |
echodimwhite "(albums) (or add a for artists, s for songs, etc.)" | |
# quit program | |
nechowhite " [" | |
nechocyan "x" | |
nechowhite "]" | |
echogreen " Exit" | |
# set menu type | |
MENUTYPE="main" | |
} | |
# navigate to previous items in cover art menu | |
function menu_back { | |
# if we are not in a cover art menu, do nothing | |
if [[ "$MENUTYPE" != "artists" ]] && [[ "$MENUTYPE" != "albums" ]] ; then | |
return | |
fi | |
# change menu offset | |
if [[ "$MENUOFFSET" -le "$MENUSIZE" ]] ; then | |
MENUOFFSET=0 | |
else | |
MENUOFFSET=$((MENUOFFSET - MENUSIZE)) | |
fi | |
# redisplay menu | |
art_menu | |
} | |
# navigate forward in cover art menu | |
function menu_more { | |
# if we are not in a cover art menu, do nothing | |
if [[ "$MENUTYPE" != "artists" ]] && [[ "$MENUTYPE" != "albums" ]] ; then | |
return | |
fi | |
# get index of last item current displayed, and how many items there are | |
CURREND=$((MENUOFFSET + MENUSIZE)) | |
TOTITEMS=${#MENULIST[@]} | |
# we are already at the end, do nothing and say so | |
if [[ "$CURREND" -ge "$TOTITEMS" ]] ; then | |
echo "Already at end." | |
return | |
fi | |
# change offset | |
MENUOFFSET=$((MENUOFFSET + MENUSIZE)) | |
# redisplay menu | |
art_menu | |
} | |
# if just a number if given what happens depends on what kind of meny | |
# we are looking at | |
function numeric_choice { | |
local ARG="$1" | |
case $MENUTYPE in | |
# in album menu, load the album in question | |
albums) | |
local I=$((ARG - 1)) | |
if [[ "$I" -lt "${#MENULIST[@]}" ]] ; then | |
load_album "${MENULIST[$I]}" | |
else | |
echo "Not a valid choice." | |
fi | |
;; | |
# in artists meny, load an album menu for that artist | |
artists) | |
local I=$((ARG - 1)) | |
if [[ "$I" -lt "${#MENULIST[@]}" ]] ; then | |
load_artist "${MENULIST[$I]}" | |
else | |
echo "Not a valid choice." | |
fi | |
;; | |
# if menu of playlists, view its songlist | |
playlist) | |
local I=$((ARG - 1)) | |
if [[ "$I" -lt "${#PLIST[@]}" ]] ; then | |
load_playlist "${PLIST[$I]}" | |
else | |
echo "Not a valid choice." | |
fi | |
;; | |
# if viewing queue, jump to that song | |
queue) | |
jump "$ARG" | |
;; | |
# if viewing a songlist, add that song to queue | |
songs) | |
enqueue "$ARG" | |
;; | |
# otherwise, do nothing | |
*) | |
;; | |
esac | |
} | |
# play something immediately | |
function play_now { | |
local ARG="$1" | |
# clear the queue | |
clearq | |
# add what is requested back to queue and play it | |
enqueue "$ARG" | |
mpc play &> /dev/null | |
} | |
# listing of playlists, either narrowed by arg search, or all | |
function playlist_menu { | |
local ARG="$1" | |
# set type of meny and clear screen | |
MENUTYPE=playlist | |
clear | |
# get list of playlists | |
PLIST=() | |
if [[ "$ARG" == "$RANDOMTOKEN" ]] ; then | |
mapfile -t PLIST < <(mpc lsplaylists | shuf | head -n 5) | |
ARG='' | |
else | |
mapfile -t PLIST < <(mpc lsplaylists) | |
fi | |
# if argument provided, filter it | |
if [[ -n "$ARG" ]] ; then | |
local NLIST=() | |
for P in "${PLIST[@]}" ; do | |
if [[ "$P" =~ "$ARG" ]] ; then | |
NLIST+=($P) | |
fi | |
done | |
PLIST=() | |
PLIST="$NLIST" | |
fi | |
# count number of results | |
local PCOUNT="${#PLIST[@]}" | |
# if there is only one, go right there | |
if [[ "$PCOUNT" -eq 1 ]] ; then | |
load_playlist "${PLIST[0]}" | |
return | |
fi | |
# if there is none, say so | |
if [[ "$PCOUNT" -gt 0 ]] ; then | |
echo "Playlists:" | |
else | |
echo "There are no playlists." | |
fi | |
# print numbered list of found playlists | |
for ((I=0;I<PCOUNT;I++)) ; do | |
C=$((I+1)) | |
printf -v C "%3s" "$C" | |
echo -n " " | |
nechocyan "$C" | |
nechowhite ") " | |
echoblue "${PLIST[$I]}" | |
done | |
# input suggestions for this menu | |
nechowhite '[' | |
nechoblue '#' | |
nechowhite '] ' | |
nechogreen 'view # ' | |
nechowhite '[' | |
nechoblue 'm' | |
nechowhite '] ' | |
nechogreen 'main menu ' | |
} | |
# save or write current queue as playlist; argument is name of playlist | |
function plwrite { | |
local ARG="$1" | |
# if no name given, ask for one | |
if [[ -z "$ARG" ]] ; then | |
echo -n "Playlist name: " | |
read -r ARG | |
fi | |
# if it works, tell them | |
if mpc save "$ARG" ; then echo "Saved playlist as $ARG" ; fi | |
} | |
# this is the basic prompt for input, and is basically the same everywhere | |
function prompt { | |
# prompt for and read choice | |
echo | |
echo -n "Your choice: " | |
read -r CHOICE | |
# react accordingly | |
case $CHOICE in | |
quit) | |
exit 0 | |
;; | |
exit|x*|X*) | |
exit 0 | |
;; | |
','|'<') | |
menu_back | |
;; | |
'.'|'>') | |
menu_more | |
;; | |
'+'*) | |
local ARG="${CHOICE:1}" | |
enqueue "$ARG" | |
;; | |
al*|l*|L*) | |
AAORA="albumartist" | |
local ARG="$(echo "$CHOICE" | sed 's/^\S*\s*//')" | |
albums "$ARG" | |
;; | |
a*|A*) | |
local ARG="$(echo "$CHOICE" | sed 's/^\S*\s*//')" | |
artists "$ARG" | |
;; | |
c*|C*) | |
local ARG="$(echo "$CHOICE" | sed 's/^[A-Za-z]*\s*//')" | |
clearq "$ARG" | |
viewqueue | |
;; | |
j*|J*) | |
local ARG="$(echo "$CHOICE" | sed 's/^[A-Za-z]*\s*//')" | |
jump "$ARG" | |
;; | |
m*|M*) | |
main_menu | |
;; | |
n*|N*) | |
local ARG="$(echo "$CHOICE" | sed 's/^[A-Za-z]*\s*//')" | |
play_now "$ARG" | |
;; | |
p*|P*) | |
local ARG="$(echo "$CHOICE" | sed 's/^\S*\s*//')" | |
playlist_menu "$ARG" | |
;; | |
q*|Q*) | |
viewqueue | |
;; | |
r*|R*) | |
local ARG="$(echo "$CHOICE" | sed 's/^\S*\s*//')" | |
random "$ARG" | |
;; | |
s*|S*) | |
local ARG="$(echo "$CHOICE" | sed 's/^\S*\s*//')" | |
songs "$ARG" | |
;; | |
w*|W*) | |
local ARG="$(echo "$CHOICE" | sed 's/^\S*\s*//')" | |
plwrite "$ARG" | |
;; | |
[0-9]*) | |
numeric_choice "$CHOICE" | |
;; | |
*) | |
;; | |
esac | |
} | |
# load stuff randomly, either albums (default), artists, songs | |
function random { | |
local ARG="$1" | |
# default to albums if no argument given | |
if [[ -z "$ARG" ]] ; then | |
ARG="l" | |
fi | |
# load appropriate kind of menu based on argument | |
case $ARG in | |
l*|al*) | |
AAORA="albumartist" | |
albums "$RANDOMTOKEN" | |
;; | |
a*) | |
artists "$RANDOMTOKEN" | |
;; | |
s*) | |
songs "$RANDOMTOKEN" | |
;; | |
p*) | |
playlist_menu "$RANDOMTOKEN" | |
;; | |
*) | |
;; | |
esac | |
} | |
# load a list of songs, which might be an album, or playlist, or random, etc. | |
function song_menu { | |
clear | |
MENUTYPE="songs" | |
# count list of list | |
SCOUNT="${#SONGLIST[@]}" | |
if [[ "$SCOUNT" -gt 0 ]] ; then | |
if [[ "$KITTY" == "yes" ]] ; then | |
# load art from first song | |
ART="$(get_art_for_file "${SONGLISTREFS[0]}")" | |
kitty +kitten icat --place ${COVERWIDTH}x${COVERHEIGHT}@2x1 --transfer-mode file "$ART" | |
tput cup "$((COVERHEIGHT + 2))" 0 | |
fi | |
echo "Songs:" | |
else | |
echo "There are no songs to view." | |
return | |
fi | |
# actual list of songs, with numbers | |
for ((I=0;I<SCOUNT;I++)) ; do | |
echo -n " "; | |
N=$((I + 1)) | |
printf -v N "%5s" "$N" | |
echo -n " " | |
nechocyan "$N" | |
nechowhite ") " | |
echoblue "${SONGLIST[$I]}" | |
done | |
# input suggests for this menu | |
nechowhite '[' | |
nechocyan '+' | |
nechowhite '] ' | |
nechogreen 'add (all/#) ' | |
nechowhite '[' | |
nechocyan 'n' | |
nechowhite '] ' | |
nechogreen 'play now (all/#) ' | |
nechowhite '[' | |
nechocyan 'm' | |
nechowhite '] ' | |
echogreen 'main menu' | |
} | |
# request a list of songs by search argument | |
function songs { | |
ARG="$1" | |
# if no search argument given, ask for first letter | |
if [[ -z "$ARG" ]] ; then | |
letter_prompt | |
if [[ "$LPROMPTCHOICE" == '#' ]] ; then | |
ARG='^[0-9]' | |
elif [[ "$LPROMPTCHOICE" == '?' ]] ; then | |
ARG="$RANDOMTOKEN" | |
else | |
ARG="^$LPROMPTCHOICE" | |
fi | |
fi | |
# songlist is displayable list of songs; songlist refs is corresponding | |
# filenames | |
# here we populate those lists | |
SONGLIST=() | |
SONGLISTREFS=() | |
# getsongs is a temporary array with both information in one | |
# we populate it first | |
local GETSONGS=() | |
if [[ "$ARG" == "$RANDOMTOKEN" ]] ; then | |
mapfile -t GETSONGS < <(mpc -f '%artist% - %title%:=:=:%file%' listall | sort | shuf | head -n 20) | |
else | |
mapfile -t GETSONGS < <(mpc search -f '%artist% - %title%:=:=:%file%' "(( title =~ \"$ARG\" ))") | |
fi | |
# loop through getsongs and split information into the two arrays we need | |
GSCOUNT="${#GETSONGS[@]}" | |
for ((I=0;I<GSCOUNT;I++)) ; do | |
local BOTH="${GETSONGS[$I]}" | |
local STITLE="${BOTH%:=:=:*}" | |
local SFILE="${BOTH#*:=:=:}" | |
SONGLIST[$I]="$STITLE" | |
SONGLISTREFS[$I]="$SFILE" | |
done | |
# display list of songs | |
song_menu | |
} | |
# view the current playlist | |
function viewqueue { | |
clear | |
MENUTYPE="queue" | |
# read list from mpc; get its size | |
local QLIST=() | |
mapfile -t QLIST < <(mpc playlist) | |
local QLENGTH="${#QLIST[@]}" | |
# get current filename and position | |
local CURRPOSITION="$(mpc current -f %position%)" | |
local CURRFILE="$(mpc -f '%file%' current)" | |
# if something playing, show the art for it | |
if [[ -n "$CURRFILE" ]] && [[ "$KITTY" == "yes" ]] ; then | |
local ART="$(get_art_for_file "$CURRFILE")" | |
kitty +kitten icat --place ${COVERWIDTH}x${COVERHEIGHT}@2x1 --transfer-mode file "$ART" | |
tput cup "$((COVERHEIGHT + 2))" 0 | |
fi | |
# header at top | |
if [[ "$QLENGTH" -eq 0 ]] ; then | |
echo "The playlist is empty." | |
else | |
echo "Current playlist:" | |
fi | |
# loop over playlist items, display with number | |
for ((I=0;I<QLENGTH;I++)) ; do | |
local CURRTRACK=no | |
local POS=$((I + 1)) | |
if [[ "$POS" -eq "$CURRPOSITION" ]] ; then | |
CURRTRACK=yes | |
fi | |
# if current, add arrow with purple background | |
if [[ "$CURRTRACK" == "yes" ]] ; then | |
nechobgpurple " → " | |
else | |
echo -n " " | |
fi | |
while [[ "${#POS}" -lt 3 ]] ; do | |
POS=" $POS" | |
done | |
nechocyan "$POS" | |
nechowhite ") " | |
# if current, give purple background | |
TT="${QLIST[$I]}" | |
if [[ "$CURRTRACK" == "yes" ]] ; then | |
nechobgpurple "$TT$(tput el)" | |
echo | |
else | |
echoblue "$TT" | |
fi | |
done | |
# Display hints for this menutype | |
# jump | |
echo | |
nechowhite '[' | |
nechocyan '#' | |
nechowhite '] ' | |
nechogreen 'jump to num ' | |
# clear | |
nechowhite '[' | |
nechocyan 'c(#)' | |
nechowhite '] ' | |
nechogreen 'clear ' | |
# save as playlist | |
nechowhite '[' | |
nechocyan 'w' | |
nechowhite '] ' | |
nechogreen 'write/save ' | |
nechowhite '[' | |
nechocyan 'm' | |
nechowhite '] ' | |
nechogreen 'main menu' | |
} | |
######################### main loop | |
function prompt_loop { | |
main_menu | |
while true ; do | |
prompt | |
done | |
} | |
prompt_loop |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment