#!/usr/bin/env bash

# Dependencies for ALT Linux:
# su -c "apt-get install ffmpeg xorg-xephyr zenity xdotool xfwm4 -y"
# on ALT<p9: mkdir -p ~/bin; ln -s $(which avconv) ~/bin/ffmpeg; ln -s $(which chromium) ~/bin/chromium-browser

# Надо PulseAudio >= 12, т.к. там виртуальные устройства не становятся дефолтными при module-switch-on-connect

DISPLAY_ORIG="$DISPLAY"
#dir0="$(pwd)"

echo_help(){
	echo " "
}

generate_random(){
	random="$(head -c 4 /dev/random |\
		base64 | sed "s/[^a-zA-Z']/ /g" |\
		tr ' ' '0' |\
		tr '[:lower:]' '[:upper:]')"
}

load_vars(){
	CONFDIR_GLOBAL="${CONFDIR_GLOBAL:-"/usr/share/wirec/"}"
	CONFDIR="${CONFDIR:-"${HOME}/.config/wirec/"}"
	# строка выше эквивалентна (спасибо klark@):
	#if [ -z "$CONFDIR" ]; then CONFDIR="${HOME}/.config/wirec/"; fi
	#RESOLUTION="${RESOLUTION:-1279x720}" #16:9
	# http://www.alandmoore.com/blog/2011/11/05/creating-a-kiosk-with-linux-and-x11-2011-edition/
	RESOLUTION="${RESOLUTION:-1024x720}" #4:3
	#if [ -z "$RESOLUTION" ]; then RESOLUTION="1024x720"; fi
	SLEEP="${SLEEP:-5}"
	#if [ -z "$SLEEP" ]; then SLEEP="15"; fi
	FRAMERATE="${FRAMERATE:-}"
	PROFILE="${PROFILE:-"./profile.tpl.sh"}"
	TIME="${TIME:-5}"
	PROG="${PROG:-"chromium-browser"}"
	URL="${URL:-"https://webinar.dumalogiya.ru/playback/presentation/2.0/playback.html?meetingId=55081f319104c357655e79c996116eaa0365005f-1526799746599"}"
	OUTPUT="${OUTPUT:-"output_$(date +%s).mp4"}"
	FULLSCREEN="${FULLSCREEN:-0}"
	H264PRESET="${H264PRESET:-medium}"
	CHROMIUM_SEPARATE_PROFILE="${CHROMIUM_SEPARATE_PROFILE:-1}"
	AUTO_SILENCE_DETECT="${AUTO_SILENCE_DETECT:-0}"
	SILENCE_CHECK_INTERVAL="${SILENCE_CHECK_INTERVAL:-20}"

	# надо переписать этот кусок кода нормально
	# сначала ищем файл с профилем в текущей директории, затем, если не нашли, в CONFDIR,
	# и это все выполняем только если переменная PROFILE_FILE не задана иными методами или заданный в ней файл не существует
	if [ -z "$PROFILE_FILE" ] || [ ! -f "$PROFILE_FILE" ]; then
		if [ -f "$(pwd)/${PROFILE}" ];
			then PROFILE_FILE="$(pwd)/${PROFILE}"
			elif [ -f "${CONFDIR}/${PROFILE}" ]; then PROFILE_FILE="${CONFDIR}/${PROFILE}"
			elif [ -f "${CONFDIR_GLOBAL}/${PROFILE}" ]; then PROFILE_FILE="${CONFDIR_GLOBAL}/${PROFILE}"
		fi
	fi
	echo "Используем профиль $PROFILE по адресу $PROFILE_FILE"
	# в профиле ТОЛЬКО ЗАДАВАТЬ ПЕРЕМЕННЫЕ, а все действия вынести в функции, которые не вызываются при source!
	# затем уже отсюда вызовем эти функции со стандартными именами

	##PR - вот так комментариями буду обозначать места с вызовом СТАНДАРТИЗИРОВАННЫХ функций и переменных, которые могут быть в профиле
	# директива для shellcheck, чтобы он сорсил скрипт-профиль
	# shellcheck source=./profile.tpl.sh
	if . "$PROFILE_FILE"
		then
			:
		else
			if [ ! "$GUI" = '1' ]; then
				echo "Не получилось загрузить профиль! Не можем продолжить работу."
				exit 1
			fi
	fi
}

print_vars(){
	for var_name in GUI XSERVER PROG TIME URL RESOLUTION SLEEP FRAMERATE CONFDIR PROFILE PROFILE_FILE OUTPUT BIGBLUEBUTTON_AUTO_LENGTH
	do
		declare -p "$var_name"
		#env | grep "$var_name"
	done
}

get_bigbluebutton_length(){
	# https://stackoverflow.com/a/42112614
	bbb_http="$(echo $URL | awk -F '://' '{print $1}')"
	bbb_domain="$(echo $URL | awk -F '://' '{print $2}' | awk -F '/playback' '{print $1}')"
	if [ "$(echo "$URL" | awk -F '/' '{print $6}')" = "2.3" ];
	then
		# new video player
		bbb_video_id="$(echo "$URL" | awk -F '/' '{print $7}')"
	else
		# old video player
		bbb_video_id="$(echo $URL | awk -F 'meetingId=' '{print $NF}')"
	fi
	bbb_webcams_video=
	for i in mp4 webm; do
		if curl \
			--output /dev/null \
			--silent \
			--head \
			--fail \
			"${bbb_http}://${bbb_domain}/presentation/${bbb_video_id}/video/webcams.${i}"
		then
			bbb_webcams_video="${bbb_http}://${bbb_domain}/presentation/${bbb_video_id}/video/webcams.${i}"
			break
		fi
	done
	if [ -z "$bbb_webcams_video" ]; then
		return 1
	fi
	local o
	o="$(ffprobe -i "${bbb_webcams_video}" -show_entries "format=duration : stream=r_frame_rate" -v quiet -of csv="p=0")"
	bbb_video_length="$(echo "$o" | tail -n 1)"
	bbb_video_rate="$(echo "$o" | head -n 1 | awk -F '/' '{print $1}')"
	# https://unix.stackexchange.com/a/167059
	bbb_rounded_length="$(LC_ALL=C "$(which printf)" "%.*f\n" 0 ${bbb_video_length})"
	echo "Rounded BigBlueButton recording length is ${bbb_rounded_length}"
	TIME="$bbb_rounded_length"
}

zenity_gui(){
	. "${CONFDIR_GLOBAL}/${PROFILE}"
	# https://askubuntu.com/a/853717
	# https://stackoverflow.com/a/21370397
	zenity_run_mode(){
		DISPLAY="$DISPLAY_ORIG" zenity \
			--list --radiolist \
			--title="Wirec" \
			--text="Выберите режим работы" \
			--width=450 \
			--separator=";;;" \
			--column="Да/Нет" --column="Режим работы" \
			FALSE "Конвертировать одну запись" TRUE "Конвертировать все записи из списка ссылок"
	}
	zenity_choose_list(){
		DISPLAY="$DISPLAY_ORIG" zenity --file-selection --title="Выберите файл со списком записей"
	}
	zenity_run_url(){
		DISPLAY="$DISPLAY_ORIG" zenity --forms \
			--title="Wirec" \
			--text="Настройки записи Wirec" \
			--separator=";;;" \
			--add-entry="Ссылка на запись:"
	}
	zenity_choose_output_dir(){
		DISPLAY="$DISPLAY_ORIG" zenity --file-selection --directory --title="Куда сохранить запись? Выберите папку."
	}
	zenity_mode="$(zenity_run_mode)"
	case "$zenity_mode" in
		"Конвертировать одну запись" )
			zenity_vars="$(zenity_run_url)"
		;;
		"Конвертировать все записи из списка ссылок" )
			recordings_list="$(zenity_choose_list)"
			OUTDIR="$(zenity_choose_output_dir)"
			SCRIPT="${tmp_dir}/script.sh"
			rm -fv "${SCRIPT:?}"
			while read -r line
			do
				# TODO: that's bad to fork the same script from this script
				# Refactoring is required
				# This is bad code!
				WIREC="$0"
				if [ ! -x "$WIREC" ]; then WIREC="wirec"; fi
				echo "$WIREC" -x --outdir "$OUTDIR" --bbb --fullscreen --sleep 10 --url "$line" --server Xfvb >> "$SCRIPT"
			done < <(grep -E '^http://|^https://' "$recordings_list")
			bash -x "$SCRIPT" | \
				zenity --progress \
					--title="Wirec" \
					--text="Идет конвертирование записей..." \
					--pulsate \
					--auto-kill
			exit
		;;
	esac
	URL="$(echo $zenity_vars | awk -F ";;;" '{print $1}')"
	get_bigbluebutton_length
	# вроде бы эта проверка толком не работает
	if [ -z "$TIME" ]
		then
			zenity --error --width=450 \
				--title="Ошибка Wirec" \
				--text="Не удалось автоматически определить длительность записи. Вероятно, вы ввели ссылку неправильно. Попробуйте запустить Wirec еще раз!"
			exit 1
		else
			OUTDIR="$(zenity_choose_output_dir)"
			OUTPUT="${OUTDIR}/${OUTPUT}"
	fi
}

