|
253 | 253 | <div class="col-8"> |
254 | 254 | <select name="voice_choice" class="form-select" data-toggle="tooltip" |
255 | 255 | data-original-title='The voice platform used for TTS generation'> |
256 | | - <option value="streamlabspolly">Streamlabspolly</option> |
257 | 256 | <option value="tiktok">TikTok</option> |
| 257 | + <option value="fishaudio">Fish Audio</option> |
| 258 | + <option value="streamlabspolly">Streamlabs Polly</option> |
258 | 259 | <option value="googletranslate">Google Translate</option> |
259 | 260 | <option value="awspolly">AWS Polly</option> |
260 | 261 | <option value="pyttsx">Python TTS (pyttsx)</option> |
261 | 262 | </select> |
262 | 263 | </div> |
263 | 264 | </div> |
| 265 | + <div class="row mb-2"> |
| 266 | + <label for="fishaudio_api_key" class="col-4">Fish Audio API Key</label> |
| 267 | + <div class="col-8"> |
| 268 | + <div class="input-group"> |
| 269 | + <div class="input-group-text"> |
| 270 | + <i class="bi bi-key-fill"></i> |
| 271 | + </div> |
| 272 | + <input value="{{ data.fishaudio_api_key }}" name="fishaudio_api_key" type="text" class="form-control" |
| 273 | + data-toggle="tooltip" |
| 274 | + data-original-title="Fish Audio API key for TTS generation. Get one at fish.audio"> |
| 275 | + </div> |
| 276 | + </div> |
| 277 | + </div> |
| 278 | + <div class="row mb-2"> |
| 279 | + <label for="fishaudio_voice_select" class="col-4">Fish Audio Voice</label> |
| 280 | + <div class="col-8"> |
| 281 | + <select id="fishaudio_voice_select" class="form-select" data-toggle="tooltip" |
| 282 | + data-original-title='Select a preset voice or choose Custom to enter your own' |
| 283 | + onchange="handleFishAudioVoiceChange(this)"> |
| 284 | + <option value="bf322df2096a46f18c579d0baa36f41d">Adrian (Default)</option> |
| 285 | + <option value="8ef4a238714b45718ce04243307c57a7">E-girl</option> |
| 286 | + <option value="802e3bc2b27e49c2995d23ef70e6ac89">Energetic Male</option> |
| 287 | + <option value="933563129e564b19a115bedd57b7406a">Sarah</option> |
| 288 | + <option value="b347db033a6549378b48d00acb0d06cd">Selene</option> |
| 289 | + <option value="536d3a5e000945adb7038665781a4aca">Ethan</option> |
| 290 | + <option value="custom">Custom Voice ID...</option> |
| 291 | + </select> |
| 292 | + <input type="hidden" name="fishaudio_voice" id="fishaudio_voice" value="{{ data.fishaudio_voice }}"> |
| 293 | + </div> |
| 294 | + </div> |
| 295 | + <div class="row mb-2" id="fishaudio_custom_row" style="display: none;"> |
| 296 | + <label for="fishaudio_custom_voice" class="col-4">Custom Voice ID</label> |
| 297 | + <div class="col-8"> |
| 298 | + <div class="input-group"> |
| 299 | + <div class="input-group-text"> |
| 300 | + <i class="bi bi-soundwave"></i> |
| 301 | + </div> |
| 302 | + <input id="fishaudio_custom_voice" type="text" class="form-control" |
| 303 | + placeholder="Enter voice ID from fish.audio" |
| 304 | + data-toggle="tooltip" |
| 305 | + data-original-title="Voice ID from fish.audio - find it in the URL of any voice"> |
| 306 | + </div> |
| 307 | + <span class="form-text text-muted"><a href="https://fish.audio/discovery" target="_blank">Browse voices at fish.audio/discovery</a></span> |
| 308 | + </div> |
| 309 | + </div> |
264 | 310 | <div class="row mb-2"> |
265 | 311 | <label for="aws_polly_voice" class="col-4">AWS Polly Voice</label> |
266 | 312 | <div class="col-8"> |
|
432 | 478 | </main> |
433 | 479 |
|
434 | 480 | <script> |
| 481 | + // Fish Audio voice change handler (global function for inline onchange) |
| 482 | + function handleFishAudioVoiceChange(selectElement) { |
| 483 | + const selected = selectElement.value; |
| 484 | + const customRow = document.getElementById('fishaudio_custom_row'); |
| 485 | + const hiddenInput = document.getElementById('fishaudio_voice'); |
| 486 | + const customInput = document.getElementById('fishaudio_custom_voice'); |
| 487 | + |
| 488 | + if (selected === 'custom') { |
| 489 | + customRow.style.display = ''; |
| 490 | + customInput.focus(); |
| 491 | + } else { |
| 492 | + customRow.style.display = 'none'; |
| 493 | + hiddenInput.value = selected; |
| 494 | + } |
| 495 | + } |
| 496 | + |
435 | 497 | // Test voices buttons |
436 | 498 | var playing = false; |
437 | 499 |
|
|
544 | 606 | validate($(this)); |
545 | 607 | }); |
546 | 608 |
|
| 609 | + // Fish Audio voice selector logic |
| 610 | + const presetVoices = [ |
| 611 | + "bf322df2096a46f18c579d0baa36f41d", |
| 612 | + "8ef4a238714b45718ce04243307c57a7", |
| 613 | + "802e3bc2b27e49c2995d23ef70e6ac89", |
| 614 | + "933563129e564b19a115bedd57b7406a", |
| 615 | + "b347db033a6549378b48d00acb0d06cd", |
| 616 | + "536d3a5e000945adb7038665781a4aca" |
| 617 | + ]; |
| 618 | + |
| 619 | + // Initialize Fish Audio voice on page load |
| 620 | + const savedVoice = data.fishaudio_voice || "bf322df2096a46f18c579d0baa36f41d"; |
| 621 | + if (presetVoices.includes(savedVoice)) { |
| 622 | + $("#fishaudio_voice_select").val(savedVoice); |
| 623 | + } else { |
| 624 | + $("#fishaudio_voice_select").val("custom"); |
| 625 | + $("#fishaudio_custom_row").show(); |
| 626 | + $("#fishaudio_custom_voice").val(savedVoice); |
| 627 | + } |
| 628 | + $("#fishaudio_voice").val(savedVoice); |
| 629 | + |
| 630 | + // Handle dropdown change |
| 631 | + $("#fishaudio_voice_select").on("change", function() { |
| 632 | + const selected = $(this).val(); |
| 633 | + if (selected === "custom") { |
| 634 | + $("#fishaudio_custom_row").show(); |
| 635 | + $("#fishaudio_custom_voice").focus(); |
| 636 | + } else { |
| 637 | + $("#fishaudio_custom_row").hide(); |
| 638 | + $("#fishaudio_voice").val(selected); |
| 639 | + } |
| 640 | + }); |
| 641 | + |
| 642 | + // Handle custom input change |
| 643 | + $("#fishaudio_custom_voice").on("input", function() { |
| 644 | + $("#fishaudio_voice").val($(this).val()); |
| 645 | + }); |
| 646 | + |
547 | 647 | function validate(object) { |
548 | 648 | let bool = check(object.prop("name"), object.prop("value")); |
549 | 649 |
|
|
0 commit comments