How to Integrate Custom TTS Providers Beyond KittenTTS in MoneyPrinterV2
You can integrate custom TTS providers in MoneyPrinterV2 by creating a provider wrapper class that implements a synthesize(text, output_file) interface, registering it in the factory map inside src/classes/Tts.py, and selecting it via the tts_provider key in config.json.
MoneyPrinterV2 currently ships with a single TTS implementation based on KittenTTS, limiting users to one voice synthesis backend. If you want to integrate custom TTS providers beyond KittenTTS in MoneyPrinterV2, the architecture supports extension through a simple provider interface and factory pattern. This guide shows you exactly how to add support for services like Coqui TTS or Google Cloud TTS without breaking existing workflows.
Add a Configuration Flag
The configuration file (config.json) already stores the voice name (tts_voice). Add a new key to specify the provider:
{
"tts_voice": "Jasper",
"tts_provider": "kitten"
}
Add a helper accessor in src/config.py to read this value:
import os
import json
from .constants import ROOT_DIR
def get_tts_provider() -> str:
"""
Returns the configured TTS provider name.
"""
with open(os.path.join(ROOT_DIR, "config.json"), "r") as file:
return json.load(file).get("tts_provider", "kitten")
Create Provider Wrappers
Create a new module at src/classes/tts_providers.py that contains concrete classes for each backend. Each class must expose a synthesize(text: str, output_file: str) -> str method, returning the file path.
# src/classes/tts_providers.py
import os
import soundfile as sf
from kittentts import KittenTTS as KittenModel
KITTEN_MODEL = "KittenML/kitten-tts-mini-0.8"
KITTEN_SAMPLE_RATE = 24000
class KittenProvider:
"""Thin wrapper around KittenTTS."""
def __init__(self, voice: str):
self._model = KittenModel(KITTEN_MODEL)
self._voice = voice
def synthesize(self, text: str, output_file: str) -> str:
audio = self._model.generate(text, voice=self._voice)
sf.write(output_file, audio, KITTEN_SAMPLE_RATE)
return output_file
class CoquiProvider:
"""Wrapper for Coqui TTS (requires `coqui-tts` package)."""
def __init__(self, voice: str):
from TTS.api import TTS as CoquiTTS
self._tts = CoquiTTS(model_name=voice, progress_bar=False, gpu=False)
def synthesize(self, text: str, output_file: str) -> str:
self._tts.tts_to_file(text=text, file_path=output_file)
return output_file
Refactor the TTS Factory
Replace the monolithic src/classes/Tts.py with a thin factory that delegates to the selected provider. This maintains backward compatibility with existing code in src/classes/YouTube.py and src/main.py.
# src/classes/Tts.py
import os
from config import ROOT_DIR, get_tts_voice, get_tts_provider
from .tts_providers import KittenProvider, CoquiProvider
_PROVIDER_MAP = {
"kitten": KittenProvider,
"coqui": CoquiProvider,
}
class TTS:
"""Factory that forwards synthesize calls to the configured provider."""
def __init__(self) -> None:
provider_name = get_tts_provider().lower()
voice = get_tts_voice()
provider_cls = _PROVIDER_MAP.get(provider_name)
if provider_cls is None:
raise ValueError(f"Unsupported TTS provider: {provider_name}")
self._provider = provider_cls(voice)
def synthesize(self, text, output_file=os.path.join(ROOT_DIR, ".mp", "audio.wav")):
"""
Generate speech using the selected TTS backend.
Returns the absolute path to the generated WAV file.
"""
return self._provider.synthesize(text, output_file)
Key implementation details:
- Factory pattern: The
TTSclass readstts_providerfrom the config, looks up the concrete class in_PROVIDER_MAP, and constructs it with the configured voice. - Unchanged public API: The
synthesize(text, output_file)method signature remains identical to the original implementation, ensuringsrc/classes/YouTube.pyand the CLI insrc/main.pyrequire zero changes.
Install Additional Dependencies
If you add a provider that requires external packages (e.g., coqui-tts or google-cloud-texttospeech), add them to requirements.txt and run the standard setup:
pip install -r requirements.txt
Or execute the provided setup script:
bash scripts/setup_local.sh
Update Documentation
Add the new configuration option to the user documentation. In docs/Configuration.md, include:
- `tts_provider`: string – Choose the TTS backend. Supported values: `kitten` (default), `coqui`, `google`.
This ensures end-users understand how to switch providers without reading source code.
Integration Examples
Switching to Coqui TTS
- Edit
config.json
{
"tts_voice": "tts_models/en/ljspeech/tacotron2",
"tts_provider": "coqui"
}
- Run the workflow
python3 src/main.py
Select any YouTube option; the system will now route calls to CoquiProvider under the hood.
Adding a New Provider (Google Cloud TTS)
- Create
GoogleProviderinsrc/classes/tts_providers.py
from google.cloud import texttospeech
class GoogleProvider:
def __init__(self, voice: str):
self.client = texttospeech.TextToSpeechClient()
self.voice = voice
def synthesize(self, text: str, output_file: str) -> str:
synthesis_input = texttospeech.SynthesisInput(text=text)
voice = texttospeech.VoiceSelectionParams(name=self.voice, language_code="en-US")
audio_config = texttospeech.AudioConfig(audio_encoding=texttospeech.AudioEncoding.LINEAR16)
response = self.client.synthesize_speech(
input=synthesis_input, voice=voice, audio_config=audio_config
)
with open(output_file, "wb") as out:
out.write(response.audio_content)
return output_file
- Register it in
src/classes/Tts.py
from .tts_providers import GoogleProvider
_PROVIDER_MAP["google"] = GoogleProvider
- Set the config
{
"tts_voice": "en-US-Wavenet-D",
"tts_provider": "google"
}
All existing calls to TTS().synthesize(...) in src/classes/YouTube.py and src/main.py will now produce speech via Google Cloud without any further code changes.
Key Files
| File | Role | Path |
|---|---|---|
src/classes/Tts.py |
Central TTS factory (updated to select a provider) | src/classes/Tts.py |
src/classes/tts_providers.py |
Concrete implementations for each TTS backend (new module) | src/classes/tts_providers.py |
src/config.py |
Configuration helpers – add get_tts_provider() here |
src/config.py |
src/classes/YouTube.py |
Consumes the TTS class to generate audio for videos |
src/classes/YouTube.py |
docs/Configuration.md |
User‑visible docs – include the new tts_provider option |
docs/Configuration.md |
Summary
- Abstract the backend: Create a provider wrapper in
src/classes/tts_providers.pythat implementssynthesize(text, output_file). - Expose via config: Add
tts_providertoconfig.jsonand a reader insrc/config.py. - Wire the factory: Update
src/classes/Tts.pyto map config values to provider classes in_PROVIDER_MAP. - Zero breaking changes: Existing code in
src/classes/YouTube.pyandsrc/main.pycontinues to callTTS().synthesize()without modification.
Frequently Asked Questions
Can I use multiple TTS providers simultaneously in the same project?
No, the current architecture selects one provider per session via the tts_provider configuration key. However, you can switch providers between runs by editing config.json before launching src/main.py, or you could extend the factory to accept a provider override parameter in the TTS constructor if you need per-call switching.
Do I need to modify YouTube.py or main.py to use a new provider?
No. The TTS class in src/classes/Tts.py acts as a stable abstraction. Because src/classes/YouTube.py and src/main.py only interact with the synthesize() method signature, switching providers happens entirely within the factory and configuration layers. You only need to edit src/classes/Tts.py and create the provider wrapper.
What audio format should the synthesize method return?
The synthesize method must return a string representing the absolute file path to a WAV file. The existing KittenProvider writes 24 kHz PCM WAV using soundfile, and the video generation pipeline in src/classes/YouTube.py expects this format for concatenation with video tracks. If your provider outputs MP3 or other formats, convert to WAV inside the wrapper before returning the path.
Is GPU acceleration supported for custom providers?
Yes, but implementation depends on the specific provider library. The CoquiProvider example accepts a gpu parameter in its constructor (set to False by default for CPU compatibility). If you are adding a provider that supports CUDA (e.g., Coqui TTS with GPU or torch-based models), modify the __init__ method of your provider class to accept a device or gpu argument and pass it to the underlying library. Ensure your requirements.txt includes the correct GPU-enabled dependencies (e.g., torch with CUDA bindings).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →