Funció PHP amb entrada.txt i sortida .mp3 o .wav

$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.

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *

Aquest lloc utilitza Akismet per reduir els comentaris brossa. Apreneu com es processen les dades dels comentaris.