Task 19: Update TTS generation endpoint to accept edited scripts
- Modified TTSRequest to accept optional segments array - Updated generate_audiobook endpoint to use segments if provided - Added generate_audiobook_from_segments method to TTSService - Ensured backward compatibility with text-based generation - Added comprehensive tests for segment-based generation - All tests passing (7/7) Requirements: 15.7
This commit is contained in:
@@ -404,3 +404,66 @@ class TTSService:
|
||||
|
||||
logger.info(f"Generated audiobook: {len(audio_bytes)} bytes")
|
||||
return audio_bytes
|
||||
|
||||
async def generate_audiobook_from_segments(
|
||||
self,
|
||||
segments: List[Tuple[str, str]],
|
||||
lang: str,
|
||||
role_voice_map: Dict[str, str],
|
||||
speed: float = 1.0
|
||||
) -> bytes:
|
||||
"""
|
||||
Generates audiobook from pre-segmented script with speaker assignments.
|
||||
|
||||
This method is used when the user has reviewed and edited the script,
|
||||
bypassing automatic segmentation.
|
||||
|
||||
Args:
|
||||
segments: List of (speaker_name, text_content) tuples
|
||||
lang: Language code
|
||||
role_voice_map: Role to voice ID mapping
|
||||
speed: Playback speed
|
||||
|
||||
Returns:
|
||||
Audio data as bytes (WAV format)
|
||||
|
||||
Raises:
|
||||
ValueError: If segments is empty or invalid
|
||||
"""
|
||||
logger.info(
|
||||
f"Generating audiobook from {len(segments)} edited segments: "
|
||||
f"lang={lang}, speed={speed}, roles={len(role_voice_map)}"
|
||||
)
|
||||
|
||||
if not segments:
|
||||
raise ValueError("No segments provided for synthesis")
|
||||
|
||||
# Get or create pipeline for language
|
||||
pipeline = self.get_pipeline(lang)
|
||||
|
||||
# Convert segments to format with voice assignments
|
||||
segments_with_voice = []
|
||||
for speaker, text in segments:
|
||||
voice = role_voice_map.get(speaker, self.DEFAULT_VOICE)
|
||||
segments_with_voice.append((speaker, voice, text))
|
||||
|
||||
logger.info(f"Processing {len(segments_with_voice)} segments with voice assignments")
|
||||
|
||||
# Synthesize each segment
|
||||
audio_segments = []
|
||||
for role_id, voice, content in segments_with_voice:
|
||||
logger.debug(f"Synthesizing segment for role '{role_id}' with voice '{voice}'")
|
||||
audio = self.synthesize_segment(pipeline, content, voice, speed)
|
||||
audio_segments.append(audio)
|
||||
|
||||
# Concatenate all audio
|
||||
final_audio = self.concatenate_audio(audio_segments)
|
||||
|
||||
# Convert to bytes (WAV format)
|
||||
buffer = io.BytesIO()
|
||||
sf.write(buffer, final_audio, self.SAMPLE_RATE, format='WAV')
|
||||
buffer.seek(0)
|
||||
audio_bytes = buffer.read()
|
||||
|
||||
logger.info(f"Generated audiobook from edited segments: {len(audio_bytes)} bytes")
|
||||
return audio_bytes
|
||||
|
||||
Reference in New Issue
Block a user