#!/bin/bash

# youread 
# read clipboard with flite or gtts, assuming input is text with some punctuations.

# needs: flite or gtts, xclip, mpv

# to install google text to speech
# pip install gTTS

# Usage
# youread                   # will read whatever is in clipboard
# youread path/to/text.txt  # will read the text file

# flite or google or ibm (you will need your own api key)
#engine="google" ; speed="1.2"
#engine="flite" ; speed="1"
engine="ibm" ; speed="1"
#engine=""

# google tld voices https://gtts.readthedocs.io/en/latest/module.html
tld=(co.uk com co.in ie)
# disabled ca co.za com.au

# ibm voices
ibmvoice=(en-GB_CharlotteV3Voice en-GB_JamesV3Voice en-GB_KateVoice en-GB_KateV3Voice en-US_AllisonVoice en-US_AllisonV3Voice en-US_EmilyV3Voice en-US_HenryV3Voice en-US_KevinV3Voice en-US_LisaVoice en-US_LisaV3Voice en-US_MichaelVoice en-US_MichaelV3Voice en-US_OliviaV3Voice)

# play
play (){
    mpv --no-resume-playback --msg-level=all=no --no-video --speed="$speed" "$1"
}

# echo line
echoline (){
    echo
    (( all > 1 )) && echo "($part/$all)"
    echo "$line" | fmt -w 80
}

# tmp dir
tmp="/tmp/$RANDOM-$$"
trap '[ -n "$tmp" ] && rm -fr "$tmp"' EXIT
mkdir -m 700 "$tmp" || { echo '!! unable to create a tmp dir' >&2; tmp=; exit 1; }

# read clipboard or file (assuming text)
if [ "$#" -eq  "0" ]
then
    # store clipboard to tmp (assuming text)

        xclip -selection clipboard -o > "$tmp/some.txt" 2>/dev/null ||  powershell.exe Get-clipboard > "$tmp/some.txt" || exit 1
 

else
    # file
    cp "$1" "$tmp/some.txt"
fi

# reflow so each line is sentence (assuming text with some punctuations)
cat "$tmp/some.txt" | tr '\r\n' ' ' | sed 's/[.!?]  */&\n/g' | sed 's/  */ /g' > "$tmp/some2.txt"
printf "\n\n\n\n" >> "$tmp/some2.txt" # new lines

# measure to get # of parts
all="0"
while read -r line1; read -r line2; read -r line3; read -r line4; read -r line5; do
    ((all=all+1))
    #echo "$all"
done < "$tmp/some2.txt"

# loop over lines and read each
part="0"
while read -r line1; read -r line2; read -r line3; read -r line4; read -r line5; do

    line="$line1 $line2 $line3 $line4 $line5" # Let's throw 5 lines at once, so that the engine tokenizer has a chance to be smart.
    ((part=part+1))

    if [ "$engine" == "flite" ]; then
        echoline
        flite -voice slt -t "$line" -o "$tmp/some.wav"
        play "$tmp/some.wav" 2>/dev/null
    elif [ "$engine" == "google" ]; then
        echoline
        rand=$[$RANDOM % ${#tld[@]}]
        echo "(tld ${tld[$rand]})"
        gtts-cli "$(echo "$line")" -t ${tld[$rand]} | play - 2>/dev/null
    elif [ "$engine" == "ibm" ]; then
        scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
        source "$scriptdir/ibmapi.txt" || exit 1
        # ibmapi.txt should look like
        # apikey="{key}"
        # ibmurl="{url}"

        # line to json
        jq -c -n --arg input "${line}" '{ text: $input }' > "$tmp/ibm.json"

        # ibm action
        rand=$[$RANDOM % ${#ibmvoice[@]}]
        
        if (( all > 3 )); then # preload
            curl -X POST -u "apikey:${apikey}" --header "Content-Type: application/json" --header "Accept: audio/flac" --data-binary "@$tmp/ibm.json" "${ibmurl}/v1/synthesize?voice=${ibmvoice[$rand]}" --output "$tmp/$part" 2>/dev/null
            wait # after the download the previous playback is probably still running, so wait here
            mv "$tmp/$part" "$tmp/read"
            echoline
            echo "(voice ${ibmvoice[$rand]}) *"
            # the 'previous' playback
            play "$tmp/read" 2>/dev/null & # go to next while loop downloading next synth
        else # just pipe play
            echoline
            echo "(voice ${ibmvoice[$rand]})"
            curl -X POST -u "apikey:${apikey}" --header "Content-Type: application/json" --header "Accept: audio/flac" --data-binary "@$tmp/ibm.json" "${ibmurl}/v1/synthesize?voice=${ibmvoice[$rand]}" 2>/dev/null | play - 2>/dev/null
        fi
        

    else # no engine test
        echoline
        echo "(test)"
    fi

done < "$tmp/some2.txt"

wait # just in case?