trap_exit(){
	kill "$(cat ${tmp_dir}/prog.pid)"
	kill "$(cat ${tmp_dir}/X-wm.pid)"
	kill "$(cat ${tmp_dir}/X-server.pid)"
	pactl unload-module "$PA_NULL_SINK_MODULE_ID"
}
trap trap_exit EXIT

pa_bug_workaround(){
# See https://github.com/wwmm/pulseeffects/issues/99 why it's needed
	pulseaudio_version="$(pulseaudio --version | awk '{print $NF}' | awk -F '.' '{print $1}')"
	if LC_ALL=C pacmd list-modules | grep -q switch-on-connect && [ "$pulseaudio_version" -lt 12 ]; then
		PA_BUG_WORKAROUND='1'
		pactl unload-module module-switch-on-connect
	fi
}

pa_bug_workaround_cleanup(){
	if [ "$PA_BUG_WORKAROUND" = '1' ]; then
		pactl load-module module-switch-on-connect
	fi
}

# считывание параметров запуска взято из:
# http://git.altlinux.org/people/klark/packages/?p=alttest.git;a=blob;f=scripts/alttest-prev
# http://linuxcommand.org/lc3_wss0120.php
# все эти переменные можно задать как в параметрах запуска скрипта, так и переменными окружения
#eval set -- "$opts"
while [ -n "$1" ]
do
	case "$1" in
		-x|--debug ) set -x ;;
		# zenity GUI
		-g|--gui ) GUI='1'; BIGBLUEBUTTON_AUTO_LENGTH='1'; FULLSCREEN='1' ;;
		# Xfvb for headless, otherwise xephyr
		-s|--server ) shift; XSERVER="$1";;
		# web browser or other program (e.g. firefox, chromium)
		-p|--prog ) shift; PROG="$1";;
		# длительность записи экрана в секундах
		-t|--time ) shift; TIME="$1";;
		# URL to open in web browser or others arguements to pass to $PROG
		-u|--url ) shift; URL="$1";;
		# resolution of virtual screen, e.g: 1024x720
		-r|--resolution ) shift; RESOLUTION="$1";;
		# запускать ли браузер в полноэкранном режиме
		-fs|--fullscreen ) FULLSCREEN='1';;
		# Automatically stop recording when there is no sound
		# Useful when the durection of the recording is not known
		-asd|--auto-silence-detect ) AUTO_SILENCE_DETECT='1';;
		# Interval in seconds to check for silence (default: 20)
		# Note that Chromium removes the PulseAudio connection not immediately after sound stops
		-sci|--silence-check-interval ) shift; SILENCE_CHECK_INTERVAL="$1";;
		# время ожидания, пока страница прогрузится в браузере перед дальнейшими действиями
		-n|--sleep ) shift; SLEEP="$1";;
		# метод кодирования libx264 (medium, fast, ultrafast и др.)
		# позволяет регулировать нагрузку на процессор
		# https://trac.ffmpeg.org/wiki/Encode/H.264
		-pr|--h264preset ) shift; H264PRESET="$1";;
		# директория (папка), где хранить профили/конфиги
		-d|--confdir ) shift; CONFDIR="$1";;
		# имя профиля для подгрузки из текущей папки или папки с конфигами
		-p|--profile ) shift; PROFILE="$1";;
		# альтерантивно путь к файлу с профилем
		-pf|--profile-file ) shift; PROFILE_FILE="$1";;
		# путь (имя) к файлу с срохранением видеозаписи, примеры: video.mp4, папка/папка2/video.mkv
		-o|--output ) shift; OUTPUT="$1";;
		# directory, where to save output files, e.g. dir1/dir2/ or /home/user/dir2
		-o|--outdir ) shift; OUTDIR="$1";;
		# Automatically get BigBlueButton recording lenght
		-bbb|--bigbluebutton-auto-length|--bbb ) echo "BigBlueButton Auto Lenght detection ON"; BIGBLUEBUTTON_AUTO_LENGTH='1';;
		# Frames per second in the recording
		# Mut be after '-bbb' to allow overwriting automatically got framerate of the video
		-r|--framerate ) shift; FRAMERATE="$1";;
		# Don't create a new Chromium profile, use the default one or the one specified via --chromium-profile-dir
		# It may be useful e.g. when you want to use not default Chromium settings
		-dcsp|--disable-chromium-separate-profile ) CHROMIUM_SEPARATE_PROFILE='0' ;;
		# Location of Chromium's profile
		# Not more than one copy of Chromium can be ran with one profile
		-cpd|--chromium-profile-dir ) shift; CHROMIUM_PROFILE_DIR="$1" ;;
		-h|--help ) echo_help; exit;;
		-v|--vars ) load_vars; print_vars; exit;;
	esac
	shift
done

load_vars
pa_bug_workaround

if [ ! -d "$CONFDIR" ]; then mkdir -p "$CONFDIR"; fi
generate_random
tmp_dir="/tmp/wirec_${random}/"
rm -fvr ${tmp_dir:?}/*
mkdir -p "${tmp_dir}/chromium"

if [ "$GUI" = '1' ]; then zenity_gui; fi

## TODO: надо проверять, что этот дисплей свободен!!!
virt_display="$(( ( RANDOM % 850 )  + 150 ))"
echo "Random DISPLAY = $virt_display"
    
# env XSERVER=Xfvb ./wirec.sh
if [ "$XSERVER" = 'Xvfb' ]
	then
		echo "Работаем в режиме Xvfb"
		Xvfb ":${virt_display}" -screen 0 "${RESOLUTION}x24" &
	else
		echo "Работаем в режиме Xephyr"
		Xephyr -br -ac -noreset -screen "${RESOLUTION}" ":${virt_display}" &
fi 

# PID запускаемых процессов записываем в файл, а не в переменную, чтобы можно было их все завершить даже после завершения этого скрипта, считав хранящиеся на диске значения
echo $! >${tmp_dir}/X-server.pid

# export DISPLAY делать ПОСЛЕ запуска Xephyr, иначе сам Xephyr пытается запуститься на еще не существующем DISPLAY
export DISPLAY=":${virt_display}"

# Window manager is needed to make browser window take needed size
sleep 1
xfwm4 &
echo $! >${tmp_dir}/X-wm.pid

pulse_sink="wirec_${virt_display}"
PA_NULL_SINK_MODULE_ID="$(pactl load-module module-null-sink sink_name="${pulse_sink}" sink_properties=device.description="${pulse_sink}")"
echo "Loaded PulseAudio module ID ${PA_NULL_SINK_MODULE_ID}"
pactl set-sink-volume "${pulse_sink}" 100%
pactl set-source-volume "${pulse_sink}.monitor" 100%

# man pulseaudio | grep PULSE_
export PULSE_SINK="${pulse_sink}"
export PULSE_SOURCE="${pulse_sink}.monitor"

if [ "$BIGBLUEBUTTON_AUTO_LENGTH" = '1' ] && [ ! "$GUI" = '1' ]; then
	get_bigbluebutton_length
fi

# we need --user-data-dir to open Chromium in Xephyr instead even if it is already working on another X display
# https://bugs.chromium.org/p/chromium/issues/detail?id=15781#c36
if [ ! -z "$CHROMIUM_PROFILE_DIR" ]; then
	CHROMIUM_EXTRA_OPTIONS="${CHROMIUM_EXTRA_OPTIONS} --user-data-dir=${CHROMIUM_PROFILE_DIR}"
fi
if [ "$CHROMIUM_SEPARATE_PROFILE" = '1' ] && [ -z "$CHROMIUM_PROFILE_DIR" ]; then
	CHROMIUM_EXTRA_OPTIONS="${CHROMIUM_EXTRA_OPTIONS} --user-data-dir=${tmp_dir}/chromium/${DISPLAY}"
fi
"$PROG" --new-window ${CHROMIUM_EXTRA_OPTIONS} "$URL" &
echo $! >${tmp_dir}/prog.pid

sleep "$SLEEP"
# execute template_run function from template
wirec_template_run

if [ ! -z "$OUTDIR" ] && [ ! "$GUI" = '1' ]
	then
		if [ ! -d "${OUTDIR}" ]; then mkdir -p "${OUTDIR}"; fi
		if [ -w "${OUTDIR}" ]; then OUTPUT="${OUTDIR}/${OUTPUT}"; fi
	else
		: #OUTPUT="${OUTPUT}"
fi

ffmpeg_run(){
# https://trac.ffmpeg.org/wiki/Capture/Desktop#Linux
# `-t duration': Restrict the transcoded/captured video sequence to the duration specified in seconds. hh:mm:ss[.xxx] syntax is also supported.
# -strict experimental is for old avconv and ffmpeg versions
# -r but not -framerate because: "Constant framerate requested for the output stream #0:0, but no information about the input framerate is available. Falling back to a default value of 25fps. Use the -r option if you want a different framerate."
	local ffmpeg_framerate
	if [ -z "$FRAMERATE" ] && [ -n "$bbb_video_rate" ]
	then
		ffmpeg_framerate="$bbb_video_rate"
	else
		# BigBlueButton default
		ffmpeg_framerate=24
	fi
	ffmpeg \
		-nostdin \
		-video_size "$RESOLUTION" \
		-r "$ffmpeg_framerate" \
		-f x11grab \
		-i :"$virt_display" \
		-f pulse -ac 2 -i default \
		-c:v libx264 \
		-strict experimental \
		-crf 20 \
		-preset "$H264PRESET" \
		-t "$TIME" \
		"${OUTPUT}"
}

if [ "$AUTO_SILENCE_DETECT" = '0' ]; then
	ffmpeg_run
fi

if [ "$AUTO_SILENCE_DETECT" = '1' ]; then
	ffmpeg_run &
	FFMPEG_PID="$!"
	#echo "${FFMPEG_PID}" > "${tmp_dir}/ffmpeg.pid"
	while true
	do
		sleep "$SILENCE_CHECK_INTERVAL"
		if ! LC_ALL=C pacmd list-sink-inputs | grep 'window.x11.display' | grep -q ":${virt_display}"; then
			echo "AUTO_SILENCE_DETECT: there is no sound"
			kill "${FFMPEG_PID}"
			break
		fi
	done
fi

pa_bug_workaround_cleanup
echo "Завершено"
