"Changes for Basque language"
parent
5071f38dab
commit
7221bbf486
|
@ -0,0 +1,3 @@
|
|||
# Disable sourcing errors
|
||||
disable=SC1090
|
||||
disable=SC1091
|
|
@ -14,7 +14,7 @@ pipeline {
|
|||
}
|
||||
environment {
|
||||
//spawns GITHUB_USR and GITHUB_PSW environment variables
|
||||
GITHUB=credentials('38b2e4a6-167a-40b2-be6f-d69be42c8190')
|
||||
GITHUB=credentials('DevOps-CLA-Checker-Github-Key')
|
||||
}
|
||||
steps {
|
||||
// Using an install of Github repo CLA tagger
|
||||
|
|
|
@ -21,4 +21,4 @@ DIR="$( dirname "$SOURCE" )"
|
|||
source "$DIR/../venv-activate.sh" -q
|
||||
|
||||
# Invoke the Command Line Interface
|
||||
python -m mycroft.client.text $@
|
||||
python -m mycroft.client.text "$@"
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
cd -P "$( dirname "$SOURCE" )"
|
||||
cd -P "$( dirname "$SOURCE" )" || exit
|
||||
DIR="$( pwd )"
|
||||
script=${0}
|
||||
script=${script##*/}
|
||||
|
@ -49,7 +49,7 @@ function help() {
|
|||
|
||||
VIEWER="nano --syntax=json --view"
|
||||
if [ -z "$EDITOR" ] ; then
|
||||
if [ $( which sensible-editor ) ] ; then
|
||||
if which sensible-editor > /dev/null ; then
|
||||
EDITOR="sensible-editor"
|
||||
else
|
||||
EDITOR="nano --syntax=json --tempfile"
|
||||
|
@ -65,11 +65,17 @@ function found_exe() {
|
|||
}
|
||||
|
||||
if found_exe tput ; then
|
||||
# shellcheck disable=SC2034
|
||||
GREEN="$(tput setaf 2)"
|
||||
# shellcheck disable=SC2034
|
||||
BLUE="$(tput setaf 4)"
|
||||
# shellcheck disable=SC2034
|
||||
CYAN="$(tput setaf 6)"
|
||||
# shellcheck disable=SC2034
|
||||
YELLOW="$(tput setaf 3)"
|
||||
# shellcheck disable=SC2034
|
||||
RESET="$(tput sgr0)"
|
||||
# shellcheck disable=SC2034
|
||||
HIGHLIGHT=${YELLOW}
|
||||
fi
|
||||
|
||||
|
@ -82,14 +88,14 @@ function validate_config_file() {
|
|||
return 0
|
||||
fi
|
||||
|
||||
echo -n ${BLUE}
|
||||
echo -n "${BLUE}"
|
||||
|
||||
# Remove any comments (lines starting with # or //) found in the file and
|
||||
# Use jq to validate and output errors
|
||||
sed 's/^\s*[#\/].*$//g' "$1" | sed '/^$/d' | jq -e "." > /dev/null
|
||||
result=$?
|
||||
|
||||
echo -n ${RESET}
|
||||
echo -n "${RESET}"
|
||||
|
||||
#xxx echo "RESULT=$result for $1"
|
||||
return $result
|
||||
|
@ -99,7 +105,7 @@ _conf_file="${XDG_CONFIG_HOME:-$HOME/.config}/mycroft/mycroft.conf"
|
|||
function name_to_path() {
|
||||
case ${1} in
|
||||
"system") _conf_file="/etc/mycroft/mycroft.conf" ;;
|
||||
"user") _conf_file=$(readlink -f ${XDG_CONFIG_HOME:-$HOME/.config}/mycroft/mycroft.conf) ;;
|
||||
"user") _conf_file=$(readlink -f "${XDG_CONFIG_HOME:-$HOME/.config}/mycroft/mycroft.conf") ;;
|
||||
"default") _conf_file="$DIR/../mycroft/configuration/mycroft.conf" ;;
|
||||
"remote") _conf_file="$HOME/.cache/mycroft/web_cache.json" ;;
|
||||
|
||||
|
@ -113,12 +119,12 @@ function name_to_path() {
|
|||
################################################################
|
||||
|
||||
function edit_config() {
|
||||
name_to_path $1
|
||||
validate_config_file $_conf_file
|
||||
name_to_path "$1"
|
||||
validate_config_file "$_conf_file"
|
||||
rc=$?
|
||||
if [ $rc -ne 0 ] ; then
|
||||
echo "${YELLOW}WARNING: ${RESET}Configuration file did not pass validation before edits."
|
||||
read -p "Review errors above and press ENTER to continue with editing."
|
||||
read -r -p "Review errors above and press ENTER to continue with editing."
|
||||
fi
|
||||
|
||||
if [ -f "${_conf_file}" ] ; then
|
||||
|
@ -128,7 +134,7 @@ function edit_config() {
|
|||
echo "}" >> "${TEMP}/mycroft.json"
|
||||
fi
|
||||
|
||||
while [ 1 ] ; do
|
||||
while true ; do
|
||||
case $1 in
|
||||
system | user)
|
||||
# Allow user to edit
|
||||
|
@ -150,19 +156,18 @@ function edit_config() {
|
|||
fi
|
||||
|
||||
# file was changed, validate changes
|
||||
validate_config_file $TEMP/mycroft.json
|
||||
if [ $? -ne 0 ] ; then
|
||||
if validate_config_file $TEMP/mycroft.json > /dev/null ; then
|
||||
key="S"
|
||||
else
|
||||
echo "${YELLOW}WARNING: ${RESET}Configuration file does not pass validation, see errors above."
|
||||
echo "Press X to abandon changes, S to force save, any other key to edit again."
|
||||
read -N1 -s key
|
||||
else
|
||||
key="S"
|
||||
read -r -N1 -s key
|
||||
fi
|
||||
|
||||
case $key in
|
||||
[Ss])
|
||||
echo "Saving..."
|
||||
mv $TEMP/mycroft.json $_conf_file
|
||||
mv $TEMP/mycroft.json "$_conf_file"
|
||||
signal_reload_config
|
||||
break
|
||||
;;
|
||||
|
@ -180,11 +185,11 @@ function signal_reload_config() {
|
|||
source "$DIR/../venv-activate.sh" -q
|
||||
|
||||
# Post a messagebus notification to reload the config file
|
||||
output=$(python -m mycroft.messagebus.send "configuration.updated" "{}")
|
||||
python -m mycroft.messagebus.send "configuration.updated" "{}" > /dev/null
|
||||
}
|
||||
|
||||
function show_config() {
|
||||
name_to_path $1
|
||||
name_to_path "$1"
|
||||
|
||||
# Use jq to display formatted nicely (after stripping out comments)
|
||||
sed 's/^\s*[#\/].*$//g' "${_conf_file}" | sed '/^$/d' | jq "."
|
||||
|
@ -201,7 +206,7 @@ function get_config() {
|
|||
json_config=$( source "$DIR/../venv-activate.sh" -q && python -c "import json; from mycroft.configuration import Configuration; print(json.dumps(Configuration.get()))" )
|
||||
|
||||
# Read the given variable from the mix
|
||||
echo ${json_config} | jq -r "${value}"
|
||||
echo "${json_config}" | jq -r "${value}"
|
||||
}
|
||||
|
||||
function set_config() {
|
||||
|
@ -212,10 +217,9 @@ function set_config() {
|
|||
value=".${value}"
|
||||
fi
|
||||
|
||||
jq "${value} = \"$2\"" ~/.mycroft/mycroft.conf > "${TEMP}/~mycroft.conf"
|
||||
if [ $? -eq 0 ] ; then
|
||||
if jq "${value} = \"$2\"" "$_conf_file" > "${TEMP}/~mycroft.conf" ; then
|
||||
# Successful update, replace the config file
|
||||
mv "${TEMP}/~mycroft.conf" ~/.mycroft/mycroft.conf
|
||||
mv "${TEMP}/~mycroft.conf" "$_conf_file"
|
||||
signal_reload_config
|
||||
fi
|
||||
}
|
||||
|
@ -223,16 +227,16 @@ function set_config() {
|
|||
_opt=$1
|
||||
case ${_opt} in
|
||||
"edit")
|
||||
edit_config $2
|
||||
edit_config "$2"
|
||||
;;
|
||||
"reload")
|
||||
signal_reload_config
|
||||
;;
|
||||
"show")
|
||||
show_config $2
|
||||
show_config "$2"
|
||||
;;
|
||||
"get")
|
||||
get_config $2
|
||||
get_config "$2"
|
||||
;;
|
||||
"set")
|
||||
set_config "$2" "$3"
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
# limitations under the License.
|
||||
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
cd -P "$( dirname "$SOURCE" )"/..
|
||||
cd -P "$( dirname "$SOURCE" )"/.. || exit
|
||||
DIR="$( pwd )"
|
||||
|
||||
echo -e "\e[36mMycroft\e[0m is your open source voice assistant. Full source"
|
||||
echo -e "\\e[36mMycroft\\e[0m is your open source voice assistant. Full source"
|
||||
echo "can be found at: ${DIR}"
|
||||
echo
|
||||
echo "Mycroft-specific commands you can use from the Linux command prompt:"
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
cd -P "$( dirname "$SOURCE" )"
|
||||
cd -P "$( dirname "$SOURCE" )" || exit
|
||||
DIR="$( pwd )"
|
||||
|
||||
# Enter the Mycroft venv
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
cd -P "$( dirname "$SOURCE" )"
|
||||
cd -P "$( dirname "$SOURCE" )" || exit
|
||||
DIR="$( pwd )"
|
||||
|
||||
restart=0
|
||||
|
@ -32,7 +32,7 @@ fi
|
|||
|
||||
|
||||
# Launch the standard audiotest
|
||||
"$DIR/../start-mycroft.sh" audiotest $@
|
||||
"$DIR/../start-mycroft.sh" audiotest "$@"
|
||||
|
||||
|
||||
if [ $restart -eq 1 ]
|
||||
|
|
|
@ -21,4 +21,4 @@ DIR="$( dirname "$SOURCE" )"
|
|||
source "$DIR/../venv-activate.sh" -q
|
||||
|
||||
# Invoke the Mycroft Skills Kit from within the venv
|
||||
msk $@
|
||||
msk "$@"
|
||||
|
|
|
@ -21,4 +21,4 @@ DIR="$( dirname "$SOURCE" )"
|
|||
source "$DIR/../venv-activate.sh" -q
|
||||
|
||||
# Invoke the Mycroft Skills Manager (msm) within the venv
|
||||
msm $@
|
||||
msm "$@"
|
||||
|
|
|
@ -21,4 +21,4 @@ DIR="$( dirname "$SOURCE" )"
|
|||
source "$DIR/../venv-activate.sh" -q
|
||||
|
||||
# Install pip packages within the Mycroft venv
|
||||
pip $@
|
||||
pip "$@"
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
cd -P "$( dirname "$SOURCE" )"
|
||||
cd -P "$( dirname "$SOURCE" )" || exit
|
||||
DIR="$( pwd )"
|
||||
|
||||
# Enter the Mycroft venv
|
||||
|
@ -25,5 +25,5 @@ source "$DIR/../venv-activate.sh" -q
|
|||
set -- "${1:-$(</dev/stdin)}" "${@:2}"
|
||||
|
||||
# Send a message to be spoken
|
||||
data="$@"
|
||||
output=$(python -m mycroft.messagebus.send "recognizer_loop:utterance" "{\"utterances\": [\"$data\"], \"lang\": \"en-us\"}")
|
||||
data="$*"
|
||||
python -m mycroft.messagebus.send "recognizer_loop:utterance" "{\"utterances\": [\"$data\"], \"lang\": \"en-us\"}" >> /dev/null
|
||||
|
|
|
@ -20,26 +20,31 @@ DIR="$( dirname "$SOURCE" )"
|
|||
# Enter the Mycroft venv
|
||||
source "$DIR/../venv-activate.sh" -q
|
||||
|
||||
function count-files() {
|
||||
# shellcheck disable=SC2012
|
||||
ls "$1" | wc -l
|
||||
}
|
||||
|
||||
function vktest-clear() {
|
||||
FEATURES_DIR="$DIR/../test/integrationtests/voight_kampff/features"
|
||||
num_feature_files=$(ls $FEATURES_DIR | wc -l)
|
||||
num_feature_files=$(count-files "$FEATURES_DIR")
|
||||
# A clean directory will have `steps/` and `environment.py`
|
||||
if [ $num_feature_files -gt "2" ] ; then
|
||||
if [ "$num_feature_files" -gt "2" ] ; then
|
||||
echo "Removing Feature files..."
|
||||
rm ${DIR}/../test/integrationtests/voight_kampff/features/*.feature
|
||||
rm ${DIR}/../test/integrationtests/voight_kampff/features/*.config.json
|
||||
rm -f "${DIR}"/../test/integrationtests/voight_kampff/features/*.feature
|
||||
rm -f "${DIR}"/../test/integrationtests/voight_kampff/features/*.config.json
|
||||
fi
|
||||
STEPS_DIR="$FEATURES_DIR/steps"
|
||||
num_steps_files=$(ls $STEPS_DIR | wc -l)
|
||||
if [ $num_steps_files -gt "2" ] ; then
|
||||
num_steps_files=$(count-files "$STEPS_DIR")
|
||||
if [ "$num_steps_files" -gt "2" ] ; then
|
||||
echo "Removing Custom Step files..."
|
||||
TMP_DIR="$STEPS_DIR/tmp"
|
||||
mkdir $TMP_DIR
|
||||
mv "$STEPS_DIR/configuration.py" $TMP_DIR
|
||||
mv "$STEPS_DIR/utterance_responses.py" $TMP_DIR
|
||||
rm ${STEPS_DIR}/*.py
|
||||
mv ${TMP_DIR}/* $STEPS_DIR
|
||||
rmdir $TMP_DIR
|
||||
mkdir "$TMP_DIR"
|
||||
mv "$STEPS_DIR/configuration.py" "$TMP_DIR"
|
||||
mv "$STEPS_DIR/utterance_responses.py" "$TMP_DIR"
|
||||
rm -f "${STEPS_DIR}"/*.py
|
||||
mv "${TMP_DIR}"/* "$STEPS_DIR"
|
||||
rmdir "$TMP_DIR"
|
||||
fi
|
||||
echo "Voight Kampff tests clear."
|
||||
}
|
||||
|
@ -55,5 +60,5 @@ elif [ "$1" = "vktest" ] ; then
|
|||
python -m test.integrationtests.voight_kampff "$@"
|
||||
fi
|
||||
else
|
||||
python -m test.integrationtests.skills.runner $@
|
||||
python -m test.integrationtests.skills.runner "$@"
|
||||
fi
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
cd -P "$( dirname "$SOURCE" )"
|
||||
cd -P "$( dirname "$SOURCE" )" || exit
|
||||
DIR="$( pwd )"
|
||||
|
||||
# Sets var 1 to stdin if no args were given
|
||||
|
@ -23,5 +23,5 @@ set -- "${1:-$(</dev/stdin)}" "${@:2}"
|
|||
source "$DIR/../venv-activate.sh" -q
|
||||
|
||||
# Send a message to be spoken
|
||||
data="$@"
|
||||
output=$(python -m mycroft.messagebus.send "speak" "{\"utterance\": \"$data\"}")
|
||||
data="$*"
|
||||
python -m mycroft.messagebus.send "speak" "{\"utterance\": \"$data\"}" >> /dev/null
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
cd -P "$( dirname "$SOURCE" )"/..
|
||||
cd -P "$( dirname "$SOURCE" )"/.. || exit
|
||||
DIR="$( pwd )"
|
||||
|
||||
. "$DIR/start-mycroft.sh" $@
|
||||
. "$DIR/start-mycroft.sh" "$@"
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
cd -P "$( dirname "$SOURCE" )"/..
|
||||
cd -P "$( dirname "$SOURCE" )"/.. || exit
|
||||
DIR="$( pwd )"
|
||||
|
||||
. "$DIR/stop-mycroft.sh" $@
|
||||
. "$DIR/stop-mycroft.sh" "$@"
|
||||
|
|
137
dev_setup.sh
137
dev_setup.sh
|
@ -22,22 +22,28 @@ export LANGUAGE=en
|
|||
# exit on any error
|
||||
set -Ee
|
||||
|
||||
cd $(dirname $0)
|
||||
ROOT_DIRNAME=$(dirname "$0")
|
||||
cd "$ROOT_DIRNAME"
|
||||
TOP=$(pwd -L)
|
||||
|
||||
function clean_mycroft_files() {
|
||||
echo '
|
||||
This will completely remove any files installed by mycroft (including pairing
|
||||
information).
|
||||
information).
|
||||
|
||||
NOTE: This will not remove Mimic (if you chose to compile it), or other files
|
||||
generated within the mycroft-core directory.
|
||||
|
||||
Do you wish to continue? (y/n)'
|
||||
while true; do
|
||||
read -N1 -s key
|
||||
read -rN1 -s key
|
||||
case $key in
|
||||
[Yy])
|
||||
sudo rm -rf /var/log/mycroft
|
||||
rm -f /var/tmp/mycroft_web_cache.json
|
||||
rm -rf "${TMPDIR:-/tmp}/mycroft"
|
||||
rm -rf "$HOME/.mycroft"
|
||||
rm -f "skills" # The Skills directory symlink
|
||||
sudo rm -rf "/opt/mycroft"
|
||||
exit 0
|
||||
;;
|
||||
|
@ -70,6 +76,7 @@ opt_forcemimicbuild=false
|
|||
opt_allowroot=false
|
||||
opt_skipmimicbuild=false
|
||||
opt_python=python3
|
||||
disable_precise_later=false
|
||||
param=''
|
||||
|
||||
for var in "$@" ; do
|
||||
|
@ -140,7 +147,7 @@ function get_YN() {
|
|||
# Loop until the user hits the Y or the N key
|
||||
echo -e -n "Choice [${CYAN}Y${RESET}/${CYAN}N${RESET}]: "
|
||||
while true; do
|
||||
read -N1 -s key
|
||||
read -rN1 -s key
|
||||
case $key in
|
||||
[Yy])
|
||||
return 0
|
||||
|
@ -176,8 +183,8 @@ your environment.'
|
|||
sleep 0.5
|
||||
# The AVX instruction set is an x86 construct
|
||||
# ARM has a range of equivalents, unsure which are (un)supported by TF.
|
||||
if ! grep -q avx /proc/cpuinfo && [[ ! $(uname -m) == 'arm'* ]]; then
|
||||
echo "
|
||||
if ! grep -q avx /proc/cpuinfo && ! [[ $(uname -m) == 'arm'* || $(uname -m) == 'aarch64' ]]; then
|
||||
echo "
|
||||
The Precise Wake Word Engine requires the AVX instruction set, which is
|
||||
not supported on your CPU. Do you want to fall back to the PocketSphinx
|
||||
engine? Advanced users can build the precise engine with an older
|
||||
|
@ -185,20 +192,20 @@ version of TensorFlow (v1.13) if desired and change use_precise to true
|
|||
in mycroft.conf.
|
||||
Y)es, I want to use the PocketSphinx engine or my own.
|
||||
N)o, stop the installation."
|
||||
if get_YN ; then
|
||||
if [[ ! -f /etc/mycroft/mycroft.conf ]]; then
|
||||
$SUDO mkdir -p /etc/mycroft
|
||||
$SUDO touch /etc/mycroft/mycroft.conf
|
||||
$SUDO bash -c 'echo "{ \"use_precise\": true }" > /etc/mycroft/mycroft.conf'
|
||||
if get_YN ; then
|
||||
if [[ ! -f /etc/mycroft/mycroft.conf ]]; then
|
||||
$SUDO mkdir -p /etc/mycroft
|
||||
$SUDO touch /etc/mycroft/mycroft.conf
|
||||
$SUDO bash -c 'echo "{ \"use_precise\": false }" > /etc/mycroft/mycroft.conf'
|
||||
else
|
||||
# Ensure dependency installed to merge configs
|
||||
disable_precise_later=true
|
||||
fi
|
||||
else
|
||||
$SUDO bash -c 'jq ". + { \"use_precise\": true }" /etc/mycroft/mycroft.conf > tmp.mycroft.conf'
|
||||
$SUDO mv -f tmp.mycroft.conf /etc/mycroft/mycroft.conf
|
||||
echo -e "$HIGHLIGHT N - quit the installation $RESET"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "$HIGHLIGHT N - quit the installation $RESET"
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
echo
|
||||
fi
|
||||
echo "
|
||||
Do you want to run on 'master' or against a dev branch? Unless you are
|
||||
|
@ -265,9 +272,11 @@ Would you like this to be added to your PATH in the .profile?'
|
|||
if [[ ! -f ~/.profile_mycroft ]] ; then
|
||||
# Only add the following to the .profile if .profile_mycroft
|
||||
# doesn't exist, indicating this script has not been run before
|
||||
echo '' >> ~/.profile
|
||||
echo '# include Mycroft commands' >> ~/.profile
|
||||
echo 'source ~/.profile_mycroft' >> ~/.profile
|
||||
{
|
||||
echo ''
|
||||
echo '# include Mycroft commands'
|
||||
echo 'source ~/.profile_mycroft'
|
||||
} >> ~/.profile
|
||||
fi
|
||||
|
||||
echo "
|
||||
|
@ -289,9 +298,9 @@ fi" > ~/.profile_mycroft
|
|||
echo 'This script will create that folder for you. This requires sudo'
|
||||
echo 'permission and might ask you for a password...'
|
||||
setup_user=$USER
|
||||
setup_group=$(id -gn $USER)
|
||||
setup_group=$(id -gn "$USER")
|
||||
$SUDO mkdir -p /opt/mycroft/skills
|
||||
$SUDO chown -R ${setup_user}:${setup_group} /opt/mycroft
|
||||
$SUDO chown -R "${setup_user}":"${setup_group}" /opt/mycroft
|
||||
echo 'Created!'
|
||||
fi
|
||||
if [[ ! -d skills ]] ; then
|
||||
|
@ -319,7 +328,7 @@ If unsure answer yes.
|
|||
fi
|
||||
|
||||
function os_is() {
|
||||
[[ $(grep "^ID=" /etc/os-release | awk -F'=' '/^ID/ {print $2}' | sed 's/\"//g') == $1 ]]
|
||||
[[ $(grep "^ID=" /etc/os-release | awk -F'=' '/^ID/ {print $2}' | sed 's/\"//g') == "$1" ]]
|
||||
}
|
||||
|
||||
function os_is_like() {
|
||||
|
@ -339,11 +348,11 @@ function redhat_common_install() {
|
|||
}
|
||||
|
||||
function debian_install() {
|
||||
APT_PACKAGE_LIST="git python3 python3-dev python3-setuptools libtool \
|
||||
APT_PACKAGE_LIST=(git python3 python3-dev python3-setuptools libtool \
|
||||
libffi-dev libssl-dev autoconf automake bison swig libglib2.0-dev \
|
||||
portaudio19-dev mpg123 screen flac curl libicu-dev pkg-config \
|
||||
libjpeg-dev libfann-dev build-essential jq pulseaudio \
|
||||
pulseaudio-utils"
|
||||
pulseaudio-utils)
|
||||
|
||||
if dpkg -V libjack-jackd2-0 > /dev/null 2>&1 && [[ -z ${CI} ]] ; then
|
||||
echo "
|
||||
|
@ -351,10 +360,10 @@ We have detected that your computer has the libjack-jackd2-0 package installed.
|
|||
Mycroft requires a conflicting package, and will likely uninstall this package.
|
||||
On some systems, this can cause other programs to be marked for removal.
|
||||
Please review the following package changes carefully."
|
||||
read -p "Press enter to continue"
|
||||
$SUDO apt-get install $APT_PACKAGE_LIST
|
||||
read -rp "Press enter to continue"
|
||||
$SUDO apt-get install "${APT_PACKAGE_LIST[@]}"
|
||||
else
|
||||
$SUDO apt-get install -y $APT_PACKAGE_LIST
|
||||
$SUDO apt-get install -y "${APT_PACKAGE_LIST[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -371,7 +380,15 @@ function fedora_install() {
|
|||
|
||||
|
||||
function arch_install() {
|
||||
$SUDO pacman -S --needed --noconfirm git python python-pip python-setuptools python-virtualenv python-gobject libffi swig portaudio mpg123 screen flac curl icu libjpeg-turbo base-devel jq pulseaudio pulseaudio-alsa
|
||||
pkgs=( git python python-pip python-setuptools python-virtualenv python-gobject libffi swig portaudio mpg123 screen flac curl icu libjpeg-turbo base-devel jq )
|
||||
|
||||
if ! pacman -Qs pipewire-pulse > /dev/null
|
||||
then
|
||||
pulse_pkgs=( pulseaudio pulseaudio-alsa )
|
||||
pkgs=( "${pkgs[@]}" "${pulse_pkgs[@]}" )
|
||||
fi
|
||||
|
||||
$SUDO pacman -S --needed --noconfirm "${pkgs[@]}"
|
||||
|
||||
pacman -Qs '^fann$' &> /dev/null || (
|
||||
git clone https://aur.archlinux.org/fann.git
|
||||
|
@ -398,11 +415,30 @@ function redhat_install() {
|
|||
}
|
||||
|
||||
function gentoo_install() {
|
||||
$SUDO emerge --noreplace dev-vcs/git dev-lang/python dev-python/setuptools dev-python/pygobject dev-python/requests sys-devel/libtool virtual/libffi virtual/jpeg dev-libs/openssl sys-devel/autoconf sys-devel/bison dev-lang/swig dev-libs/glib media-libs/portaudio media-sound/mpg123 media-libs/flac net-misc/curl sci-mathematics/fann sys-devel/gcc app-misc/jq media-libs/alsa-lib dev-libs/icu
|
||||
$SUDO emerge --noreplace dev-vcs/git dev-lang/python dev-python/setuptools dev-python/pygobject dev-python/requests sys-devel/libtool dev-libs/libffi virtual/jpeg dev-libs/openssl sys-devel/autoconf sys-devel/bison dev-lang/swig dev-libs/glib media-libs/portaudio media-sound/mpg123 media-libs/flac net-misc/curl sci-mathematics/fann sys-devel/gcc app-misc/jq media-libs/alsa-lib dev-libs/icu
|
||||
}
|
||||
|
||||
function alpine_install() {
|
||||
$SUDO apk add --virtual makedeps-mycroft-core alpine-sdk git python3 py3-pip py3-setuptools py3-virtualenv mpg123 vorbis-tools pulseaudio-utils fann-dev automake autoconf libtool pcre2-dev pulseaudio-dev alsa-lib-dev swig python3-dev portaudio-dev libjpeg-turbo-dev
|
||||
$SUDO apk add --virtual .makedeps-mycroft-core \
|
||||
alpine-sdk \
|
||||
alsa-lib-dev \
|
||||
autoconf \
|
||||
automake \
|
||||
fann-dev \
|
||||
git \
|
||||
libjpeg-turbo-dev \
|
||||
libtool \
|
||||
mpg123 \
|
||||
pcre2-dev \
|
||||
portaudio-dev \
|
||||
pulseaudio-utils \
|
||||
py3-pip \
|
||||
py3-setuptools \
|
||||
py3-virtualenv \
|
||||
python3 \
|
||||
python3-dev \
|
||||
swig \
|
||||
vorbis-tools
|
||||
}
|
||||
|
||||
function install_deps() {
|
||||
|
@ -445,7 +481,7 @@ function install_deps() {
|
|||
${YELLOW}Make sure to manually install:$BLUE git python3 python-setuptools python-venv pygobject libtool libffi libjpg openssl autoconf bison swig glib2.0 portaudio19 mpg123 flac curl fann g++ jq\n$RESET"
|
||||
|
||||
echo 'Warning: Failed to install all dependencies. Continue? y/N'
|
||||
read -n1 continue
|
||||
read -rn1 continue
|
||||
if [[ $continue != 'y' ]] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
@ -457,16 +493,30 @@ VIRTUALENV_ROOT=${VIRTUALENV_ROOT:-"${TOP}/.venv"}
|
|||
|
||||
function install_venv() {
|
||||
$opt_python -m venv "${VIRTUALENV_ROOT}/" --without-pip
|
||||
|
||||
# Check if old script for python 3.6 is needed
|
||||
if "${VIRTUALENV_ROOT}/bin/${opt_python}" --version | grep " 3.6" > /dev/null; then
|
||||
GET_PIP_URL="https://bootstrap.pypa.io/pip/3.6/get-pip.py"
|
||||
else
|
||||
GET_PIP_URL="https://bootstrap.pypa.io/get-pip.py"
|
||||
fi
|
||||
|
||||
# Force version of pip for reproducability, but there is nothing special
|
||||
# about this version. Update whenever a new version is released and
|
||||
# verified functional.
|
||||
curl https://bootstrap.pypa.io/get-pip.py | "${VIRTUALENV_ROOT}/bin/python" - 'pip==20.0.2'
|
||||
curl "${GET_PIP_URL}" | "${VIRTUALENV_ROOT}/bin/${opt_python}" - 'pip==20.0.2'
|
||||
# Function status depending on if pip exists
|
||||
[[ -x ${VIRTUALENV_ROOT}/bin/pip ]]
|
||||
}
|
||||
|
||||
install_deps
|
||||
|
||||
# It's later. Update existing config with jq.
|
||||
if [[ $disable_precise_later == true ]]; then
|
||||
$SUDO bash -c 'jq ". + { \"use_precise\": false }" /etc/mycroft/mycroft.conf > tmp.mycroft.conf'
|
||||
$SUDO mv -f tmp.mycroft.conf /etc/mycroft/mycroft.conf
|
||||
fi
|
||||
|
||||
# Configure to use the standard commit template for
|
||||
# this repo only.
|
||||
git config commit.template .gitmessage
|
||||
|
@ -479,7 +529,7 @@ else
|
|||
# first, look for a build of mimic in the folder
|
||||
has_mimic=''
|
||||
if [[ -f ${TOP}/mimic/bin/mimic ]] ; then
|
||||
has_mimic=$(${TOP}/mimic/bin/mimic -lv | grep Voice) || true
|
||||
has_mimic=$("${TOP}"/mimic/bin/mimic -lv | grep Voice) || true
|
||||
fi
|
||||
|
||||
# in not, check the system path
|
||||
|
@ -506,6 +556,7 @@ if [[ ! -x ${VIRTUALENV_ROOT}/bin/activate ]] ; then
|
|||
fi
|
||||
|
||||
# Start the virtual environment
|
||||
# shellcheck source=/dev/null
|
||||
source "${VIRTUALENV_ROOT}/bin/activate"
|
||||
cd "$TOP"
|
||||
|
||||
|
@ -514,7 +565,7 @@ HOOK_FILE='./.git/hooks/pre-commit'
|
|||
if [[ -n $INSTALL_PRECOMMIT_HOOK ]] || grep -q 'MYCROFT DEV SETUP' $HOOK_FILE; then
|
||||
if [[ ! -f $HOOK_FILE ]] || grep -q 'MYCROFT DEV SETUP' $HOOK_FILE; then
|
||||
echo 'Installing PEP8 check as precommit-hook'
|
||||
echo "#! $(which python)" > $HOOK_FILE
|
||||
echo "#! $(command -v python)" > $HOOK_FILE
|
||||
echo '# MYCROFT DEV SETUP' >> $HOOK_FILE
|
||||
cat ./scripts/pre-commit >> $HOOK_FILE
|
||||
chmod +x $HOOK_FILE
|
||||
|
@ -532,17 +583,15 @@ if [[ ! -f $VENV_PATH_FILE ]] ; then
|
|||
echo "import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)" >> "$VENV_PATH_FILE" || return 1
|
||||
fi
|
||||
|
||||
if ! grep -q "$TOP" $VENV_PATH_FILE ; then
|
||||
if ! grep -q "$TOP" "$VENV_PATH_FILE" ; then
|
||||
echo 'Adding mycroft-core to virtualenv path'
|
||||
sed -i.tmp '1 a\
|
||||
'"$TOP"'
|
||||
' "$VENV_PATH_FILE"
|
||||
sed -i.tmp "1 a$TOP" "$VENV_PATH_FILE"
|
||||
fi
|
||||
|
||||
# install required python modules
|
||||
if ! pip install -r requirements/requirements.txt ; then
|
||||
echo 'Warning: Failed to install required dependencies. Continue? y/N'
|
||||
read -n1 continue
|
||||
read -rn1 continue
|
||||
if [[ $continue != 'y' ]] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
@ -553,7 +602,7 @@ if [[ ! $(pip install -r requirements/extra-audiobackend.txt) ||
|
|||
! $(pip install -r requirements/extra-stt.txt) ||
|
||||
! $(pip install -r requirements/extra-mark1.txt) ]] ; then
|
||||
echo 'Warning: Failed to install some optional dependencies. Continue? y/N'
|
||||
read -n1 continue
|
||||
read -rn1 continue
|
||||
if [[ $continue != 'y' ]] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
@ -565,7 +614,7 @@ if ! pip install -r requirements/tests.txt ; then
|
|||
fi
|
||||
|
||||
SYSMEM=$(free | awk '/^Mem:/ { print $2 }')
|
||||
MAXCORES=$(($SYSMEM / 2202010))
|
||||
MAXCORES=$((SYSMEM / 2202010))
|
||||
MINCORES=1
|
||||
CORES=$(nproc)
|
||||
|
||||
|
@ -590,7 +639,7 @@ cd "$TOP"
|
|||
|
||||
if [[ $build_mimic == 'y' || $build_mimic == 'Y' ]] ; then
|
||||
echo 'WARNING: The following can take a long time to run!'
|
||||
"${TOP}/scripts/install-mimic.sh" " $CORES"
|
||||
"${TOP}/scripts/install-mimic.sh" "$CORES"
|
||||
else
|
||||
echo 'Skipping mimic build.'
|
||||
fi
|
||||
|
|
|
@ -74,13 +74,7 @@ def handle_speak(event):
|
|||
# so we likely will want to get rid of this when not running on Mimic
|
||||
if (config.get('enclosure', {}).get('platform') != "picroft" and
|
||||
len(re.findall('<[^>]*>', utterance)) == 0):
|
||||
# Remove any whitespace present after the period,
|
||||
# if a character (only alpha) ends with a period
|
||||
# ex: A. Lincoln -> A.Lincoln
|
||||
# so that we don't split at the period
|
||||
utterance = re.sub(r'\b([A-za-z][\.])(\s+)', r'\g<1>', utterance)
|
||||
chunks = re.split(r'(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\;|\?)\s',
|
||||
utterance)
|
||||
chunks = tts.preprocess_utterance(utterance)
|
||||
# Apply the listen flag to the last chunk, set the rest to False
|
||||
chunks = [(chunks[i], listen if i == len(chunks) - 1 else False)
|
||||
for i in range(len(chunks))]
|
||||
|
@ -116,10 +110,9 @@ def mute_and_speak(utterance, ident, listen=False):
|
|||
# update TTS object if configuration has changed
|
||||
if tts_hash != hash(str(config.get('tts', ''))):
|
||||
global tts
|
||||
# Stop tts playback thread
|
||||
tts.playback.stop()
|
||||
tts.playback.join()
|
||||
# Create new tts instance
|
||||
if tts:
|
||||
tts.playback.detach_tts(tts)
|
||||
tts = TTSFactory.create()
|
||||
tts.init(bus)
|
||||
tts_hash = hash(str(config.get('tts', '')))
|
||||
|
|
|
@ -264,7 +264,7 @@ class Enclosure:
|
|||
|
||||
# Load any already stored Data
|
||||
data = self.datastore.get(namespace, {})
|
||||
for key in data:
|
||||
for key in dict(data):
|
||||
msg = {"type": "mycroft.session.set",
|
||||
"namespace": namespace,
|
||||
"data": {key: data[key]}}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import subprocess
|
||||
import time
|
||||
from alsaaudio import Mixer
|
||||
from os.path import join
|
||||
from threading import Thread, Timer
|
||||
|
||||
import serial
|
||||
|
@ -165,10 +166,11 @@ class EnclosureReader(Thread):
|
|||
if "unit.factory-reset" in data:
|
||||
self.bus.emit(Message("speak", {
|
||||
'utterance': mycroft.dialog.get("reset to factory defaults")}))
|
||||
subprocess.call(
|
||||
(f'rm {xdg.BaseDirectory.save_config_path("mycroft")}'
|
||||
'/mycroft/identity/identity2.json'),
|
||||
shell=True)
|
||||
xdg_identity_path = join(xdg.BaseDirectory.xdg_config_home,
|
||||
'mycroft',
|
||||
'identity',
|
||||
'identity2.json')
|
||||
subprocess.call(f'rm {xdg_identity_path}', shell=True)
|
||||
subprocess.call(
|
||||
'rm ~/.mycroft/identity/identity2.json',
|
||||
shell=True)
|
||||
|
|
|
@ -77,7 +77,7 @@ class HotWordEngine:
|
|||
self.key_phrase = str(key_phrase).lower()
|
||||
|
||||
if config is None:
|
||||
config = Configuration.get().get("hot_words", {})
|
||||
config = Configuration.get().get("hotwords", {})
|
||||
config = config.get(self.key_phrase, {})
|
||||
self.config = config
|
||||
|
||||
|
@ -201,7 +201,7 @@ class PreciseHotword(HotWordEngine):
|
|||
# Make sure we pick the key we need from wherever it's located,
|
||||
# but save to a writeable location only
|
||||
local_conf = LocalConf(
|
||||
join(xdg.BaseDirectory.save_config_path('mycroft'), 'mycroft.conf')
|
||||
join(xdg.BaseDirectory.xdg_config_home, 'mycroft', 'mycroft.conf')
|
||||
)
|
||||
|
||||
for conf_dir in xdg.BaseDirectory.load_config_paths('mycroft'):
|
||||
|
|
|
@ -185,7 +185,7 @@ def load_settings():
|
|||
LOG.warning(" Note that this location is deprecated and will" +
|
||||
" not be used in the future")
|
||||
LOG.warning(" Please move it to " +
|
||||
os.path.join(xdg.BaseDirectory.save_config_path('mycroft'),
|
||||
os.path.join(xdg.BaseDirectory.xdg_config_home, 'mycroft',
|
||||
filename))
|
||||
config_file = path
|
||||
|
||||
|
|
|
@ -14,19 +14,26 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
|
||||
import inflection
|
||||
import json
|
||||
from os.path import exists, isfile, join
|
||||
import os
|
||||
import re
|
||||
from os.path import exists, isfile, join, dirname
|
||||
|
||||
from requests import RequestException
|
||||
import xdg.BaseDirectory
|
||||
from requests import RequestException
|
||||
|
||||
from mycroft.util.combo_lock import ComboLock
|
||||
from mycroft.util.file_utils import get_temp_path
|
||||
from mycroft.util import camel_case_split
|
||||
from mycroft.util.json_helper import load_commented_json, merge_dict
|
||||
from mycroft.util.log import LOG
|
||||
|
||||
from .locations import DEFAULT_CONFIG, USER_CONFIG, OLD_USER_CONFIG
|
||||
from .locations import SYSTEM_CONFIG
|
||||
from .locations import (
|
||||
DEFAULT_CONFIG,
|
||||
OLD_USER_CONFIG,
|
||||
SYSTEM_CONFIG,
|
||||
USER_CONFIG
|
||||
)
|
||||
|
||||
|
||||
def is_remote_list(values):
|
||||
|
@ -53,7 +60,8 @@ def translate_remote(config, setting):
|
|||
if k not in IGNORED_SETTINGS:
|
||||
# Translate the CamelCase values stored remotely into the
|
||||
# Python-style names used within mycroft-core.
|
||||
key = inflection.underscore(re.sub(r"Setting(s)?", "", k))
|
||||
key = re.sub(r"Setting(s)?", "", k)
|
||||
key = camel_case_split(key).replace(" ", "_").lower()
|
||||
if isinstance(v, dict):
|
||||
config[key] = config.get(key, {})
|
||||
translate_remote(config[key], v)
|
||||
|
@ -85,8 +93,11 @@ def translate_list(config, values):
|
|||
|
||||
class LocalConf(dict):
|
||||
"""Config dictionary from file."""
|
||||
_lock = ComboLock(get_temp_path('local-conf.lock'))
|
||||
|
||||
def __init__(self, path):
|
||||
super(LocalConf, self).__init__()
|
||||
self.is_valid = True # is loaded json valid, updated when load occurs
|
||||
if path:
|
||||
self.path = path
|
||||
self.load_local(path)
|
||||
|
@ -107,29 +118,54 @@ class LocalConf(dict):
|
|||
except Exception as e:
|
||||
LOG.error("Error loading configuration '{}'".format(path))
|
||||
LOG.error(repr(e))
|
||||
self.is_valid = False
|
||||
else:
|
||||
LOG.debug("Configuration '{}' not defined, skipping".format(path))
|
||||
|
||||
def store(self, path=None):
|
||||
"""Cache the received settings locally.
|
||||
def store(self, path=None, force=False):
|
||||
"""Save config to disk.
|
||||
|
||||
The cache will be used if the remote is unreachable to load settings
|
||||
that are as close to the user's as possible.
|
||||
|
||||
path (str): path to store file to, if missing will use the path from
|
||||
where the config was loaded.
|
||||
force (bool): Set to True if writing should occur despite the original
|
||||
was malformed.
|
||||
|
||||
Returns:
|
||||
(bool) True if save was successful, else False.
|
||||
"""
|
||||
path = path or self.path
|
||||
with open(path, 'w') as f:
|
||||
json.dump(self, f, indent=2)
|
||||
result = False
|
||||
with self._lock:
|
||||
path = path or self.path
|
||||
config_dir = dirname(path)
|
||||
if not exists(config_dir):
|
||||
os.makedirs(config_dir)
|
||||
|
||||
if self.is_valid or force:
|
||||
with open(path, 'w') as f:
|
||||
json.dump(self, f, indent=2)
|
||||
result = True
|
||||
else:
|
||||
LOG.warning((f'"{path}" was not a valid config file when '
|
||||
'loaded, will not save config. Please correct '
|
||||
'the json or remove it to allow updates.'))
|
||||
result = False
|
||||
return result
|
||||
|
||||
def merge(self, conf):
|
||||
merge_dict(self, conf)
|
||||
|
||||
|
||||
class RemoteConf(LocalConf):
|
||||
_lock = ComboLock(get_temp_path('remote-conf.lock'))
|
||||
"""Config dictionary fetched from mycroft.ai."""
|
||||
|
||||
def __init__(self, cache=None):
|
||||
super(RemoteConf, self).__init__(None)
|
||||
|
||||
cache = cache or join(xdg.BaseDirectory.save_cache_path('mycroft'),
|
||||
cache = cache or join(xdg.BaseDirectory.xdg_cache_home, 'mycroft',
|
||||
'web_cache.json')
|
||||
from mycroft.api import is_paired
|
||||
if not is_paired():
|
||||
|
@ -158,7 +194,7 @@ class RemoteConf(LocalConf):
|
|||
translate_remote(config, setting)
|
||||
for key in config:
|
||||
self.__setitem__(key, config[key])
|
||||
self.store(cache)
|
||||
self.store(cache, force=True)
|
||||
|
||||
except RequestException as e:
|
||||
LOG.error("RequestException fetching remote configuration: {}"
|
||||
|
@ -179,7 +215,7 @@ def _log_old_location_deprecation():
|
|||
" Note that this location is deprecated and will"
|
||||
" not be used in the future\n"
|
||||
" Please move it to "
|
||||
f"{xdg.BaseDirectory.save_config_path('mycroft')}")
|
||||
f"{join(xdg.BaseDirectory.xdg_config_home, 'mycroft')}")
|
||||
|
||||
|
||||
class Configuration:
|
||||
|
@ -236,13 +272,13 @@ class Configuration:
|
|||
_log_old_location_deprecation()
|
||||
configs.append(LocalConf(OLD_USER_CONFIG))
|
||||
|
||||
# Then use the system config (/etc/mycroft/mycroft.conf)
|
||||
configs.append(LocalConf(SYSTEM_CONFIG))
|
||||
|
||||
# Then use remote config
|
||||
if remote:
|
||||
configs.append(RemoteConf())
|
||||
|
||||
# Then use the system config (/etc/mycroft/mycroft.conf)
|
||||
configs.append(LocalConf(SYSTEM_CONFIG))
|
||||
|
||||
# Then use the config that comes with the package
|
||||
configs.append(LocalConf(DEFAULT_CONFIG))
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import os
|
||||
from os.path import join, dirname, expanduser, exists
|
||||
from os.path import join, dirname, expanduser
|
||||
|
||||
import xdg.BaseDirectory
|
||||
|
||||
|
@ -23,24 +23,11 @@ SYSTEM_CONFIG = os.environ.get('MYCROFT_SYSTEM_CONFIG',
|
|||
# Make sure we support the old location still
|
||||
# Deprecated and will be removed eventually
|
||||
OLD_USER_CONFIG = join(expanduser('~'), '.mycroft/mycroft.conf')
|
||||
USER_CONFIG = join(xdg.BaseDirectory.save_config_path('mycroft'),
|
||||
'mycroft.conf')
|
||||
USER_CONFIG = join(xdg.BaseDirectory.xdg_config_home,
|
||||
'mycroft',
|
||||
'mycroft.conf'
|
||||
)
|
||||
|
||||
REMOTE_CONFIG = "mycroft.ai"
|
||||
WEB_CONFIG_CACHE = os.environ.get('MYCROFT_WEB_CACHE',
|
||||
'/var/tmp/mycroft_web_cache.json')
|
||||
|
||||
|
||||
def __ensure_folder_exists(path):
|
||||
""" Make sure the directory for the specified path exists.
|
||||
|
||||
Args:
|
||||
path (str): path to config file
|
||||
"""
|
||||
directory = dirname(path)
|
||||
if not exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
|
||||
__ensure_folder_exists(WEB_CONFIG_CACHE)
|
||||
__ensure_folder_exists(USER_CONFIG)
|
||||
|
|
|
@ -263,6 +263,14 @@
|
|||
// If not defined, the default log level is INFO.
|
||||
//"log_level": "INFO",
|
||||
|
||||
// Format of logs to store.
|
||||
// NOTE: This configuration setting is special and can only be changed in the
|
||||
// SYSTEM or USER configuration file, it will not be read if defined in the
|
||||
// DEFAULT (here) or in the REMOTE mycroft config.
|
||||
// If not defined, the default log format is:
|
||||
// {asctime} | {levelname:8} | {process:5} | {name} | {message}
|
||||
//"log_format": "{asctime} | {levelname:8} | {process:5} | {name} | {message}",
|
||||
|
||||
// Messagebus types that will NOT be output to logs
|
||||
"ignore_logs": ["enclosure.mouth.viseme", "enclosure.mouth.display"],
|
||||
|
||||
|
@ -297,7 +305,7 @@
|
|||
// Override: REMOTE
|
||||
"tts": {
|
||||
// Engine. Options: "mimic", "mimic2", "google", "marytts", "fatts", "espeak",
|
||||
// "spdsay", "responsive_voice", "yandex", "polly", "mozilla"
|
||||
// "spdsay", "yandex", "polly", "mozilla"
|
||||
"pulse_duck": false,
|
||||
"module": "mimic",
|
||||
"polly": {
|
||||
|
|
|
@ -373,5 +373,5 @@ class SkillGUI:
|
|||
Clear pages loaded through this interface and remove the skill
|
||||
reference to make ref counting warning more precise.
|
||||
"""
|
||||
self.clear()
|
||||
self.release()
|
||||
self.skill = None
|
||||
|
|
|
@ -43,9 +43,14 @@ class IdentityManager:
|
|||
def _load():
|
||||
LOG.debug('Loading identity')
|
||||
try:
|
||||
with FileSystemAccess('identity').open('identity2.json', 'r') as f:
|
||||
IdentityManager.__identity = DeviceIdentity(**json.load(f))
|
||||
except Exception:
|
||||
identity_dir = FileSystemAccess('identity')
|
||||
if identity_dir.exists('identity2.json'):
|
||||
with identity_dir.open('identity2.json', 'r') as f:
|
||||
IdentityManager.__identity = DeviceIdentity(**json.load(f))
|
||||
else:
|
||||
IdentityManager.__identity = DeviceIdentity()
|
||||
except Exception as e:
|
||||
LOG.exception(f'Failed to load identity file: {repr(e)}')
|
||||
IdentityManager.__identity = DeviceIdentity()
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
və
|
|
@ -0,0 +1 @@
|
|||
Deyəsən, Mycroft serverlərinə qoşula bilmirəm. Zəhmət olmasa mənimlə danışmazdan əvvəl bir neçə dəqiqə gözləyin.
|
|
@ -0,0 +1,3 @@
|
|||
ləğv et
|
||||
ötür
|
||||
bürax
|
|
@ -0,0 +1,2 @@
|
|||
Yeniləmələr yoxlanılır
|
||||
Bir an gözləyin, özümü yeniləyirəm.
|
|
@ -0,0 +1 @@
|
|||
gün
|
|
@ -0,0 +1 @@
|
|||
saat
|
|
@ -0,0 +1,3 @@
|
|||
Bağışlayın, mən bunu başa düşmədim
|
||||
bir daha deyə bilərsinizmi?
|
||||
lütfən tekrar söyləyə bilərsinizmi?
|
|
@ -0,0 +1,2 @@
|
|||
son seçim
|
||||
sonuncu
|
|
@ -0,0 +1 @@
|
|||
< < < YÜKLƏNİR < < <
|
|
@ -0,0 +1 @@
|
|||
YENİDƏN İŞƏ SALINIR...
|
|
@ -0,0 +1 @@
|
|||
< < < SYNC < < <
|
|
@ -0,0 +1 @@
|
|||
< < < YENİLƏNİR < < <
|
|
@ -0,0 +1 @@
|
|||
dəqiqə
|
|
@ -0,0 +1 @@
|
|||
Salam, mən Mycroft, sizin yeni yardımcınızam. Sizə yardım etmək üçün mən internetə qoşulmalıyam. Siz ya məni şəbəkə kabeli ilə qoşa, ya da Wi-Fi istifadə edə bilərsiniz. WiFi qurmaq üçün bu təlimatları yerinə yetirin:
|
|
@ -0,0 +1,3 @@
|
|||
yox
|
||||
yo
|
||||
mənfi
|
|
@ -0,0 +1,4 @@
|
|||
Deyəsən, mən İnternetə qoşulmamışam, lütfən, şəbəkə bağlantınızı yoxlayın.
|
||||
Mən internetə bağlı deyiləm, lütfən, şəbəkə bağlantınızı yoxlayın.
|
||||
indi internetə qoşula bilmirəm, lütfən, şəbəkə bağlantınızı yoxlayınş
|
||||
İnternetə qoşula bilmirəm, şəbəkə bağlantınızı yoxlayın.
|
|
@ -0,0 +1 @@
|
|||
Yükləməni bitirənə kimi bir az gözləyin.
|
|
@ -0,0 +1 @@
|
|||
ya
|
|
@ -0,0 +1,3 @@
|
|||
ai: S.İ.
|
||||
mycroftai: maykraft S.İ.
|
||||
spotify: spatifay
|
|
@ -0,0 +1 @@
|
|||
Mən fabrik tənzimlərimə qayıtmışam.
|
|
@ -0,0 +1 @@
|
|||
saniyə
|
|
@ -0,0 +1 @@
|
|||
{{skill}} Bacarıqlar işlərkən xəta baş verdi
|
|
@ -0,0 +1 @@
|
|||
Bacarıqlar Yeniləndi. Mən sizə yardım etməyə hazıram.
|
|
@ -0,0 +1 @@
|
|||
bacarıqları yeniləyərkən xəta baş verdi
|
|
@ -0,0 +1 @@
|
|||
Saatımı internetlə sinxronlaşdırdıqdan sonra yenidən başlamalıyam, birazajan qayıdacağam.
|
|
@ -0,0 +1,4 @@
|
|||
bəli
|
||||
evet
|
||||
hən
|
||||
lütfən
|
|
@ -0,0 +1,30 @@
|
|||
where
|
||||
what's
|
||||
which
|
||||
them
|
||||
they
|
||||
when
|
||||
what
|
||||
that
|
||||
will
|
||||
from
|
||||
that
|
||||
also
|
||||
who
|
||||
how
|
||||
did
|
||||
and
|
||||
but
|
||||
the
|
||||
too
|
||||
why
|
||||
for
|
||||
is
|
||||
it
|
||||
do
|
||||
or
|
||||
to
|
||||
of
|
||||
a
|
||||
|
||||
|
|
@ -3,3 +3,5 @@ ai: A.I.
|
|||
mycroftai: mycroft A.I.
|
||||
spotify: spot-ify
|
||||
corgi: core-gee
|
||||
ip: eye pea
|
||||
wikipedia: wickee-peedia
|
|
@ -0,0 +1 @@
|
|||
и
|
|
@ -0,0 +1,4 @@
|
|||
Испытываю затруднения с поключением к серверам Mycroft. Подождите несколько минут, а потом можете спросить меня снова.
|
||||
Испытываю затруднения с поключением к серверам Mycroft. Пожалуйста, дайте мне пару минут, а потом можете спросить снова.
|
||||
Похоже я не могу подключиться к серверам Mycroft. Подождите несколько минут, а потом можете спросить меня снова.
|
||||
Похоже я не могу подключиться к серверам Mycroft. Пожалуйста, дайте мне пару минут, а потом можете спросить снова.
|
|
@ -0,0 +1,3 @@
|
|||
отмена
|
||||
не важно
|
||||
ну и ладно
|
|
@ -0,0 +1,2 @@
|
|||
Проверяю обновления
|
||||
Подождите минутку, я всё ещё обновляюсь
|
|
@ -1 +1 @@
|
|||
дни
|
||||
дней
|
||||
|
|
|
@ -1 +1 @@
|
|||
часы
|
||||
часов
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Извините, мне не понятно
|
||||
Боюсь, мне не удалось вас понять
|
||||
Не могли бы вы повторить?
|
||||
Не могли бы повторить снова?
|
||||
Повторите ещё раз, пожалуйста
|
|
@ -0,0 +1,4 @@
|
|||
последний
|
||||
последний вариант
|
||||
последний выбор
|
||||
самый последний
|
|
@ -0,0 +1 @@
|
|||
Данные о взаимодействии больше не будут отправляться в Mycroft AI
|
|
@ -0,0 +1 @@
|
|||
Теперь я загружу данные взаимодействия в Mycroft AI, чтобы стать умнее. В настоящее время это включает записи активации слов для пробуждения
|
|
@ -0,0 +1 @@
|
|||
< < < ЗАГРУЗКА < < <
|
|
@ -0,0 +1 @@
|
|||
ПЕРЕЗАГРУЗКА...
|
|
@ -0,0 +1 @@
|
|||
< < < СИХРОНИЗАЦИЯ < < <
|
|
@ -0,0 +1 @@
|
|||
< < < ОБНОВЛЕНИЕ < < <
|
|
@ -1 +1 @@
|
|||
минуты
|
||||
минут
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Привет, я Майкрофт, ваш новый ассистент. Для помощи вам мне нужно подключиться к интернету. Вы можете подключить меня к сетевому кабелю или использовать Wi-Fi. Следуйте этим инструкциям для подключения Wi-Fi:
|
|
@ -1,4 +1,4 @@
|
|||
нет
|
||||
неа
|
||||
не-а
|
||||
нет
|
||||
отрицательно
|
||||
отказ
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Кажется, у меня нет подключения к интернету. Проверьте соединение, пожалуйста
|
||||
Похоже, у меня нет подключения к интернету. Проверьте соединение, пожалуйста
|
||||
Не могу подключиться к интернету. Проверьте соединение, пожалуйста
|
||||
Не могу попасть в интернет. Проверьте соединение, пожалуйста
|
||||
У меня проблема с подключением к интернету. Проверьте соединение, пожалуйста
|
|
@ -0,0 +1 @@
|
|||
Подождите минутку, пока я закончу загружаться
|
|
@ -0,0 +1 @@
|
|||
или
|
|
@ -0,0 +1,19 @@
|
|||
ai: эй-ай
|
||||
duckduckgo: дак-дак-гоу
|
||||
fairytalez: фэйри-тэйлс
|
||||
homeassistant: хоум-ассистант
|
||||
iot: ай-оу-ти
|
||||
lifx: лайф-икс
|
||||
mycroft: май-крафт
|
||||
mycroftai: май-крафт эй-ай
|
||||
myepisodes: май-эпизодс
|
||||
spotify: споти-фай
|
||||
ssh: эс-эс-эйч
|
||||
wifi: вай-фай
|
||||
wi-fi: вай-фай
|
||||
wikiquote: вики-квоут
|
||||
майкрофт: май-крафт
|
||||
майкрофта: май-крафта
|
||||
майкрофте: май-крафте
|
||||
майкрофтом: май-крафтом
|
||||
майкрофту: май-крафту
|
|
@ -0,0 +1 @@
|
|||
Меня сбросили до заводских настроек
|
|
@ -1 +1 @@
|
|||
секунду
|
||||
секунда
|
||||
|
|
|
@ -1 +1 @@
|
|||
секунды
|
||||
секунд
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Во время обработки запроса в {{skill}} прозошла ошибка
|
|
@ -0,0 +1,2 @@
|
|||
Теперь я в актуальном состоянии
|
||||
Умения обновлены. Хотите я помогу вам?
|
|
@ -0,0 +1 @@
|
|||
При обновлении тем оформления произошла ошибка
|
|
@ -0,0 +1 @@
|
|||
Логин по SSH был отключён
|
|
@ -0,0 +1 @@
|
|||
Логин по SSH теперь разрешён
|
|
@ -0,0 +1 @@
|
|||
Мне нужно перезагрузиться после синхронизации часов с интернетом, скоро вернусь
|
|
@ -1,4 +1,3 @@
|
|||
да
|
||||
ага
|
||||
да
|
||||
конечно
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
var
|
||||
vad
|
||||
vilken
|
||||
dom
|
||||
dem
|
||||
när
|
||||
där
|
||||
som
|
||||
kommer
|
||||
från
|
||||
också
|
||||
vem
|
||||
hur
|
||||
gjorde
|
||||
gör
|
||||
kommer
|
||||
och
|
||||
men
|
||||
också
|
||||
varför
|
||||
för
|
||||
är
|
||||
det
|
||||
den
|
||||
eller
|
||||
till
|
||||
av
|
||||
en
|
||||
ett
|
|
@ -5,37 +5,40 @@ import org.kde.kirigami 2.4 as Kirigami
|
|||
|
||||
import Mycroft 1.0 as Mycroft
|
||||
|
||||
Mycroft.Delegate {
|
||||
Mycroft.CardDelegate {
|
||||
id: systemTextFrame
|
||||
skillBackgroundColorOverlay: "#000000"
|
||||
cardBackgroundOverlayColor: "#000000"
|
||||
|
||||
property bool hasTitle: sessionData.title.length > 0 ? true : false
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log(hasTitle)
|
||||
}
|
||||
contentItem: Rectangle {
|
||||
color: "transparent"
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
Label {
|
||||
id: systemTextFrameTitle
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: Math.min(systemTextFrame.height/4, Math.max(systemTextFrame.height/10, systemTextFrameMainBody.fontInfo.pixelSize * 1.4))
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: hasTitle
|
||||
enabled: hasTitle
|
||||
font.family: "Noto Sans"
|
||||
font.weight: Font.Bold
|
||||
text: sessionData.title
|
||||
}
|
||||
|
||||
Mycroft.AutoFitLabel {
|
||||
id: systemTextFrameMainBody
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
wrapMode: Text.Wrap
|
||||
font.family: "Noto Sans"
|
||||
font.weight: Font.Bold
|
||||
text: sessionData.text
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Mycroft.AutoFitLabel {
|
||||
id: systemTextFrameTitle
|
||||
wrapMode: Text.Wrap
|
||||
visible: hasTitle
|
||||
enabled: hasTitle
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
font.family: "Noto Sans"
|
||||
font.weight: Font.Bold
|
||||
text: sessionData.title
|
||||
}
|
||||
|
||||
Mycroft.AutoFitLabel {
|
||||
id: systemTextFrameMainBody
|
||||
wrapMode: Text.Wrap
|
||||
font.family: "Noto Sans"
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
font.weight: Font.Bold
|
||||
text: sessionData.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,12 @@
|
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from enum import IntEnum
|
||||
from abc import ABC, abstractmethod
|
||||
from .mycroft_skill import MycroftSkill
|
||||
|
||||
from mycroft.util.file_utils import resolve_resource_file
|
||||
|
||||
|
||||
class CQSMatchLevel(IntEnum):
|
||||
EXACT = 1 # Skill could find a specific answer for the question
|
||||
|
@ -32,11 +33,19 @@ def is_CQSVisualMatchLevel(match_level):
|
|||
return isinstance(match_level, type(CQSVisualMatchLevel.EXACT))
|
||||
|
||||
|
||||
VISUAL_DEVICES = ['mycroft_mark_2']
|
||||
"""these are for the confidence calculation"""
|
||||
# how much each topic word is worth
|
||||
# when found in the answer
|
||||
TOPIC_MATCH_RELEVANCE = 5
|
||||
|
||||
# elevate relevance above all else
|
||||
RELEVANCE_MULTIPLIER = 2
|
||||
|
||||
def handles_visuals(platform):
|
||||
return platform in VISUAL_DEVICES
|
||||
# we like longer articles but only so much
|
||||
MAX_ANSWER_LEN_FOR_CONFIDENCE = 50
|
||||
|
||||
# higher number - less bias for word length
|
||||
WORD_COUNT_DIVISOR = 100
|
||||
|
||||
|
||||
class CommonQuerySkill(MycroftSkill, ABC):
|
||||
|
@ -49,8 +58,29 @@ class CommonQuerySkill(MycroftSkill, ABC):
|
|||
This class works in conjunction with skill-query which collects
|
||||
answers from several skills presenting the best one available.
|
||||
"""
|
||||
|
||||
def __init__(self, name=None, bus=None):
|
||||
super().__init__(name, bus)
|
||||
noise_words_filepath = "text/%s/noise_words.list" % (self.lang,)
|
||||
noise_words_filename = resolve_resource_file(noise_words_filepath)
|
||||
self.translated_noise_words = []
|
||||
try:
|
||||
if noise_words_filename:
|
||||
with open(noise_words_filename) as f:
|
||||
read_noise_words = f.read().strip()
|
||||
self.translated_noise_words = read_noise_words.split()
|
||||
else:
|
||||
raise FileNotFoundError
|
||||
except FileNotFoundError:
|
||||
self.log.warning("Missing noise_words.list file in "
|
||||
f"res/text/{self.lang}")
|
||||
|
||||
# these should probably be configurable
|
||||
self.level_confidence = {
|
||||
CQSMatchLevel.EXACT: 0.9,
|
||||
CQSMatchLevel.CATEGORY: 0.6,
|
||||
CQSMatchLevel.GENERAL: 0.5
|
||||
}
|
||||
|
||||
def bind(self, bus):
|
||||
"""Overrides the default bind method of MycroftSkill.
|
||||
|
@ -80,7 +110,8 @@ class CommonQuerySkill(MycroftSkill, ABC):
|
|||
level = result[1]
|
||||
answer = result[2]
|
||||
callback = result[3] if len(result) > 3 else None
|
||||
confidence = self.__calc_confidence(match, search_phrase, level)
|
||||
confidence = self.__calc_confidence(
|
||||
match, search_phrase, level, answer)
|
||||
self.bus.emit(message.response({"phrase": search_phrase,
|
||||
"skill_id": self.skill_id,
|
||||
"answer": answer,
|
||||
|
@ -92,27 +123,57 @@ class CommonQuerySkill(MycroftSkill, ABC):
|
|||
"skill_id": self.skill_id,
|
||||
"searching": False}))
|
||||
|
||||
def __calc_confidence(self, match, phrase, level):
|
||||
def remove_noise(self, phrase):
|
||||
"""remove noise to produce essence of question"""
|
||||
phrase = ' ' + phrase + ' '
|
||||
for word in self.translated_noise_words:
|
||||
mtch = ' ' + word + ' '
|
||||
if phrase.find(mtch) > -1:
|
||||
phrase = phrase.replace(mtch, " ")
|
||||
phrase = ' '.join(phrase.split())
|
||||
return phrase.strip()
|
||||
|
||||
def __calc_confidence(self, match, phrase, level, answer):
|
||||
# Assume the more of the words that get consumed, the better the match
|
||||
consumed_pct = len(match.split()) / len(phrase.split())
|
||||
if consumed_pct > 1.0:
|
||||
consumed_pct = 1.0
|
||||
consumed_pct /= 10
|
||||
|
||||
# bonus for more sentences
|
||||
num_sentences = float(float(len(answer.split("."))) / float(10))
|
||||
|
||||
# Add bonus if match has visuals and the device supports them.
|
||||
platform = self.config_core.get('enclosure', {}).get('platform')
|
||||
if is_CQSVisualMatchLevel(level) and handles_visuals(platform):
|
||||
bonus = 0.0
|
||||
if is_CQSVisualMatchLevel(level) and self.gui.connected:
|
||||
bonus = 0.1
|
||||
else:
|
||||
bonus = 0
|
||||
|
||||
if int(level) == int(CQSMatchLevel.EXACT):
|
||||
return 0.9 + (consumed_pct / 10) + bonus
|
||||
elif int(level) == int(CQSMatchLevel.CATEGORY):
|
||||
return 0.6 + (consumed_pct / 10) + bonus
|
||||
elif int(level) == int(CQSMatchLevel.GENERAL):
|
||||
return 0.5 + (consumed_pct / 10) + bonus
|
||||
else:
|
||||
return 0.0 # should never happen
|
||||
# extract topic
|
||||
topic = self.remove_noise(match)
|
||||
|
||||
# calculate relevance
|
||||
answer = answer.lower()
|
||||
matches = 0
|
||||
for word in topic.split(' '):
|
||||
if answer.find(word) > -1:
|
||||
matches += TOPIC_MATCH_RELEVANCE
|
||||
|
||||
answer_size = len(answer.split(" "))
|
||||
answer_size = min(MAX_ANSWER_LEN_FOR_CONFIDENCE, answer_size)
|
||||
|
||||
relevance = 0.0
|
||||
if answer_size > 0:
|
||||
relevance = float(float(matches) / float(answer_size))
|
||||
|
||||
relevance = relevance * RELEVANCE_MULTIPLIER
|
||||
|
||||
# extra credit for more words up to a point
|
||||
wc_mod = float(float(answer_size) / float(WORD_COUNT_DIVISOR)) * 2
|
||||
|
||||
confidence = self.level_confidence[level] + \
|
||||
consumed_pct + bonus + num_sentences + relevance + wc_mod
|
||||
|
||||
return confidence
|
||||
|
||||
def __handle_query_action(self, message):
|
||||
"""Message handler for question:action.
|
||||
|
|
|
@ -21,7 +21,10 @@ from mycroft.util.log import LOG
|
|||
from mycroft.util.parse import normalize
|
||||
from mycroft.metrics import report_timing, Stopwatch
|
||||
from .intent_services import (
|
||||
AdaptService, AdaptIntent, FallbackService, PadatiousService, IntentMatch
|
||||
AdaptService, AdaptIntent,
|
||||
FallbackService,
|
||||
PadatiousService, PadatiousMatcher,
|
||||
IntentMatch
|
||||
)
|
||||
from .intent_service_interface import open_intent_envelope
|
||||
|
||||
|
@ -280,13 +283,16 @@ class IntentService:
|
|||
|
||||
stopwatch = Stopwatch()
|
||||
|
||||
# Create matchers
|
||||
padatious_matcher = PadatiousMatcher(self.padatious_service)
|
||||
|
||||
# List of functions to use to match the utterance with intent.
|
||||
# These are listed in priority order.
|
||||
match_funcs = [
|
||||
self._converse, self.padatious_service.match_high,
|
||||
self._converse, padatious_matcher.match_high,
|
||||
self.adapt_service.match_intent, self.fallback.high_prio,
|
||||
self.padatious_service.match_medium, self.fallback.medium_prio,
|
||||
self.padatious_service.match_low, self.fallback.low_prio
|
||||
padatious_matcher.match_medium, self.fallback.medium_prio,
|
||||
padatious_matcher.match_low, self.fallback.low_prio
|
||||
]
|
||||
|
||||
match = None
|
||||
|
@ -305,6 +311,10 @@ class IntentService:
|
|||
# Launch skill if not handled by the match function
|
||||
if match.intent_type:
|
||||
reply = message.reply(match.intent_type, match.intent_data)
|
||||
# Add back original list of utterances for intent handlers
|
||||
# match.intent_data only includes the utterance with the
|
||||
# highest confidence.
|
||||
reply.data["utterances"] = utterances
|
||||
self.bus.emit(reply)
|
||||
|
||||
else:
|
||||
|
@ -354,12 +364,18 @@ class IntentService:
|
|||
Args:
|
||||
message (Message): message containing vocab info
|
||||
"""
|
||||
start_concept = message.data.get('start')
|
||||
end_concept = message.data.get('end')
|
||||
# TODO: 22.02 Remove backwards compatibility
|
||||
if _is_old_style_keyword_message(message):
|
||||
LOG.warning('Deprecated: Registering keywords with old message. '
|
||||
'This will be removed in v22.02.')
|
||||
_update_keyword_message(message)
|
||||
|
||||
entity_value = message.data.get('entity_value')
|
||||
entity_type = message.data.get('entity_type')
|
||||
regex_str = message.data.get('regex')
|
||||
alias_of = message.data.get('alias_of')
|
||||
self.adapt_service.register_vocab(start_concept, end_concept,
|
||||
alias_of, regex_str)
|
||||
self.adapt_service.register_vocabulary(entity_value, entity_type,
|
||||
alias_of, regex_str)
|
||||
self.registered_vocab.append(message.data)
|
||||
|
||||
def handle_register_intent(self, message):
|
||||
|
@ -434,17 +450,20 @@ class IntentService:
|
|||
lang = message.data.get("lang", "en-us")
|
||||
combined = _normalize_all_utterances([utterance])
|
||||
|
||||
# Create matchers
|
||||
padatious_matcher = PadatiousMatcher(self.padatious_service)
|
||||
|
||||
# List of functions to use to match the utterance with intent.
|
||||
# These are listed in priority order.
|
||||
# TODO once we have a mechanism for checking if a fallback will
|
||||
# trigger without actually triggering it, those should be added here
|
||||
match_funcs = [
|
||||
self.padatious_service.match_high,
|
||||
padatious_matcher.match_high,
|
||||
self.adapt_service.match_intent,
|
||||
# self.fallback.high_prio,
|
||||
self.padatious_service.match_medium,
|
||||
padatious_matcher.match_medium,
|
||||
# self.fallback.medium_prio,
|
||||
self.padatious_service.match_low,
|
||||
padatious_matcher.match_low,
|
||||
# self.fallback.low_prio
|
||||
]
|
||||
# Loop through the matching functions until a match is found.
|
||||
|
@ -550,3 +569,29 @@ class IntentService:
|
|||
self.bus.emit(message.reply(
|
||||
"intent.service.padatious.entities.manifest",
|
||||
{"entities": self.padatious_service.registered_entities}))
|
||||
|
||||
|
||||
def _is_old_style_keyword_message(message):
|
||||
"""Simple check that the message is not using the updated format.
|
||||
|
||||
TODO: Remove in v22.02
|
||||
|
||||
Args:
|
||||
message (Message): Message object to check
|
||||
|
||||
Returns:
|
||||
(bool) True if this is an old messagem, else False
|
||||
"""
|
||||
return ('entity_value' not in message.data and 'start' in message.data)
|
||||
|
||||
|
||||
def _update_keyword_message(message):
|
||||
"""Make old style keyword registration message compatible.
|
||||
|
||||
Copies old keys in message data to new names.
|
||||
|
||||
Args:
|
||||
message (Message): Message to update
|
||||
"""
|
||||
message.data['entity_value'] = message.data['start']
|
||||
message.data['entity_type'] = message.data['end']
|
||||
|
|
|
@ -44,15 +44,27 @@ class IntentServiceInterface:
|
|||
|
||||
vocab_type(str): Keyword reference
|
||||
entity (str): Primary keyword
|
||||
aliases (list): List of alternative kewords
|
||||
aliases (list): List of alternative keywords
|
||||
"""
|
||||
# TODO 22.02: Remove compatibility data
|
||||
aliases = aliases or []
|
||||
self.bus.emit(Message("register_vocab",
|
||||
{'start': entity, 'end': vocab_type}))
|
||||
entity_data = {'entity_value': entity, 'entity_type': vocab_type}
|
||||
compatibility_data = {'start': entity, 'end': vocab_type}
|
||||
|
||||
self.bus.emit(
|
||||
Message("register_vocab",
|
||||
{**entity_data, **compatibility_data})
|
||||
)
|
||||
for alias in aliases:
|
||||
self.bus.emit(Message("register_vocab", {
|
||||
'start': alias, 'end': vocab_type, 'alias_of': entity
|
||||
}))
|
||||
alias_data = {
|
||||
'entity_value': alias,
|
||||
'entity_type': vocab_type,
|
||||
'alias_of': entity}
|
||||
compatibility_data = {'start': alias, 'end': vocab_type}
|
||||
self.bus.emit(
|
||||
Message("register_vocab",
|
||||
{**alias_data, **compatibility_data})
|
||||
)
|
||||
|
||||
def register_adapt_regex(self, regex):
|
||||
"""Register a regex with the intent service.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .adapt_service import AdaptService, AdaptIntent
|
||||
from .base import IntentMatch
|
||||
from .fallback_service import FallbackService
|
||||
from .padatious_service import PadatiousService
|
||||
from .padatious_service import PadatiousService, PadatiousMatcher
|
||||
|
|
|
@ -249,14 +249,35 @@ class AdaptService:
|
|||
ret = None
|
||||
return ret
|
||||
|
||||
# TODO 22.02: Remove this deprecated method
|
||||
def register_vocab(self, start_concept, end_concept, alias_of, regex_str):
|
||||
"""Register vocabulary."""
|
||||
"""Register Vocabulary. DEPRECATED
|
||||
|
||||
This method should not be used, it has been replaced by
|
||||
register_vocabulary().
|
||||
"""
|
||||
self.register_vocabulary(start_concept, end_concept,
|
||||
alias_of, regex_str)
|
||||
|
||||
def register_vocabulary(self, entity_value, entity_type,
|
||||
alias_of, regex_str):
|
||||
"""Register skill vocabulary as adapt entity.
|
||||
|
||||
This will handle both regex registration and registration of normal
|
||||
keywords. if the "regex_str" argument is set all other arguments will
|
||||
be ignored.
|
||||
|
||||
Argument:
|
||||
entity_value: the natural langauge word
|
||||
entity_type: the type/tag of an entity instance
|
||||
alias_of: entity this is an alternative for
|
||||
"""
|
||||
with self.lock:
|
||||
if regex_str:
|
||||
self.engine.register_regex_entity(regex_str)
|
||||
else:
|
||||
self.engine.register_entity(
|
||||
start_concept, end_concept, alias_of=alias_of)
|
||||
entity_value, entity_type, alias_of=alias_of)
|
||||
|
||||
def register_intent(self, intent):
|
||||
"""Register new intent with adapt engine.
|
||||
|
|
|
@ -26,6 +26,76 @@ from mycroft.util.log import LOG
|
|||
from .base import IntentMatch
|
||||
|
||||
|
||||
class PadatiousMatcher:
|
||||
"""Matcher class to avoid redundancy in padatious intent matching."""
|
||||
def __init__(self, service):
|
||||
self.service = service
|
||||
self.has_result = False
|
||||
self.ret = None
|
||||
self.conf = None
|
||||
|
||||
def _match_level(self, utterances, limit):
|
||||
"""Match intent and make sure a certain level of confidence is reached.
|
||||
|
||||
Args:
|
||||
utterances (list of tuples): Utterances to parse, originals paired
|
||||
with optional normalized version.
|
||||
limit (float): required confidence level.
|
||||
"""
|
||||
if not self.has_result:
|
||||
padatious_intent = None
|
||||
LOG.debug('Padatious Matching confidence > {}'.format(limit))
|
||||
for utt in utterances:
|
||||
for variant in utt:
|
||||
intent = self.service.calc_intent(variant)
|
||||
if intent:
|
||||
best = padatious_intent.conf \
|
||||
if padatious_intent else 0.0
|
||||
if best < intent.conf:
|
||||
padatious_intent = intent
|
||||
padatious_intent.matches['utterance'] = utt[0]
|
||||
|
||||
if padatious_intent:
|
||||
skill_id = padatious_intent.name.split(':')[0]
|
||||
self.ret = IntentMatch(
|
||||
'Padatious', padatious_intent.name,
|
||||
padatious_intent.matches, skill_id
|
||||
)
|
||||
self.conf = padatious_intent.conf
|
||||
self.has_result = True
|
||||
|
||||
if self.conf and self.conf > limit:
|
||||
return self.ret
|
||||
return None
|
||||
|
||||
def match_high(self, utterances, _=None, __=None):
|
||||
"""Intent matcher for high confidence.
|
||||
|
||||
Args:
|
||||
utterances (list of tuples): Utterances to parse, originals paired
|
||||
with optional normalized version.
|
||||
"""
|
||||
return self._match_level(utterances, 0.95)
|
||||
|
||||
def match_medium(self, utterances, _=None, __=None):
|
||||
"""Intent matcher for medium confidence.
|
||||
|
||||
Args:
|
||||
utterances (list of tuples): Utterances to parse, originals paired
|
||||
with optional normalized version.
|
||||
"""
|
||||
return self._match_level(utterances, 0.8)
|
||||
|
||||
def match_low(self, utterances, _=None, __=None):
|
||||
"""Intent matcher for low confidence.
|
||||
|
||||
Args:
|
||||
utterances (list of tuples): Utterances to parse, originals paired
|
||||
with optional normalized version.
|
||||
"""
|
||||
return self._match_level(utterances, 0.5)
|
||||
|
||||
|
||||
class PadatiousService:
|
||||
"""Service class for padatious intent matching."""
|
||||
def __init__(self, bus, config):
|
||||
|
@ -167,63 +237,6 @@ class PadatiousService:
|
|||
self.registered_entities.append(message.data)
|
||||
self._register_object(message, 'entity', self.container.load_entity)
|
||||
|
||||
def _match_level(self, utterances, limit):
|
||||
"""Match intent and make sure a certain level of confidence is reached.
|
||||
|
||||
Args:
|
||||
utterances (list of tuples): Utterances to parse, originals paired
|
||||
with optional normalized version.
|
||||
limit (float): required confidence level.
|
||||
"""
|
||||
padatious_intent = None
|
||||
LOG.debug('Padatious Matching confidence > {}'.format(limit))
|
||||
for utt in utterances:
|
||||
for variant in utt:
|
||||
intent = self.calc_intent(variant)
|
||||
if intent:
|
||||
best = padatious_intent.conf if padatious_intent else 0.0
|
||||
if best < intent.conf:
|
||||
padatious_intent = intent
|
||||
padatious_intent.matches['utterance'] = utt[0]
|
||||
|
||||
if padatious_intent and padatious_intent.conf > limit:
|
||||
skill_id = padatious_intent.name.split(':')[0]
|
||||
ret = IntentMatch(
|
||||
'Padatious', padatious_intent.name, padatious_intent.matches,
|
||||
skill_id
|
||||
)
|
||||
else:
|
||||
ret = None
|
||||
return ret
|
||||
|
||||
def match_high(self, utterances, _=None, __=None):
|
||||
"""Intent matcher for high confidence.
|
||||
|
||||
Args:
|
||||
utterances (list of tuples): Utterances to parse, originals paired
|
||||
with optional normalized version.
|
||||
"""
|
||||
return self._match_level(utterances, 0.95)
|
||||
|
||||
def match_medium(self, utterances, _=None, __=None):
|
||||
"""Intent matcher for medium confidence.
|
||||
|
||||
Args:
|
||||
utterances (list of tuples): Utterances to parse, originals paired
|
||||
with optional normalized version.
|
||||
"""
|
||||
return self._match_level(utterances, 0.8)
|
||||
|
||||
def match_low(self, utterances, _=None, __=None):
|
||||
"""Intent matcher for low confidence.
|
||||
|
||||
Args:
|
||||
utterances (list of tuples): Utterances to parse, originals paired
|
||||
with optional normalized version.
|
||||
"""
|
||||
return self._match_level(utterances, 0.5)
|
||||
|
||||
@lru_cache(maxsize=2) # 2 catches both raw and normalized utts in cache
|
||||
def calc_intent(self, utt):
|
||||
"""Cached version of container calc_intent.
|
||||
|
||||
|
|
|
@ -1168,9 +1168,8 @@ class MycroftSkill:
|
|||
entity: word to register
|
||||
entity_type: Intent handler entity to tie the word to
|
||||
"""
|
||||
self.bus.emit(Message('register_vocab', {
|
||||
'start': entity, 'end': to_alnum(self.skill_id) + entity_type
|
||||
}))
|
||||
keyword_type = to_alnum(self.skill_id) + entity_type
|
||||
self.intent_service.register_adapt_keyword(keyword_type, entity)
|
||||
|
||||
def register_regex(self, regex_str):
|
||||
"""Register a new regex.
|
||||
|
|
|
@ -41,7 +41,7 @@ def remove_submodule_refs(module_name):
|
|||
module_name: name of skill module.
|
||||
"""
|
||||
submodules = []
|
||||
LOG.debug('Skill module'.format(module_name))
|
||||
LOG.debug('Skill module: {}'.format(module_name))
|
||||
# Collect found submodules
|
||||
for m in sys.modules:
|
||||
if m.startswith(module_name + '.'):
|
||||
|
|
|
@ -37,6 +37,18 @@ class STT(metaclass=ABCMeta):
|
|||
self.recognizer = Recognizer()
|
||||
self.can_stream = False
|
||||
|
||||
@property
|
||||
def available_languages(self) -> set:
|
||||
"""Return languages supported by this STT implementation in this state
|
||||
|
||||
This property should be overridden by the derived class to advertise
|
||||
what languages that engine supports.
|
||||
|
||||
Returns:
|
||||
set: supported languages
|
||||
"""
|
||||
return set()
|
||||
|
||||
@staticmethod
|
||||
def init_language(config_core):
|
||||
"""Helper method to get language code from Mycroft config."""
|
||||
|
|
|
@ -32,8 +32,32 @@ class ESpeak(TTS):
|
|||
Returns:
|
||||
tuple ((str) file location, None)
|
||||
"""
|
||||
subprocess.call(['espeak', '-v', self.lang + '+' + self.voice,
|
||||
'-w', wav_file, sentence])
|
||||
|
||||
# Create Argument String for Espeak
|
||||
arguments = ['espeak', '-v', self.lang + '+' + self.voice]
|
||||
amplitude = self.config.get('amplitude')
|
||||
if amplitude:
|
||||
arguments.append('-a '+amplitude)
|
||||
|
||||
gap = self.config.get('gap')
|
||||
if gap:
|
||||
arguments.append('-g '+gap)
|
||||
|
||||
capital = self.config.get('capital')
|
||||
if capital:
|
||||
arguments.append('-k '+capital)
|
||||
|
||||
pitch = self.config.get('pitch')
|
||||
if pitch:
|
||||
arguments.append('-p '+pitch)
|
||||
|
||||
speed = self.config.get('speed')
|
||||
if speed:
|
||||
arguments.append('-s '+speed)
|
||||
|
||||
arguments.extend(['-w', wav_file, sentence])
|
||||
|
||||
subprocess.call(arguments)
|
||||
return wav_file, None
|
||||
|
||||
|
||||
|
|
|
@ -70,6 +70,13 @@ class GoogleTTS(TTS):
|
|||
self._google_lang = None
|
||||
super(GoogleTTS, self).__init__(lang, config, GoogleTTSValidator(
|
||||
self), 'mp3')
|
||||
LOG.warning(
|
||||
"The Google TTS module uses the gTTS Python package which itself "
|
||||
"interfaces with the Google Translate text-to-speech API. This is "
|
||||
"not intended for commercial or production usage. The service "
|
||||
"may break at any time, and you are subject to their Terms of "
|
||||
"Service that can be found at https://policies.google.com/terms"
|
||||
)
|
||||
|
||||
@property
|
||||
def google_lang(self):
|
||||
|
|
|
@ -130,8 +130,6 @@ class Mimic(TTS):
|
|||
)
|
||||
self.default_binary = get_mimic_binary()
|
||||
|
||||
self.clear_cache()
|
||||
|
||||
# Download subscriber voices if needed
|
||||
self.subscriber_voices = get_subscriber_voices()
|
||||
self.is_subscriber = DeviceApi().is_subscriber
|
||||
|
|
|
@ -39,22 +39,43 @@ from mycroft.util.plugins import load_plugin
|
|||
from queue import Queue, Empty
|
||||
from .cache import hash_sentence, TextToSpeechCache
|
||||
|
||||
|
||||
_TTS_ENV = deepcopy(os.environ)
|
||||
_TTS_ENV['PULSE_PROP'] = 'media.role=phone'
|
||||
|
||||
|
||||
EMPTY_PLAYBACK_QUEUE_TUPLE = (None, None, None, None, None)
|
||||
|
||||
SSML_TAGS = re.compile(r'<[^>]*>')
|
||||
WHITESPACE_AFTER_PERIOD = re.compile(r'\b([A-za-z][\.])(\s+)')
|
||||
SENTENCE_DELIMITERS = re.compile(
|
||||
r'(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\;|\?)\s'
|
||||
)
|
||||
|
||||
|
||||
def default_preprocess_utterance(utterance):
|
||||
"""Default method for preprocessing Mycroft utterances for TTS.
|
||||
|
||||
Args:
|
||||
utteance (str): Input utterance
|
||||
|
||||
Returns:
|
||||
[str]: list of preprocessed sentences
|
||||
"""
|
||||
|
||||
utterance = WHITESPACE_AFTER_PERIOD.sub(r'\g<1>', utterance)
|
||||
chunks = SENTENCE_DELIMITERS.split(utterance)
|
||||
return chunks
|
||||
|
||||
|
||||
class PlaybackThread(Thread):
|
||||
"""Thread class for playing back tts audio and sending
|
||||
viseme data to enclosure.
|
||||
"""
|
||||
|
||||
def __init__(self, queue):
|
||||
super(PlaybackThread, self).__init__()
|
||||
self.queue = queue
|
||||
self.tts = []
|
||||
self.bus = None
|
||||
|
||||
self._terminated = False
|
||||
self._processing_queue = False
|
||||
self.enclosure = None
|
||||
|
@ -66,7 +87,28 @@ class PlaybackThread(Thread):
|
|||
self.pulse_env = None
|
||||
|
||||
def init(self, tts):
|
||||
self.tts = tts
|
||||
"""DEPRECATED! Init the TTS Playback thread.
|
||||
|
||||
TODO: 22.02 Remove this
|
||||
"""
|
||||
self.attach_tts(tts)
|
||||
self.set_bus(tts.bus)
|
||||
|
||||
def set_bus(self, bus):
|
||||
"""Provide bus instance to the TTS Playback thread.
|
||||
|
||||
Args:
|
||||
bus (MycroftBusClient): bus client
|
||||
"""
|
||||
self.bus = bus
|
||||
|
||||
def attach_tts(self, tts):
|
||||
"""Add TTS to be cache checked."""
|
||||
self.tts.append(tts)
|
||||
|
||||
def detach_tts(self, tts):
|
||||
"""Remove TTS from cache check."""
|
||||
self.tts.remove(tts)
|
||||
|
||||
def clear_queue(self):
|
||||
"""Remove all pending playbacks."""
|
||||
|
@ -90,7 +132,7 @@ class PlaybackThread(Thread):
|
|||
the loop then wait for the playback process to finish before starting
|
||||
checking the next position in queue.
|
||||
|
||||
If the queue is empty the tts.end_audio() is called possibly triggering
|
||||
If the queue is empty the end_audio() is called possibly triggering
|
||||
listening.
|
||||
"""
|
||||
while not self._terminated:
|
||||
|
@ -100,7 +142,7 @@ class PlaybackThread(Thread):
|
|||
self.blink(0.5)
|
||||
if not self._processing_queue:
|
||||
self._processing_queue = True
|
||||
self.tts.begin_audio()
|
||||
self.begin_audio()
|
||||
|
||||
stopwatch = Stopwatch()
|
||||
with stopwatch:
|
||||
|
@ -116,7 +158,7 @@ class PlaybackThread(Thread):
|
|||
report_timing(ident, 'speech_playback', stopwatch)
|
||||
|
||||
if self.queue.empty():
|
||||
self.tts.end_audio(listen)
|
||||
self.end_audio(listen)
|
||||
self._processing_queue = False
|
||||
self.blink(0.2)
|
||||
except Empty:
|
||||
|
@ -124,9 +166,42 @@ class PlaybackThread(Thread):
|
|||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
if self._processing_queue:
|
||||
self.tts.end_audio(listen)
|
||||
self.end_audio(listen)
|
||||
self._processing_queue = False
|
||||
|
||||
def begin_audio(self):
|
||||
"""Perform befining of speech actions."""
|
||||
# Create signals informing start of speech
|
||||
if self.bus:
|
||||
self.bus.emit(Message("recognizer_loop:audio_output_start"))
|
||||
else:
|
||||
LOG.warning("Speech started before bus was attached.")
|
||||
|
||||
def end_audio(self, listen):
|
||||
"""Perform end of speech output actions.
|
||||
|
||||
Will inform the system that speech has ended and trigger the TTS's
|
||||
cache checks. Listening will be triggered if requested.
|
||||
|
||||
Args:
|
||||
listen (bool): True if listening event should be emitted
|
||||
"""
|
||||
if self.bus:
|
||||
# Send end of speech signals to the system
|
||||
self.bus.emit(Message("recognizer_loop:audio_output_end"))
|
||||
if listen:
|
||||
self.bus.emit(Message('mycroft.mic.listen'))
|
||||
|
||||
# Clear cache for all attached tts objects
|
||||
# This is basically the only safe time
|
||||
for tts in self.tts:
|
||||
tts.cache.curate()
|
||||
|
||||
# This check will clear the filesystem IPC "signal"
|
||||
check_for_signal("isSpeaking")
|
||||
else:
|
||||
LOG.warning("Speech started before bus was attached.")
|
||||
|
||||
def show_visemes(self, pairs):
|
||||
"""Send viseme data to enclosure
|
||||
|
||||
|
@ -167,6 +242,8 @@ class TTS(metaclass=ABCMeta):
|
|||
phonetic_spelling (bool): Whether to spell certain words phonetically
|
||||
ssml_tags (list): Supported ssml properties. Ex. ['speak', 'prosody']
|
||||
"""
|
||||
queue = None
|
||||
playback = None
|
||||
|
||||
def __init__(self, lang, config, validator, audio_ext='wav',
|
||||
phonetic_spelling=True, ssml_tags=None):
|
||||
|
@ -183,9 +260,12 @@ class TTS(metaclass=ABCMeta):
|
|||
self.filename = get_temp_path('tts.wav')
|
||||
self.enclosure = None
|
||||
random.seed()
|
||||
self.queue = Queue()
|
||||
self.playback = PlaybackThread(self.queue)
|
||||
self.playback.start()
|
||||
|
||||
if TTS.queue is None:
|
||||
TTS.queue = Queue()
|
||||
TTS.playback = PlaybackThread(TTS.queue)
|
||||
TTS.playback.start()
|
||||
|
||||
self.spellings = self.load_spellings()
|
||||
self.tts_name = type(self).__name__
|
||||
self.cache = TextToSpeechCache(
|
||||
|
@ -193,6 +273,18 @@ class TTS(metaclass=ABCMeta):
|
|||
)
|
||||
self.cache.clear()
|
||||
|
||||
@property
|
||||
def available_languages(self) -> set:
|
||||
"""Return languages supported by this TTS implementation in this state
|
||||
|
||||
This property should be overridden by the derived class to advertise
|
||||
what languages that engine supports.
|
||||
|
||||
Returns:
|
||||
set: supported languages
|
||||
"""
|
||||
return set()
|
||||
|
||||
def load_spellings(self):
|
||||
"""Load phonetic spellings of words as dictionary."""
|
||||
path = join('text', self.lang.lower(), 'phonetic_spellings.txt')
|
||||
|
@ -240,9 +332,10 @@ class TTS(metaclass=ABCMeta):
|
|||
bus: Mycroft messagebus connection
|
||||
"""
|
||||
self.bus = bus
|
||||
self.playback.init(self)
|
||||
TTS.playback.set_bus(bus)
|
||||
TTS.playback.attach_tts(self)
|
||||
self.enclosure = EnclosureAPI(self.bus)
|
||||
self.playback.enclosure = self.enclosure
|
||||
TTS.playback.enclosure = self.enclosure
|
||||
|
||||
def get_tts(self, sentence, wav_file):
|
||||
"""Abstract method that a tts implementation needs to implement.
|
||||
|
@ -294,7 +387,7 @@ class TTS(metaclass=ABCMeta):
|
|||
return self.remove_ssml(utterance)
|
||||
|
||||
# find ssml tags in string
|
||||
tags = re.findall('<[^>]*>', utterance)
|
||||
tags = SSML_TAGS.findall(utterance)
|
||||
|
||||
for tag in tags:
|
||||
if any(supported in tag for supported in self.ssml_tags):
|
||||
|
@ -306,6 +399,21 @@ class TTS(metaclass=ABCMeta):
|
|||
# return text with supported ssml tags only
|
||||
return utterance.replace(" ", " ")
|
||||
|
||||
def preprocess_utterance(self, utterance):
|
||||
"""Preprocess utterance into list of chunks suitable for the TTS.
|
||||
|
||||
Perform general chunking and TTS specific chunking.
|
||||
"""
|
||||
# Remove any whitespace present after the period,
|
||||
# if a character (only alpha) ends with a period
|
||||
# ex: A. Lincoln -> A.Lincoln
|
||||
# so that we don't split at the period
|
||||
chunks = default_preprocess_utterance(utterance)
|
||||
result = []
|
||||
for chunk in chunks:
|
||||
result += self._preprocess_sentence(chunk)
|
||||
return result
|
||||
|
||||
def _preprocess_sentence(self, sentence):
|
||||
"""Default preprocessing is no preprocessing.
|
||||
|
||||
|
@ -335,13 +443,7 @@ class TTS(metaclass=ABCMeta):
|
|||
sentence = self.validate_ssml(sentence)
|
||||
|
||||
create_signal("isSpeaking")
|
||||
try:
|
||||
self._execute(sentence, ident, listen)
|
||||
except Exception:
|
||||
# If an error occurs end the audio sequence through an empty entry
|
||||
self.queue.put(EMPTY_PLAYBACK_QUEUE_TUPLE)
|
||||
# Re-raise to allow the Exception to be handled externally as well.
|
||||
raise
|
||||
self._execute(sentence, ident, listen)
|
||||
|
||||
def _execute(self, sentence, ident, listen):
|
||||
if self.phonetic_spelling:
|
||||
|
@ -350,6 +452,8 @@ class TTS(metaclass=ABCMeta):
|
|||
sentence = sentence.replace(word,
|
||||
self.spellings[word.lower()])
|
||||
|
||||
# TODO: 22.02 This is no longer needed and can be removed
|
||||
# Just kept for compatibility for now
|
||||
chunks = self._preprocess_sentence(sentence)
|
||||
# Apply the listen flag to the last chunk, set the rest to False
|
||||
chunks = [(chunks[i], listen if i == len(chunks) - 1 else False)
|
||||
|
@ -397,7 +501,7 @@ class TTS(metaclass=ABCMeta):
|
|||
audio_file, phoneme_file
|
||||
)
|
||||
viseme = self.viseme(phonemes) if phonemes else None
|
||||
self.queue.put(
|
||||
TTS.queue.put(
|
||||
(self.audio_ext, str(audio_file.path), viseme, ident, l)
|
||||
)
|
||||
|
||||
|
@ -477,10 +581,6 @@ class TTS(metaclass=ABCMeta):
|
|||
LOG.debug("Failed to read .PHO from cache")
|
||||
return None
|
||||
|
||||
def __del__(self):
|
||||
self.playback.stop()
|
||||
self.playback.join()
|
||||
|
||||
|
||||
class TTSValidator(metaclass=ABCMeta):
|
||||
"""TTS Validator abstract class to be implemented by all TTS engines.
|
||||
|
@ -488,7 +588,6 @@ class TTSValidator(metaclass=ABCMeta):
|
|||
It exposes and implements ``validate(tts)`` function as a template to
|
||||
validate the TTS engines.
|
||||
"""
|
||||
|
||||
def __init__(self, tts):
|
||||
self.tts = tts
|
||||
|
||||
|
@ -560,7 +659,6 @@ class TTSFactory:
|
|||
from mycroft.tts.spdsay_tts import SpdSay
|
||||
from mycroft.tts.bing_tts import BingTTS
|
||||
from mycroft.tts.ibm_tts import WatsonTTS
|
||||
from mycroft.tts.responsive_voice_tts import ResponsiveVoice
|
||||
from mycroft.tts.mimic2_tts import Mimic2
|
||||
from mycroft.tts.yandex_tts import YandexTTS
|
||||
from mycroft.tts.dummy_tts import DummyTTS
|
||||
|
@ -578,7 +676,6 @@ class TTSFactory:
|
|||
"spdsay": SpdSay,
|
||||
"watson": WatsonTTS,
|
||||
"bing": BingTTS,
|
||||
"responsive_voice": ResponsiveVoice,
|
||||
"yandex": YandexTTS,
|
||||
"polly": PollyTTS,
|
||||
"mozilla": MozillaTTS,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue