Last active
December 13, 2019 10:23
-
-
Save michd/253e8e4635aa645bdd386b2ab3df009b to your computer and use it in GitHub Desktop.
A user-friendly bash script for playing content on a TV through Raspberry Pi
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 | |
# This script collects files in a media folder and presents them in a dialog, | |
# where you can select one with arrow keys and enter. It is then played with | |
# omxplayer. | |
# The script allows navigating into subdirectories, and back up as well. Spaces | |
# in file names should also work properly. | |
# Assumptions: | |
# - `whiplash` is installed. `dialog` is an alternative, but it didn't work well | |
# for me. | |
# Directory containing TV. You will need read access. | |
# Won't allow navigating up beyond this point. | |
mainTvDir="/mnt/TV/" | |
# Current directory we're listing files in | |
tvDir=${mainTvDir} | |
# Directory we're in at launch, so we can return to it at exit | |
initialPwd=$(pwd) | |
# Dialog dimensions based on terminal size | |
dHeight=$(($(tput lines)-4)) | |
dWidth=$(($(tput cols)-4)) | |
choiceHeight=$((dHeight-11)) | |
# Where to direct terminal output | |
terminal=$(tty) | |
# Array of entries in current directory | |
fileList=("") | |
# Start in the tv directory | |
cd $tvDir | |
# At bottom of file, after all functions are implemented, we invoke main. | |
# Gets called at the beginning, and every time we need to refresh / return | |
# or navigate to a different folder | |
main() { | |
# Load the list of files into fileList | |
getFiles | |
# Show navigation dialog and await user choice | |
local choice=$(getChoice) | |
if [[ -z $choice ]]; then | |
# If no choice was given, this indicates "exit" (cancel) was picked. | |
# Ask whether to indeed exit | |
askExit | |
elif [[ $choice = "reload" ]]; then | |
# If refresh was picked, just run main again which will get a new list of | |
# files. Do this if you've added files to the directory after starting the | |
# script. | |
main | |
elif [[ $choice = ".." ]]; then | |
# Navigate up a directory, then reload file list | |
cd .. | |
tvDir="$(pwd)/" | |
main | |
else | |
# Any other option is either a directory or a file to try and play. | |
# Let navigate deal with it by index in fileList. | |
local index=$((choice-1)) | |
navigate $index | |
fi | |
} | |
# Collects files and stores them into the fileList array | |
getFiles() { | |
# Clear previous array of files | |
unset fileList | |
cd $tvDir | |
# Populate file list anew with all files found. | |
# Note: this previous used `ls`, but that's a bad idea in bash scripts, | |
# due to issues with spaces and quotes and such. ls isn't meant for script | |
# parsing. | |
for f in *; do | |
fileList+=("$f") | |
done | |
} | |
# Present a dialog with directory entries and a couple of options to the user | |
getChoice() { | |
# tag counter for whiptail dialog - each entry gets a tag. Our file/directory | |
# entries get a numeric tag. | |
local cnt=1 | |
# Array index for menu options. Every menu entry takes two array entries: | |
# One tag, one item. | |
local i=2 | |
# Array to contain menu options | |
local menuOpts=(reload "[Refresh]") | |
if [[ "$tvDir" != "$mainTvDir" ]]; then | |
# If we're not in the top level TV directory, add an entry to go to the | |
# parent directory | |
menuOpts+=(.. "../") | |
# Increase array index by two, for the entries we've just added | |
((i+=2)) | |
fi | |
# Add menu entries for each entry in fileList | |
for file in "${fileList[@]}"; do | |
# Assign numeric tag | |
menuOpts[i]=$cnt | |
if [[ -d $file ]]; then | |
# If the entry is a directory, add the "/" suffix as a visual indicator | |
menuOpts[i+1]="$file/" | |
else | |
# Otherwise just use the filename as-is | |
menuOpts[i+1]="$file" | |
fi | |
# Increment menuOpts array index by the two entries just added | |
((i+=2)) | |
# Increment tag counter by one | |
((cnt++)) | |
done | |
# Render menu and output choice to terminal | |
# Line by line comments: | |
# Print the current directory in the back | |
# Set title on the dialog | |
# Don't display tags as they're useless here | |
# Chunky buttons | |
# Use "Exit" as the text for the cancel button | |
# Start menu options + text above menu | |
# Pass dialog dimensions (height, width, choice area height) | |
# Pass all the menu options | |
# Redirect all output to the tty | |
whiptail --backtitle "$tvDir" \ | |
--title "Select file" \ | |
--notags \ | |
--fullbuttons \ | |
--cancel-button "Exit" \ | |
--menu "Select a file to watch" \ | |
$dHeight $dWidth $choiceHeight \ | |
"${menuOpts[@]}" \ | |
2>&1 > $terminal | |
} | |
# Navigate to an item by index (in $1), corresponding to an item in $fileList | |
# If this is a directory, navigates to that directory and renders the menu again | |
# If it's a file, attempt to play it with omxplayer, then render menu again | |
navigate() { | |
local fullFilePath="${tvDir}${fileList[$1]}" | |
if [[ -d $fullFilePath ]]; then | |
# If it's a directory, set that as our working directory and go back to menu | |
tvDir=$fullFilePath | |
main | |
else | |
echo "Trying to play ${fileList[$1]} with omxplayer..." | |
mediaFile=("${fileList[$1]}") | |
omxplayer "$mediaFile" | |
main | |
fi | |
} | |
# Prompt user whether to exit, with optionally a custom message. | |
# If Yes is picked, the script returns to the original directory, and exits. | |
# Otherwise, it returns to the main menu. | |
askExit() { | |
msg=$1 | |
if [[ -z $msg ]]; then | |
msg="Are you sure you wish to exit?" | |
fi | |
if (whiptail --yesno "$msg" \ | |
7 34 \ | |
2>&1 > $terminal); then | |
echo "Bye." | |
cd $initialPwd | |
else | |
main | |
fi | |
} | |
# Launch main menu | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment