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:
parent
536382641d
commit
518d1c8f27
4 changed files with 33 additions and 18 deletions
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue