- 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>
53 lines
1.6 KiB
Python
53 lines
1.6 KiB
Python
#!/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)
|
|
"""Adapter for the vendored github.com/aedocw/epub2tts-edge repository."""
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from .vendor_utils import build_run_command
|
|
|
|
DEFAULT_SPEAKER = "en-US-AndrewNeural"
|
|
|
|
|
|
def _normalize_path(path_input: Path | str) -> Path:
|
|
return Path(path_input)
|
|
|
|
|
|
def _build_command(
|
|
input_source: Path,
|
|
voice: Optional[str] = None,
|
|
backend_cmd: Optional[str] = None,
|
|
) -> list[str]:
|
|
command = build_run_command(
|
|
"epub2tts-edge", "epub2tts_edge", backend_cmd=backend_cmd
|
|
)
|
|
command.append(str(input_source))
|
|
if voice:
|
|
command.extend(["--speaker", voice])
|
|
return command
|
|
|
|
|
|
def run(
|
|
input_source: Path | str,
|
|
output_dest: Path | str,
|
|
voice: Optional[str] = None,
|
|
backend_cmd: Optional[str] = None,
|
|
) -> subprocess.CompletedProcess:
|
|
input_path = _normalize_path(input_source)
|
|
output_path = _normalize_path(output_dest)
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
speaker = voice or DEFAULT_SPEAKER
|
|
command = _build_command(input_path, voice=speaker, backend_cmd=backend_cmd)
|
|
completed = subprocess.run(command, cwd=str(input_path.parent), check=True)
|
|
|
|
generated_path = input_path.parent / f"{input_path.stem} ({speaker}).m4b"
|
|
if generated_path != output_path:
|
|
generated_path.replace(output_path)
|
|
|
|
return completed
|