Spaces:
Running
Running
Fix podcast playback: valid MP3 output, Gradio file route, and TTS input cap
Browse files- pages/artifacts.py +1 -0
- services/podcast_service.py +18 -18
pages/artifacts.py
CHANGED
|
@@ -60,6 +60,7 @@ def _render_artifact_content(artifact) -> str:
|
|
| 60 |
f'border-radius:10px;">'
|
| 61 |
f'<div style="font-size:0.82rem; color:#8090d0; margin-bottom:8px;">🎧 Podcast Audio</div>'
|
| 62 |
f'<audio controls style="width:100%; border-radius:6px;">'
|
|
|
|
| 63 |
f'<source src="/file={artifact.audio_path}" type="audio/mpeg">'
|
| 64 |
f'Your browser does not support the audio element.'
|
| 65 |
f'</audio>'
|
|
|
|
| 60 |
f'border-radius:10px;">'
|
| 61 |
f'<div style="font-size:0.82rem; color:#8090d0; margin-bottom:8px;">🎧 Podcast Audio</div>'
|
| 62 |
f'<audio controls style="width:100%; border-radius:6px;">'
|
| 63 |
+
f'<source src="/gradio_api/file={artifact.audio_path}" type="audio/mpeg">'
|
| 64 |
f'<source src="/file={artifact.audio_path}" type="audio/mpeg">'
|
| 65 |
f'Your browser does not support the audio element.'
|
| 66 |
f'</audio>'
|
services/podcast_service.py
CHANGED
|
@@ -104,7 +104,7 @@ def _tts_request(text: str, token: str) -> bytes | None:
|
|
| 104 |
|
| 105 |
|
| 106 |
def _synthesize_audio(lines: list[tuple[str, str]], output_path: Path) -> bool:
|
| 107 |
-
"""Synthesize audio via OpenAI TTS
|
| 108 |
try:
|
| 109 |
from openai import OpenAI
|
| 110 |
|
|
@@ -115,26 +115,26 @@ def _synthesize_audio(lines: list[tuple[str, str]], output_path: Path) -> bool:
|
|
| 115 |
full_text = "\n".join([f"{speaker}: {text}" for speaker, text in lines])
|
| 116 |
|
| 117 |
logger.info("TTS: Generating audio for %d lines (%d chars)", len(lines), len(full_text))
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
for i, chunk in enumerate(chunks):
|
| 127 |
-
logger.info("TTS chunk %d/%d (%d chars)", i + 1, len(chunks), len(chunk))
|
| 128 |
-
response = client.audio.speech.create(
|
| 129 |
-
model="tts-1",
|
| 130 |
-
voice="alloy", # neutral voice
|
| 131 |
-
input=chunk,
|
| 132 |
)
|
| 133 |
-
|
| 134 |
|
| 135 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
with open(str(output_path), "wb") as f:
|
| 137 |
-
f.write(
|
| 138 |
|
| 139 |
file_size = os.path.getsize(str(output_path))
|
| 140 |
logger.info("Podcast audio saved to %s (%d bytes)", output_path, file_size)
|
|
|
|
| 104 |
|
| 105 |
|
| 106 |
def _synthesize_audio(lines: list[tuple[str, str]], output_path: Path) -> bool:
|
| 107 |
+
"""Synthesize audio via OpenAI TTS and save a valid MP3 file."""
|
| 108 |
try:
|
| 109 |
from openai import OpenAI
|
| 110 |
|
|
|
|
| 115 |
full_text = "\n".join([f"{speaker}: {text}" for speaker, text in lines])
|
| 116 |
|
| 117 |
logger.info("TTS: Generating audio for %d lines (%d chars)", len(lines), len(full_text))
|
| 118 |
+
|
| 119 |
+
max_chars = 4000
|
| 120 |
+
if len(full_text) > max_chars:
|
| 121 |
+
trimmed = _chunk_text(full_text, max_chars)[0]
|
| 122 |
+
logger.warning(
|
| 123 |
+
"TTS input exceeded limit; trimming from %d to %d chars",
|
| 124 |
+
len(full_text),
|
| 125 |
+
len(trimmed),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
)
|
| 127 |
+
full_text = trimmed
|
| 128 |
|
| 129 |
+
# Generate a single MP3 response and write it directly.
|
| 130 |
+
response = client.audio.speech.create(
|
| 131 |
+
model="tts-1",
|
| 132 |
+
voice="alloy",
|
| 133 |
+
input=full_text,
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
with open(str(output_path), "wb") as f:
|
| 137 |
+
f.write(response.content)
|
| 138 |
|
| 139 |
file_size = os.path.getsize(str(output_path))
|
| 140 |
logger.info("Podcast audio saved to %s (%d bytes)", output_path, file_size)
|