feat: add Fish Audio TTS support (#2434)

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
develop
James Ding 4 days ago committed by GitHub
parent b86a116d20
commit df1876c2d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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">
@ -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;
@ -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"));

@ -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,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

@ -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" }

@ -5,6 +5,7 @@ from rich.console import Console
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
@ -23,6 +24,7 @@ TTSProviders = {
"pyttsx": pyttsx,
"ElevenLabs": elevenlabs,
"OpenAI": OpenAITTS,
"FishAudio": FishAudio,
}

Loading…
Cancel
Save