mirror of
				https://github.com/Jonny007-MKD/OTR-DecodeAll
				synced 2025-11-03 16:15:36 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			432 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			432 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable file
		
	
	
	
	
#!/bin/bash
 | 
						|
 | 
						|
##########
 | 
						|
# Script #
 | 
						|
##########
 | 
						|
 | 
						|
remove=1;				# 1 => Delete when torrent deleted, 2 => Delete always, 0 => Delete never
 | 
						|
forceRun=0;				# Skip lock file test
 | 
						|
validate="-q";			# unused with pyropeters otrtool
 | 
						|
logMsgTypes=("      " "error" "warn" "info" "debug" "debugV")
 | 
						|
logMsgColor=("\033[37m" "\033[31m" "\033[33m" "\033[37m" "\033[37m" "\033[37m")
 | 
						|
logLevel=0
 | 
						|
echoLevel=5
 | 
						|
lastKodiCheck=0
 | 
						|
umask 0002				# Set permissions 775/664 per default
 | 
						|
 | 
						|
 | 
						|
PwD=$(readlink -e $0)	# Get the path to this script
 | 
						|
PwD=$(dirname "$PwD")
 | 
						|
 | 
						|
# trap ctrl-c and call ctrl_c()
 | 
						|
trap ctrl_c INT
 | 
						|
 | 
						|
function ctrl_c() {
 | 
						|
	funcUnlock
 | 
						|
	exit
 | 
						|
}
 | 
						|
 | 
						|
function funcLog {
 | 
						|
	if [ $1 -le $logLevel ]; then
 | 
						|
		echo -e "`date +"%d.%m.%y %T"` ${logMsgTypes[$1]}\t$2" >> $logFile
 | 
						|
	fi
 | 
						|
	if [ $1 -le $echoLevel ]; then
 | 
						|
		echo -e "${logMsgColor[$(($1))]}${logMsgTypes[$1]}:\t$2\033[37m"
 | 
						|
	fi
 | 
						|
}
 | 
						|
 | 
						|
function funcGetConfig {
 | 
						|
	if [ ! -r "$PwD/config" ]; then
 | 
						|
		funcLog 1 "$PwD/config does not exist"
 | 
						|
		exit 1
 | 
						|
	else
 | 
						|
		. "$PwD/config"
 | 
						|
		funcConfigPre
 | 
						|
	fi
 | 
						|
}
 | 
						|
 | 
						|
function funcPerformChecks {
 | 
						|
	local exet;
 | 
						|
	exet=0
 | 
						|
 | 
						|
	if ! type "$cmdDecode" >/dev/null 2>&1; then
 | 
						|
		funcLog 1 "Please install otrtool"
 | 
						|
		exet=1
 | 
						|
	fi
 | 
						|
	if [ -n "$cmdCut" ] && ! type "$cmdCut" >/dev/null 2>&1; then
 | 
						|
		funcLog 1 "Please check the path to multicutmkv"
 | 
						|
		exet=1
 | 
						|
	fi
 | 
						|
	if [ -n "$cmdSaneRenamix" ] && ! type "$cmdSaneRenamix" >/dev/null 2>&1; then
 | 
						|
		funcLog 1 "Please check the path to SaneRenamix (set to empty if not wanted)"
 | 
						|
		exet=1
 | 
						|
	fi
 | 
						|
	if [ -n "$kodiUrl" ] && ! type "curl" >/dev/null 2>&1; then
 | 
						|
		funcLog 1 "Please install curl to check the Kodi state"
 | 
						|
		exet=1
 | 
						|
	fi
 | 
						|
	if [ $exet -eq 1 ]; then
 | 
						|
		funcUnlock
 | 
						|
		exit 1
 | 
						|
	fi
 | 
						|
 | 
						|
	funcPerformKodiCheck
 | 
						|
}
 | 
						|
 | 
						|
function funcPerformKodiCheck {
 | 
						|
	local curTimestamp;
 | 
						|
	if [ $forceRun -eq 0 ] && [ -n "$kodiUrl" ]; then			# If we can and have to check whether Kodi is playing something
 | 
						|
		curTimestamp=$(date +%s)
 | 
						|
		if [ $(($curTimestamp-$lastKodiCheck)) -gt 10 ]; then	# Only check all 10 seconds
 | 
						|
			playerID="$(curl -s $kodiUrl/jsonrpc -H 'content-type: application/json;' --data-binary '{"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1}' | grep -o "[0-9],.type.:.$1" | grep -o '[0-9]')"
 | 
						|
			if [ -n "$playerID" ]; then
 | 
						|
				funcLog 3 "Kodi is running, exiting now"
 | 
						|
				funcUnlock
 | 
						|
				exit
 | 
						|
			fi
 | 
						|
			lastKodiCheck=$curTimestamp
 | 
						|
			funcLog 5 "Kodi is not running"
 | 
						|
		fi
 | 
						|
	fi
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
# Parse the parameters
 | 
						|
function funcParam {
 | 
						|
	while getopts "frkvh?e:p:i:o:d:t:l:" optval; do
 | 
						|
		case $optval in
 | 
						|
			"f")
 | 
						|
				funcLog 4 "Set forceRun = 1"
 | 
						|
				forceRun=1;;			# ignore lock file
 | 
						|
			"r")
 | 
						|
				funcLog 4 "Set remove = 2 (always)"
 | 
						|
				remove=2;;				# delete the otrkey
 | 
						|
			"k")
 | 
						|
				funcLog 4 "Set remove = 0 (never)"
 | 
						|
				remove=0;;				# keep the otrkey
 | 
						|
			"v")
 | 
						|
				funcLog 4 "Set validate = '' (yes)"
 | 
						|
				validate="";;			# validate the output file after decoding. Not needed when downloaded via torrent
 | 
						|
			"e")
 | 
						|
				funcLog 4 "Set user = $OPTARG"
 | 
						|
				user=$OPTARG;;			# different user
 | 
						|
			"p")
 | 
						|
				funcLog 4 "Set pass = $OPTARG"
 | 
						|
				pass=$OPTARG;;			# different password
 | 
						|
			"i")
 | 
						|
				funcLog 4 "Set indir = $OPTARG"
 | 
						|
				inDir=$OPTARG;;			# different input dir
 | 
						|
			"o")
 | 
						|
				funcLog 4 "Set outDir = $OPTARG"
 | 
						|
				outDir=$OPTARG;;		# different output dir
 | 
						|
			"d")
 | 
						|
				funcLog 4 "Set uncutDir = $OPTARG"
 | 
						|
				uncutDir=$OPTARG		# different uncut dir
 | 
						|
				cutDir=$OPTARG;;		# different cut dir
 | 
						|
			"c")
 | 
						|
				funcLog 4 "Set delugeDir = $OPTARG"
 | 
						|
				delugeDir=$OPTARG;;		# different deluge dir
 | 
						|
			"l")
 | 
						|
				funcLog 4 "Set labelFilter = $OPTARG"
 | 
						|
				labelFilter=$OPTARG;;
 | 
						|
			"h"|"?")
 | 
						|
				funcHelp;				# print help
 | 
						|
				exit;;
 | 
						|
			":")
 | 
						|
				echo "No argument value for option $OPTARG";;
 | 
						|
		esac
 | 
						|
	done
 | 
						|
	funcConfigPost
 | 
						|
}
 | 
						|
 | 
						|
# Print some help text and explain parameters
 | 
						|
function funcHelp {
 | 
						|
	echo -e "\n\033[1;31m./otrDecodeAll.sh [-f -k -v -h] [-e user] [-p password] [-i inputDir] [-d decodedDir] [-c delugeDir] [-o outputDir]\033[0;37m"
 | 
						|
	echo "-> FORCE"
 | 
						|
	echo -e "	\033[36m-f\033[37m Make this script ignore the lockfile and Kodi. This may result into decoding some files multiple times (perhaps corrupting them) and making Kodi less smooth."
 | 
						|
	echo "-> REMOVE"
 | 
						|
	echo -e "	\033[36m-r\033[37m Make this script delete the original otrkey files."
 | 
						|
	echo "-> KEEP"
 | 
						|
	echo -e "	\033[36m-k\033[37m Make this script keep the original otrkey files. If not given, the otrkey will be kept after decoding and when torrent file is not found."
 | 
						|
	echo "-> NO VALIDATE"
 | 
						|
	echo -e "	\033[36m-v\033[37m Make otrdecoder validate the output file. This should not be neccessarry when you downloaded it via torrent."
 | 
						|
	echo "-> USER"
 | 
						|
	echo -e "	\033[36m-e\033[37m Specify the OTR email address if you do not want to use the default."
 | 
						|
	echo "-> PASSWORD"
 | 
						|
	echo -e "	\033[36m-p\033[37m Specify the OTR password if you do not want to use the default."
 | 
						|
	echo "-> INPUTDIR"
 | 
						|
	echo -e "	\033[36m-i\033[37m Specify the input directory where the *.otrkey files are located."
 | 
						|
	echo "-> DECODEDDIR"
 | 
						|
	echo -e "	\033[36m-d\033[37m Specify the output directory where the decoded file shall be put before cutting."
 | 
						|
	echo "-> UNCUTDIR"
 | 
						|
	echo -e "	\033[36m-o\033[37m Specify the directory where the decoded and cut file shall be put."
 | 
						|
	echo "-> DELUGED CONFIG DIR"
 | 
						|
	echo -e "	\033[36m-c\033[37m Specify the config directory of deluged.\n	   This script will check whether the torrent exists and delete the otrkey only when the torrent was deleted."
 | 
						|
	echo "-> HELP"
 | 
						|
	echo -e "	\033[36m-h\033[37m Show this help."
 | 
						|
}
 | 
						|
 | 
						|
# Look for lock file and exit if it is existing and $forceRun == 0
 | 
						|
function funcLock {
 | 
						|
	if [ -f /tmp/.otrDecodeAll.lock -a "$forceRun" != "1" ]; then
 | 
						|
		funcLog 1 "/tmp/.otrDecodeAll.lock existing! exiting..."
 | 
						|
		exit 1
 | 
						|
	else
 | 
						|
		funcLog 4 "Creating lock file /tmp/.otrDecodeAll.lock"
 | 
						|
		touch /tmp/.otrDecodeAll.lock
 | 
						|
	fi
 | 
						|
}
 | 
						|
 | 
						|
# Delete lock file
 | 
						|
function funcUnlock {
 | 
						|
	funcLog 4 "Removing lock file /tmp/.otrDecodeAll.lock"
 | 
						|
	rm -f /tmp/.otrDecodeAll.lock
 | 
						|
}
 | 
						|
 | 
						|
function funcGetLabel {
 | 
						|
	label="$(grep -m 1 "$filename" $torrentDb | grep -o ' [a-zA-Z0-9_-]*$' | grep -o '[a-zA-Z0-9_-]*$')"
 | 
						|
	funcLog 5 "label: $label"
 | 
						|
}
 | 
						|
 | 
						|
function funcMakeVars {
 | 
						|
	# This contains the OTR name of the file (e.g. Good_Wife_15.02.17_23-55_sixx_50_TVOON_DE.mpg.HQ.avi)
 | 
						|
	funcLog 5 "filename: $filename"
 | 
						|
 | 
						|
	# This will be our name for the subfolder (movie name, e.g. Good.Wife)
 | 
						|
	bibname=${filename%%_[0-9][0-9].*}
 | 
						|
	bibname="${bibname//_/.}"
 | 
						|
	funcLog 5 "bibname: $bibname"
 | 
						|
 | 
						|
 | 
						|
	# This is the absolute path to the encoded file (e.g. /stuff/Good_Wife_15.02.17_23-55_sixx_50_TVOON_DE.mpg.HQ.avi.otrkey)
 | 
						|
	pathAbsEncoded="$inDir/$filename.otrkey"
 | 
						|
	funcLog 5 "pathAbsEncoded: $pathAbsEncoded"
 | 
						|
 | 
						|
	# This is the absolute path to the decoded file (e.g. /stuff/Good_Wife_15.02.17_23-55_sixx_50_TVOON_DE.mpg.HQ.avi)
 | 
						|
	pathTmpAbsDecoded="$uncutDir/$filename"
 | 
						|
	funcLog 5 "pathTmpAbsDecoded: $pathTmpAbsDecoded"
 | 
						|
 | 
						|
	# This is the absolute path to the cut file (e.g. /stuff/Good_Wife_15.02.17_23-55_sixx_50_TVOON_DE.mpg.HQ.avi-cut.mkv)
 | 
						|
	pathTmpAbsCut="$cutDir/$filename-cut.mkv"
 | 
						|
	funcLog 5 "pathTmpAbsCut: $pathTmpAbsCut"
 | 
						|
 | 
						|
	# Now we will determine the path where to put the file in the end (depends on label and saneRenamix)
 | 
						|
	# sanename is normally the filename. In case of tv series the sanename will also contain the series and episode number and title
 | 
						|
	## TODO: Introduce some dictionary in the config and make this more general
 | 
						|
	sanename=$filename		# Default value (in case of error)
 | 
						|
	if [ -n "$label" ]; then
 | 
						|
		case $label in
 | 
						|
			"movie")					# Normal movies
 | 
						|
				pathAbsOutDecoded="Filme";;
 | 
						|
			"movie-en")					# English movies
 | 
						|
				pathAbsOutDecoded="Filme-En";;
 | 
						|
			"docu")						# Documentations
 | 
						|
				pathAbsOutDecoded="Dokumentationen";;
 | 
						|
			"tvserie")					# TV Series. Use saneRenamix here
 | 
						|
				pathAbsOutDecoded="Serien"
 | 
						|
				if [ -n "$cmdSaneRenamix" ]; then
 | 
						|
					tmp="$($cmdSaneRenamix $cmdSaneRenamixArgs $filename)"
 | 
						|
					err=$?
 | 
						|
					case $err in
 | 
						|
						0)
 | 
						|
							bibname="${tmp%%..*}"
 | 
						|
							sanename="$tmp"
 | 
						|
							funcLog 5 "sanename: $sanename";;
 | 
						|
						1)
 | 
						|
							funcLog 1 "SaneRenamix: General error!";;
 | 
						|
						2)
 | 
						|
							funcLog 1 "SaneRenamix: Specified language not recognized";;
 | 
						|
						3)
 | 
						|
							funcLog 3 "SaneRenamix: Aborted (Ctrl+C)";;
 | 
						|
						10)
 | 
						|
							funcLog 2 "SaneRenamix: Series not found in TvDB";;
 | 
						|
						11)
 | 
						|
							funcLog 2 "SaneRenamix: Series not found in EPG";;
 | 
						|
						20)
 | 
						|
							funcLog 2 "SaneRenamix: No info for this episode found";;
 | 
						|
						21)
 | 
						|
							funcLog 2 "SaneRenamix: No episode title found in EPG";;
 | 
						|
						40)
 | 
						|
							funcLog 1 "SaneRenamix: Downloading EPG data failed";;
 | 
						|
						41)
 | 
						|
							funcLog 1 "SaneRenamix: Downloading list of episodes from TvDB failed";;
 | 
						|
						*)
 | 
						|
							funcLog 1 "SaneRenamix: Unknown error $err";;
 | 
						|
						esac
 | 
						|
				fi;;
 | 
						|
			*)
 | 
						|
				funcLog 2 "Unrecognized label: $label"
 | 
						|
				label=""
 | 
						|
				pathAbsOutDecoded="";;
 | 
						|
		esac
 | 
						|
		pathAbsOutDecoded="$pathAbsOutDecoded/$bibname"		# Append bibname: This is the series name or the movie name (Kodi likes this)
 | 
						|
	else
 | 
						|
		pathAbsOutDecoded=""
 | 
						|
	fi
 | 
						|
 | 
						|
	# Save the insane filename in case saneRenamix did not work once before
 | 
						|
	if [ $sanename != $filename ]; then	# No sanename
 | 
						|
		pathAbsOutDecodedInsane="$outDir/$pathAbsOutDecoded/$filename"
 | 
						|
		funcLog 5 "pathAbsOutDecodedInsane: $pathAbsOutDecodedInsane"
 | 
						|
	fi
 | 
						|
 | 
						|
	# This will be the absolute path to the output file (e.g. /final/Good.Wife/Good.Wife..S05E14..Ein.paar.Worte.HQ.avi)
 | 
						|
	pathAbsOutDecoded="$outDir/$pathAbsOutDecoded/$sanename"
 | 
						|
	funcLog 5 "pathAbsOutDecoded: $pathAbsOutDecoded"
 | 
						|
 | 
						|
	# This will be the absolute path to the cut output file (e.g. /final/Good.Wife/Good.Wife..S05E14..Ein.paar.Worte.HQ.avi-cut.mkv)
 | 
						|
	pathAbsOutCut="$pathAbsOutDecoded-cut.mkv"
 | 
						|
	funcLog 5 "pathAbsOutCut: $pathAbsOutCut"
 | 
						|
}
 | 
						|
 | 
						|
function funcProcessFiles {
 | 
						|
	local files="`ls $inDir/*.otrkey 2> /dev/null `"					# All otrkeys
 | 
						|
	for file in $files; do												# For each otrkey
 | 
						|
		funcPerformKodiCheck					# Check whether Kodi is running
 | 
						|
 | 
						|
		filename="$(basename $file)"
 | 
						|
		filename="${filename%.otrkey}"
 | 
						|
		echo;
 | 
						|
		funcLog 0 "Processing $filename";
 | 
						|
		funcGetLabel
 | 
						|
 | 
						|
 | 
						|
		if [ -n "$labelFilter" ] && [[ "$labelFilter" != "$label" ]]; then
 | 
						|
			funcLog 4 "Label did not match filter. Skipping";
 | 
						|
			continue;
 | 
						|
		fi
 | 
						|
		if [ -z "$label" ]; then
 | 
						|
			funcLog 1 "No label specified for this movie. Skipping"
 | 
						|
			continue;
 | 
						|
		fi
 | 
						|
 | 
						|
		funcMakeVars
 | 
						|
		echo -e "	>> \033[32m$sanename\033[37m";
 | 
						|
 | 
						|
 | 
						|
		if [ -n "$pathAbsOutDecodedInsane" -a -f "$pathAbsOutDecodedInsane" ]; then		# Sanerenamix could not name this file before, but now it can
 | 
						|
			funcLog 4 "Renamed $filename to $sanename"
 | 
						|
			mv $pathAbsOutDecodedInsane $pathAbsOutDecoded
 | 
						|
		fi
 | 
						|
		if [ -f "$pathAbsOutDecoded" ]; then
 | 
						|
			funcLog 4 "File was already handled."
 | 
						|
			funcRemove $file
 | 
						|
			continue;
 | 
						|
		fi
 | 
						|
 | 
						|
		funcDecode "$pathAbsEncoded";
 | 
						|
		if [ $success -ne 1 ]; then									# Decoding failed, we can skip the rest
 | 
						|
			continue;
 | 
						|
		fi
 | 
						|
 | 
						|
		funcRemove $file
 | 
						|
		if [ $success -ne 1 ]; then									# Removing failed, we can skip the rest
 | 
						|
			continue;
 | 
						|
		fi
 | 
						|
 | 
						|
		#funcCut "$pathTmpAbsDecoded"
 | 
						|
		if [ $success -ne 1 ]; then									# Cutting failed, we can skip the rest
 | 
						|
			continue;
 | 
						|
		fi
 | 
						|
 | 
						|
		funcMove "$pathMovie"
 | 
						|
		if [ $success -ne 1 ]; then									# Moving the file failed, we can skip the rest
 | 
						|
			continue;
 | 
						|
		fi
 | 
						|
 | 
						|
	done
 | 
						|
}
 | 
						|
 | 
						|
# Make the decoding stuff
 | 
						|
