#!/bin/bash
#
# timeshift.sh - bash script for TV timeshift
#
# Copyright (C) 2006 Antonio Ruiz <yrmateo@yahoo.com>
#
#
#This program is free software: you can redistribute it and/or modify it under
#the terms of the GNU General Public License as published by the Free Software
#Foundation, either version 3 of the License, or (at your option) any later
#version. This program is distributed in the hope that it will be useful, but
#WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
#details. You should have received a copy of the GNU General Public License along
#with this program. If not, see the file /usr/share/common-licenses/GPL or
#<http://www.gnu.org/copyleft/gpl.txt>.


#******************************************************************************************************************************
#START user configurable variables ( defaults without commandline parameters )
#******************************************************************************************************************************
# ... for all players set repeat all playlist, for vlc set skins 2
# REPRO=none is no player, m3u playlist and only player script will be created in $RUTA, press Ctrl-C inside "terminal" or configure LIVETV=preview for exit
# All players have multiuser support, vlc and none too single user multi-instances support ( timeshift.sh copies with different configuration )
# See ./timeshift.sh --help for command line parameters

# player executable ( none, kaffeine, kplayer or vlc... may be others players that support m3u playlists as argument )
REPRO=vlc
# preview window mode ( null - no preview window, pv - no closes transcode [uses only xv], preview - closes transcode [xv, sdl or gtk])
LIVETV=pv
# directory for buffers ( directory must exist, i.e.: /mnt/ramdisk0, $HOME/buffers_video0 )
RUTA=$HOME
# optional directory for clients for client-server operation ( i.e.: /mnt/channel01, $HOME/timeshift, T:\LINUX_TV ), if any then playlists will has permisive permissions
SERPA=
# prefix name for buffers (  for vlc and none m3u playlist will be create in $RUTA/playlist_$FNAME.m3u... FNAME must change for multiple instances with same directory )
FNAME=buffer
# number of file buffers
BUF=6
# minutes for each buffer ( total time = BUF*MIN )
MIN=20
# delete buffers on exit? ( 0 = NO, 1 = YES, 2 = ask in terminal... if 0 you can continue from last session )
BORRA=0
# launch xawtv (if REPRO=none ttv for tty instead) for tv-card initialization ( 0 = NO, 1 = wait until user closes xawtv, 2 or more = launch and kill xawtv after n seconds )
TVINIT=1
# video capture device
DISPV=/dev/video0
# video capture resolution ( i.e.: 480x576, 320x240, ... max. PAL = 768x576, max. NTSC = 720x480 )
RESOL=320x240
# video bitrate ( kbits/s )
BIT=1800
# video framerate ( PAL = 25, NTSC = 30 )
FRAME=25
# sound capture device ( none = no sound )
DISPA=/dev/dsp
# audio bitrate ( kbits/s )
BAT=192
# audio mode ( 0 = Joint Stereo, 1 = Full stereo, 2 = Mono )
AUD=0
# audio capture sampling frequency ( khz: 32000, 44100, 48000,... )
MUEST=44100
# bits per channel ( must be supported by soundcard )
BITA=16


#******************************************************************************************************************************
#END user configurable variables
#******************************************************************************************************************************



# command line parameters

 	# function: help output

	function usage()
	{
		echo
		echo -e 'Usage: timeshift.sh [OPTION1 value] [OPTION2 value] ...\n\nbash script for doing live TV timeshift (pause, fast forward and rewind while watching television) with kaffeine, vlc or kplayer. timeshift.sh do not need terminal window, stops when player closes.\n\nHelp:\n\nFor all players set repeat all playlist, for vlc set skins 2. Use --TVINIT 1 or 2 for tv-card initialization. --REPRO none is no player: m3u playlist and only player script will be created in RUTA, press Ctrl-C inside "terminal" or configure --LIVETV preview for exit. All players have multiuser support, vlc and none too single user multi-instances support ( timeshift.sh copies with different configuration ).\n\nOptions:\n\nOPTION [DEFAULT_VALUE] 	# COMMENTS\n\n-x or --REPRO ['"$REPRO"']     	# player executable (none, kaffeine, kplayer or\n				vlc... may be others players that support m3u\n				playlists as argument)\n-l or --LIVETV ['"$LIVETV"']		# preview window mode (null = no preview window,\n				pv = no closes transcode & xv, \n 				preview=xv/sdl/gtk = closes transcode)\n-d or --RUTA ['"$RUTA"']	# directory for buffers (directory must exist,\n				i.e.: /mnt/ramdisk0, $HOME/buffers_video0)\n-f or --FNAME ['"$FNAME"']		# prefix name for buffers (for vlc and none m3u\n				playlist will be create RUTA/playlist_FNAME.m3u.\n				FNAME must change for multiple instances with\n				same directory)\n-n or --BUF ['"$BUF"']			# number of file buffers\n-m or --MIN ['"$MIN"']		# minutes for each buffer (total time = BUF*MIN)\n-e or --BORRA ['"$BORRA"']		# delete buffers on exit? (0 = NO, 1 = YES, 2 =\n				ask in terminal... if 0 you can continue from\n				last session)\n-i or --TVINIT ['"$TVINIT"']		# launch xawtv (if REPRO=none then ttv for tty)\n                                for tv-card initialization ( 0 = NO,\n			        1 = wait until user closes xawtv, 2 or more\n				= launch and kill xawtv after n seconds )\n-v or --DISPV ['"$DISPV"']	# video capture device\n-r or --RESOL ['"$RESOL"']		# video capture resolution (i.e.: 480x576,\n				320x240, ... max. PAL = 768x576, max. NTSC =\n				720x480)\n-b or --BIT ['"$BIT"']		# video bitrate (kbits/s)\n-t or --FRAME ['"$FRAME"']		# video framerate (PAL = 25, NTSC = 30)\n-s or --DISPA ['"$DISPA"']	# sound capture device (none = no sound)\n-w or --BAT ['"$BAT"']		# audio bitrate (kbits/s)\n-a or --AUD ['"$AUD"']			# audio mode  (0 = Joint Stereo, 1 = Full\n				stereo, 2 = Mono)\n-y or --MUEST ['"$MUEST"']		# audio capture sampling frequency (khz: 32000,\n				44100, 48000,...)\n-q or --BITA ['"$BITA"']		# bits per channel (must be supported by\n				soundcard)\n-h or --help			# display this help message and exit'
		exit $SERROR
	}

	while (( "$#" )); do
		case $1 in
			-x | --REPRO ) shift; REPRO=$1;;
			-l | --LIVETV ) shift; LIVETV=$1;;
			-d | --RUTA ) shift; RUTA=$1;;
			-f | --FNAME ) shift; FNAME=$1;;
			-n | --BUF ) shift; BUF="10#"$1; BUF=$((1*BUF));;
			-m | --MIN ) shift; MIN="10#"$1; MIN=$((1*MIN));;
			-e | --BORRA ) shift; BORRA="10#"$1; BORRA=$((1*BORRA));;
			-i | --TVINIT ) shift; TVINIT="10#"$1; TVINIT=$((1*TVINIT));;
			-v | --DISPV ) shift; DISPV=$1;;
			-r | --RESOL ) shift; RESOL=$1;;
			-b | --BIT ) shift; BIT="10#"$1; BIT=$((1*BIT));;
			-t | --FRAME ) shift; FRAME="10#"$1; FRAME=$((1*FRAME));;
			-s | --DISPA ) shift; DISPA=$1;;
			-w | --BAT ) shift; BAT="10#"$1; BAT=$((1*BAT));;
			-a | --AUD ) shift; AUD="10#"$1; AUD=$((1*AUD));;
			-y | --MUEST ) shift; MUEST="10#"$1; MUEST=$((1*MUEST));;
			-q | --BITA ) shift; BITA="10#"$1; BITA=$((1*BITA));;
			-h | --help ) SERROR=0; usage;;
			* ) echo "*** Wrong option $1 ***"; SERROR=1; usage;;
		esac
		shift
	done

	if [ $BUF -lt 2 ] || [ $MIN -lt 1 ] || [ $BORRA -lt 0 ] || [ $BORRA -gt 2 ] || [ $TVINIT -lt 0 ] || [ $AUD -lt 0 ] || [ $AUD -gt 2 ] || [ $BIT -lt 0 ] || [ $FRAME -lt 0 ] || [ $BAT -lt 0 ] || [ $MUEST -lt 0 ] || [ $BITA -lt 0 ]; then # check values
		echo "*** Wrong value ***"
		SERROR=1
		usage
	fi

# general information output

echo "timeshift.sh version 6.7 Copyright (C) 2006 Antonio Ruiz"
echo "This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. For more details: http://www.gnu.org/copyleft/gpl.txt"
echo
echo "*** Configure TVINT = 1 or 2, OR, first of all, launch any television application to select the channel (or video input) and set the values of brightness, contrast and color ***"
echo
echo "**************************************************************************"
echo "******* timeshift without terminal, with vlc, kaffeine or kplayer ********"
echo "*************** timeshift stops when player closes  **********************"
echo "**************************************************************************"
echo
echo "*** Close all television applications before continuing ***"
echo

# TV timeshift function version 4

 	# function: check player and transcode, manage exit

	function salir5()
	{
		if [ $PIDTRANS -ne 0 ]; then
			ps $PIDTRANS 1>/dev/null # check transcode
		fi
		if [ $? -ne 0 ]; then
			echo
			echo "*** transcode finished ***"
			echo
			if [ $CEXIT -eq 0 ] && [ $LIVETV != "preview" ]; then
				SERROR=1
			elif [ $LIVETV == "preview" ]; then
				PEXIT=1
			fi
		fi
		if [ $REPRO != "none" ] && [ $PIDPLAY -ne 0 ]; then
			ps $PIDPLAY 1>/dev/null # check pidof player
		fi
		if  [ $? -ne 0 ] || [ $CEXIT -eq 1 ] || [ $PEXIT -eq 1 ] || [ $PIDPLAY -eq 0 ]; then # timeshift.sh exit
			if  [ $CEXIT -ne 1 ]; then
				trap 2
			fi
			if [ $REPRO != "none" ] && [ $CEXIT -eq 1 ]; then
				kill $PIDPLAY 2>/dev/null
				ps $PIDPLAY 1>/dev/null
				while [ $? -eq 0 ]; do #wait kill
					sleep 0.50
					echo
					echo "kill "$REPRO
					ps $PIDPLAY 1>/dev/null
				done
			elif [ $REPRO != "none" ] && [ $PEXIT -ne 1 ]; then
				kill $PIDTRANS 2>/dev/null
				ps $PIDTRANS 1>/dev/null
				while [ $? -eq 0 ]; do #wait kill
					sleep 0.50
					echo
					echo "kill transcode"
					ps $PIDTRANS 1>/dev/null
				done
			fi
			if [ $BORRA -eq 2 ]; then # delete buffers, ask if option is configured
				echo
				echo "? Press ENTER to leave the "$BUF" file buffers "$RUTA"/"$FNAME"*.avi or input any character to delete them ..."
				read TECLA
			fi

			if [ "$TECLA" != "" ] || [ $BORRA -eq 1 ] && [ $SERROR -eq 0 ]; then
				rm $RUTA/play_$FNAME.sh # too delete playlist
				rm $RUTA/playlist_$FNAME.m3u 2>/dev/null
				X=0
				rm $RUTA/"$FNAME"a.avi
				if [ -f $RUTA/"$FNAME"a.avi-$ABUF ]; then # may be there is one more
					rm $RUTA/"$FNAME"a.avi-$ABUF
				fi
				echo
				echo "*** Removing buffers ***"
				while [ $X -lt $BUF ]; do
					if [ -h $RUTA/$FNAME$X.avi ] || [ -f $RUTA/$FNAME$X.avi ]; then
						rm $RUTA/$FNAME$X.avi
						echo
						echo "*** Removing buffers ***"
					fi
					X=$((X+1))
				done
			else
				echo
				echo "*** "$RUTA"/"$FNAME"*.avi have NOT been deleted ***"
			fi
			echo
			echo "*** Finished ..."
			exit 0

		elif [ $SERROR -ne 0 ]; then # an error has occurred
			kill $PIDTRANS
			ps $PIDTRANS 1>/dev/null
			while [ $? -eq 0 ]; do #wait kill
				sleep 0.50
				echo
				echo "kill transcode"
				ps $PIDTRANS 1>/dev/null
			done
			kill $PIDPLAY
			ps $PIDPLAY 1>/dev/null
			while [ $? -eq 0 ]; do #wait kill
				sleep 0.50
				echo
				echo "kill "$REPRO
				ps $PIDPLAY 1>/dev/null
			done
			echo
			echo "An unknown error has occurred in timeshift.sh"
			exit $SERROR
		fi
	}

	# function: command transcode

	function zsplit()
	{
		transcode -x v4l2=resync_margin=1:resync_interval=250$SNULL -i $DISPV $PDISPA -y ffmpeg$SNULL -F mpeg4 -g $RESOL -f $FRAME -w $BIT -e $MUEST,$BITA,2 -b $BAT,0,2,$AUD -J smartyuv=highq=1:diffmode=2:cubic=1:Blend=1:chromath-res=4:threshold=8:doChroma=1,$LIVETV --avi_limit $((((MIN*60)*(BIT*1000+BAT*1024))/8388608)) -o $RUTA/"$FNAME"a.avi&
		PIDTRANS=$!
	}

	# function: Ctrl-C handler

	function ctrc()
	{
		trap 2
		echo
		echo "*** Ctrl-C >> timeshift.sh stopping ***"
		echo
		kill $PIDTRANS
		ps $PIDTRANS 1>/dev/null
		while [ $? -eq 0 ]; do #wait kill
			sleep 0.50
			echo
			echo "kill transcode"
			ps $PIDTRANS 1>/dev/null
		done
		CEXIT=1
		salir5
	}

	trap "ctrc" 2 # Ctrl-C handler
	LIM=1000
	SERROR=0
	CEXIT=0
	PEXIT=0
	PIDPLAY=0
	PIDTRANS=0
	N=0

	if [ $DISPA == "none" ]; then # check no sound option
		SNULL=",null"
		BAT=0
		PDISPA=""
	else 
		PDISPA="-p "$DISPA
		SNULL=""
	fi

	if [ $TVINIT -eq 1 ]; then
		echo "*** timeshift.sh waiting xawtv or ttv exit ***"
		echo
		if [ $REPRO == "none" ]; then
			ttv -c $DISPV
		else
			xawtv -nodga -c $DISPV -C $DISPA
		fi
	elif [ $TVINIT -ne 0 ]; then
		echo "*** timeshift.sh waiting "$TVINIT" seconds for tv-card initialization ***"
		echo
		if [ $REPRO == "none" ]; then
			ttv -c $DISPV&
		else
			xawtv -nodga -c $DISPV -C $DISPA&
		fi
		PIDXAWTV=$!
		sleep $TVINIT
		kill $PIDXAWTV 2>/dev/null
		ps $PIDXAWTV 1>/dev/null
		while [ $? -eq 0 ]; do #wait kill
			sleep 0.50
			echo
			echo "kill xawtv"
			ps $PIDXAWTV 1>/dev/null
		done
	fi

	# new N value searching old buffers

	if [ -f $RUTA/"$FNAME"a.avi ]; then
		X=$(stat -c%s $RUTA/"$FNAME"a.avi 2>/dev/null)
		sleep 1
		if [ $X -eq  $(stat -c%s $RUTA/"$FNAME"a.avi 2>/dev/null) ]; then # check timeshift.sh running with same buffer filename
			if [ -f $RUTA/"$FNAME"0.avi ]; then
				while [ -f $RUTA/$FNAME$((N+1)).avi ] && [ $RUTA/$FNAME$((N+1)).avi -nt $RUTA/$FNAME$N.avi ] && [ $((N+1)) -lt $BUF ]; do # search oldest buffer
					N=$((N+1))
				done
				mv -f $RUTA/"$FNAME"a.avi $RUTA/$FNAME$N.avi # store last buffer
				N=$((N+1))
				if [ $N -ge $BUF ]; then
					N=0
				fi
			fi
		else # safe exit condition
			echo "*** timeshift.sh is already running with same buffer filename ***"
			REPRO="none"
			PEXIT=1
			BORRA=0
			salir5
		fi
	fi

	#first run transcode

	zsplit
	sleep 10 # wait to avoid a player jump buffer error

	echo
	echo "*** Buffers will use aproximately $((((BUF*MIN*60)*(BIT*1000+BAT*1024))/8388608)) MBytes of hard disk ***"
	echo

	#build playlist

	PLA=""
	if [ $REPRO == "kplayer" ]; then

		# kplayer arguments
		X=$N
		ARG=""
		while [ $X -lt $BUF ]; do
			ARG=""$ARG""$RUTA"/"$FNAME""$X".avi "
			X=$((X+1))
		done
		X=0
		while [ $X -lt $N ]; do
			ARG=""$ARG""$RUTA"/"$FNAME""$X".avi "
			X=$((X+1))
		done
	else
		ARG='#EXTM3U' # vlc, kaffeine or others: build m3u playlist
		X=$N
		while [ $X -lt $BUF ]; do
			ARG="$ARG"'\n'"$FNAME"''"$X"'.avi'
			X=$((X+1))
		done
		X=0
		while [ $X -lt $N ]; do
			ARG="$ARG"'\n'"$FNAME"''"$X"'.avi'
			X=$((X+1))
		done
		echo -e $ARG > $RUTA/playlist_$FNAME.m3u
		chmod 640 $RUTA/playlist_$FNAME.m3u
	fi

	# delete old $RUTA/"$FNAME"a.avi-* buffers at the beginning

	RBUF=-1
	while [ $? -eq 0 ]; do
		while [ $RBUF -lt $LIM ]; do
			RBUF=$((RBUF+1))
			if [ $RBUF -lt 10 ]; then # split number, character value
				ARBUF="00"$RBUF""
			elif [ $RBUF -lt 100 ]; then
				ARBUF="0"$RBUF""
			else
				ARBUF="$RBUF"
			fi
			if [ -h $RUTA/$FNAME$RBUF.avi ]; then
				rm $RUTA/$FNAME$RBUF.avi
			fi
			if [ -f $RUTA/"$FNAME"a.avi-$ARBUF ]; then
				rm $RUTA/"$FNAME"a.avi-$ARBUF
				echo
				echo "*** Removing old buffers ***"
			fi
		done
	LIM=$((LIM+1000))
	ls $RUTA/"$FNAME"a.avi-* 2>/dev/null
	done

	# launch player

	ln -f -s $RUTA/"$FNAME"a.avi $RUTA/$FNAME$N.avi # symlink first buffer at start

	if [ $REPRO == "kplayer" ]; then
		TMPSCR="kplayer $ARG"
	elif [ $REPRO == "none" ]; then
		TMPSCR="vlc $RUTA/playlist_$FNAME.m3u"
	else
		TMPSCR="$REPRO $RUTA/playlist_$FNAME.m3u"
	fi
	echo -e '#!/bin/sh\n# temp script, play buffers by timeshift.sh\n'"$TMPSCR"' 2>/dev/null\necho "*** '"$RUTA"'/play_'"$FNAME"'.sh ***"\necho\n' > $RUTA/play_$FNAME.sh
	chmod 750 $RUTA/play_$FNAME.sh
	if [ $REPRO == "vlc" ]; then
		vlc $RUTA/playlist_$FNAME.m3u 2>/dev/null &
		PIDPLAY=$! # pidof vlc
		sleep 5
		X=$(ps k -start_time exo pid,comm | grep ' vlc') # check pidof vlc
		X=${X%%vlc*}
		if [ ! $X ]; then
			PIDPLAY=0
			salir5
		fi

	elif [ $REPRO != "none" ]; then
		$TMPSCR 2>/dev/null &
		sleep 5
		PIDPLAY=$(ps k -start_time exo pid,comm,tty | grep ' '"$REPRO"' ')
		PIDPLAY=${PIDPLAY%%$REPRO*} # pidof player ( $! no works for K-players )
		if [ ! $PIDPLAY ]; then
			PIDPLAY=0
			salir5
		fi
	else
		PIDPLAY=$PIDTRANS
	fi

	# handle buffers, repeat until player closes

	TBUF=0
	ABUF="000"
	CUC=0
	ACU=0
	HORAA=$(date +%H)
	MINUTA=$(date +%M)
	HORAA="10#"$HORAA"" #avoid octal value
	MINUTA="10#"$MINUTA""
	HORAA=$((HORAA*1))
	MINUTA=$((MINUTA*1))
	TOMA=$((HORAA*60+MINUTA))

	while [ $? -eq 0 ]; do

		sleep  5
		salir5 #check player

		if [ -f $RUTA/"$FNAME"a.avi-$ABUF ]; then # check transcode split
			mv -f $RUTA/"$FNAME"a.avi-$ABUF $RUTA/$FNAME$N.avi
			if [ $N -eq $((BUF-1)) ]; then # restart buffer value when last buffer reached
				N=-1
			fi
			TBUF=$((TBUF+1))
			if [ $TBUF -lt 10 ]; then # split number, character value
				ABUF="00"$TBUF""
			elif [ $TBUF -lt 100 ]; then
				ABUF="0"$TBUF""
			else
				ABUF="$TBUF"
			fi
			N=$((N+1))
			ln -f -s $RUTA/"$FNAME"a.avi $RUTA/$FNAME$N.avi
		fi

		HORAB=$(date +%H) # show internal information
		MINUTB=$(date +%M)
		HORAB="10#"$HORAB"" #avoid octal value
		MINUTB="10#"$MINUTB""
		HORAB=$((HORAB*1))
		MINUTB=$((MINUTB*1))
		SEG=$(date +%S)
		TIME=$((CUC+HORAB*60+MINUTB-TOMA))
		if [ $TIME -lt 0 ]; then
			CUC=1440
			TIME=$((TIME+CUC))
		elif [ $TIME -gt 1440 ]; then
			CUC=0
			ACU=$((ACU+1))
		fi
		echo
		echo "timeshift.sh->[Buffer "$N" # $((TIME+ACU*1440)):"$SEG"]"

	done

	SERROR=1

	salir5





#*******************************************************************************
#
# timeshift.sh version 6.7 for CentOS-Red Hat (kernel 2.6.18,
# gnome 2.16.0, transcode 1.1.0, vlc 0.9.9a, kplayer, kaffeine 0.8.7), also
# should work in other linux distributions.
#
# script for doing live TV timeshift (pause, fast forward and rewind while
#watching television) with kaffeine, vlc or kplayer. All players have multiuser
#support, vlc and none too single user multi-instances support ( timeshift.sh
#copies with different configuration ).
#
# timeshift.sh do not need terminal window, stops when player closes
#
#
# Latest version release notes:
#
# 6.7: -supports kaffeine KDE4 and any others kaffeines
#
# 6.6: -fixed echoed buffer size and split size in command transcode
#
#      -added sound device=none for no sound
#
#      -added command line parameters
#
#      -added tv card initialization option
#
#         -live TV preview now works with recent transcode
#         -changed #!/bin/bash , now it is compatible with squeeze
#         -ttv as option for tv-init
#
# Website:
#
#	http://timeshift.110mb.com
#	http://www.geocities.ws/yrmateo/
#
#*******************************************************************************

