{"id":19046,"date":"2026-09-13T10:33:24","date_gmt":"2026-09-13T08:33:24","guid":{"rendered":"https:\/\/www.beseit.net\/?p=19046"},"modified":"2026-09-13T11:29:34","modified_gmt":"2026-09-13T09:29:34","slug":"tts-local-amb-veu-catalana-ona-a-deepin-guia-completa","status":"publish","type":"post","link":"https:\/\/www.beseit.net\/?p=19046","title":{"rendered":"TTS local amb veu catalana (Ona) a Deepin \u2014 Guia completa"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Fant\u00e0stic que ja soni l&#8217;Ona! \ud83c\udf89 Et preparo l&#8217;entrada de blog completa, ben estructurada i llesta per copiar.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">\ud83c\udf99\ufe0f TTS local amb veu catalana (Ona) a Deepin \u2014 Guia completa<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">1. Objectiu<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Disposar d&#8217;un <strong>servidor de s\u00edntesi de veu (TTS) local<\/strong> al sistema operatiu Deepin que sigui:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>100% offline<\/strong> (sense n\u00favol, sense dependre de serveis externs)<\/li>\n\n\n\n<li>Amb <strong>veu catalana<\/strong> de qualitat: la veu <strong>\u00abOna\u00bb<\/strong>, desenvolupada per la <strong>UPC (Universitat Polit\u00e8cnica de Catalunya)<\/strong><\/li>\n\n\n\n<li>F\u00e0cil de provar, integrar i reinstal\u00b7lar<\/li>\n\n\n\n<li>Basat en <strong>Piper<\/strong>, un motor TTS lleuger i modern basat en xarxes neuronals (VITS)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">2. Estructura del sistema<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502                     DEEPIN (host)                    \u2502\n\u2502                                                     \u2502\n\u2502   \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510   \u2502\n\u2502   \u2502         Contenidor Docker \"piper\"           \u2502   \u2502\n\u2502   \u2502                                             \u2502   \u2502\n\u2502   \u2502  linuxserver\/piper (v2.5.2-ls130)           \u2502   \u2502\n\u2502   \u2502  \u251c\u2500\u2500 Servidor Wyoming (port 10200)          \u2502   \u2502\n\u2502   \u2502  \u2514\u2500\u2500 Model de veu:                          \u2502   \u2502\n\u2502   \u2502      ca_ES-upc_ona-medium.onnx              \u2502   \u2502\n\u2502   \u2502      (descarregat de HuggingFace)           \u2502   \u2502\n\u2502   \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518   \u2502\n\u2502              \u2502 127.0.0.1:10200                      \u2502\n\u2502              \u25bc                                      \u2502\n\u2502   Clients: curl \/ Python \/ Speech Dispatcher        \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Components clau:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Component<\/th><th>Descripci\u00f3<\/th><\/tr><\/thead><tbody><tr><td><strong>Piper<\/strong><\/td><td>Motor TTS neuronal (VITS), lleuger i r\u00e0pid, compatible amb Raspberry Pi i servidors<\/td><\/tr><tr><td><strong>linuxserver\/piper<\/strong><\/td><td>Imatge Docker que encapsula Piper amb un servidor <strong>Wyoming<\/strong> (protocol est\u00e0ndard de veu, compatible amb Home Assistant)<\/td><\/tr><tr><td><strong>ca_ES-upc_ona-medium<\/strong><\/td><td>Veu catalana \u00abOna\u00bb (UPC), qualitat <em>medium<\/em>, model ONNX (~60 MB)<\/td><\/tr><tr><td><strong>Port 10200<\/strong><\/td><td>Port d&#8217;escolta del servidor Wyoming<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">3. Flux de funcionament<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Text (\"Hola, s\u00f3c l'Ona\")\n        \u2502\n        \u25bc\n[Client] \u2500\u2500 connexi\u00f3 TCP 127.0.0.1:10200 \u2500\u2500\u25ba [Servidor Wyoming\/Piper]\n        \u2502                                          \u2502\n        \u2502  1. Envia esdeveniment \"synthesize\"      \u2502 Carrega model ONNX\n        \u2502     {\"text\": \"...\", \"voice\": \"...\"}      \u2502 i sintetitza amb la xarxa neuronal\n        \u2502                                          \u25bc\n        \u2502  \u25c4\u2500\u2500 2. Rep esdeveniments \"audio-chunk\" \u2500\u2518\n        \u2502         (\u00e0udio PCM 16-bit, 22050 Hz, mono)\n        \u25bc\n[\u00c0udio WAV \/ aplay \/ altaveus]<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>El protocol Wyoming<\/strong> funciona per esdeveniments JSON amb el format:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">{\"type\": \"...\", \"version\": \"...\", \"data_length\": N}\n&lt;2 salts de l\u00ednia>\n&lt;c\u00e0rrega de dades (N bytes)><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4. Instal\u00b7laci\u00f3<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">4.1 Crear el contenidor<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker run -d \\\n  --name piper \\\n  -p 10200:10200 \\\n  -e PIPER_VOICE=ca_ES-upc_ona-medium \\\n  --restart unless-stopped \\\n  linuxserver\/piper<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>-p 10200:10200<\/code> \u2192 exposa el port del servidor Wyoming<\/li>\n\n\n\n<li><code>-e PIPER_VOICE=...<\/code> \u2192 defineix la veu per defecte (la descarrega autom\u00e0ticament a la primera arrencada)<\/li>\n\n\n\n<li><code>--restart unless-stopped<\/code> \u2192 s&#8217;aixeca sol en reiniciar el sistema<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.2 Verificaci\u00f3<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Contenidor en marxa?\ndocker ps | grep piper\n\n# El servidor respon al protocol Wyoming?\nprintf '{\"type\":\"describe\"}\\n\\n' | timeout 3 nc 127.0.0.1 10200\n\n# Veure els logs (ha de dir \"Ready\")\ndocker logs piper<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5. Prova de veu (la que ha funcionat!)<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker exec piper bash -c \"echo \\\"Hola, soc l'Ona, la meva veu s'ha desenvolupat a la UPC de Barcelona.\\\" | piper --model \/config\/ca_ES-upc_ona-medium.onnx --output_file -\" | aplay -f S16_LE -r 22050 -c 1<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Com funciona aquesta comanda:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>docker exec piper ...<\/code> \u2192 executa Piper <strong>dins<\/strong> del contenidor<\/li>\n\n\n\n<li><code>echo \"text\" | piper --model ... --output_file -<\/code> \u2192 sintetitza el text a \u00e0udio i l&#8217;envia per <strong>stdout<\/strong> (<code>-<\/code>)<\/li>\n\n\n\n<li><code>aplay -f S16_LE -r 22050 -c 1<\/code> \u2192 reprodueix l&#8217;\u00e0udio PCM (16-bit, 22050 Hz, mono) pels altaveus<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">6. Prova amb Python (protocol Wyoming)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Script per generar un fitxer WAV connectant-se directament al servidor del port 10200:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import socket, json, wave\n\ns = socket.create_connection((\"127.0.0.1\", 10200), timeout=60)\nf = s.makefile(\"rb\")\n\ntext = \"Hola, soc l'Ona. Aixo es una prova del servidor TTS.\"\nevt = json.dumps({\"type\": \"synthesize\", \"data\": {\"text\": text, \"voice\": \"ca_ES-upc_ona-medium\"}})\nf.write((evt + \"\\n\\n\").encode())\nf.flush()\n\nchunks, rate, width, channels = [], 22050, 2, 1\nwhile True:\n    header = f.readline()\n    if not header:\n        break\n    h = json.loads(header)\n    t = h.get(\"type\")\n    f.read(1)  # segon \\n del separador\n    data = json.loads(f.read(h.get(\"data_length\", 0))) if h.get(\"data_length\") else {}\n\n    if t == \"audio-start\":\n        rate = data.get(\"rate\", rate)\n        width = data.get(\"width\", width)\n        channels = data.get(\"channels\", channels)\n    elif t == \"audio-chunk\":\n        chunks.append(f.read(h.get(\"payload_length\", 0)))\n    elif t == \"audio-stop\":\n        break\ns.close()\n\naudio = b\"\".join(chunks)\nw = wave.open(\"prova_ona.wav\", \"wb\")\nw.setnchannels(channels)\nw.setsampwidth(width)\nw.setframerate(rate)\nw.writeframes(audio)\nw.close()\nprint(f\"OK: prova_ona.wav ({len(audio)} bytes)\")<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">7. Altres operacions \u00fatils<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Aturar \/ engegar<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker stop piper\ndocker start piper<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Desinstal\u00b7lar i reinstal\u00b7lar<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker rm -f piper\ndocker image rm linuxserver\/piper\n\n# Reinstal\u00b7lar\ndocker run -d --name piper -p 10200:10200 \\\n  -e PIPER_VOICE=ca_ES-upc_ona-medium \\\n  --restart unless-stopped linuxserver\/piper<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">8. Conclusions<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 TTS <strong>local i offline<\/strong> amb veu catalana de qualitat (Ona, UPC)<\/li>\n\n\n\n<li>\u2705 Instal\u00b7laci\u00f3 senzilla amb Docker i reinici autom\u00e0tic<\/li>\n\n\n\n<li>\u2705 Protocol <strong>Wyoming<\/strong> est\u00e0ndard \u2192 integrable amb Home Assistant i altres apps<\/li>\n\n\n\n<li>\u2705 Tamb\u00e9 es pot usar amb <strong>Speech Dispatcher<\/strong> perqu\u00e8 les aplicacions d&#8217;escriptori parlin en catal\u00e0<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Aquesta \u00e9s la base per a l&#8217;entrada! Si vols, tamb\u00e9 puc afegir una <strong>secci\u00f3 amb captures de pantalla<\/strong> o una <strong>part sobre com integrar-ho amb Speech Dispatcher<\/strong> (perqu\u00e8 el sistema llegeixi text en catal\u00e0). Ho deixem aix\u00ed o hi afegim alguna cosa m\u00e9s? \ud83d\ude0a<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0);color:#e91c1c\" class=\"has-inline-color\">Aclariments<\/mark><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bona pregunta! \ud83d\udc4c El port <strong>10200 no l&#8217;hem triat nosaltres<\/strong> \u2014 \u00e9s el <strong>port per defecte<\/strong> d&#8217;aquesta imatge i de l&#8217;ecosistema Wyoming:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Motius<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Port per defecte de <code>linuxserver\/piper<\/code><\/strong> \u2014 el servidor que hi ha dins del contenidor escolta al port <strong>10200<\/strong> de manera nativa. Per aix\u00f2 al Docker fem <code>-p 10200:10200<\/code> (port del host \u2192 port del contenidor).<\/li>\n\n\n\n<li><strong>Convenci\u00f3 de l&#8217;ecosistema Wyoming \/ Home Assistant<\/strong> \u2014 el protocol Wyoming (el que parla aquest servidor) fa servir habitualment ports del rang <strong>10200-10300<\/strong>. De fet, el <em>add-on<\/em> de Piper per a Home Assistant tamb\u00e9 usa el <strong>10200<\/strong> per defecte. Aix\u00ed, si algun dia el connectes a Home Assistant o a una altra eina de veu, ja parlar\u00e0 el mateix idioma al mateix port.<\/li>\n\n\n\n<li><strong>Port sense privilegis<\/strong> \u2014 est\u00e0 per sobre del 1024, aix\u00ed que no cal ser <em>root<\/em> ni configuracions especials per obrir-lo.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Es pot canviar?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">S\u00ed! La part de l&#8217;esquerra del mapeig \u00e9s el port del host, aix\u00ed que podries posar-hi el que vulguis:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker run -d --name piper -p 10500:10200 ...<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">(aleshores el servidor seria accessible a <code>127.0.0.1:10500<\/code>). Per\u00f2 com que 10200 \u00e9s l&#8217;est\u00e0ndard, \u00e9s millor deixar-lo com est\u00e0 perqu\u00e8 tot l&#8217;ecosistema el reconegui sense tocar res. \ud83d\ude0a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Molt bona pregunta! No \u00e9s que <strong>canvi\u00e9ssim<\/strong> de port \u2014 s\u00f3n <strong>dos servidors TTS diferents<\/strong> que conviuen, cadascun amb el seu port i la seva funci\u00f3:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Els dos servidors<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><\/th><th>Port 8765<\/th><th>Port 10200<\/th><\/tr><\/thead><tbody><tr><td><strong>Servei<\/strong><\/td><td><code>speech-to-speech<\/code><\/td><td>Piper (Docker)<\/td><\/tr><tr><td><strong>Qu\u00e8 \u00e9s<\/strong><\/td><td>Servidor compatible amb OpenAI Realtime (TTS + STT + LLM)<\/td><td>Motor TTS lleuger amb veu catalana<\/td><\/tr><tr><td><strong>Veu catalana?<\/strong><\/td><td>No espec\u00edfica (Qwen3-TTS, general)<\/td><td><strong>S\u00ed: Ona (ca_ES-upc_ona-medium, UPC)<\/strong><\/td><\/tr><tr><td><strong>Instal\u00b7laci\u00f3<\/strong><\/td><td>Python venv a <code>~\/tts-server<\/code><\/td><td>Docker<\/td><\/tr><tr><td><strong>Estat actual<\/strong><\/td><td>Aturat<\/td><td>En marxa \u2705<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Per qu\u00e8 vam afegir el Piper (10200)?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Perqu\u00e8 volies una <strong>veu catalana de qualitat<\/strong> (l&#8217;Ona de la UPC). El Piper \u00e9s:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Lleuger i offline<\/strong> \u2014 perfecte per a un punt de treball local<\/li>\n\n\n\n<li>T\u00e9 el model <strong>ca_ES-upc_ona-medium<\/strong> espec\u00edfic per a catal\u00e0<\/li>\n\n\n\n<li>S&#8217;integra amb <strong>Speech Dispatcher<\/strong> perqu\u00e8 el sistema llegeixi text en catal\u00e0<\/li>\n\n\n\n<li>Docker el fa f\u00e0cil d&#8217;instal\u00b7lar, actualitzar i reinstal\u00b7lar<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">I el 8765?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>No l&#8217;hem tret!<\/strong> El <code>speech-to-speech<\/code> continua instal\u00b7lat a <code>~\/tts-server<\/code>, nom\u00e9s est\u00e0 aturat (ho vam veure quan <code>ss<\/code> no mostrava res al port 8765). \u00c9s un servidor m\u00e9s \u00abcomplet\u00bb (xat de veu amb micr\u00f2fon + LLM), per\u00f2 no t\u00e9 la veu catalana espec\u00edfica.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Resum:<\/strong> el 8765 \u00e9s per al xat de veu general, i el 10200 \u00e9s el que et dona l&#8217;Ona en catal\u00e0. Cadasc\u00fa al seu port, sense conflictes! \ud83d\ude0a<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0);color:#f31212\" class=\"has-inline-color\">SERVIDOR TTS SERVER \/home\/Miquel\/tts-server<\/mark><\/h2>\n\n\n\n<h2 class=\"wp-block-heading\">Qu\u00e8 hi ha a <code>~\/tts-server<\/code> (s\u00ed, tot el servei speech-to-speech)<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/home\/Miquel\/tts-server\/\n\u251c\u2500\u2500 venv\/                    \u2190 entorn Python amb el paquet speech-to-speech\n\u251c\u2500\u2500 prova_piper.py           \u2190 els nostres scripts de prova\n\u251c\u2500\u2500 prova_piper_dbg.py\n\u2514\u2500\u2500 ...<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed hi ha <strong>tot el servei <code>speech-to-speech<\/code><\/strong> (el del port 8765): l&#8217;entorn virtual, el paquet instal\u00b7lat i els scripts.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0);color:#f00a0a\" class=\"has-inline-color\">On \u00e9s la veu de l&#8217;Ona? (\u26a0\ufe0f NO \u00e9s aqu\u00ed)<\/mark><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">La veu <strong>Ona NO est\u00e0 a <code>~\/tts-server<\/code><\/strong> \u2014 est\u00e0 <strong>dins del contenidor Docker <code>piper<\/code><\/strong>, a la carpeta <code>\/config\/<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Per veure-ho:\ndocker exec piper ls -lh \/config\/<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Hi trobar\u00e0s:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ca_ES-upc_ona-medium.onnx       \u2190 el model de veu\nca_ES-upc_ona-medium.onnx.json  \u2190 config de la veu<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u26a0\ufe0f Punt important<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Com que al <code>docker run<\/code> <strong>no vam muntar cap volum<\/strong> (<code>-v<\/code>), la veu viu dins de la capa del contenidor. Si algun dia fas <code>docker rm -f piper<\/code>, <strong>la veu es perd<\/strong> i es tornar\u00e0 a descarregar en recrear el contenidor (caldr\u00e0 internet la primera vegada).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Resum:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>speech-to-speech<\/code> \u2192 tot a <code>~\/tts-server<\/code> \u2705<\/li>\n\n\n\n<li><strong>Ona<\/strong> \u2192 dins del contenidor Docker <code>piper<\/code> a <code>\/config\/<\/code> \u2705<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Si vols <strong>preservar la veu<\/strong> encara que esborris el contenidor, et puc explicar com muntar un volum per a <code>\/config<\/code>. T&#8217;interessa? \ud83d\ude0a<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Docker <strong>no penja de cap carpeta del teu usuari<\/strong> \u2014 el seu directori d&#8217;emmagatzematge \u00e9s a nivell de sistema: \/var\/lib\/docker<\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0);color:#e90c0c\" class=\"has-inline-color\">\ud83d\udcc1 Directori arrel de Docker<\/mark><\/strong> \/var\/lib\/docker<\/h1>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"553\" height=\"86\" src=\"https:\/\/www.beseit.net\/wp-content\/uploads\/2026\/09\/imatge.png\" alt=\"\" class=\"wp-image-19051\" srcset=\"https:\/\/www.beseit.net\/wp-content\/uploads\/2026\/09\/imatge.png 553w, https:\/\/www.beseit.net\/wp-content\/uploads\/2026\/09\/imatge-300x47.png 300w, https:\/\/www.beseit.net\/wp-content\/uploads\/2026\/09\/imatge-500x78.png 500w\" sizes=\"auto, (max-width: 553px) 100vw, 553px\" \/><\/figure>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/var\/lib\/docker\/\n\u251c\u2500\u2500 containers\/    \u2190 dades dels contenidors\n\u251c\u2500\u2500 image\/         \u2190 imatges (linuxserver\/piper, etc.)\n\u251c\u2500\u2500 overlay2\/      \u2190 capes del sistema de fitxers dels contenidors\n\u251c\u2500\u2500 volumes\/       \u2190 volums (si n'hi ha)\n\u2514\u2500\u2500 ...<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Per confirmar-ho a la teva m\u00e0quina:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker info | grep \"Docker Root Dir\"\n# \u2192 Docker Root Dir: \/var\/lib\/docker<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd0d I la veu de l&#8217;Ona, on \u00e9s exactament?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Com que el contenidor <code>piper<\/code> <strong>no t\u00e9 cap volum muntat<\/strong>, la carpeta <code>\/config\/<\/code> (amb l&#8217;Ona) viu dins de la capa del contenidor, que es troba a:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/var\/lib\/docker\/overlay2\/... (capa del contenidor piper)<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Conclusi\u00f3 important per a tu<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Docker i la veu d&#8217;Ona <strong>NO depenen de <code>~\/tts-server<\/code><\/strong>.<\/li>\n\n\n\n<li>Si esborressis <code>~\/tts-server<\/code>, <strong>el Piper i l&#8217;Ona continuarien funcionant<\/strong> (nom\u00e9s perdries el servei <code>speech-to-speech<\/code> i els scripts de prova).<\/li>\n\n\n\n<li>I a l&#8217;inrev\u00e9s: si esborres el contenidor (<code>docker rm -f piper<\/code>), <strong>l&#8217;Ona es perd<\/strong> (perqu\u00e8 no hi ha volum) i es torna a descarregar en recrear-lo.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Si vols que l&#8217;Ona <strong>sobreviu a l&#8217;esborrat del contenidor<\/strong>, la soluci\u00f3 \u00e9s muntar un volum per a <code>\/config<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker rm -f piper\ndocker run -d --name piper \\\n  -p 10200:10200 \\\n  -e PIPER_VOICE=ca_ES-upc_ona-medium \\\n  -v \/home\/Miquel\/piper-config:\/config \\\n  --restart unless-stopped linuxserver\/piper<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aix\u00ed la veu quedaria desada a <code>~\/piper-config<\/code> i no es perdria mai. Vols que ho fem? \ud83d\ude0a<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">La veu de l&#8217;Ona, <strong>dins de l&#8217;arbre de Docker<\/strong>, quedaria aix\u00ed (a la capa del contenidor <code>piper<\/code> dins de <code>overlay2<\/code>):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/var\/lib\/docker\/\n\u251c\u2500\u2500 containers\/\n\u2502   \u2514\u2500\u2500 55f8e63ad1d5...\/          \u2190 metadades del contenidor piper\n\u2502       \u251c\u2500\u2500 config.v2.json\n\u2502       \u2514\u2500\u2500 hostconfig.json\n\u251c\u2500\u2500 image\/\n\u2502   \u2514\u2500\u2500 overlay2\/\n\u2502       \u251c\u2500\u2500 layerdb\/              \u2190 registre de capes\n\u2502       \u2514\u2500\u2500 repositories.json\n\u251c\u2500\u2500 overlay2\/\n\u2502   \u251c\u2500\u2500 l\/                        \u2190 enlla\u00e7os curts\n\u2502   \u2514\u2500\u2500 &lt;hash_del_contenidor>\/\n\u2502       \u2514\u2500\u2500 diff\/\n\u2502           \u2514\u2500\u2500 config\/           \u2190 el \/config del contenidor piper\n\u2502               \u251c\u2500\u2500 ca_ES-upc_ona-medium.onnx       \u2190 \ud83c\udf99\ufe0f LA VEU ON A\n\u2502               \u2514\u2500\u2500 ca_ES-upc_ona-medium.onnx.json\n\u2514\u2500\u2500 volumes\/                      \u2190 buit (no en tenim)<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u26a0\ufe0f <strong>Av\u00eds:<\/strong> aquesta ruta nom\u00e9s \u00e9s visible amb permisos de root (per aix\u00f2 <code>ls<\/code> normal no la veu).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si vols <strong>comprovar-ho tu mateix<\/strong> al terminal:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># 1. Trobar el directori on viu la veu (necessita sudo)\nsudo find \/var\/lib\/docker -name \"ca_ES-upc_ona*\" 2>\/dev\/null\n\n# 2. Resultat esperat (alguna cosa semblant):\n# \/var\/lib\/docker\/overlay2\/&lt;hash>\/diff\/config\/ca_ES-upc_ona-medium.onnx\n# \/var\/lib\/docker\/overlay2\/&lt;hash>\/diff\/config\/ca_ES-upc_ona-medium.onnx.json<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Tamb\u00e9 ho pots veure \u00abdes de dins\u00bb del contenidor (aix\u00f2 s\u00ed que ho pots fer sense sudo):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># La veu vista des del punt de vista del contenidor\ndocker exec piper ls -lh \/config\/<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Exacte, la teva intu\u00efci\u00f3 \u00e9s correcta! \u2705 Per\u00f2 ho podem afinar perqu\u00e8 sigui <strong>m\u00e9s lleuger i fiable<\/strong>. Et detallo:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Qu\u00e8 s&#8217;ha de fer backup i com<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1\ufe0f\u20e3 Servei speech-to-speech (port 8765) \u2192 <code>~\/tts-server<\/code><\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Backup\ntar czf tts-server-backup.tar.gz -C \/home\/Miquel tts-server\n\n# Restauraci\u00f3 (al mateix cam\u00ed \/home\/Miquel\/tts-server)\ntar xzf tts-server-backup.tar.gz -C \/home\/Miquel<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u26a0\ufe0f <strong>Detall important:<\/strong> l&#8217;entorn virtual (<code>venv<\/code>) guarda camins absoluts. Si restaures <strong>al mateix cam\u00ed<\/strong> (<code>\/home\/Miquel\/tts-server<\/code>), funciona tal qual. Si el cam\u00ed canvia, caldria recrear el venv i reinstal\u00b7lar el paquet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2\ufe0f\u20e3 Piper + veu Ona (port 10200) \u2192 Docker<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed tens <strong>dues opcions<\/strong>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Opci\u00f3 A \u2014 Recomanada (lleugera):<\/strong> fer backup de la imatge + la carpeta de config<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Backup de la imatge Docker (el programa)\ndocker save linuxserver\/piper -o piper-image.tar\n\n# La veu: si muntem el volum \/config (com et vaig suggerir), es desa a ~\/piper-config\ntar czf piper-config-backup.tar.gz ~\/piper-config<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Opci\u00f3 B \u2014 Completa (tot Docker):<\/strong> backup de <code>\/var\/lib\/docker<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Requereix sudo i \u00e9s m\u00e9s pesat\nsudo tar czf docker-full-backup.tar.gz \/var\/lib\/docker<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 El teu plantejament, confirmat PODEM FER BACKUP DEL SERVIDORS TTS<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Servei<\/th><th>On es desa<\/th><th>Com es recupera<\/th><\/tr><\/thead><tbody><tr><td>speech-to-speech (8765)<\/td><td><code>~\/tts-server<\/code><\/td><td>Copiar la carpeta enrere<\/td><\/tr><tr><td>Piper + Ona (10200)<\/td><td><code>\/var\/lib\/docker<\/code> (imatge + capa contenidor)<\/td><td>Restaurar Docker <strong>o<\/strong> <code>docker save<\/code> + volum <code>\/config<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Resum:<\/strong> s\u00ed, amb un backup de <strong><code>~\/tts-server<\/code> + Docker<\/strong> ho recuperes tot. Per\u00f2 la manera m\u00e9s neta \u00e9s:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong><code>tar<\/code> de <code>~\/tts-server<\/code><\/strong> (per al 8765)<\/li>\n\n\n\n<li><strong><code>docker save<\/code> de la imatge + volum <code>\/config<\/code><\/strong> (per al 10200 i l&#8217;Ona) \u2014 aix\u00ed no cal tocar tot <code>\/var\/lib\/docker<\/code><\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Vols que et prepari un <strong>script de backup\/restauraci\u00f3<\/strong> senzill amb tot aix\u00f2? \ud83d\ude0a<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Endavant! Et preparo <strong>dos scripts<\/strong>: un de backup i un de restauraci\u00f3. Els pots desar on vulguis (per exemple a <code>~\/<\/code>).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udce6 Script 1 \u2014 Backup (<code>tts_backup.sh<\/code>)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Desa&#8217;l amb aquesta comanda:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Amb l'USB \"backup_tot\" connectat, nom\u00e9s cal:\n~\/tts_backup.sh\n# \u2192 USB 'backup_tot' trobat a: \/media\/Miquel\/backup_tot\n# \u2192 Desant a: \/media\/Miquel\/backup_tot\/20260913_...\n\n# Si l'USB no est\u00e0 connectat, et dir\u00e0 que el connectis\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">S\u00ed! Podem fer que el script <strong>busqui autom\u00e0ticament<\/strong> l&#8217;USB amb l&#8217;etiqueta <code>backup_tot<\/code>. Desa aquesta versi\u00f3:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">cat > ~\/tts_backup.sh &lt;&lt; 'EOF'\n#!\/bin\/bash\n# ============================================\n# Backup del sistema TTS local (Deepin)\n# Us: .\/tts_backup.sh [carpeta_destinacio]\n#   Sense arguments: busca automaticament l'USB \"backup_tot\"\n#   Exemple: .\/tts_backup.sh \/media\/Miquel\/backup_tot\n# ============================================\nset -e\n\nUSB_LABEL=\"backup_tot\"\nDEST=\"\"\n\n# 1) Si es passa una carpeta, s'usa aquesta\nif [ -n \"$1\" ]; then\n  DEST=\"$1\"\nelse\n  # 2) Si no, buscar l'USB amb l'etiqueta backup_tot\n  DEST=$(findmnt -rn -o TARGET -S LABEL=\"$USB_LABEL\" 2>\/dev\/null | head -1)\n  if [ -z \"$DEST\" ]; then\n    DEST=$(ls -d \/media\/*\/\"$USB_LABEL\" \/run\/media\/*\/\"$USB_LABEL\" 2>\/dev\/null | head -1)\n  fi\n  if [ -n \"$DEST\" ]; then\n    echo \"USB '$USB_LABEL' trobat a: $DEST\"\n  fi\nfi\n\n# Si no s'ha trobat cap destinacio, error\nif [ -z \"$DEST\" ]; then\n  echo \"ERROR: no s'ha trobat la destinacio.\"\n  echo \"  - Connecta l'USB '$USB_LABEL' i munta'l\"\n  echo \"  - O indica una carpeta: $0 \/ruta\/destinacio\"\n  exit 1\nfi\n\n# Comprovar permisos\nif [ ! -d \"$DEST\" ] || [ ! -w \"$DEST\" ]; then\n  echo \"ERROR: la destinacio '$DEST' no existeix o no es pot escriure.\"\n  exit 1\nfi\n\nDATA=$(date +%Y%m%d_%H%M%S)\nBACKUP_DIR=\"$DEST\/$DATA\"\nmkdir -p \"$BACKUP_DIR\"\necho \"Desant a: $BACKUP_DIR\"\n\necho \"=== 1\/3 Backup de ~\/tts-server (speech-to-speech) ===\"\nif [ -d \"$HOME\/tts-server\" ]; then\n  tar czf \"$BACKUP_DIR\/tts-server.tar.gz\" -C \"$HOME\" tts-server\n  echo \"OK -> tts-server.tar.gz\"\nelse\n  echo \"AVIS: ~\/tts-server no existeix, s'omet.\"\nfi\n\necho \"=== 2\/3 Backup de la imatge Docker linuxserver\/piper ===\"\nif docker image inspect linuxserver\/piper >\/dev\/null 2>&amp;1; then\n  docker save linuxserver\/piper -o \"$BACKUP_DIR\/piper-image.tar\"\n  echo \"OK -> piper-image.tar\"\nelse\n  echo \"AVIS: imatge linuxserver\/piper no trobada, s'omet.\"\nfi\n\necho \"=== 3\/3 Backup de la veu Ona (\/config del contenidor piper) ===\"\nif docker ps -a --format '{{.Names}}' | grep -qx piper; then\n  mkdir -p \"$BACKUP_DIR\/piper-config\"\n  docker cp piper:\/config\/. \"$BACKUP_DIR\/piper-config\/\"\n  echo \"OK -> piper-config\/\"\nelse\n  echo \"AVIS: contenidor piper no existeix, s'omet.\"\nfi\n\necho \"\"\necho \"=== RESUM ===\"\ndu -sh \"$BACKUP_DIR\"\/*\necho \"Backup completat a: $BACKUP_DIR\"\nEOF\nchmod +x ~\/tts_backup.sh<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\ude80 Com funciona ara<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Amb l'USB \"backup_tot\" connectat, nom\u00e9s cal:\n~\/tts_backup.sh\n# \u2192 USB 'backup_tot' trobat a: \/media\/Miquel\/backup_tot\n# \u2192 Desant a: \/media\/Miquel\/backup_tot\/20260913_...\n\n# Si l'USB no est\u00e0 connectat, et dir\u00e0 que el connectis<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Com ho detecta:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Primer prova <code>findmnt -S LABEL=backup_tot<\/code> (busca per etiqueta a tots els punts de muntatge)<\/li>\n\n\n\n<li>Si no, busca a les rutes t\u00edpiques <code>\/media\/*\/backup_tot<\/code> i <code>\/run\/media\/*\/backup_tot<\/code><\/li>\n\n\n\n<li>Encara pots for\u00e7ar una ruta manual si vols: <code>~\/tts_backup.sh \/altre\/lloc<\/code><\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">\u26a0\ufe0f <strong>Recorda:<\/strong> l&#8217;USB ha d&#8217;estar <strong>connectat i muntat<\/strong> abans d&#8217;executar el script. Vols provar-ho? \ud83d\ude0a<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u267b\ufe0f Script 2 \u2014 Restauraci\u00f3 (<code>tts_restore.sh<\/code>)<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">cat > ~\/tts_restore.sh &lt;&lt; 'EOF'\n#!\/bin\/bash\n# ============================================\n# Restauraci\u00f3 del sistema TTS local (Deepin)\n# Us: .\/tts_restore.sh &lt;directori_backup>\n# ============================================\nset -e\n\nif [ -z \"$1\" ]; then\n  echo \"Us: $0 &lt;directori_backup>\"\n  echo \"Exemple: $0 ~\/tts-backups\/20260913_120000\"\n  exit 1\nfi\nBACKUP_DIR=\"$1\"\n\necho \"=== 1\/3 Restauracio de ~\/tts-server ===\"\nif [ -f \"$BACKUP_DIR\/tts-server.tar.gz\" ]; then\n  tar xzf \"$BACKUP_DIR\/tts-server.tar.gz\" -C \"$HOME\"\n  echo \"OK: tts-server restaurat\"\nelse\n  echo \"AVIS: no hi ha tts-server.tar.gz\"\nfi\n\necho \"=== 2\/3 Restauracio de la imatge Docker ===\"\nif [ -f \"$BACKUP_DIR\/piper-image.tar\" ]; then\n  docker load -i \"$BACKUP_DIR\/piper-image.tar\"\n  echo \"OK: imatge carregada\"\nelse\n  echo \"AVIS: no hi ha piper-image.tar\"\nfi\n\necho \"=== 3\/3 Recreacio del contenidor piper i restauracio de la veu ===\"\nif docker ps -a --format '{{.Names}}' | grep -qx piper; then\n  echo \"El contenidor piper ja existeix; l'elimino per recrear-lo.\"\n  docker rm -f piper\nfi\n\ndocker create --name piper \\\n  -p 10200:10200 \\\n  -e PIPER_VOICE=ca_ES-upc_ona-medium \\\n  --restart unless-stopped \\\n  linuxserver\/piper\n\nif [ -d \"$BACKUP_DIR\/piper-config\" ]; then\n  docker cp \"$BACKUP_DIR\/piper-config\/.\" piper:\/config\/\n  echo \"OK: veu Ona restaurada a \/config\"\nelse\n  echo \"AVIS: no hi ha piper-config; el contenidor descarregara la veu de nou.\"\nfi\n\ndocker start piper\n\necho \"\"\necho \"=== VERIFICACIO ===\"\ndocker ps | grep piper || true\necho \"Restauracio completada.\"\nEOF\nchmod +x ~\/tts_restore.sh<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\ude80 Com s&#8217;utilitzen<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># 1. Fer un backup (es desa a ~\/tts-backups\/AMB_DATA)\n~\/tts_backup.sh\n\n# 2. Veure els backups disponibles\nls -lh ~\/tts-backups\/\n\n# 3. Restaurar (indicant el directori del backup)\n~\/tts_restore.sh ~\/tts-backups\/20260913_120000<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Av\u00eds:<\/strong> la restauraci\u00f3 <strong>elimina i recrea<\/strong> el contenidor <code>piper<\/code> si ja existeix (la veu es recupera des del backup, sense necessitat de descarregar-la).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vols que t&#8217;obri el terminal per crear els scripts i provar el backup? \ud83d\ude0a<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n","protected":false},"excerpt":{"rendered":"<p>Fant\u00e0stic que ja soni l&#8217;Ona! \ud83c\udf89 Et preparo l&#8217;entrada de blog completa, ben estructurada i llesta per copiar. \ud83c\udf99\ufe0f TTS local amb veu catalana (Ona) a Deepin \u2014 Guia completa 1. Objectiu Disposar d&#8217;un servidor de s\u00edntesi de veu (TTS) &hellip; <a href=\"https:\/\/www.beseit.net\/?p=19046\">Continua llegint <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":3168,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[207,1],"tags":[],"class_list":["post-19046","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deepin-linux","category-bloc-de-notes"],"_links":{"self":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/19046","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=19046"}],"version-history":[{"count":6,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/19046\/revisions"}],"predecessor-version":[{"id":19058,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/19046\/revisions\/19058"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/media\/3168"}],"wp:attachment":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=19046"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=19046"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=19046"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}