[epub-tts] Vendor aedocw backends and add adapter subclasses
- Add git submodules under src/vendor for the aedocw repositories: - epub2tts, epub2tts-edge, epub2tts-chatterbox, epub2tts-kokoro - Add adapter modules in src/epub_tts to run vendored packages in isolated venvs: - vendor_utils.py (creates/uses per-repo .venv and installs package) - epub2tts.py, epub2tts_edge.py, epub2tts_chatterbox.py, epub2tts_kokoro.py - Add aedocw_backend.py providing AedocwBackend and concrete subclasses that dispatch to the vendored adapters when selected via the CLI. - Add tts_edge shim for backward compatibility. - Register new backends in src/epub_tts/__main__.py and invoke the selected backend at runtime. - Update pyproject.toml to exclude vendored packages from setuptools package discovery. This change vendors upstream tools while providing thin adapters that invoke their CLIs inside isolated virtualenvs, and integrates them into the epub-tts backend registry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
||||
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||
"""Utilities for running vendored EPUB->TTS repositories in isolated venvs."""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import venv
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_vendor_root() -> Path:
|
||||
return Path(__file__).resolve().parents[1] / "vendor"
|
||||
|
||||
|
||||
def get_repo_path(repo_name: str) -> Path:
|
||||
repo_path = get_vendor_root() / repo_name
|
||||
if not repo_path.exists():
|
||||
raise FileNotFoundError(
|
||||
f"Vendored repository '{repo_name}' not found under {get_vendor_root()}"
|
||||
)
|
||||
return repo_path
|
||||
|
||||
|
||||
def get_venv_path(repo_name: str) -> Path:
|
||||
return get_repo_path(repo_name) / ".venv"
|
||||
|
||||
|
||||
def get_venv_python(repo_name: str) -> Path:
|
||||
venv_path = get_venv_path(repo_name)
|
||||
python_executable = (
|
||||
venv_path / "Scripts" / "python.exe"
|
||||
if sys.platform == "win32"
|
||||
else venv_path / "bin" / "python"
|
||||
)
|
||||
if not python_executable.exists():
|
||||
raise FileNotFoundError(
|
||||
f"Python executable not found in virtualenv at {python_executable}"
|
||||
)
|
||||
return python_executable
|
||||
|
||||
|
||||
def ensure_venv(repo_name: str) -> Path:
|
||||
repo_path = get_repo_path(repo_name)
|
||||
venv_path = get_venv_path(repo_name)
|
||||
if not venv_path.exists():
|
||||
venv.EnvBuilder(with_pip=True).create(venv_path)
|
||||
python_executable = get_venv_python(repo_name)
|
||||
subprocess.run(
|
||||
[str(python_executable), "-m", "pip", "install", "-e", str(repo_path)],
|
||||
check=True,
|
||||
)
|
||||
return get_venv_python(repo_name)
|
||||
|
||||
|
||||
def build_run_command(
|
||||
repo_name: str,
|
||||
module_name: str,
|
||||
backend_cmd: Optional[str] = None,
|
||||
) -> list[str]:
|
||||
if backend_cmd is None or backend_cmd == repo_name:
|
||||
python_executable = ensure_venv(repo_name)
|
||||
return [str(python_executable), "-m", module_name]
|
||||
return [backend_cmd]
|
||||
Reference in New Issue
Block a user