function funcDecode {
 | 
						|
	local sizeEn;
 | 
						|
	local sizeDe;
 | 
						|
	if [ -e "$pathTmpAbsDecoded" ]; then									# If we decoded this file before
 | 
						|
		sizeEn=`stat -c%s "$pathAbsEncoded"`
 | 
						|
		sizeDe=`stat -c%s "$pathTmpAbsDecoded"`
 | 
						|
		if [ $(($sizeEn-$sizeDe)) -eq 522 ]; then 					# If decoding was successful
 | 
						|
			funcLog 3 "File was already decoded: $pathTmpAbsDecoded"		# Simply do nothing
 | 
						|
			pathMovie="$pathTmpAbsDecoded"
 | 
						|
		else														# Else decode it again
 | 
						|
			funcLog 3 "Previous decoding was not successfull"
 | 
						|
			rm "$pathTmpAbsDecoded"
 | 
						|
		fi
 | 
						|
	fi
 | 
						|
	if [ ! -e "$pathTmpAbsDecoded" ]; then								# If don't find the decoded file in $uncutDir
 | 
						|
		funcLog 4 "Decoding $filename"
 | 
						|
		funcLog 5 "     $cmdDecode $cmdDecodeArgs $pathAbsEncoded"
 | 
						|
		#echo "     $cmdDecode $cmdDecodeArgs $pathAbsEncoded"
 | 
						|
	
 | 
						|
		$cmdDecode $cmdDecodeArgs "$pathAbsEncoded"					# Deocde the file
 | 
						|
 | 
						|
		success=$?
 | 
						|
		if [ $success -eq 0 ]; then									# if otrdecoder exited successfully
 | 
						|
			if [ -f "$pathTmpAbsDecoded" ]; then
 | 
						|
				funcLog 4 "Successfully decoded"
 | 
						|
				echo -e "\033[32mDecoding successfull\033[37m";
 | 
						|
				pathMovie="$pathTmpAbsDecoded"
 | 
						|
				success=1;
 | 
						|
			else
 | 
						|
				funcLog 1 "Decoding failed but decoder exited with success status!"
 | 
						|
				success=0;
 | 
						|
			fi
 | 
						|
		else
 | 
						|
			funcLog 1 "Decoding failed (returned $success)!"
 | 
						|
			success=0
 | 
						|
		fi
 | 
						|
	fi
 | 
						|
}
 | 
						|
 | 
						|
function funcRemove {
 | 
						|
	case $remove in
 | 
						|
		2)																# if we shall delete the file
 | 
						|
			funcLog 4 "Deleting $pathAbsEncoded"
 | 
						|
			rm -f "$pathAbsEncoded";;
 | 
						|
		1)
 | 
						|
			if [ -n "$delugeDir" ] && [ -d "$delugeDir/state" ]; then	# If deluge config dir is defined
 | 
						|
				if [ -n "`grep "$filename" "$delugeDir/state" -R --include=*.torrent`" ]; then
 | 
						|
					funcLog 4 "Torrent still exists in Deluge"
 | 
						|
				else
 | 
						|
					funcLog 3 "Deleting otrkey, torrent was removed"
 | 
						|
					rm -f "$pathAbsEncoded";								# Delete otrkey, too
 | 
						|
				fi
 | 
						|
			fi;;
 | 
						|
	esac
 | 
						|
	success=1;
 | 
						|
}
 | 
						|
 | 
						|
function funcCut {
 | 
						|
	funcLog 4 "Cutting $pathTmpAbsDecoded"
 | 
						|
	funcLog 5 "     $cmdCut $cmdCutArgs $pathTmpAbsDecoded"
 | 
						|
	
 | 
						|
	$cmdCut $cmdCutArgs "$pathTmpAbsDecoded"
 | 
						|
	success=$?
 | 
						|
	case $success in
 | 
						|
		0)
 | 
						|
			funcLog 4 "Successfully cut"
 | 
						|
			pathMove="$pathTmpAbsCut"
 | 
						|
			success=1;;
 | 
						|
		5)
 | 
						|
			funcLog 3 "No cutlist found"
 | 
						|
			pathMove="$pathTmpAbsCut"
 | 
						|
			success=1;;
 | 
						|
		*)
 | 
						|
			funcLog 1 "An error occured while cutting: $success!"
 | 
						|
			success=0;;
 | 
						|
	esac
 | 
						|
}
 | 
						|
 | 
						|
function funcMove {
 | 
						|
	if [ ! -d "$pathAbsOutDecoded" ]; then
 | 
						|
		mkdir -p "$(dirname $pathAbsOutDecoded)"
 | 
						|
	fi
 | 
						|
	mv -f "$pathMovie" "$pathAbsOutDecoded"
 | 
						|
	success=1
 | 
						|
}
 | 
						|
 | 
						|
funcGetConfig
 | 
						|
funcParam "$@"
 | 
						|
funcPerformChecks
 | 
						|
funcLock
 | 
						|
funcProcessFiles
 | 
						|
funcUnlock
 |