#!/bin/bash

## Dieses Script ist ein kleiner Offline-Helfer zum Vorbereiten der Bilder fürs Blog.
## Vom Bild wird eine angepasste Version für das Blog erzeugt, auf Wunsch auch ein großes.
## Zeitlich einsortiert wird es mit Hilfe der Exif-Daten. Ein Link zum Einfügen ins Blog
## wird in der Zwischenablage abgelegt. 
## Author swg, janeemussja.de, November 2013
## letzte Änderungen
##  - Februar 2024, Version 1.08, logger function replaced with bash snipped
##  - März 2023, Version 1.07, which_is Funktion repariert
##  - Juni 2021, Version 1.06, Progress bar eingebaut
##  - Januar 2021, Version 1.05, Test auf fehlende Software geändert.
##  - Oktober 2019, Version 1.03, Das extrahieren des Vorschaubildes aus Exif war in Version 1.02 verloren gegangen -> ist wieder drin
##  - Oktober 2018, Version 1.02, Exifdaten werden erhalten und zusätzlich OriginalFileName gesetzt
##  - November 2017, Version 1.01, Vorschaubild wird aus Exif geholt, wenn vorhanden
## MODIFY AND SHARE AS YOU LIKE!

# Log levels for messages
LLMute=0
LLMdtry=1  # mandatory: messages that are always written to the log (except mute)
LLError=2
LLWarning=3
LLInfo=4
LLDebug=5
## Actual log level of the logger used
Log_Level=$LLInfo

## base name of the logging script.
Log_Name="${0##*/}";

## Write log of the script.
## $1 Loglevel to log the message with.
## $2 message to log.
function logger() {
	local _logging_level=$1;
	local _log_message=$2;
	if [[ $Log_Level -lt $_logging_level ]]; then
		return;
	fi
	local _now=$($DATE +%Y%m%d_%H:%M:%S);
	if [[ $_logging_level -eq $LLInfo ]]; then
		_type=" Info:";
	elif [[ $_logging_level -eq $LLError ]]; then
		_type=" ERROR!";
	elif [[ $_logging_level -eq $LLWarning ]]; then
		_type=" Warning:";
	elif [[ $_logging_level -eq $LLDebug ]]; then
		_type=" DEBUG:";
	else
		_type=":";
	fi
	printf "$Log_Name ${_now}${_type} ${_log_message}\n" >> $Log_File;
	if [[ $Log_Level -eq $LLDebug ]]; then
		printf "${_now}${_type} ${_log_message}\n";
	fi
} 

function CleanUp()
{
if [ -f "$local_blog_path/preview.jpg" ]; then
   rm "$local_blog_path/preview.jpg";
fi
unset FILE;
unset FILELIST;
unset EXIFORIGINALLIST;
unset EXIFDATETIMELIST;
kill $ZenityPID;
exec 3>&-
exec 4>&-
}

## Returns the binaries full path if existent.
function which_is() {
	declare -n _ret_val=$1;  # declare RETVAL a reference of $1
	local _executable=$2;
	local _with_full_path=$(which ${_executable} 2>/dev/null);
	if [ -x "$_with_full_path" ]; then
		_ret_val="$_with_full_path";
		return 0;
	else
		_ret_val="$_executable not found!";
		return 1;
	fi
}

# Was wir am Anfang brauchen
PATH=$PATH:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
SCRIPTNAME=$(echo $0 | sed -e 's/.*\///g');
VERSION="1.08";
ME=$(whoami);
TMPPFAD=/tmp;
MYHOME=$(echo ~);
USTRICH="_";

## Prüfen der notwendigen Software auf Existenz und Ausführbarkeit
# date
which_is DATE 'date';
if [ $? != 0 ]; then echo "$DATE not found, aborting"; exit 1; fi

## Logfile in nextcloud to have a visible result.
Run_Date=$($DATE +%Y_%m_%d)
Log_File="$MYHOME/${Run_Date}_${Log_Name}.log";
touch $Log_File;
logger $LLInfo "Path: $PATH";

# zenity
which_is MSGDIALOG 'zenity';
if [ $? != 0 ]; then logger $LLError "$MSGDIALOG not found, aborting"; exit 1; fi

# imagemagick
which_is CONVERT 'convert';
if [ $? != 0 ]; then logger $LLError "$CONVERT not found, aborting"; exit 1; fi

# imagemagick
which_is IMGDISPLAY 'display';
if [ $? != 0 ]; then logger $LLError "$IMGDISPLAY not found, aborting"; exit 1; fi

# exiftool
which_is EXIFTOOL 'exiftool';
if [ $? != 0 ]; then logger $LLError "$EXIFTOOL not found, aborting"; exit 1; fi

# xclip
which_is XCLIP 'xclip';
if [ $? != 0 ]; then logger $LLError "$XCLIP not found, aborting"; exit 1; fi

# printf
which_is PRINTF 'printf';
if [ $? != 0 ]; then logger $LLError "$PRINTF not found, aborting"; exit 1; fi

# sed
which_is SED 'sed';
if [ $? != 0 ]; then logger $LLError "$SED not found, aborting"; exit 1; fi

exec 3> >($MSGDIALOG --notification --listen)
# The PID of zenity is needed to kill the process at the end of the script
ZenityPID=$!;
echo "tooltip:Script ${SCRIPTNAME}, Version ${VERSION}" >&3
logger $LLDebug "Script ${SCRIPTNAME}, Version ${VERSION}.";
if [ ! $# -gt 0 ]; then
	echo "img-helpr, Version: $VERSION, June 2021"
	echo "Syntax:  img-helpr.sh IMG_FILE1 [IMG_FILE2 ...]";
	echo "MODIFY AND SHARE AS YOU LIKE!"
	exit;
fi;

## Nutzerdaten
USERDATAFILE=$MYHOME/.img_helpr;

## If userdatafile does not exist create one with default values.
if [[ -f $USERDATAFILE ]]; then
	source $USERDATAFILE;
else
	printf "#!/bin/bash\nlocal_blog_path='%s/Blog/';	# Wohin sollen die Bilder lokal gespeichert werden\nw_blog_img=450; # Wieviele Pixel breit soll das Blogbild sein\nw_img=1200; # Wieviele Pixel breit soll das verlinkte Bild maximal sein\nh_img=900;  # Wieviele Pixel hoch soll das verlinkte Bild maximal sein\nblog_address='http://www.example.com/wp-content'; # Webadresse des Blogs\nblog_path='./wp-content';	# wo im Blog liegen die Bilder\nSSH_LOGIN=''	# ssh-Login beim Hoster\nSSH_PASSWD=''" $MYHOME > $USERDATAFILE;
	chmod 750 $USERDATAFILE;
	source $USERDATAFILE;
fi

if [ ! -d "$local_blog_path" ]; then
	logger $LLError "Lokaler Speicherpfad ($local_blog_path) für die Bilder existiert nicht!";
	exit 1;
fi

## Aus den übergebenen Argumenten die Dateien rausklauben
## Leerzeichen behandeln:
logger $LLDebug "Taking care of spaces in file names.";
ARGS="$*";
INP_C=0;
INP_TEMP="";
for INP in $ARGS
do
	logger $LLDebug "INP ist: $INP";
	INP_TEMP="$INP_TEMP$INP";
	if [ -f "$INP_TEMP" ]; then
		FILE[$INP_C]="$INP_TEMP"; 
		logger $LLDebug "Content of File[$INP_C]: ${FILE[$INP_C]}";
		((INP_C=INP_C+1));
		INP_TEMP="";
	else
		INP_TEMP="$INP_TEMP ";
	fi
done

## Schleife für Bildumwandlung: Pfad erzeugen
declare -A FILELIST;	# Associative Array for BASH: http://www.artificialworlds.net/blog/2012/10/17/bash-associative-array-examples/
declare -A EXIFORIGINALLIST;  # nimmt die Original-Dateinamen auf
declare -A EXIFDATETIMELIST;  # nimmt das Aufnahmedatum + Uhrzeit des Originals auf 
MAX_INP_C=$INP_C;
for((INP_C=0;INP_C<${#FILE[*]};INP_C++));
do
	logger $LLDebug "Bearbeite: ${FILE[$INP_C]}";
	logger $LLDebug "Preview: $local_blog_path/preview.jpg";
	$EXIFTOOL -b -W  "$local_blog_path/preview.jpg" -preview:PreviewImage "${FILE[$INP_C]}";
	if [ -f "$local_blog_path/preview.jpg" ]; then  # Wenn exifvorschau existierte: Anzeigen
		origOrient=$($EXIFTOOL -s -s -s -n -Orientation "${FILE[$INP_C]}");
		$EXIFTOOL -n -Orientation=$origOrient "$local_blog_path/preview.jpg";
		$IMGDISPLAY -auto-orient -resize 400x400 "$local_blog_path/preview.jpg" &
	else  # Es existierte keine exif-Vorschau
		$IMGDISPLAY -auto-orient -resize 400x400 "${FILE[$INP_C]}" &
	fi
	IMGDISPLAYPID=$!;

	## Datum aus den Exifdaten des Bildes ermitteln, um die Bilder in der passenden Struktur zu speichern  
	OUT_PATH_Y=$($EXIFTOOL -DateTimeOriginal -d %Y -S "${FILE[$INP_C]}" | $SED -e 's/DateTimeOriginal:\ //');
	OUT_PATH_M=$($EXIFTOOL -DateTimeOriginal -d %m -S "${FILE[$INP_C]}" | $SED -e 's/DateTimeOriginal:\ //');
	OUT_DAY=$($EXIFTOOL -DateTimeOriginal -d %d -S "${FILE[$INP_C]}" | $SED -e 's/DateTimeOriginal:\ //');
	DATETIMEORIGINAL=$($EXIFTOOL -DateTimeOriginal -S "${FILE[$INP_C]}" | $SED -e 's/DateTimeOriginal:\ //');
	## Datum alternativ von heute nehmen
	if [[ -z "${OUT_PATH_Y##?([-+])+([0-9])}" ]] || [[ -z "${OUT_PATH_M##?([-+])+([0-9])}" ]] || [[ -z "${OUT_DAY##?([-+])+([0-9])}" ]]; then
		logger $LLWarning "Kein Datum in Exif gefunden (Jahr=$OUT_PATH_Y, Monat=$OUT_PATH_M, Tag=$OUT_DAY)";
		OUT_PATH_Y=$(date +%Y);
		OUT_PATH_M=$(date +%m);
		OUT_DAY=$(date +%d);
	fi

	## Lokalen Speicherpfad erzeugen
	if [ ! -d "$local_blog_path/${OUT_PATH_Y}/${OUT_PATH_M}" ]; then
		echo "tooltip: erzeuge lokalen Speicherpfad $local_blog_path/${OUT_PATH_Y}/${OUT_PATH_M}."  >&3
		mkdir "$local_blog_path/${OUT_PATH_Y}/${OUT_PATH_M}";
		if [ "$?" -ne "0" ]; then
			echo "tooltip:Konnte lokalen Speicherpfad $local_blog_path/${OUT_PATH_Y}/${OUT_PATH_M} für Bilder nicht erzeugen!"  >&3
			kill $IMGDISPLAYPID;
			CleanUp;
			exit 1;
		fi
	fi

	## Wartezeit, während Vorschaubild geladen wird, damit es nicht das dialogfeld überlagert
	sleep 5;

	## Dateinamen erfragen
	FILENAME="";
	while [ -z "${FILENAME}" ]
	do    
		FILENAME=$($MSGDIALOG --entry --title "Dateiname" --text "Welchen Dateinamen soll das Bild bekommen?" --entry-text="$OUT_DAY$USTRICH" 2> /dev/null);
		if [ $? -eq 1 ]; then  # Es wurde Abbruch geklickt, Script endet:
			kill $IMGDISPLAYPID;
			CleanUp;
			exit 1;
		fi
	done
	OUTFILE="${local_blog_path}/${OUT_PATH_Y}/${OUT_PATH_M}/${FILENAME}.jpg";
	OUTFILE_="${local_blog_path}/${OUT_PATH_Y}/${OUT_PATH_M}/${FILENAME}_.jpg";
	
	## Muss das Bild gedreht werden?
	case $($MSGDIALOG --list --title "Bild drehen" --text "Richtung" --radiolist --hide-header --column "eins" --column "zwei" FALSE links TRUE 0 FALSE rechts FALSE 180 2> /dev/null) in
		links)	ROTATE="-90";;
				0)	ROTATE="0";;
	 rechts)	ROTATE="90";;
			180)	ROTATE="180";;
				*)	kill $IMGDISPLAYPID; # Es wurde Abbruch geklickt, Script endet:  
						CleanUp;
						exit 1;;  
	esac
	
	## Soll ein großes Bild erzeugt werden?
	$MSGDIALOG --question --title "Großes Bild" --text "Soll ein großes Bild erzeugt werden?" 2> /dev/null;
	
	if [ "$?" -eq "0" ]; then ## Großes Bild erzeugen
	 	$PRINTF "<a href=\"%s/%s/%s/%s.jpg\" data-fancybox=\"\" data-caption=\"\"><img src=\"%s/%s/%s/%s_.jpg\" class=\"aligncenter\" alt=\"\"></a>" ${blog_address} ${OUT_PATH_Y} ${OUT_PATH_M} ${FILENAME} ${blog_address} ${OUT_PATH_Y} ${OUT_PATH_M} ${FILENAME} | $XCLIP; # Linkadresse in die Zwischenablage legen
		$CONVERT "${FILE[$INP_C]}" -auto-orient -rotate $ROTATE -resize "${w_blog_img}x${h_blog_img}" -quality 95  -unsharp 1.5x1.0+1.5+0.02 "${OUTFILE_}" &
		CONVERTPID=$!;		
		FILELIST+=( ["${CONVERTPID}"]=${OUTFILE_} );	 # Process IDs der Convertierprozesse sammeln
		EXIFORIGINALLIST+=( ["${CONVERTPID}"]=${FILE[$INP_C]} );   # Original-Dateiname		
		EXIFDATETIMELIST+=( ["${CONVERTPID}"]=$DATETIMEORIGINAL );  # Datum und Zeit des Originals speichern	
		$CONVERT "${FILE[$INP_C]}" -auto-orient -rotate $ROTATE -resize "${w_img}x${h_img}" -quality 95 -unsharp 1.5x1.0+1.5+0.02 "${OUTFILE}" &
		CONVERTPID=$!;
		FILELIST+=( ["${CONVERTPID}"]=${OUTFILE} );	 # Process IDs der Convertierprozesse sammeln	
		EXIFORIGINALLIST+=( ["${CONVERTPID}"]=${FILE[$INP_C]} );		# Original-Dateiname
		EXIFDATETIMELIST+=( ["${CONVERTPID}"]=$DATETIMEORIGINAL );  # Datum und Zeit des Originals speichern		
	else		## Nur kleines Bild erzeugen
		$PRINTF "<img src=\"%s/%s/%s/%s.jpg\" class=\"aligncenter\" alt=\"\">" ${blog_address} ${OUT_PATH_Y} ${OUT_PATH_M} ${FILENAME} | $XCLIP; # Linkadresse in die Zwischenablage legen
		$CONVERT "${FILE[$INP_C]}" -auto-orient -rotate $ROTATE -resize "${w_blog_img}x${h_img}" -quality 95 -unsharp 1.5x1.0+1.5+0.02 "${OUTFILE}" &
		CONVERTPID=$!;	
		FILELIST+=( ["${CONVERTPID}"]=${OUTFILE} );	# Process IDs der Convertierprozesse mit dem zugehörigen Dateinamen sammeln
		EXIFORIGINALLIST+=( ["${CONVERTPID}"]=${FILE[$INP_C]} );		# Original-Dateiname
		EXIFDATETIMELIST+=( ["${CONVERTPID}"]=$DATETIMEORIGINAL );  # Datum und Zeit des Originals speichern	
	fi
	kill $IMGDISPLAYPID;	# Bildanzeige der Vorschau beenden
	if [ -f "$local_blog_path/preview.jpg" ]; then
		rm "$local_blog_path/preview.jpg";
	fi
done

# Progress bar
FILECOUNT=${#FILELIST[*]};
exec 4> >(${MSGDIALOG} --progress --title="${SCRIPTNAME} ($FILECOUNT files)" --percentage=0 --auto-close --width=400)
PROGRESS=0;
## Wenn keine Logindaten hinterlegt sind, muss das Script nur noch die exif-Daten schreiben
if [ -z "${SSH_LOGIN}" ]; then
	logger $LLDebug "No ssh upload, just setting exif.";
	while [ ${#FILELIST[@]} -gt 0 ] # solange noch CONVERTPIDs vorhanden sind
	do
		for CPID in "${!FILELIST[@]}";
		do
			if [ -n "${CPID}" -a -d "/proc/${CPID}" ]; then
				logger $LLDebug "Process $CPID still running, exif postphoned.";
				sleep 2;
			else
				logger $LLDebug "${FILELIST[${CPID}]} converted, doing exif.";
				echo "# Setting exif ${FILELIST[${CPID}]}" >&4
				((PROGRESS++))
				echo $(( ($PROGRESS*100/$MAX_INP_C)-1 )) >&4
				$EXIFTOOL -overwrite_original -DateTimeOriginal="${EXIFDATETIMELIST[${CPID}]}" ${FILELIST[${CPID}]};
				$EXIFTOOL -overwrite_original -OriginalFileName="${EXIFORIGINALLIST[${CPID}]##*/}" ${FILELIST[${CPID}]}; 
				logger $LLDebug "tooltip:Argumente: ${EXIFORIGINALLIST[${CPID}]##*/}";
				unset FILELIST["${CPID}"];
				unset EXIFDATETIMELIST["${CPID}"];
				unset EXIFORIGINALLIST["${CPID}"];
		   fi
	   done
   done
   echo "100" >&4 # trigger auto-close of progressbar
   CleanUp;
   exec 4>&-
   exit 0;
fi

## Hochladen der Bilder aufs Blog
while [ ${#FILELIST[@]} -gt 0 ] # solange noch CONVERTPIDs vorhanden sind
do
	logger $LLDebug "Länge Liste: ${#FILELIST[@]}";
	for CPID in "${!FILELIST[@]}";
	do
		if [ -n "${CPID}" -a -d "/proc/${CPID}" ]; then
			logger $LLDebug "Process $CPID still running, exif and upload postphoned.";
			sleep 2;
		else
			logger $LLDebug "${FILELIST[${CPID}]} converted, doing exif and upload.";
			echo "# Setting exif ${FILELIST[${CPID}]}" >&4
			((PROGRESS++))
			echo $(( ($PROGRESS*25/$MAX_INP_C)-1 )) >&4
			$EXIFTOOL -overwrite_original -DateTimeOriginal="${EXIFDATETIMELIST[${CPID}]}" ${FILELIST[${CPID}]};
			$EXIFTOOL -overwrite_original -OriginalFileName="${EXIFORIGINALLIST[${CPID}]##*/}" ${FILELIST[${CPID}]};
			# Bild für den Upload mit cat ausgeben, Passwort an ssh geben, wenn Zielverzeichnis nicht existiert: erzeugen, auf Remote-Host Bild mit cat in Zieldatei schreiben. ( Dieses Konstrukt ${FILELIST[${CPID}]##*/} extrahiert den Dateinamen aus dem Pfad )
			echo "# Uploading ${FILELIST[${CPID}]}" >&4
			((PROGRESS++))
			echo $(( ($PROGRESS*25/$MAX_INP_C)-1 )) >&4
			cat ${FILELIST[${CPID}]} | sshpass -p $SSH_PASSWD ssh ${SSH_LOGIN} "mkdir -p $blog_path/${OUT_PATH_Y}/${OUT_PATH_M}/;cat > $blog_path/${OUT_PATH_Y}/${OUT_PATH_M}/${FILELIST[${CPID}]##*/}" &
			#scp ${FILELIST[${CPID}]} ${SSH_LOGIN}:$blog_path/${OUT_PATH_Y}/${OUT_PATH_M}/${FILELIST[${CPID}]##*/} &
			unset EXIFDATETIMELIST["${CPID}"];
			unset EXIFORIGINALLIST["${CPID}"];
			unset FILELIST["${CPID}"];
		fi
	done
done

sleep 2;
echo "100" >&4 # trigger auto-close of progressbar
CleanUp;
exec 4>&-
exit 0;

# EOF
