feat(backend/story-integration): Phase 5 - Audio Generation

- Fixed audio file path generation in StoryGenerationService.GenerateAudioAsync
- Audio files now stored at wwwroot/audio/story/level{id}-segment{order}.wav
- Added static file serving in Program.cs (app.UseStaticFiles)
- Added wwwroot/audio/story directory creation on startup
- Updated StoryController.GetSegmentAudioAsync to return audio URL
- Static file middleware serves audio files automatically
- All 324 tests still pass

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
Mistral Vibe 2026-06-13 14:47:32 +02:00
parent 536382641d
commit 518d1c8f27
4 changed files with 33 additions and 18 deletions

View file

@ -227,25 +227,31 @@ public class StoryGenerationService
return StorySegmentDto.FromEntity(segment); return StorySegmentDto.FromEntity(segment);
} }
// Generate audio file path // Generate file system path for audio file (relative to current directory)
var audioPath = $"/audio/story/level{segment.LevelId}-segment{segment.Order}.wav"; var audioDirectory = Path.Combine("wwwroot", "audio", "story");
var filename = $"level{segment.LevelId}-segment{segment.Order}.wav";
var audioFilePath = Path.Combine(audioDirectory, filename);
try try
{ {
// Generate audio using the TTS service directly // Ensure directory exists
Directory.CreateDirectory(audioDirectory);
// Generate audio using the TTS service
await _ttsService.GenerateAudioToFileAsync( await _ttsService.GenerateAudioToFileAsync(
segment.Content, segment.Content,
audioPath, audioFilePath,
null, null,
"de", "de",
cancellationToken); cancellationToken);
// Update segment with audio URL // Convert to URL path for storage
segment.UpdateAudioUrl(audioPath); var audioUrl = $"/audio/story/{filename}";
segment.UpdateAudioUrl(audioUrl);
await _storyRepository.UpdateAsync(segment, cancellationToken); await _storyRepository.UpdateAsync(segment, cancellationToken);
_logger.LogInformation("Generated audio for segment {SegmentId}: {AudioPath}", _logger.LogInformation("Generated audio for segment {SegmentId}: {AudioUrl}",
segmentId, audioPath); segmentId, audioUrl);
return StorySegmentDto.FromEntity(segment); return StorySegmentDto.FromEntity(segment);
} }

View file

@ -411,8 +411,9 @@ public class StoryController : ControllerBase
return NotFound(); return NotFound();
} }
// In a real implementation, return the actual file // Static files are served from wwwroot, and audio files are stored at wwwroot/audio/story/
// For now, return the URL // The segment.AudioUrl is already in the format "/audio/story/levelN-segmentM.wav"
// The static file middleware will serve it automatically
return Ok(new { AudioUrl = segment.AudioUrl }); return Ok(new { AudioUrl = segment.AudioUrl });
} }

View file

@ -22,6 +22,7 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Serilog; using Serilog;
using System.IO;
using System.Text; using System.Text;
// Configure Serilog // Configure Serilog
@ -218,11 +219,17 @@ try
var app = builder.Build(); var app = builder.Build();
// Create static file directories
Directory.CreateDirectory(Path.Combine("wwwroot", "audio", "story"));
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
// Use exception middleware first (to catch all exceptions) // Use exception middleware first (to catch all exceptions)
app.UseExceptionMiddleware(); app.UseExceptionMiddleware();
// Serve static files (audio, etc.)
app.UseStaticFiles();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
app.MapOpenApi(); app.MapOpenApi();

View file

@ -1,7 +1,7 @@
# Feature: Story Integration # Feature: Story Integration
> **Status**: 🚀 In Progress > **Status**: 🚀 In Progress
> **📊 Current Progress**: Phase 1-4 ✅ Complete (Database & Models, Backend Services, Unit Tests, AI Integration), Phase 5 Next (Audio Generation) > **📊 Current Progress**: Phase 1-5 ✅ Complete (Database & Models, Backend Services, Unit Tests, AI Integration, Audio Generation), Phase 6 Next (Frontend Integration)
> **Priority**: High > **Priority**: High
> **Complexity**: High > **Complexity**: High
> **Estimate**: 8-12 hours > **Estimate**: 8-12 hours
@ -165,12 +165,12 @@ Order: 1
- [x] Handle AI API errors gracefully (AiServiceException with error codes) - [x] Handle AI API errors gracefully (AiServiceException with error codes)
- [x] Add retry logic for failed generations (ExecuteWithRetryAsync with exponential backoff) - [x] Add retry logic for failed generations (ExecuteWithRetryAsync with exponential backoff)
### Phase 5: Audio Generation (2 hours) ### Phase 5: Audio Generation (2 hours) - COMPLETE
- [ ] Integrate with Coqui TTS service - [x] Integrate with Coqui TTS service (ITtsService, TtsService with CoquiConfig)
- [ ] Generate audio for each story segment - [x] Generate audio for each story segment (GenerateAudioAsync in StoryGenerationService)
- [ ] Store audio files with consistent naming - [x] Store audio files with consistent naming (wwwroot/audio/story/level{id}-segment{order}.wav)
- [ ] Update StorySegment.AudioUrl after generation - [x] Update StorySegment.AudioUrl after generation (segment.UpdateAudioUrl)
- [ ] Add audio file serving endpoint - [x] Add audio file serving endpoint (UseStaticFiles + GET segments/{id}/audio)
### Phase 5: Frontend Integration (2-3 hours) ### Phase 5: Frontend Integration (2-3 hours)
- [ ] Create StoryPage component - [ ] Create StoryPage component
@ -194,7 +194,7 @@ Order: 1
| Backend Services | June 13, 2025 | ✅ | | Backend Services | June 13, 2025 | ✅ |
| Unit Tests | June 13, 2025 | ✅ | | Unit Tests | June 13, 2025 | ✅ |
| AI Integration | June 13, 2025 | ✅ | | AI Integration | June 13, 2025 | ✅ |
| Audio Generation | - | ⏳ | | Audio Generation | June 13, 2025 | ✅ |
| Frontend Integration | - | ⏳ | | Frontend Integration | - | ⏳ |
| User Progress | - | ⏳ | | User Progress | - | ⏳ |
@ -417,6 +417,7 @@ Make the story engaging and suitable for adult learners.
| June 13, 2025 | Phase 1-2 Complete | Database & Models, Backend Services implemented | | June 13, 2025 | Phase 1-2 Complete | Database & Models, Backend Services implemented |
| June 13, 2025 | Phase 3 Complete | All unit tests written and passing (324 tests) | | June 13, 2025 | Phase 3 Complete | All unit tests written and passing (324 tests) |
| June 13, 2025 | Phase 4 Complete | Enhanced prompts with CEFR-level-specific requirements for story generation | | June 13, 2025 | Phase 4 Complete | Enhanced prompts with CEFR-level-specific requirements for story generation |
| June 13, 2025 | Phase 5 Complete | Audio generation with Coqui TTS, static file serving, and endpoints |
--- ---