Implement script generation endpoint (Task 18)

- Added ScriptRequest, ScriptResponse, and ScriptSegment Pydantic models
- Created /api/tts/script POST endpoint in main.py
- Integrated with TTSService to generate script segments using AI
- Added generate_script_segments method to TTSService
- Returns segments with unique IDs for frontend tracking
- Handles errors gracefully when AI segmentation fails
- Falls back to pattern-based segmentation when Ollama unavailable
- Added comprehensive tests for the endpoint
- Validates: Requirements 15.1, 15.2
This commit is contained in:
2025-12-09 11:43:04 +00:00
parent 1c495079eb
commit e2196c592a
4 changed files with 447 additions and 4 deletions

View File

@@ -288,6 +288,62 @@ class TTSService:
return concatenated
async def generate_script_segments(
self,
text: str,
roles: List[str]
) -> List[Tuple[str, str]]:
"""
Generates script segments with speaker assignments using AI.
This method is used for the script preview feature, allowing users to
review and edit speaker assignments before generating audio.
Args:
text: Input text to segment
roles: List of detected role IDs/names
Returns:
List of tuples: (speaker_name, text_content)
Raises:
Exception: If both AI and pattern-based segmentation fail
"""
logger.info(f"Generating script segments for {len(text)} chars with {len(roles)} roles")
# Try AI segmentation first
try:
is_available = await self.ollama_service.is_available()
if is_available:
logger.info("Using AI for script segmentation")
segments = await self.ollama_service.segment_text(text, roles)
if segments:
logger.info(f"AI segmentation produced {len(segments)} segments")
return segments
else:
logger.warning("AI segmentation returned empty result")
else:
logger.info("Ollama not available for script segmentation")
except Exception as e:
logger.warning(f"AI segmentation failed: {e}")
# Fall back to pattern-based segmentation
logger.info("Using pattern-based segmentation as fallback")
# Create a dummy role_voice_map for pattern-based segmentation
# We don't need actual voices here, just role assignments
role_voice_map = {role: self.DEFAULT_VOICE for role in roles}
# Use existing segment_text method
segments_with_voice = self.segment_text(text, role_voice_map)
# Convert from (role, voice, text) to (role, text)
segments = [(role, text) for role, voice, text in segments_with_voice]
logger.info(f"Pattern-based segmentation produced {len(segments)} segments")
return segments
async def generate_audiobook(
self,
text: str,