Spaces:
Running
Running
File size: 3,120 Bytes
7135eb0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | #!/usr/bin/env bash
# Chunked TTS for long text - splits into sentences, generates each, concatenates
# Usage: voice-long.sh "text" [voice] [format]
TTS_URL="https://hf4uwho-pocket-tts.hf.space/tts"
TEXT="${1:?Usage: voice-long.sh 'text' [voice] [format]}"
VOICE="${2:-af_alloy}"
FORMAT="${3:-ogg}"
OUTDIR="/tmp/tts"
mkdir -p "$OUTDIR"
HASH=$(echo -n "$TEXT$VOICE$(date +%s%N)" | md5sum | cut -c1-12)
OUTFILE="$OUTDIR/voice_${HASH}.${FORMAT}"
CHUNKDIR="$OUTDIR/chunks_${HASH}"
mkdir -p "$CHUNKDIR"
# Split text into sentences (rough but effective)
CHUNKS=$(python3 -c "
import re
text = '''$TEXT'''
# Split on sentence boundaries, group into chunks of ~200 chars
sentences = re.split(r'(?<=[.!?])\s+', text)
chunks = []
current = ''
for s in sentences:
if len(current) + len(s) > 300 and current:
chunks.append(current.strip())
current = s
else:
current = (current + ' ' + s).strip()
if current.strip():
chunks.append(current.strip())
for i, c in enumerate(chunks):
print(f'CHUNK_{i}:{c}')
")
# Generate each chunk
INDEX=0
CHUNK_FILES=()
while IFS= read -r line; do
if [[ "$line" == CHUNK_* ]]; then
CHUNK_TEXT="${line#CHUNK_[0-9]*:}"
# Strip the index prefix
CHUNK_TEXT="${line#*:}"
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''$CHUNK_TEXT'''))")
CHUNKFILE="$CHUNKDIR/chunk_${INDEX}.${FORMAT}"
HTTP_CODE=$(curl -s -w "%{http_code}" -o "$CHUNKFILE" "${TTS_URL}?text=${ENCODED}&voice=${VOICE}&format=${FORMAT}" --max-time 60 2>/dev/null)
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: Chunk $INDEX failed with HTTP $HTTP_CODE" >&2
rm -rf "$CHUNKDIR"
exit 1
fi
# Verify audio
MIME=$(file -b --mime-type "$CHUNKFILE" 2>/dev/null)
if [[ "$MIME" == audio/* ]] || [[ "$MIME" == application/ogg ]]; then
CHUNK_FILES+=("$CHUNKFILE")
INDEX=$((INDEX + 1))
else
echo "WARNING: Chunk $INDEX not audio ($MIME), skipping" >&2
fi
fi
done <<< "$CHUNKS"
if [ ${#CHUNK_FILES[@]} -eq 0 ]; then
echo "ERROR: No chunks generated" >&2
rm -rf "$CHUNKDIR"
exit 1
fi
# If only one chunk, just use it
if [ ${#CHUNK_FILES[@]} -eq 1 ]; then
cp "${CHUNK_FILES[0]}" "$OUTFILE"
else
# Concatenate Ogg files using sox or ffmpeg, fallback to simple cat
if command -v sox &>/dev/null; then
sox "${CHUNK_FILES[@]}" "$OUTFILE" 2>/dev/null
elif command -v ffmpeg &>/dev/null; then
# Create concat list
CONCFILE="$CHUNKDIR/concat.txt"
> "$CONCFILE"
for f in "${CHUNK_FILES[@]}"; do
echo "file '$f'" >> "$CONCFILE"
done
ffmpeg -y -f concat -safe 0 -i "$CONCFILE" -c copy "$OUTFILE" 2>/dev/null
else
# Simple cat (works for raw Ogg but may have glitches)
cat "${CHUNK_FILES[@]}" > "$OUTFILE"
fi
fi
rm -rf "$CHUNKDIR"
if [ -f "$OUTFILE" ] && [ -s "$OUTFILE" ]; then
echo "$OUTFILE"
else
echo "ERROR: Final file empty or missing" >&2
exit 1
fi
|