You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

193 lines
5.8 KiB

#!/bin/bash
# Script to download Piper TTS voices and place them in the correct structure
# Voices are downloaded from Hugging Face: https://huggingface.co/rhasspy/piper-voices
# Don't exit on error - we want to continue downloading other voices even if one fails
set +e
PIPER_DATA_DIR="./piper-data"
VOICES_BASE_URL="https://huggingface.co/rhasspy/piper-voices/resolve/main"
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}Piper TTS Voice Downloader${NC}"
echo "================================"
echo ""
# Create piper-data directory if it doesn't exist
mkdir -p "$PIPER_DATA_DIR"
# Function to download a voice
download_voice() {
local lang=$1
local locale=$2
local voice=$3
local quality=$4
local voice_name="${locale}-${voice}-${quality}"
local voice_dir="${PIPER_DATA_DIR}/voices/${locale}/${voice}/${quality}"
local onnx_file="${voice_dir}/${voice_name}.onnx"
local json_file="${voice_dir}/${voice_name}.onnx.json"
# Create directory structure
mkdir -p "$voice_dir"
# Check if voice already exists
if [ -f "$onnx_file" ] && [ -f "$json_file" ]; then
echo -e "${YELLOW}Voice ${voice_name} already exists, skipping...${NC}"
return 0
fi
echo "Downloading ${voice_name}..."
# Download .onnx.json file
local json_url="${VOICES_BASE_URL}/${lang}/${locale}/${voice}/${quality}/${voice_name}.onnx.json"
local curl_output=$(curl -L -f -w "\n%{http_code}" -o "$json_file" "$json_url" 2>&1)
local http_code=$(echo "$curl_output" | tail -n1)
local curl_error=$(echo "$curl_output" | head -n-1)
if [ "$http_code" = "200" ] && [ -f "$json_file" ] && [ -s "$json_file" ]; then
echo " ✓ Downloaded ${voice_name}.onnx.json"
else
echo " ✗ Failed to download ${voice_name}.onnx.json"
echo " URL: ${json_url}"
echo " HTTP Code: ${http_code:-unknown}"
if [ -n "$curl_error" ]; then
echo " Error: $(echo "$curl_error" | head -n1)"
fi
echo " This quality level may not be available for this voice."
rm -f "$json_file"
return 1
fi
# Download .onnx file
local onnx_url="${VOICES_BASE_URL}/${lang}/${locale}/${voice}/${quality}/${voice_name}.onnx"
curl_output=$(curl -L -f -w "\n%{http_code}" -o "$onnx_file" "$onnx_url" 2>&1)
http_code=$(echo "$curl_output" | tail -n1)
curl_error=$(echo "$curl_output" | head -n-1)
if [ "$http_code" = "200" ] && [ -f "$onnx_file" ] && [ -s "$onnx_file" ]; then
local file_size=$(stat -c%s "$onnx_file" 2>/dev/null || echo "0")
local file_size_mb=$(echo "scale=2; $file_size / 1024 / 1024" | bc 2>/dev/null || echo "?")
echo " ✓ Downloaded ${voice_name}.onnx (${file_size_mb} MB)"
else
echo " ✗ Failed to download ${voice_name}.onnx"
echo " URL: ${onnx_url}"
echo " HTTP Code: ${http_code:-unknown}"
if [ -n "$curl_error" ]; then
echo " Error: $(echo "$curl_error" | head -n1)"
fi
echo " This quality level may not be available for this voice."
rm -f "$onnx_file" "$json_file"
return 1
fi
echo -e "${GREEN} ✓ Successfully downloaded ${voice_name}${NC}"
return 0
}
# List of voices to download (based on the language detection function)
# Format: language_code locale voice quality
VOICES=(
# English (US) - all quality levels
"en en_US lessac low"
"en en_US lessac medium"
"en en_US lessac high"
# English (GB)
"en en_GB alba medium"
# German
"de de_DE thorsten medium"
"de de_DE thorsten low"
# French
"fr fr_FR siwis medium"
"fr fr_FR siwis low"
# Spanish
"es es_ES davefx medium"
# Note: es_ES-davefx-low doesn't exist
# Italian - riccardo doesn't exist, removing
# "it it_IT riccardo medium" - not available
# "it it_IT riccardo low" - not available
# Russian
"ru ru_RU ruslan medium"
# Note: ru_RU-ruslan-low doesn't exist
# Chinese
"zh zh_CN huayan medium"
# Arabic - hafez doesn't exist, removing
# "ar ar_SA hafez medium" - not available
# Polish
"pl pl_PL darkman medium"
# Portuguese - edresson doesn't exist, removing
# "pt pt_BR edresson medium" - not available
# Dutch
"nl nl_NL mls medium"
# Czech
"cs cs_CZ jirka medium"
# Turkish
"tr tr_TR dfki medium"
# Japanese - nanami doesn't exist, removing
# "ja ja_JP nanami medium" - not available
# Korean - kyungha doesn't exist, removing
# "ko ko_KR kyungha medium" - not available
)
# Check if specific voices are requested
if [ $# -gt 0 ]; then
VOICES=("$@")
fi
echo "Downloading ${#VOICES[@]} voice(s)..."
echo ""
SUCCESS=0
FAILED=0
for voice_spec in "${VOICES[@]}"; do
# Parse voice specification
read -r lang locale voice_name quality <<< "$voice_spec"
# Validate voice specification
if [ -z "$lang" ] || [ -z "$locale" ] || [ -z "$voice_name" ] || [ -z "$quality" ]; then
echo -e "${YELLOW}⚠ Skipping invalid voice specification: '${voice_spec}'${NC}"
((FAILED++))
echo ""
continue
fi
if download_voice "$lang" "$locale" "$voice_name" "$quality"; then
((SUCCESS++))
else
((FAILED++))
fi
echo ""
done
echo "================================"
echo -e "${GREEN}Download complete!${NC}"
echo "Successfully downloaded: $SUCCESS"
if [ $FAILED -gt 0 ]; then
echo -e "${YELLOW}Failed: $FAILED${NC}"
fi
echo ""
echo "Voices are now in: $PIPER_DATA_DIR"
echo "This directory is mounted into the Docker container at /data"
echo ""
echo "To use these voices, restart your Docker containers:"
echo " docker-compose down"
echo " docker-compose up --build"