#!/bin/bash # awsread, txt to speech using cloudy aws polly # needs aws, mpv, xclip?, md5sum, awk # usage: # awsread # will read from clipboard # awsreas /path/to/file.txt # will read from file # config voices=(Amy Brian Joanna Matthew Kendra Joey) #disabled Ivy Justin Emma Kimberly Salli #aws polly describe-voices --language-code en-GB ; aws polly describe-voices --language-code en-US resume="please" # non-empty is true keepAudio="" # non-empty is true # split text at (max:3000) bytes="600" # text display width in chars (50-80) width="50" # center text block centertext="mkay" # non-empty is true # storeroot storeroot="$HOME/tmp/recAWS" # debug 1 is true debug="0" # end config storeInit (){ # hash based on file path hash="$(readlink -f "$input" | md5sum | awk '{print $1}')" (( debug )) && echo "$hash" store="$storeroot/${hash}/" mkdir -p "$store" || exit touch "$store" || exit } resumeSave (){ if [[ "$base" != "clipboard" ]]; then echo "$part" > "$store/resume" echo "$base" > "$store/name" echo "$fullpath" >> "$store/name" fi } # padding to center calcPad(){ if [[ -n $centertext ]]; then columns="$(tput cols)" pad="$((( columns - width )/2))" else pad="0" fi } # calc percentage (will need perc2 script to actually display that?) perc () { local tmp tmp="$(bc -l <<< "$1/$2*100")" awk 'BEGIN{printf "%."'"2"'"f\n", "'"$tmp"'"}' } help () { cat << EOF awsread, txt to speech using cloudy aws polly needs aws (free validated aws account), mpv, xclip?, md5sum, awk ----------------------------------------------------------------- usage: awsread # will read from clipboard awsreas file.txt # will read from file awsread --goto 100 file.txt # or -g goto page 100, will set resume to this new entry awsread --list # or -l list over store awsread --random # or -r read random page awsread --hash ab # or -x will read first file that includes those letters in hash interactive keys: q # skip to next block (mpv) space # toggle playback (mpv) ctrl+c # press for exit and to get the normal cursor back config: 'resume' is enabled by default and should work for files only, not for clipboard. 'keepAudio' is disabled by default, if enabled it will keep ogg-vorbis audio files in configured directory. For more, see the header section of the script. EOF } # switches # help && exit if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then # print help help exit fi # goto if [ "$1" == "--goto" ] || [ "$1" == "-g" ]; then # goto chunk shift jumpto="$1" # assuming --goto 120 shift fi # list && exit if [ "$1" == "--list" ] || [ "$1" == "-l" ]; then # list stored resumes/names ... cd "$storeroot" || exit # sort by date horror find . -type f -iname "name" -printf "%T@\t%Tc %h\n" | sort -n | cut -d " " -f 8- |\ while read -r path; do name="$(tail -1 "$path/name")" from="$(cat "$path/resume")" printf -v from "% 4d" "$from" # pad with spaces path="$(basename "$path")" #&& path="${path:0:8}..." # clean and shorten path/hash echo -e "$path\t$from\t$name" done exit fi # random if [ "$1" == "--random" ] || [ "$1" == "-r" ]; then # goto random page random="1" # 1 is true (( debug )) && echo "Random page requested" shift # assuming --random file.txt fi # hash if [ "$1" == "--hash" ] || [ "$1" == "-x" ]; then # find file by hash shift search="$1" cd "$storeroot" || exit cd -- "$(find . -name "*$search*" -type d | tail -1)" || exit input="$(cat name | tail -1)" || exit fi # else just path to file if [ -f "$1" ]; then input="$1" fi # start # assuming we have a valid $input file path here, lets check again if [ -f "$input" ]; then echo "$input" fullpath="$(readlink -f "$input")" (( debug )) && echo "$fullpath" # else its probably clipboard, checked again later in the code ˇ fi # storeInit storeInit # gets done in any case # hide cursor tput civis # checks command -v aws >/dev/null 2>&1 || { echo "I need aws cli, exiting." >&2; exit 1; } command -v awk >/dev/null 2>&1 || { echo "I need awk, exiting." >&2; exit 1; } command -v mpv >/dev/null 2>&1 || { echo "I need mpv player, exiting." >&2; exit 1; } command -v md5sum >/dev/null 2>&1 || { echo "I need md5sum, exiting." >&2; exit 1; } cleanup () { (( debug )) && echo "$?" [ -n "$tmp" ] && rm -fr "$tmp" tput cnorm } # tmp dir tmp="/tmp/$RANDOM-$$" trap cleanup EXIT SIGTERM SIGINT # https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html mkdir -m 700 "$tmp" || { echo '!! unable to create a tmp dir' >&2; tmp=; exit 1; } # named pipe pipe="$tmp/pipe" if [[ ! -p $pipe ]]; then mkfifo $pipe fi # play play (){ if [[ -n $keepAudio ]]; then name="$(($(date +%s%N)/1000000))" # lets name parts by epoch miliseconds mpv --no-resume-playback --msg-level=all=no --no-video --record-file="${store}${name}.ogg" "$1" else mpv --no-resume-playback --msg-level=all=no --no-video "$1" fi } # 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 base="clipboard" else # file [[ -f "$input" ]] || exit 1 cp "$input" "$tmp/some.txt" # base baseext=${input##*/} # file.ext base="${baseext%.*}" # file # resume if [[ -n $resume ]] && ((! random )); then if [[ -n $jumpto ]]; then # jumpto is set, assuming --goto was used echo "Goto $jumpto" elif [[ -f "$store/resume" ]]; then jumpto="$(cat "$store/resume")" else jumpto="1" fi (( jumpto > 1 )) && echo "Resume from part $jumpto" fi fi # tr remove newlines, sed add newlines where punctuations are, # sed remove double spaces, split by some bytes, keep lines cd "$tmp" || exit cat "some.txt" | tr '\r\n' ' ' | sed 's/[.!?] */&\n/g' | sed 's/ \{1,\}/ /g' | split --line-bytes="${bytes}" || exit 1 # count generated files all="$(find . -type f -name "x*" | wc -l)" (( random )) && randomPage="$(( RANDOM % all ))" (( debug )) && echo "all $all, randomPage $randomPage" # read part="0" for file in x*; do ((part=part+1)) # random if (( random )); then (( part < randomPage )) && continue resume="" # just to skip next section fi # resume if [[ -n $resume ]]; then (( part < jumpto )) && continue fi (( random )) || resumeSave # store position if random is false calcPad # How much to pad to center block rand="(($RANDOM % ${#voices[@]}))" echoBlock (){ echo # print header and text part (( all > 1 )) && echo "(${part}/${all})" cat "$file" | fmt -w ${width} | sed 's/ \{1,\}/ /g' echo "─── (voice ${voices[$rand]}) ─── ${perc}%" } perc="$( perc "$part" "$all" )" echoBlock | pr -T -o "$pad" # perc2 "$perc" # < to much stuff on screen # replace text that is ofen read wrong, like 'dr.' sed -i 's/Dr\./Doctor/' "$file" # synth aws polly synthesize-speech \ --output-format ogg_vorbis \ --voice-id "${voices[$rand]}" \ --text "$(cat "$file")" "$tmp/pipe" | play "$tmp/pipe" 2>/dev/null || exit done