#!/bin/sh
#
# Purpose {{{
# This script will try to control Kodi with kodi-send command
#   1. Start Kodi if not already running.
#   2. Send user command to Kodi running instance.
#
# In Kodi, pause|play|toggle = TOGGLE ! 😑
# So for a real PAUSE, some extra tests are required :
#   * Request the API (with curl) to check if a playlist is active.
#   * Request the API to get the media speed.
#   * Exit if the media is already paused (speed = 0).
#
# 2022-07-04
# }}}
# Vars {{{
PROGNAME=$(basename "${0}"); readonly PROGNAME
PROGDIR=$(readlink -m $(dirname "${0}")); readonly PROGDIR
ARGS="${*}"; readonly ARGS
readonly NBARGS="${#}"
[ -z "${DEBUG}" ] && DEBUG=1
## Export DEBUG for sub-script
export DEBUG

## Default values for some vars
readonly USER_MULTIMEDIA_COMMAND_DEFAULT="toggle"
readonly KODI_PROC_REGEXP="kodi.bin"
readonly KODI_HOST_DEFAULT="127.0.0.1"
readonly KODI_PORT_DEFAULT="8080"

## Colors
readonly PURPLE='\033[1;35m'
readonly RED='\033[0;31m'
readonly RESET='\033[0m'
readonly COLOR_DEBUG="${PURPLE}"
# }}}
usage() {                                                       # {{{

	cat <<- HELP
usage: $PROGNAME [-c|-d|-h]

Manage Kodi player with some commands.

EXAMPLES :
    - Starts playing current playlist
        ${PROGNAME} --command start

    - Start Kodi if not already running
        ${PROGNAME}

    - Stop current current media
        ${PROGNAME} --command stop

    - Send default action (${USER_MULTIMEDIA_COMMAND_DEFAULT}) to default Kodi instance
        ${PROGNAME}

    - Toggle current status on remote host (default : ${KODI_HOST_DEFAULT})
        ${PROGNAME} --host 192.168.1.33 --command toggle

OPTIONS :
    -c,--command
        Send a command to running Kodi. Available commands :
          * toggle, play
          * pause (real one thanks to the API and curl!)
          * stop
          * next
          * previous
          * random, party, partymode

    -h,--host
        Set the Kodi host to manage.

    -p,--port
        Set the Kodi API's port to use.

    -d,--debug
        Enable debug messages.

    --help
        Print this help message.
HELP

}
# }}}
debug_message() {                                               # {{{

	local_debug_message="${1}"

	## Print message if DEBUG is enable (=0)
	[ "${DEBUG}" -eq "0" ] && printf '\e[1;35m%-6b\e[m\n' "DEBUG − ${PROGNAME} : ${local_debug_message}"

	unset local_debug_message

	return 0
}
# }}}
error_message() {                                               # {{{

	local_error_message="${1}"
	local_error_code="${2}"

	## Print message
	printf '%b\n' "ERROR − ${PROGNAME} : ${RED}${local_error_message}${RESET}"

	exit "${local_error_code:=66}"
}
# }}}
define_vars() {                                                 # {{{

	## If KODI_HOST wasn't defined (argument) {{{
	if [ -z "${KODI_HOST}" ]; then
		## Use default value
		readonly KODI_HOST="${KODI_HOST_DEFAULT}"
	fi
	## }}}
	## If KODI_PORT wasn't defined (argument) {{{
	if [ -z "${KODI_PORT}" ]; then
		## Use default value
		readonly KODI_PORT="${KODI_PORT_DEFAULT}"
	fi
	## }}}
	## If USER_MULTIMEDIA_COMMAND wasn't defined (argument) {{{
	if [ -z "${USER_MULTIMEDIA_COMMAND}" ]; then
		## Use default value
		readonly USER_MULTIMEDIA_COMMAND="${USER_MULTIMEDIA_COMMAND_DEFAULT}"
	fi
	## }}}
	## Translate user command to Kodi action {{{
	case "${USER_MULTIMEDIA_COMMAND}" in
		toggle|play|pause )                   ## Toggle current play
			KODI_ACTION="PlayerControl(play)"
			;;
		stop )                                ## Stop current play
			KODI_ACTION="PlayerControl(stop)"
			;;
		next )                                ## Next element in the playlist
			KODI_ACTION="PlayerControl(next)"
			;;
		previous )                            ## Next element in the playlist
			KODI_ACTION="PlayerControl(previous)"
			;;
		random|party|partymode )              ## Start random music
			KODI_ACTION="PlayerControl(partymode)"
			;;
		* )                                   ## unknow option
			printf '%b\n' "${RED}Invalid Kodi command: ${USER_MULTIMEDIA_COMMAND}${RESET}"
			printf '%b\n' "---"
			usage
			exit 1
			;;
	esac
	## }}}

}
# }}}
is_proc_absent() {                                              # {{{

	local_proc_pattern="${1}"

	## Proc is absent by default
	return_proc_absent="0"

	local_count_proc_pattern="$(pgrep -f -- "${local_proc_pattern}" | wc -l)"

	case "${local_count_proc_pattern}" in
		0 ) ## No procs related to this pattern are running
			return_proc_absent="0"
			;;
		* ) ## At least one proc seems running
			return_proc_absent="1"
			;;
esac

	## Simple debug message to valid current variables
	debug_message "is_proc_absent − \
procs running (with the pattern: ${RED}${local_proc_pattern}${COLOR_DEBUG}) on the current host: ${RED}${local_count_proc_pattern}${COLOR_DEBUG}."

	return "${return_proc_absent}"

}
# }}}
get_current_media_type() {                                      # {{{

	debug_message "get_current_media_type − \
Run a request to ${KODI_HOST}:${KODI_PORT} API."

	KODI_CURRENT_MEDIA_TYPE=$(curl -s -X POST http://"${KODI_HOST}:${KODI_PORT}"/jsonrpc -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","method":"Player.GetActivePlayers","id":0}' | sed -n 's/.*"type":"\(.*\)".*/\1/p')
}
# }}}
is_audio_paused() {                                             # {{{

	## Media is playing by default
	return_is_audio_paused="1"

	debug_message "is_audio_paused − \
Run a request to ${KODI_HOST}:${KODI_PORT} API to get status of audio media."

	KODI_CURRENT_AUDIO_SPEED=$(curl -s -X POST http://"${KODI_HOST}:${KODI_PORT}"/jsonrpc -H 'Content-Type: application/json' --data '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": { "playerid": 0, "properties": [ "speed" ] }, "id": "AudioGetItem" }' | sed -n 's/.*"speed":\(.\).*/\1/p')

	case "${KODI_CURRENT_AUDIO_SPEED}" in
		0 )                 ## Media is paused
			debug_message "is_audio_paused − \
The current media is paused (speed: ${RED}${KODI_CURRENT_MEDIA_TYPE}${COLOR_DEBUG})."
			return_is_audio_paused="0"
			;;
		1 )                 ## Media is playing
			debug_message "is_audio_paused − \
The current media is playing (speed: ${RED}${KODI_CURRENT_MEDIA_TYPE}${COLOR_DEBUG})."
			return_is_audio_paused="1"
			;;
		* )                 ## Error getting current media speed
			error_message "Error with media speed (${KODI_CURRENT_AUDIO_SPEED})." 1
			;;
	esac

	return "${return_is_audio_paused}"
}
# }}}
is_video_paused() {                                             # {{{

	## Media is playing by default
	return_is_video_paused="1"

	debug_message "is_video_paused − \
Run a request to ${KODI_HOST}:${KODI_PORT} API to get status of video media."

	KODI_CURRENT_VIDEO_SPEED=$(curl -s -X POST http://"${KODI_HOST}:${KODI_PORT}"/jsonrpc -H 'Content-Type: application/json' --data '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": { "playerid": 1, "properties": [ "speed" ] }, "id": "VideoGetItem" }' | sed -n 's/.*"speed":\(.\).*/\1/p')

	case "${KODI_CURRENT_VIDEO_SPEED}" in
		0 )                 ## Media is paused
			debug_message "is_video_paused − \
The current video is paused (speed: ${RED}${KODI_CURRENT_MEDIA_TYPE}${COLOR_DEBUG})."
			return_is_video_paused="0"
			;;
		1 )                 ## Media is playing
			debug_message "is_video_paused − \
The current video is playing (speed: ${RED}${KODI_CURRENT_MEDIA_TYPE}${COLOR_DEBUG})."
			return_is_video_paused="1"
			;;
		* )                 ## Error getting current media speed
			error_message "Error with media speed (${KODI_CURRENT_VIDEO_SPEED})." 1
			;;
	esac

	return "${return_is_video_paused}"
}
# }}}

main() {                                                        # {{{

	## Define all vars
	define_vars

	## For localhost
	if [ "${KODI_HOST}" = "${KODI_HOST_DEFAULT}" ]; then
		## If kodi is not running {{{
		### AND Start it
		### AND exit 0
		is_proc_absent "${KODI_PROC_REGEXP}" \
			&& debug_message "Start a new Kodi instance." \
			&& kodi \
			&& exit 0
		## }}}
	fi

	## If the media should be paused {{{
	if [ "${USER_MULTIMEDIA_COMMAND}" = "pause" ]; then
		## Get current playlist type
		get_current_media_type
		case "${KODI_CURRENT_MEDIA_TYPE}" in
			audio )                ## Manage audio media
				is_audio_paused \
					&& debug_message "${RED}audio${COLOR_DEBUG} media is already paused, no more action is required." \
					&& exit 0
				;;
			video )                ## Manage video media
				is_video_paused \
					&& debug_message "${RED}video${COLOR_DEBUG} media is already paused, no more action is required." \
					&& exit 0
				;;
			* )                    ## Unknown media type or no media at all
				debug_message "Unknown media type ->${RED}${KODI_CURRENT_MEDIA_TYPE}${COLOR_DEBUG}<- or no media is playing (if empty), exit."
				exit 0
				;;
		esac

	fi
	## }}}

	## Send Kodi action to running instance {{{
	### And exit
	debug_message "Send ${RED}${USER_MULTIMEDIA_COMMAND}${COLOR_DEBUG} (${RED}${KODI_ACTION}${COLOR_DEBUG}) action to running Kodi instance (on ${RED}${KODI_HOST}${COLOR_DEBUG} host)." \
		&& kodi-send --host="${KODI_HOST}" --action="${KODI_ACTION}" > /dev/null \
		&& exit 0
	## }}}

}
# }}}

# Manage arguments                                                # {{{
# This code can't be in a function due to argument management

if [ ! "${NBARGS}" -eq "0" ]; then

	manage_arg="0"

	## If the first argument is not an option
	if ! printf -- '%s' "${1}" | grep -q -E -- "^-+";
	then
		## Print help message and exit
		printf '%b\n' "${RED}Invalid option: ${1}${RESET}"
		printf '%b\n' "---"
		usage

		exit 1
	fi

	# Parse all options (start with a "-") one by one
	while printf -- '%s' "${1}" | grep -q -E -- "^-+"; do

	case "${1}" in
		-c|--command )                        ## User command
			### Move to the next argument
			shift
			### Define var
			USER_MULTIMEDIA_COMMAND="${1}"
			;;
		-d|--debug )                          ## debug
			DEBUG=0
			;;
		-h|--host )                           ## Kodi host to manage
			### Move to the next argument
			shift
			### Define var
			KODI_HOST="${1}"
			;;
		-p|--port )                           ## Kodi API port
			### Move to the next argument
			shift
			### Define var
			KODI_PORT="${1}"
			;;
		--help )                              ## help
			usage
			## Exit after help informations
			exit 0
			;;
		* )                                   ## unknow option
			printf '%b\n' "${RED}Invalid option: ${1}${RESET}"
			printf '%b\n' "---"
			usage
			exit 1
			;;
	esac

	debug_message "Arguments management − \
${RED}${1}${COLOR_DEBUG} option|command managed."

	## Move to the next argument
	shift
	manage_arg=$((manage_arg+1))

	done

	debug_message "Arguments management − \
${RED}${manage_arg}${COLOR_DEBUG} argument(s) successfully managed."
else
	debug_message "Arguments management − \
No arguments/options to manage."
fi

# }}}

main

exit 255