#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa # Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> # See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html) """Backends for the aedocw collection of EPUB->TTS repositories. This module defines subclasses of GenericTTSBackend that provide sane default backend command names for the aedocw repositories available on GitHub: epub2tts, epub2tts-edge, epub2tts-chatterbox and epub2tts-kokoro. The vendored packages are run in isolated virtual environments when the corresponding console script is not available in the current environment. """ from .tts_generic import GenericTTSBackend from . import logger, PathLike class AedocwBackend(GenericTTSBackend): """Generic aedocw backend wrapper. Parameters: repo: repository name (one of the keys in _CMD_MAP) backend_cmd: explicit command/executable to use (overrides repo mapping) language: optional language code voice: optional voice name The GenericTTSBackend stores the default command name in self.backend_cmd, but the vendored adapters may still run the package in their own venv if the console script is not present. """ FLAGS = { "speaker": "--speaker", "voice": "--speaker", "language": "--language", "cover": "--cover", } INTERMEDIATE_TXT = True INTERMEDIATE_CALL = GenericTTSBackend._replace_map def gen_input_flag(self, input_path) -> list[str]: """Return backend flag for input file or directory.""" p = self._normalize_path(input_path) return [str(p)] # epub2tts-edge expects a single path, no flag gen_output_flag = None class AedocwEpub2TTS(AedocwBackend): """Backend configured for github.com/aedocw/epub2tts.""" pass class AedocwEpub2TTSEdge(AedocwBackend): """Backend configured for github.com/aedocw/epub2tts-edge.""" DEFAULT_SPEAKER = "en-US-AndrewNeural" CMD = ["-c", "from epub2tts_edge import main;main()"] REPO = "epub2tts-edge" class TTSEdge(AedocwEpub2TTSEdge): """Backward-compatible edge backend wrapper.""" pass class AedocwChatterbox(AedocwBackend): """Backend configured for github.com/aedocw/epub2tts-chatterbox.""" DEFAULT_SAMPLE = "none" class AedocwKokoro(AedocwBackend): """Backend configured for github.com/aedocw/epub2tts-kokoro.""" #DEFAULT_SPEAKER = "af_heart" DEFAULT_SPEAKER = "am_liam" CMD = ["-c", "from epub2tts_kokoro import main;main()"] REPO = "epub2tts-kokoro" FLAGS = { "--speaker": "voice", # str, default="af_heart" "--cover": "cover", # str, default=None "--paragraphpause": "long_pause", # int, default=600 (ms) "--speed": "speed", # float, default=1.3 } DEFAULT_FLAGS = { "voice": "am_liam", "cover": None, "long_pause": 600, "speed": 1.3, } def get_speakers(self) -> list[str]: """Return list of available speakers.""" return ["af_heart", "af_joy", "af_sad", "af_angry", "af_fear", "af_surprise"] def gen_speaker_samples( self, samples: list =None, output_path: PathLike=None, ) -> list[str]: """Generate sample speakers audio in output_path.""" result = self._build_command("gen_samples.py", *samples, output_path=output_path) return [str(self._normalize_path(s)) for s in samples] class Epub2TTS(AedocwBackend): """Backward-compatible chatterbox backend wrapper.""" pass __all__ = [ "AedocwBackend", "AedocwEpub2TTS", "AedocwEpub2TTSEdge", "AedocwChatterbox", "AedocwKokoro", "TTSEdge", "Epub2TTS", ]