$text = "Bon dia, com esteu?"; $lang = "ca"; fitxer = generarTTS($lang, $text);
i que produeixi automàticament:
sortida_ca.mp3 sortida_es.mp3 sortida_en.mp3
o bé .wav.
Com que tu ja tens gTTS al Synology, una opció molt pràctica és que PHP cridi un petit script Python que faci la conversió. Així PHP només s’encarrega de passar $lang i $text.
Per exemple, l’arquitectura seria:
PHP │ │ $lang + $text ▼ Python / gTTS │ ├── ca → sortida_ca.mp3 ├── es → sortida_es.mp3 └── en → sortida_en.mp3
També podem fer que la mateixa funció decideixi automàticament el nom:
generarTTS("ca", "Bon dia");
→ sortida_ca.mp3
generarTTS("es", "Buenos días");
→ sortida_es.mp3
generarTTS("en", "Good morning");
→ sortida_en.mp3
I encara millor: podem fer que la funció PHP retorni el nom/ruta del fitxer generat, cosa que encaixa molt bé amb el teu Flask/Python actual i amb la reproducció automàtica al navegador.
Si vols, et puc preparar ara mateix la funció PHP completa generarTTS($lang, $text), preparada per al teu Synology, incloent-hi ca, es i en i controlant errors.
Sí. Aquí veig un punt molt probable de problema de codificació: estem passant el text català directament com a argument de la shell:
escapeshellarg($text)
Amb textos com:
Bon dia, sóc l'Ona. Què tal?
els caràcters ó, è, ç, à, etc. poden donar problemes segons la configuració de locale de PHP/Apache.
La manera més segura és no passar mai el text UTF-8 directament per la shell. El codifiquem en Base64 a PHP i Python el recupera. Així ó, è, ç, emojis, apòstrofs, cometes, etc. arriben intactes.
PHP
Canviaria la part que crida Python per aquesta:
<?php
header('Content-Type: text/html; charset=UTF-8');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$idioma = trim($_POST['lang'] ?? 'ca');
$text = trim($_POST['text'] ?? '');
if ($text !== '') {
// ------------------------------------------------
// CODIFICACIÓ SEGURA DEL TEXT
// ------------------------------------------------
// El text original és UTF-8.
// El passem a Base64 per evitar problemes amb la shell.
$text_base64 = base64_encode($text);
// Fitxer de sortida
$fitxer = __DIR__ . '/sortides/audio_' . $idioma . '.mp3';
// Script Python
$python = '/usr/bin/python3';
$script = __DIR__ . '/python_server/tts.py';
// ------------------------------------------------
// ORDRE
// ------------------------------------------------
$ordre = $python . ' '
. escapeshellarg($script) . ' '
. escapeshellarg($idioma) . ' '
. escapeshellarg($text_base64) . ' '
. escapeshellarg($fitxer);
// Executar Python
exec($ordre . ' 2>&1', $sortida, $retorn);
// ------------------------------------------------
// RESULTAT
// ------------------------------------------------
if ($retorn === 0 && file_exists($fitxer)) {
echo "<p style='font-family:Segoe UI; font-size:18px;'>";
echo "Text: <strong>" . htmlspecialchars($text, ENT_QUOTES, 'UTF-8') . "</strong><br>";
echo "Idioma: <strong>" . htmlspecialchars($idioma, ENT_QUOTES, 'UTF-8') . "</strong><br>";
echo "Fitxer generat: <strong>"
. htmlspecialchars(basename($fitxer), ENT_QUOTES, 'UTF-8')
. "</strong>";
echo "</p>";
echo "<audio controls autoplay>";
echo "<source src='sortides/"
. htmlspecialchars(basename($fitxer), ENT_QUOTES, 'UTF-8')
. "' type='audio/mpeg'>";
echo "El navegador no suporta àudio.";
echo "</audio>";
} else {
echo "<p style='color:red;'>Error executant TTS:</p>";
echo "<pre>";
echo htmlspecialchars(
implode("\n", $sortida),
ENT_QUOTES,
'UTF-8'
);
echo "</pre>";
}
}
}
?>
Python
I el tts.py recuperaria el text així:
#!/usr/bin/env python3
import sys
import base64
from gtts import gTTS
if len(sys.argv) != 4:
print("ERROR: arguments incorrectes")
sys.exit(1)
idioma = sys.argv[1]
text_base64 = sys.argv[2]
fitxer = sys.argv[3]
try:
# ------------------------------------------------
# RECUPERAR TEXT UTF-8
# ------------------------------------------------
text = base64.b64decode(text_base64).decode("utf-8")
print("IDIOMA:", idioma)
print("TEXT:", text)
# ------------------------------------------------
# gTTS
# ------------------------------------------------
tts = gTTS(
text=text,
lang=idioma,
slow=False
)
tts.save(fitxer)
print("OK:", fitxer)
except Exception as e:
print("ERROR:", repr(e))
sys.exit(1)
Per què aquesta versió és més segura?
El recorregut queda:
FORMULARI │ │ "Bon dia, sóc l'Ona. Què tal?" ▼ PHP │ │ UTF-8 ▼ base64_encode() │ │ "Qm9uIGRpYSwgc..." ▼ Shell │ ▼ Python │ │ base64.b64decode() ▼ UTF-8 │ │ "Bon dia, sóc l'Ona. Què tal?" ▼ gTTS │ ▼ audio_ca.mp3
Així el text no passa mai directament per la shell.
I això és especialment interessant en el teu cas perquè ja hem vist anteriorment que els accents són precisament un punt delicat en la cadena TTS.
Una prova molt bona
Abans de provar tot el formulari, podem fer una prova específica amb:
Bon dia, sóc l'Ona. Què tal? Àngel, després prendré cafè.
Si al print("TEXT:", text) de Python apareix exactament així:
TEXT: Bon dia, sóc l'Ona. Què tal? Àngel, després prendré cafè.
ja sabrem que PHP → Python està passant UTF-8 correctament.
Després només quedarà comprovar Python → gTTS → MP3.
Si vols, podem fer-ho encara més segur: mirem primer quin python3 està utilitzant el teu Apache/PHP i quin locale té, perquè al teu Synology això pot ser important.