Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 101 additions & 1 deletion GUI/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,60 @@
<div class="col-8">
<select name="voice_choice" class="form-select" data-toggle="tooltip"
data-original-title='The voice platform used for TTS generation'>
<option value="streamlabspolly">Streamlabspolly</option>
<option value="tiktok">TikTok</option>
<option value="fishaudio">Fish Audio</option>
<option value="streamlabspolly">Streamlabs Polly</option>
<option value="googletranslate">Google Translate</option>
<option value="awspolly">AWS Polly</option>
<option value="pyttsx">Python TTS (pyttsx)</option>
</select>
</div>
</div>
<div class="row mb-2">
<label for="fishaudio_api_key" class="col-4">Fish Audio API Key</label>
<div class="col-8">
<div class="input-group">
<div class="input-group-text">
<i class="bi bi-key-fill"></i>
</div>
<input value="{{ data.fishaudio_api_key }}" name="fishaudio_api_key" type="text" class="form-control"
data-toggle="tooltip"
data-original-title="Fish Audio API key for TTS generation. Get one at fish.audio">
</div>
</div>
</div>
<div class="row mb-2">
<label for="fishaudio_voice_select" class="col-4">Fish Audio Voice</label>
<div class="col-8">
<select id="fishaudio_voice_select" class="form-select" data-toggle="tooltip"
data-original-title='Select a preset voice or choose Custom to enter your own'
onchange="handleFishAudioVoiceChange(this)">
<option value="bf322df2096a46f18c579d0baa36f41d">Adrian (Default)</option>
<option value="8ef4a238714b45718ce04243307c57a7">E-girl</option>
<option value="802e3bc2b27e49c2995d23ef70e6ac89">Energetic Male</option>
<option value="933563129e564b19a115bedd57b7406a">Sarah</option>
<option value="b347db033a6549378b48d00acb0d06cd">Selene</option>
<option value="536d3a5e000945adb7038665781a4aca">Ethan</option>
<option value="custom">Custom Voice ID...</option>
</select>
<input type="hidden" name="fishaudio_voice" id="fishaudio_voice" value="{{ data.fishaudio_voice }}">
</div>
</div>
<div class="row mb-2" id="fishaudio_custom_row" style="display: none;">
<label for="fishaudio_custom_voice" class="col-4">Custom Voice ID</label>
<div class="col-8">
<div class="input-group">
<div class="input-group-text">
<i class="bi bi-soundwave"></i>
</div>
<input id="fishaudio_custom_voice" type="text" class="form-control"
placeholder="Enter voice ID from fish.audio"
data-toggle="tooltip"
data-original-title="Voice ID from fish.audio - find it in the URL of any voice">
</div>
<span class="form-text text-muted"><a href="https://fish.audio/discovery" target="_blank">Browse voices at fish.audio/discovery</a></span>
</div>
</div>
<div class="row mb-2">
<label for="aws_polly_voice" class="col-4">AWS Polly Voice</label>
<div class="col-8">
Expand Down Expand Up @@ -432,6 +478,22 @@
</main>

<script>
// Fish Audio voice change handler (global function for inline onchange)
function handleFishAudioVoiceChange(selectElement) {
const selected = selectElement.value;
const customRow = document.getElementById('fishaudio_custom_row');
const hiddenInput = document.getElementById('fishaudio_voice');
const customInput = document.getElementById('fishaudio_custom_voice');

if (selected === 'custom') {
customRow.style.display = '';
customInput.focus();
} else {
customRow.style.display = 'none';
hiddenInput.value = selected;
}
}

// Test voices buttons
var playing = false;

Expand Down Expand Up @@ -544,6 +606,44 @@
validate($(this));
});

// Fish Audio voice selector logic
const presetVoices = [
"bf322df2096a46f18c579d0baa36f41d",
"8ef4a238714b45718ce04243307c57a7",
"802e3bc2b27e49c2995d23ef70e6ac89",
"933563129e564b19a115bedd57b7406a",
"b347db033a6549378b48d00acb0d06cd",
"536d3a5e000945adb7038665781a4aca"
];

// Initialize Fish Audio voice on page load
const savedVoice = data.fishaudio_voice || "bf322df2096a46f18c579d0baa36f41d";
if (presetVoices.includes(savedVoice)) {
$("#fishaudio_voice_select").val(savedVoice);
} else {
$("#fishaudio_voice_select").val("custom");
$("#fishaudio_custom_row").show();
$("#fishaudio_custom_voice").val(savedVoice);
}
$("#fishaudio_voice").val(savedVoice);

// Handle dropdown change
$("#fishaudio_voice_select").on("change", function() {
const selected = $(this).val();
if (selected === "custom") {
$("#fishaudio_custom_row").show();
$("#fishaudio_custom_voice").focus();
} else {
$("#fishaudio_custom_row").hide();
$("#fishaudio_voice").val(selected);
}
});

// Handle custom input change
$("#fishaudio_custom_voice").on("input", function() {
$("#fishaudio_voice").val($(this).val());
});

function validate(object) {
let bool = check(object.prop("name"), object.prop("value"));

Expand Down
44 changes: 44 additions & 0 deletions TTS/fishaudio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import random

from fishaudio import FishAudio as FishAudioClient
from fishaudio.utils import save

from utils import settings


class FishAudio:
def __init__(self):
self.max_chars = 5000
self.client = None
self.voices = [
"8ef4a238714b45718ce04243307c57a7", # E-girl
"802e3bc2b27e49c2995d23ef70e6ac89", # Energetic Male
"933563129e564b19a115bedd57b7406a", # Sarah
"bf322df2096a46f18c579d0baa36f41d", # Adrian
"b347db033a6549378b48d00acb0d06cd", # Selene
"536d3a5e000945adb7038665781a4aca", # Ethan
]

def run(self, text, filepath, random_voice: bool = False):
if self.client is None:
self.initialize()

if random_voice:
voice_id = self.randomvoice()
else:
voice_id = str(settings.config["settings"]["tts"]["fishaudio_voice"])

audio = self.client.tts.convert(text=text, reference_id=voice_id)
save(audio, filepath)

def initialize(self):
api_key = settings.config["settings"]["tts"].get("fishaudio_api_key")
if not api_key:
raise ValueError(
"You didn't set a Fish Audio API key! Please set the config variable fishaudio_api_key to a valid API key."
)

self.client = FishAudioClient(api_key=api_key)

def randomvoice(self):
return random.choice(self.voices)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
boto3==1.36.8
botocore==1.36.8
fish-audio-sdk==1.1.0
gTTS==2.5.4
moviepy==2.2.1
playwright==1.49.1
Expand Down
4 changes: 3 additions & 1 deletion utils/.config.template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ background_thumbnail_font_size = { optional = true, type = "int", default = 96,
background_thumbnail_font_color = { optional = true, default = "255,255,255", example = "255,255,255", explanation = "Font color in RGB format for the thumbnail text" }

[settings.tts]
voice_choice = { optional = false, default = "tiktok", options = ["elevenlabs", "streamlabspolly", "tiktok", "googletranslate", "awspolly", "pyttsx", "OpenAI"], example = "tiktok", explanation = "The voice platform used for TTS generation. " }
voice_choice = { optional = false, default = "tiktok", options = ["elevenlabs", "fishaudio", "streamlabspolly", "tiktok", "googletranslate", "awspolly", "pyttsx", "OpenAI"], example = "tiktok", explanation = "The voice platform used for TTS generation. " }
random_voice = { optional = false, type = "bool", default = true, example = true, options = [true, false,], explanation = "Randomizes the voice used for each comment" }
elevenlabs_voice_name = { optional = false, default = "Bella", example = "Bella", explanation = "The voice used for elevenlabs", options = ["Adam", "Antoni", "Arnold", "Bella", "Domi", "Elli", "Josh", "Rachel", "Sam", ] }
elevenlabs_api_key = { optional = true, example = "21f13f91f54d741e2ae27d2ab1b99d59", explanation = "Elevenlabs API key" }
fishaudio_api_key = { optional = true, example = "your_fish_audio_api_key", explanation = "Fish Audio API key for TTS generation" }
fishaudio_voice = { optional = false, default = "bf322df2096a46f18c579d0baa36f41d", example = "bf322df2096a46f18c579d0baa36f41d", explanation = "The voice ID used for Fish Audio TTS. Find voice IDs at fish.audio/discovery" }
aws_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for AWS Polly" }
streamlabs_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for Streamlabs Polly" }
tiktok_voice = { optional = true, default = "en_us_001", example = "en_us_006", explanation = "The voice used for TikTok TTS" }
Expand Down
2 changes: 2 additions & 0 deletions video_creation/voices.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from TTS.aws_polly import AWSPolly
from TTS.elevenlabs import elevenlabs
from TTS.engine_wrapper import TTSEngine
from TTS.fishaudio import FishAudio
from TTS.GTTS import GTTS
from TTS.openai_tts import OpenAITTS
from TTS.pyttsx import pyttsx
Expand All @@ -23,6 +24,7 @@
"pyttsx": pyttsx,
"ElevenLabs": elevenlabs,
"OpenAI": OpenAITTS,
"FishAudio": FishAudio,
}


Expand Down
Loading