#!/bin/bash

# Nuno Silva, 2019

# Fetches a file (filename) from BASEURL and watches it for changes while running omxplayer in the background
# $filename is used during downloading; $playfilename is played by omxplayer and replaced when a download finishes.

BASEURL="http://acornerbist.tecnico.ulisboa.pt"
#BASEURL="http://192.168.1.1/TV"

# filename to fetch from BASEURL
filename=acornertv.mp4

# filename for the cached version that is playing while filename is being downloaded
playfilename=live.mp4

# How often to check for a new video. Any format accepted by SLEEP(1) is good.
polling_interval=1h

# play this if playfilename does not exist
fallbackfilename=fallback.mp4

audio_output=hdmi # e.g. hdmi/local/both. See 'man omxplayer'

# extra arguments to pass to wget:
# --read-timeout: exit after xx seconds "idle time" (no data received)
wget_extra_args="--read-timeout=30"


# When a new video is downloaded, start playing it without letting the old one finish playing.
# If this is false, the new video is used only after the old video finishes playing.
play_new_video_immediately=false # true/false


### no need to edit beyond this point ###

# get the file size (in bytes) of a file
# $1: filename or path
get_file_size() {
	stat --printf="%s" $1
}

# play the given file in the background using omxplayer
# $1: file
function play {
	test -z "$1" && return 1
	local sleep=0
	while sleep $sleep && $running; do
		local file=$1
		if ( ! test -r $file) || test $(get_file_size $file) -eq 0; then
			echo "$file is 0 bytes or doesn't exist... falling back to $fallbackfilename"
			file=$fallbackfilename
		fi
		if test -r $file && omxplayer -p -o $audio_output $file | ts ' omxplayer:' && test ${PIPESTATUS[0]} -eq 0; then
			sleep=0
		else
			# something was wrong... increase the backoff period so we can start seeing the terminal
			((sleep+=2))
		fi
	done
}


# downloads a given file from BASEURL if the server's timestamp is newer.
# $1: filename
fetchfile(){
	test -z "$1" && return 1
	# only fetches the file if it is newer than the existing one
	wget --timestamping --progress=bar:force $wget_extra_args  "$BASEURL/$1"
	local ret=$?
	echo "wget exited with code $ret"
	return $ret
}

# wait until we have in internet connection
wait_net() {
	echo -n "Waiting for internet connection"
	for i in {1..600}; do
		echo -n .
		ping -c1 -w1 tecnico.ulisboa.pt &>/dev/null && echo " OK" && return 0
		sleep 1
	done
	echo failed
	return 1
}

function abort() {
	echo $@
	exit 1
}

# check that a given executable is available and (abort the script otherwise)
check_installed() {
	which $1 >/dev/null || abort "please install $1 $2"
}

function check_requirements() {
	check_installed omxplayer
	check_installed wget
	check_installed ping "(iputils-ping package)"
	check_installed tee "(coreutils package)"
	check_installed ts "(moreutils package)"
}



#### this is where the fun begins ####


# make sure everything is installed
check_requirements || exit 1

running=true
# kill children on exit
trap 'echo "$(date)     exiting"; running=false; test -n "$(jobs -p)" && kill $(jobs -p)' EXIT


# start playing the file we already have downloaded
play $playfilename &

# wait until we have an internet connection before trying to download anything
wait_net

# redirect fd 5 to stdout (so we can see wget's output and store it in a variable at the same time)
exec 5>&1

# loop (almost) forever
while sleep 1 && $running; do

	# a little randomness... this will eventually avoid having the PI's downloading at the same time
	sleep $((RANDOM%10))


	if test -r $filename && test $(get_file_size $filename) -gt 0; then
		# filesize is greater than 0 before we downloaded it. Probably a corrupted download.
		# Note that a successful download is moved to $playfilename, leaving $filename with 0 bytes.
		rm -fv $filename
	fi

	# try to download a new video... tee the output to fd 5 so we can see it
	if out=$(fetchfile $filename 2>&1 | tee /dev/fd/5; test ${PIPESTATUS[0]} -eq 0); then
		if grep -i "304 Not Modified"  <<<"$out"; then
			echo "304 Not Modified"
		elif grep -i "404 Not Found"  <<<"$out"; then
			echo "404 Not Found... not good"
		else
			# make sure we actually downloaded something (to protect against untested server errors)
			if test $(get_file_size $filename) -gt 0; then
				mv -v $playfilename{,.old}     # backup the video (even though it's not used)
				mv -v $filename $playfilename  # replace the current video with the freshly downloaded one

				# need the old file to check the modification date in future requests (the size is ignored)
				touch $filename -r $playfilename
				
				# make sure everything was written to disk/card
				sync

				if $play_new_video_immediately; then
					sleep 1
					pkill omxplayer
				fi
			else
				echo "$filename is 0 bytes... refusing to use it."
			fi
		fi
	else
		echo "fetch failed: $out" >&2
		# force it to try again next time
		rm -fv $filename
		sync
		sleep 5
	fi

	sleep $polling_interval

	# delete the timestamp file if it's older than 30 days, just in case
	# doesn't work sice the file's modification date is not the local time
	#find -maxdepth 1 -mtime +30 -name $filename -type f -exec rm -vf {} +
done

# just in case
pkill omxplayer
pkill sleep
echo "bye"
