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 TTS class reads tts_provider from 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, ensuring src/classes/YouTube.py and the CLI in src/main.py require 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

  1. Edit config.json
{
  "tts_voice": "tts_models/en/ljspeech/tacotron2",
  "tts_provider": "coqui"
}
  1. 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)

  1. Create GoogleProvider in src/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
  1. Register it in src/classes/Tts.py
from .tts_providers import GoogleProvider
_PROVIDER_MAP["google"] = GoogleProvider
  1. 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

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →