From 65c2ea3d504e975d971594e0864772e7bf87b156 Mon Sep 17 00:00:00 2001 From: hermes Date: Sat, 4 Jul 2026 01:38:23 +0800 Subject: [PATCH] feat(pinokio): clean one-click launcher for multi-tts-server - install: clone app source, create venv, install deps + torch + models - start: daemon uvicorn on 127.0.0.1 with auto-captured URL - reset: remove venv/cache/models but keep source - update: pull both launcher and app, then re-install - sanitized: no local env details, no TODO/DONE - icon.png included --- .gitignore | 10 ++++++ README.md | 47 ++++++++++++++++++++++++++ icon.png | Bin 0 -> 3127 bytes install.js | 74 ++++++++++++++++++++++++++++++++++++++++ pinokio.js | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ pinokio.json | 8 +++++ reset.js | 19 +++++++++++ start.js | 48 ++++++++++++++++++++++++++ torch.js | 86 ++++++++++++++++++++++++++++++++++++++++++++++ update.js | 17 ++++++++++ 10 files changed, 403 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 icon.png create mode 100644 install.js create mode 100644 pinokio.js create mode 100644 pinokio.json create mode 100644 reset.js create mode 100644 start.js create mode 100644 torch.js create mode 100644 update.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3438f9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +Thumbs.db +app/venv/ +app/cache/ +app/models/ +app/__pycache__/ +app/*.pyc +app/.env +logs/ +pinokio/logs/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe0784a --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Multi-TTS-Server Pinokio Launcher + +One-click Pinokio launcher for [hermes/multi-tts-server](https://gitea.amakusa.ru/hermes/multi-tts-server). + +## What it installs + +This launcher downloads the TTS server source code, creates a Python virtual environment, installs dependencies, installs the correct PyTorch build for your GPU, and downloads the required voice models. + +## Requirements + +- Windows, macOS, or Linux +- NVIDIA GPU with CUDA 12.8, AMD GPU with DirectML (Windows only), or CPU +- At least 16 GB of system RAM +- ~8 GB of free disk space (venv + models) + +## Included engines + +- **F5-TTS** — Russian model `Misha24-10/F5-TTS_RUSSIAN` (~1.3 GB) +- **XTTS-v2** — Russian model `NeuroDonu/RU-XTTS-DonuModel` (~5.3 GB) +- **CPU fallback** for machines without a compatible GPU + +## One-click usage + +1. Open Pinokio. +2. Install this launcher. +3. Click **Install**. +4. When install completes, click **Start**. +5. The Gradio Web UI and FastAPI docs will open automatically. + +## Features + +- OpenAI-compatible speech endpoint: `POST /v1/audio/speech` +- MultiTTS Android-compatible endpoint: `GET /tts?text=...&voice=...&engine=f5-tts` +- Engine load/unload controls: `POST /engines/{name}/load` and `/engines/{name}/unload` +- MultiTTS Android export: `GET /export/multitts` + +## Reset + +Click **Reset** to remove the virtual environment, cached files, and downloaded models. The source code in `app/` is kept so the next install only recreates the venv and re-downloads models. + +## Update + +Click **Update** to pull the latest launcher and app source code, then re-run the install steps. + +## License + +This launcher wraps the multi-tts-server project. Use in accordance with the licenses of F5-TTS, XTTS-v2, and the upstream project. diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7e6f5eb5a2f1f1c882d265cf479de25caa925645 GIT binary patch literal 3127 zcmV-749N3|P)i z7)}s4L53SJCkR}iVi00SFk;`MXX*#X*kkwKs@nFGS}c;=?XFjU|G$3t^5sjIVS2G+ zw)WGF83CpoGXhLGW(1gW%uV|X7>1P6VhCX=Ux)Lb!*DZ%@I3!{Gsf7d?gtIQ%nQiK z3%(LUSkBji;C5Rfgd6$VsF@H`Pk@xtY6t<>FNR-pD}=C~$?)9pdm3XZ36N5PNWYjb z$xd$yNQR9N!dfj-Vd@BwQo^FIIWPPmT&sZyQ$v81(sCBV=PGy{0wltEjB%~h157*t zvbe_!{=I_783x!0t1-r#-d{Y?ae$Q4N_Nd^Ui^@y(%)Gjou6y<3^XJdu{rmUf-Me?)zZ>9OR&6U5H*cK; z$gUlB{g0O4gN0sLSO|Of?hU(l?;h(jA3uH!Z{EBKuV23ouU@^Y6#%v+QG;>e*E}%?wlu-NT4DG zs)z)7WbLr)vGAu(ohrKc^em@OpO&f~6_>E61n_e0_V3@{U3^O;j{`^mNCJUj_>;7v zsMs6Hu3g7+@v+lSo;=yTYFqq}jZmQ-BK8K{C4kqi_i*jBaQE(Au0607V-zKeT;EPg zX(`vrn=L+e74+-Tqeok@_`tDa$G9I|$nTU5H*2V8@y()n*zqM?J1G!-1aX;CfDC9B zTnJ#j_%*n8Qb1)re*Bno7g0RG{Eb;IK14irJYJp$5Z6ac9~b_P?+5t~95~SRG$g?1 znFJ7p$xV&GZ18m~79TGRdfsc-BcX$9yXTR*n)mPD@1~O(_?cT$ZvFPucRmGlq&se0 zKrcUf^k}4hM*biEJOWKzz!qQe;CB_ZtSOO9Owg#lZAc=s65^rb{fZe(TYu_rk!wKkEf}RIt=#Om( zR8mN`DM<^xj~59euMMspBolVN zAPTr8sSDI104orIAdmL$uOXn*6hga1G+0WD0E?UtabxC#VC~vf3|10|phW;yQ3CY8 z2CM=)ErF;xq-YJ5G|um}>*1#E+O_Mu|Nr#qQ&G1P-NMq@f?@*XUcSbV?tX=)ilM-Q zBZP|!Bpv0V;#ojKcpc7$=eqO;#Uy~#?^kNI{vSZfLx&DEt~LTmaKWXcx=joubklI<*Aw z>LtMaQ7DR<1I2LkWvwyu#Rwn~;ezT}_g(@5l3h?W%-a86Y-t#O1PubP+z<%?V5D(U zy57A6{h+{?kOZp7&WKZR+=sznMJ}+Dnpo=C_0%R_x_t~J5T?E_{+))l5v1%52>)d-`iiZyx|5!%M2Fb2dU zW3~MwwpEH9Rhue+k$UIOoo($Ds!NbOyMR36fRHu;*15(YcA7siIZk#%JWz>P!qX1?IUojG&nKR>^gArBt2 zit(ETyZ=@V&7mv_Fi4bABcnwP+jzQuHcfU&BrAV91u-rFvEi7y-KnWsvHH=d2 zgAk(GKm_S8RcTJ>2N3~&Hbwp{Z3NF_Xeh}g4Eke)V&dY{W(3&b1j9t4yK_aYJisZZ{1rcU5- z;eD>K;ndPq&B-8yA_S0F!4ThA&{1{x)H<#?k9a#6Pc6L?V^s0``ynL&D;p(!Nmx`Y zFkHex{4p!Ggm^@DlehW}iHHVi}~u=$&N? z(NEBLQ#UxxAkdW>X9LnqUr#t4Lu0=9L8&o>JsqTtT5|%gb3QA~hr0pED71+iFFr)dZ=Q=E6ng{NE{Z~0)C?deO#?Aj zSDQ$z#TeC2T^|=}6GBo-&$;E{HL3!q3Z-szuf)O=G#zDjin4SSP%o%6+2IT#sLjQa ziyxFFz~LMjWY+_a5H!U6%a<=b7QVP^ z*90a62;bVq{?@)P6^DWd^Yilq4|YTV2Nw!Yu;a1lPI-sxR)rf@Fe5DhDP7FH zZZ%4S*1C30P;|O+jB!1;m|rXT90Sm5*RBbQN`PKu+hDD*S^yE(CdtSfg=z>u$cIj> z { + // Marker: app/venv exists after install + let installed = info.exists("app/venv/Scripts/python.exe") || + info.exists("app/venv/bin/python") + + let running = { + install: info.running("install.js"), + start: info.running("start.js"), + update: info.running("update.js"), + reset: info.running("reset.js"), + } + + if (running.install) { + return [{ + default: true, + icon: "fa-solid fa-plug", + text: "Installing", + href: "install.js", + }] + } + + if (running.start) { + let local = info.local("start.js") + if (local && local.url) { + return [{ + default: true, + icon: "fa-solid fa-rocket", + text: "Open Web UI", + href: local.url, + }, { + icon: "fa-solid fa-terminal", + text: "Terminal", + href: "start.js", + }] + } + return [{ + default: true, + icon: "fa-solid fa-terminal", + text: "Starting", + href: "start.js", + }] + } + + if (running.update) { + return [{ + default: true, + icon: "fa-solid fa-terminal", + text: "Updating", + href: "update.js", + }] + } + + if (running.reset) { + return [{ + default: true, + icon: "fa-solid fa-terminal", + text: "Resetting", + href: "reset.js", + }] + } + + if (installed) { + return [{ + default: true, + icon: "fa-solid fa-power-off", + text: "Start", + href: "start.js", + }, { + icon: "fa-solid fa-plug", + text: "Update", + href: "update.js", + }, { + icon: "fa-regular fa-circle-xmark", + text: "
Reset
Revert to pre-install state
", + href: "reset.js", + confirm: "Are you sure you wish to reset the app? This deletes the venv and any downloaded models.", + }] + } + + return [{ + default: true, + icon: "fa-solid fa-plug", + text: "Install", + href: "install.js", + }] + } +} diff --git a/pinokio.json b/pinokio.json new file mode 100644 index 0000000..e92e314 --- /dev/null +++ b/pinokio.json @@ -0,0 +1,8 @@ +{ + "name": "multi-tts-server-launcher", + "version": "1.0.0", + "description": "Pinokio one-click launcher for Multi-TTS-Server (XTTS + F5-TTS + OpenAI-compatible API + MultiTTS Android export)", + "repository": "https://gitea.amakusa.ru/hermes/multi-tts-server-launcher", + "icon": "icon.png", + "license": "MIT" +} diff --git a/reset.js b/reset.js new file mode 100644 index 0000000..43e2ec1 --- /dev/null +++ b/reset.js @@ -0,0 +1,19 @@ +// Reset Multi-TTS-Server to pre-install state. +// Removes the venv, downloaded caches, and downloaded models. +// Keeps the cloned app/ source code so the next install is just venv + models. +module.exports = { + run: [ + { + method: "fs.rm", + params: { path: "app/venv" } + }, + { + method: "fs.rm", + params: { path: "app/cache" } + }, + { + method: "fs.rm", + params: { path: "app/models" } + } + ] +} diff --git a/start.js b/start.js new file mode 100644 index 0000000..5bfcf91 --- /dev/null +++ b/start.js @@ -0,0 +1,48 @@ +// Multi-TTS-Server launcher daemon. +// Runs uvicorn in the background and exposes the URL via local.set so the +// menu can render an "Open Web UI" button. +module.exports = { + requires: { bundle: "ai" }, + daemon: true, + run: [ + { + method: "shell.run", + params: { + venv: "venv", + path: "app", + env: { + // Workaround for Coqui TTS on Windows when no system Local AppData is available + TTS_HOME: "{{cwd}}\\app\\cache\\tts_home", + HF_HOME: "{{cwd}}\\app\\cache\\hf", + XDG_DATA_HOME: "{{cwd}}\\app\\cache\\appdata", + LOCALAPPDATA: "{{cwd}}\\app\\cache\\appdata", + TRANSFORMERS_CACHE: "{{cwd}}\\app\\cache\\hf", + HF_HUB_CACHE: "{{cwd}}\\app\\cache\\hf", + PYTHONUTF8: "1", + }, + message: [ + "python -m uvicorn app.main:app --host 127.0.0.1 --port {{port}}" + ], + on: [ + // Watch stdout for uvicorn's "Uvicorn running on..." line and capture URL. + // The single capture group is fed to local.set.url below via input.event[1]. + { + "event": "/Uvicorn running on (https?:\\/\\/[^\\s]+)/", + "done": true + }, + { + "event": "/error:/i", + "break": false + } + ] + } + }, + { + method: "local.set", + params: { + // input.event[1] is the first capture group from the regex (the URL). + url: "{{input.event && input.event[1] ? input.event[1] : ''}}" + } + } + ] +} diff --git a/torch.js b/torch.js new file mode 100644 index 0000000..8ffe9f7 --- /dev/null +++ b/torch.js @@ -0,0 +1,86 @@ +// Platform-aware PyTorch installer. +// CUDA 12.8 wheels for NVIDIA, DirectML for AMD, CPU fallback. +module.exports = { + run: [ + // Windows + NVIDIA + { + "when": "{{platform === 'win32' && gpu === 'nvidia'}}", + method: "shell.run", + params: { + venv: "{{args.venv}}", + path: "{{args.path}}", + message: [ + "uv pip install torch==2.11.0 torchvision==0.21.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cu128 --force-reinstall" + ] + }, + next: null + }, + // Windows + AMD (DirectML) + { + "when": "{{platform === 'win32' && gpu === 'amd'}}", + method: "shell.run", + params: { + venv: "{{args.venv}}", + path: "{{args.path}}", + message: "uv pip install torch-directml torchaudio torchvision --force-reinstall" + }, + next: null + }, + // Windows CPU + { + "when": "{{platform === 'win32' && (gpu !== 'nvidia' && gpu !== 'amd')}}", + method: "shell.run", + params: { + venv: "{{args.venv}}", + path: "{{args.path}}", + message: "uv pip install torch==2.11.0 torchvision==0.21.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cpu --force-reinstall" + }, + next: null + }, + // macOS (CPU only — Apple Silicon uses MPS automatically) + { + "when": "{{platform === 'darwin'}}", + method: "shell.run", + params: { + venv: "{{args.venv}}", + path: "{{args.path}}", + message: "uv pip install torch==2.11.0 torchvision==0.21.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cpu --force-reinstall" + }, + next: null + }, + // Linux + NVIDIA + { + "when": "{{platform === 'linux' && gpu === 'nvidia'}}", + method: "shell.run", + params: { + venv: "{{args.venv}}", + path: "{{args.path}}", + message: [ + "uv pip install torch==2.11.0 torchvision==0.21.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cu128 --force-reinstall" + ] + }, + next: null + }, + // Linux + AMD (ROCm 6.4) + { + "when": "{{platform === 'linux' && gpu === 'amd'}}", + method: "shell.run", + params: { + venv: "{{args.venv}}", + path: "{{args.path}}", + message: "uv pip install torch==2.11.0 torchvision==0.21.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/rocm6.4 --force-reinstall" + }, + next: null + }, + // Linux CPU + { + "when": "{{platform === 'linux' && (gpu !== 'amd' && gpu !== 'nvidia')}}", + method: "shell.run", + params: { + venv: "{{args.venv}}", + path: "{{args.path}}", + message: "uv pip install torch==2.11.0 torchvision==0.21.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cpu --force-reinstall" + } + } + ] +} diff --git a/update.js b/update.js new file mode 100644 index 0000000..bf91992 --- /dev/null +++ b/update.js @@ -0,0 +1,17 @@ +// Pull latest launcher scripts and app source code, then re-run install. +module.exports = { + run: [ + { + method: "shell.run", + params: { message: "git pull" } + }, + { + method: "shell.run", + params: { path: "app", message: "git pull" } + }, + { + method: "script.start", + params: { uri: "install.js" } + } + ] +}