Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
13ee449
feat: add button to check dependencies in extension management UI
rsxdalv Nov 8, 2025
f6f1d5c
fix: correct command construction in pip_install_wrapper
rsxdalv Nov 8, 2025
b29b7b8
feat: refactor Gradio UI integration to use main_block function
rsxdalv Nov 10, 2025
1c5290a
feat: improve readability and structure in extension external installer
rsxdalv Nov 10, 2025
7f6870c
feat: implement install button for extension marketplace
rsxdalv Nov 10, 2025
9b1506c
chore: readme
rsxdalv Nov 10, 2025
f058245
style: format code for consistency and readability in test files
rsxdalv Nov 10, 2025
38efede
style: format code for consistency and readability in tts_webui files
rsxdalv Nov 10, 2025
79ca713
style: reorganize imports for consistency and readability across test…
rsxdalv Nov 10, 2025
399d72a
style: reorganize imports for consistency and readability across tts_…
rsxdalv Nov 10, 2025
91add15
fix: add missing import for messaging_js in external extensions insta…
rsxdalv Nov 10, 2025
e148fbd
refactor: remove unnecessary global declarations in config management…
rsxdalv Nov 10, 2025
755618c
fix: handle directory changes safely in tests to avoid errors in CI e…
rsxdalv Nov 10, 2025
ccbeb67
fix: update tests to verify loading of all extension files, including…
rsxdalv Nov 10, 2025
291bff8
chore: run black on tests
rsxdalv Nov 10, 2025
6a56d55
fix: update tts-webui extensions versions in Dockerfile and CI test
rsxdalv Nov 10, 2025
027022f
fix: test --only-binary :all:
rsxdalv Nov 10, 2025
54fd293
fix: test --only-binary :all: without antlr4-python3-runtime
rsxdalv Nov 10, 2025
e30d91f
fix: quote extension package names in requirements installation
rsxdalv Nov 10, 2025
716de42
fix: remove deprecated extension package versions from requirements i…
rsxdalv Nov 12, 2025
51e7462
chore: migrate tts-webui-extension.audiocraft and vall-e-x to require…
rsxdalv Nov 14, 2025
9d43f77
chore: migrate tts-webui-extension.audiocraft and vall-e-x to require…
rsxdalv Nov 14, 2025
4cc0c61
chore: increase TTS WebUI version
rsxdalv Nov 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/test-pip-install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,10 @@ jobs:
shell: bash
run: |
python -m pip install -r requirements.txt \
tts-webui-extension.bark_voice_clone>=0.0.1 \
tts-webui-extension.rvc>=0.0.3 \
tts-webui-extension.audiocraft>=0.0.2 \
tts-webui-extension.styletts2>=0.1.0 \
tts-webui-extension.vall_e_x>=0.1.0 \
tts-webui-extension.stable_audio>=0.1.1 \
"tts-webui-extension.bark_voice_clone>=0.0.2" \
"tts-webui-extension.rvc>=0.0.5" \
"tts-webui-extension.styletts2>=0.1.0" \
"tts-webui-extension.stable_audio>=0.1.1" \
--extra-index-url https://tts-webui.github.io/extensions-index/

- name: Test tts_webui
Expand Down
10 changes: 4 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,10 @@ WORKDIR /app/tts-webui
# Install all requirements
RUN pip3 install --no-cache-dir torch==$TORCH_VERSION -r requirements.txt
# RUN pip install --no-cache-dir --verbose torch==$TORCH_VERSION -r requirements.txt
RUN pip install tts-webui-extension.bark_voice_clone>=0.0.1 --extra-index-url https://tts-webui.github.io/extensions-index/
RUN pip install tts-webui-extension.rvc>=0.0.3 --extra-index-url https://tts-webui.github.io/extensions-index/
RUN pip install tts-webui-extension.audiocraft>=0.0.2 --extra-index-url https://tts-webui.github.io/extensions-index/
RUN pip install tts-webui-extension.styletts2>=0.1.0 --extra-index-url https://tts-webui.github.io/extensions-index/
RUN pip install tts-webui-extension.vall_e_x>=0.1.0 --extra-index-url https://tts-webui.github.io/extensions-index/
# RUN pip install tts-webui-extension.stable_audio>=0.1.1 --extra-index-url https://tts-webui.github.io/extensions-index/
RUN pip install "tts-webui-extension.bark_voice_clone>=0.0.2" --extra-index-url https://tts-webui.github.io/extensions-index/
RUN pip install "tts-webui-extension.rvc>=0.0.6" --extra-index-url https://tts-webui.github.io/extensions-index/
RUN pip install "tts-webui-extension.styletts2>=0.1.0" --extra-index-url https://tts-webui.github.io/extensions-index/
# RUN pip install "tts-webui-extension.stable_audio>=0.1.1" --extra-index-url https://tts-webui.github.io/extensions-index/


# add postgres & run setup
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ November:
* Add extension marketplace directly in the Gradio UI using iframe
* Move voices-tortoise to voices/tortoise/ (and maha-tts to voices/maha_tts/)
* feat: add comprehensive test suite for tts_webui modules

* feat: implement install button for extension marketplace
* feat: add button to check dependencies in extension management UI

October:
* Update Gradio to 5.49.1
Expand Down
4 changes: 1 addition & 3 deletions documentation/manual_installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,15 @@ To install the default extensions, run and restart the server:
pip install -r requirements.txt \
tts-webui-extension.bark_voice_clone>=0.0.1 \
tts-webui-extension.rvc>=0.0.3 \
tts-webui-extension.audiocraft>=0.0.2 \
tts-webui-extension.styletts2>=0.1.0 \
tts-webui-extension.vall_e_x>=0.1.0 \
tts-webui-extension.stable_audio>=0.1.1 \
--extra-index-url https://tts-webui.github.io/extensions-index/
```

(One line in case of copy-paste issues)

```bash
pip install -r requirements.txt tts-webui-extension.bark_voice_clone>=0.0.1 tts-webui-extension.rvc>=0.0.3 tts-webui-extension.audiocraft>=0.0.2 tts-webui-extension.styletts2>=0.1.0 tts-webui-extension.vall_e_x>=0.1.0 tts-webui-extension.stable_audio>=0.1.1 hydra-core==1.3.2 nvidia-ml-py --extra-index-url https://tts-webui.github.io/extensions-index/
pip install -r requirements.txt tts-webui-extension.bark_voice_clone>=0.0.1 tts-webui-extension.rvc>=0.0.3 tts-webui-extension.styletts2>=0.1.0 tts-webui-extension.stable_audio>=0.1.1 hydra-core==1.3.2 nvidia-ml-py --extra-index-url https://tts-webui.github.io/extensions-index/
```

In case of failures, try installing the extensions one by one.
Expand Down
Empty file.
86 changes: 73 additions & 13 deletions extensions/builtin/extension_external_extensions_installer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import gradio as gr

from tts_webui.utils.pip_install import pip_install_wrapper
from .messaging_js import messaging_js

EXTERNAL_EXTENSIONS_FILE = "extensions.external.json"
CATALOG_DIR = os.path.join("data", "extensions-catalog")
Expand Down Expand Up @@ -99,17 +100,24 @@ def _render_preview(entries: List[Dict[str, Any]]) -> str:
header = "| Name | Package | Class | Type |\n|---|---|---|---|\n"
rows = []
for e in entries:
rows.append(f"| {e['name']} | {e['package_name']} | {e['extension_class']} | {e['extension_type']} |")
rows.append(
f"| {e['name']} | {e['package_name']} | {e['extension_class']} | {e['extension_type']} |"
)
return gr.Markdown(header + "\n".join(rows))


def _sync_catalog_via_git() -> Tuple[str, str]:
# Try: clone if missing, else pull fast-forward; return output or error.
try:
if not os.path.isdir(CATALOG_DIR) or not os.path.isdir(os.path.join(CATALOG_DIR, ".git")):
if not os.path.isdir(CATALOG_DIR) or not os.path.isdir(
os.path.join(CATALOG_DIR, ".git")
):
cmd = ["git", "clone", "--depth=1", CATALOG_REPO, CATALOG_DIR]
else:
cmd = ["git", "-C", CATALOG_DIR, "pull", "--ff-only"]
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
proc = subprocess.run(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
)
if proc.returncode != 0:
msg = proc.stderr.strip() or proc.stdout.strip() or "Unknown git error"
return f"Git failed: {msg}", ""
Expand Down Expand Up @@ -143,7 +151,10 @@ def _add_to_external(entries: List[Dict[str, Any]]) -> Tuple[str, str]:
added.append(e.get("package_name"))
data["tabs"] = tabs
ok, msg = _save_external_extensions(data)
status = ("Saved" if ok else msg) + f" | Added: {', '.join(added) if added else 'none'} | Skipped (exists): {', '.join(skipped) if skipped else 'none'}"
status = (
("Saved" if ok else msg)
+ f" | Added: {', '.join(added) if added else 'none'} | Skipped (exists): {', '.join(skipped) if skipped else 'none'}"
)
return status, json.dumps(data, indent=2, ensure_ascii=False)


Expand All @@ -158,21 +169,44 @@ def _install_selected(entries: List[Dict[str, Any]]):
yield from pip_install_wrapper(req, name)()


EXTENSION_CATALOG_IFRAME_ID = "extension-catalog"


def extension__tts_generation_webui():
gr.Markdown("""
## External Extensions Installer
gr.Markdown(
"""
Paste one or more extension JSON objects below. We'll validate, preview, and let you add them to `extensions.external.json` and optionally install their requirements right away.
"""
)

You can discover and copy extension JSON entries from the [TTS WebUI Extension Catalog](https://rsxdalv.github.io/tts-webui-extension-catalog/).
""")

with gr.Accordion("Browse Extension Catalog", open=True):
gr.HTML(
"""<iframe width="100%" height="600px" src="https://rsxdalv.github.io/tts-webui-extension-catalog/?browse=true" title="TTS WebUI Extension Catalog" frameborder="0" allowfullscreen></iframe>"""
f"""<iframe id={EXTENSION_CATALOG_IFRAME_ID}
width="100%"
height="800px"
src="https://rsxdalv.github.io/tts-webui-extension-catalog/?browse=true&iframe=true"
title="TTS WebUI Extension Catalog"
frameborder="0"
></iframe>
<div id="json-container" hidden></div>
"""
)

init = gr.Timer(0.5)
init.tick(
fn=lambda: gr.Timer(active=False),
inputs=[],
outputs=[init],
js=messaging_js,
)

with gr.Row():
json_input = gr.Textbox(label="Extension JSON", lines=16, placeholder="Paste JSON object or array of objects here")
json_input = gr.Textbox(
label="Extension JSON",
lines=16,
placeholder="Paste JSON object or array of objects here",
)
json_input_automatic = gr.Textbox(visible=False)
with gr.Row():
parse_btn = gr.Button("Parse JSON", variant="primary")
add_btn = gr.Button("Add to external list", variant="secondary")
Expand All @@ -199,6 +233,32 @@ def _on_parse(text: str):
entries, info = _parse_json_input(text)
return entries, _render_preview(entries), info

gr.Button("", elem_id="receive_extension_button").click(
fn=None,
inputs=[],
outputs=[json_input, json_input_automatic],
js="""
() => {
json_value = document.getElementById('json-container').innerText;
return [json_value, json_value];
}
""",
)

json_input_automatic.change(
fn=_on_parse,
inputs=[json_input_automatic],
outputs=[parsed_state, preview_md, parse_info],
).then(
fn=_add_to_external,
inputs=[parsed_state],
outputs=[parse_info, current_json],
).then(
fn=_install_selected,
inputs=[parsed_state],
outputs=[console_html],
)

parse_btn.click(
fn=_on_parse,
inputs=[json_input],
Expand Down Expand Up @@ -231,7 +291,7 @@ def _on_sync():
"package_name": "extensions.builtin.extension_external_extensions_installer",
"name": "External Extensions Installer",
"requirements": "builtin",
"description": "Add external extension entries via JSON and install them without restarts. Sync and apply the public extensions catalog via Git.",
"description": "Add external extension entries via JSON and install them without restarts. Sync and apply the public extensions catalog via Git.",
"extension_type": "interface",
"extension_class": "settings",
"author": "rsxdalv",
Expand All @@ -245,7 +305,7 @@ def _on_sync():

if __name__ == "__main__":
if "demo" in locals():
demo.close() # type: ignore
locals()["demo"].close()
with gr.Blocks() as demo:
with gr.Tab("External Extensions Installer"):
extension__tts_generation_webui()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

messaging_js = """
() => {{
const iframe = document.getElementById('extension-catalog');

window.addEventListener('message', (event) => {
if (event.source !== iframe.contentWindow) return;
if (event.data.type === 'install-extension') {
const extension = event.data.data;

document.getElementById('json-container').innerText =
JSON.stringify(extension, null, 2);
document.getElementById('receive_extension_button').click();
}
});

console.log("Extension catalog iframe messaging initialized.");
}}
"""
6 changes: 2 additions & 4 deletions installer_scripts/js/initializeApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,8 @@ const extensions = [
name: "Bark Voice Clone",
package: '"tts-webui-extension.bark_voice_clone>=0.0.1"',
},
{ name: "RVC", package: '"tts-webui-extension.rvc>=0.0.3"' },
{ name: "Audiocraft", package: '"tts-webui-extension.audiocraft>=0.0.2"' },
{ name: "StyleTTS", package: '"tts-webui-extension.styletts2>=0.1.0"' },
{ name: "Vall-E-X", package: '"tts-webui-extension.vall_e_x>=0.1.0"' },
{ name: "RVC", package: '"tts-webui-extension.rvc>=0.0.5"' },
{ name: "StyleTTS", package: '"tts-webui-extension.styletts2>=0.1.1"' }, // pypi
{
name: "Stable Audio",
package: '"tts-webui-extension.stable_audio>=0.1.1"',
Expand Down
2 changes: 1 addition & 1 deletion installer_scripts/versions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": "1.0.0",
"pip_packages": 22,
"pip_packages": 30,
"npm_packages": 4,
"react_ui": 4
}
2 changes: 0 additions & 2 deletions notebooks/google_colab.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@
"!pip install uv\n",
"!uv pip install --no-progress --system tts-webui-extension.bark_voice_clone>=0.0.1 --extra-index-url https://tts-webui.github.io/extensions-index/\n",
"!uv pip install --no-progress --system tts-webui-extension.rvc>=0.0.3 --extra-index-url https://tts-webui.github.io/extensions-index/\n",
"!uv pip install --no-progress --system tts-webui-extension.audiocraft>=0.0.2 --extra-index-url https://tts-webui.github.io/extensions-index/\n",
"!uv pip install --no-progress --system tts-webui-extension.styletts2>=0.1.0 --extra-index-url https://tts-webui.github.io/extensions-index/\n",
"!uv pip install --no-progress --system tts-webui-extension.vall_e_x>=0.1.0 --extra-index-url https://tts-webui.github.io/extensions-index/\n",
"!uv pip install --no-progress --system tts-webui-extension.stable_audio>=0.1.1 --extra-index-url https://tts-webui.github.io/extensions-index/\n"
]
},
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "TTS-WebUI"
version = "0.2.2"
version = "0.3.0"
description = "TTS WebUI"
readme = "README.md"
authors = [
Expand Down Expand Up @@ -84,4 +84,7 @@ norecursedirs = [".git", ".tox", "dist", "build", "*.egg", "__pycache__", "noteb
# Alternative configuration if you want to use find_namespace_packages
# [tool.setuptools.packages.find]
# namespaces = true
# include = ["tts_webui.*"]
# include = ["tts_webui.*"]

[tool.isort]
profile = "black"
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ tts_webui_extension.seamless_m4t>=0.0.6
tts_webui_extension.vocos>=0.0.3
tts_webui_extension.openai_tts_api>=0.15.0
tts_webui_extension.log_viewer>=0.1.0
tts-webui-extension.audiocraft>=0.0.4
tts-webui-extension.vall_e_x>=0.1.0

gradio-goodtabs>=0.0.5
gradio-goodtab>=0.0.5
Expand Down
13 changes: 9 additions & 4 deletions server.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import os
import tts_webui.dotenv_manager.init as dotenv_init

import tts_webui.dotenv_manager.init as dotenv_init
from tts_webui.config.config import config
from tts_webui.gradio.print_gradio_options import print_gradio_options
from tts_webui.utils.suppress_warnings import suppress_warnings
from tts_webui.utils.torch_load_patch import apply_torch_load_patch


def create_output_folders():
if not os.path.exists("outputs"):
os.makedirs("outputs")
if not os.path.exists("favorites"):
os.makedirs("favorites")


print("Starting TTS WebUI... ", end="")


def tts_webui_init_environment():
create_output_folders()
dotenv_init.init()
apply_torch_load_patch()
suppress_warnings()


tts_webui_init_environment()

argv = os.sys.argv
Expand Down Expand Up @@ -49,9 +54,9 @@ def upgrade_gradio_options(options):
parsed_options = upgrade_gradio_options(gr_options)
print_gradio_options(parsed_options)

from tts_webui.gradio.main_ui import main_ui
from tts_webui.gradio.blocks import main_block

demo = main_ui(config=config)
demo = main_block(config=config)

try:
demo.queue().launch(
Expand All @@ -64,8 +69,8 @@ def upgrade_gradio_options(options):


def server_hypervisor():
import subprocess
import signal
import subprocess
import sys

if "--no-react" not in argv:
Expand Down
20 changes: 15 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
Pytest configuration and fixtures for tts_webui tests.
"""

import json
import os
import shutil
import sys
import tempfile
import shutil
import json
from pathlib import Path
from typing import Generator, Dict, Any
from typing import Any, Dict, Generator

import pytest

# Add project root to Python path
Expand Down Expand Up @@ -118,9 +119,18 @@ def mock_extensions_data() -> Dict[str, Any]:
@pytest.fixture
def original_cwd():
"""Preserve and restore the original working directory."""
original = os.getcwd()
try:
original = os.getcwd()
except (FileNotFoundError, OSError):
# If current directory doesn't exist (e.g., in CI), use project root
original = str(project_root)
os.chdir(original)
yield original
os.chdir(original)
try:
os.chdir(original)
except (FileNotFoundError, OSError):
# If original directory no longer exists, change to project root
os.chdir(str(project_root))


@pytest.fixture
Expand Down
Loading
Loading