summaryrefslogtreecommitdiff
path: root/internal/gui
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-02 08:16:57 +0300
committerPaul Buetow <paul@buetow.org>2026-04-02 08:16:57 +0300
commite905d0d7435a827825017c5612b6e61d1f1f5da7 (patch)
tree26d9bef15305895c0b1703c629f042800367fb04 /internal/gui
parent0349a83743f8b8c8125a35b93b2271e402352b99 (diff)
Centralize audio sidecar generation
Diffstat (limited to 'internal/gui')
-rw-r--r--internal/gui/generator.go74
-rw-r--r--internal/gui/generator_test.go34
2 files changed, 64 insertions, 44 deletions
diff --git a/internal/gui/generator.go b/internal/gui/generator.go
index 7143cff..45cd7bc 100644
--- a/internal/gui/generator.go
+++ b/internal/gui/generator.go
@@ -421,6 +421,7 @@ func (a *Application) imagePromptCallback(cardDir, word string) func(prompt stri
// saveAudioAttribution saves attribution info for generated audio
func (a *Application) saveAudioAttribution(word, audioFile, voice string, speed float64) error {
+ processedText := audio.ProcessedTextForWord(word)
var attribution string
switch a.audioProviderName() {
case "gemini":
@@ -429,11 +430,12 @@ func (a *Application) saveAudioAttribution(word, audioFile, voice string, speed
model = a.audioConfig.GeminiTTSModel
}
attribution = audio.BuildGeminiAttribution(audio.AttributionParams{
- Word: word,
- Model: model,
- Voice: voice,
- Speed: speed,
- GeneratedAt: time.Now(),
+ Word: word,
+ Model: model,
+ Voice: voice,
+ Speed: speed,
+ ProcessedText: processedText,
+ GeneratedAt: time.Now(),
})
default:
model := audio.DefaultProviderConfig().OpenAIModel
@@ -447,12 +449,13 @@ func (a *Application) saveAudioAttribution(word, audioFile, voice string, speed
}
}
attribution = audio.BuildOpenAIAttribution(audio.AttributionParams{
- Word: word,
- Model: model,
- Voice: voice,
- Speed: speed,
- Instruction: instruction,
- GeneratedAt: time.Now(),
+ Word: word,
+ Model: model,
+ Voice: voice,
+ Speed: speed,
+ Instruction: instruction,
+ ProcessedText: processedText,
+ GeneratedAt: time.Now(),
})
}
@@ -476,39 +479,22 @@ func (a *Application) saveAudioMetadata(cardDir string, audioConfig audio.Config
}
}
- metadata := strings.Builder{}
-
- fmt.Fprintf(&metadata, "provider=%s\n", audioConfig.Provider)
- switch strings.ToLower(strings.TrimSpace(audioConfig.Provider)) {
- case "gemini":
- model := strings.TrimSpace(audioConfig.GeminiTTSModel)
- if model == "" {
- model = audio.DefaultProviderConfig().GeminiTTSModel
- }
- fmt.Fprintf(&metadata, "model=%s\n", model)
- default:
- model := strings.TrimSpace(audioConfig.OpenAIModel)
- if model == "" {
- model = audio.DefaultProviderConfig().OpenAIModel
- }
- fmt.Fprintf(&metadata, "model=%s\n", model)
- }
- voiceLine := strings.TrimSpace(voice)
- if voiceLine == "" && strings.ToLower(strings.TrimSpace(audioConfig.Provider)) == "gemini" {
- voiceLine = "model-default"
- }
- fmt.Fprintf(&metadata, "voice=%s\n", voiceLine)
- fmt.Fprintf(&metadata, "speed=%.2f\n", speed)
- fmt.Fprintf(&metadata, "format=%s\n", audioConfig.OutputFormat)
- fmt.Fprintf(&metadata, "cardtype=%s\n", cardType)
- if audioFile != "" {
- fmt.Fprintf(&metadata, "audio_file=%s\n", filepath.Base(audioFile))
- }
- if audioFileBack != "" {
- fmt.Fprintf(&metadata, "audio_file_back=%s\n", filepath.Base(audioFileBack))
- }
-
- if err := os.WriteFile(metadataFile, []byte(metadata.String()), 0644); err != nil {
+ metadata := audio.BuildSidecarMetadata(audio.SidecarMetadataParams{
+ Provider: audioConfig.Provider,
+ OutputFormat: audioConfig.OutputFormat,
+ CardType: cardType,
+ AudioFile: audioFile,
+ AudioFileBack: audioFileBack,
+ OpenAIModel: audioConfig.OpenAIModel,
+ OpenAIVoice: voice,
+ OpenAISpeed: speed,
+ OpenAIInstruction: audioConfig.OpenAIInstruction,
+ GeminiTTSModel: audioConfig.GeminiTTSModel,
+ GeminiVoice: voice,
+ GeminiSpeed: speed,
+ })
+
+ if err := os.WriteFile(metadataFile, []byte(metadata), 0644); err != nil {
return fmt.Errorf("failed to write audio metadata file: %w", err)
}
diff --git a/internal/gui/generator_test.go b/internal/gui/generator_test.go
index 31b4aa2..0c1ac41 100644
--- a/internal/gui/generator_test.go
+++ b/internal/gui/generator_test.go
@@ -239,6 +239,19 @@ func TestGenerateAudioUsesSharedOpenAIVoices(t *testing.T) {
if strings.Contains(metadata, "sentinel-gemini-model") {
t.Fatalf("openai metadata should not use Gemini model when provider is OpenAI: %q", metadata)
}
+ if !strings.Contains(metadata, "cardtype=en-bg") {
+ t.Fatalf("openai metadata missing card type: %q", metadata)
+ }
+
+ attrPath := audio.AttributionPath(outputPath)
+ attributionData, err := os.ReadFile(attrPath)
+ if err != nil {
+ t.Fatalf("expected attribution file %q: %v", attrPath, err)
+ }
+ attribution := string(attributionData)
+ if !strings.Contains(attribution, "Processed text sent to TTS: ябълка...") {
+ t.Fatalf("openai attribution missing processed text: %q", attribution)
+ }
}
func TestGenerateAudioUsesGeminiModelDefaultVoiceAndAttribution(t *testing.T) {
@@ -318,6 +331,9 @@ func TestGenerateAudioUsesGeminiModelDefaultVoiceAndAttribution(t *testing.T) {
if strings.Contains(attribution, "sentinel-gemini-voice") {
t.Fatalf("gemini attribution should not use the shared voice list when voice is unset: %q", attribution)
}
+ if !strings.Contains(attribution, "Processed text sent to TTS: ябълка...") {
+ t.Fatalf("gemini attribution missing processed text: %q", attribution)
+ }
metadataData, err := os.ReadFile(filepath.Join(cardDir, "audio_metadata.txt"))
if err != nil {
@@ -422,6 +438,24 @@ func TestGenerateAudioBgBgUsesSharedOpenAIVoices(t *testing.T) {
if !strings.Contains(metadata, "cardtype=bg-bg") {
t.Fatalf("bg-bg metadata missing card type: %q", metadata)
}
+
+ for _, tc := range []struct {
+ audioPath string
+ wantText string
+ }{
+ {audioPath: frontPath, wantText: "ябълка..."},
+ {audioPath: backPath, wantText: "круша..."},
+ } {
+ attrPath := audio.AttributionPath(tc.audioPath)
+ attributionData, err := os.ReadFile(attrPath)
+ if err != nil {
+ t.Fatalf("expected attribution file %q: %v", attrPath, err)
+ }
+ attribution := string(attributionData)
+ if !strings.Contains(attribution, "Processed text sent to TTS: "+tc.wantText) {
+ t.Fatalf("bg-bg attribution missing processed text %q: %q", tc.wantText, attribution)
+ }
+ }
}
func TestGenerateAudioFrontUsesSharedOpenAIVoices(t *testing.T) {