From e905d0d7435a827825017c5612b6e61d1f1f5da7 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 2 Apr 2026 08:16:57 +0300 Subject: Centralize audio sidecar generation --- internal/processor/processor.go | 61 +++++++++--------------------------- internal/processor/processor_test.go | 36 +++++++++++++++++++++ 2 files changed, 50 insertions(+), 47 deletions(-) (limited to 'internal/processor') diff --git a/internal/processor/processor.go b/internal/processor/processor.go index 28b2352..92e7b29 100644 --- a/internal/processor/processor.go +++ b/internal/processor/processor.go @@ -963,13 +963,7 @@ func (p *Processor) isWordFullyProcessed(word string) bool { return true // All required files exist } func (p *Processor) saveAudioAttribution(word, audioFile string, config *audio.Config) error { - // Add preprocessing information - cleanedWord := strings.TrimSpace(word) - punctuationToRemove := []string{"!", "?", ".", ",", ";", ":", "\"", "'", "(", ")", "[", "]", "{", "}", "-", "—", "–"} - for _, punct := range punctuationToRemove { - cleanedWord = strings.ReplaceAll(cleanedWord, punct, "") - } - processedText := fmt.Sprintf("%s...", strings.TrimSpace(cleanedWord)) + processedText := audio.ProcessedTextForWord(word) var attribution string switch strings.ToLower(strings.TrimSpace(config.Provider)) { case "gemini": @@ -1012,47 +1006,20 @@ func (p *Processor) saveAudioAttribution(word, audioFile string, config *audio.C } func (p *Processor) buildAudioMetadata(config *audio.Config, audioFile string) string { - var b strings.Builder - provider := strings.ToLower(strings.TrimSpace(config.Provider)) - if provider == "" { - provider = "openai" - } - - fmt.Fprintf(&b, "provider=%s\n", provider) - switch provider { - case "gemini": - fmt.Fprintf(&b, "model=%s\n", config.GeminiTTSModel) - voice := strings.TrimSpace(config.GeminiVoice) - if voice == "" { - voice = "model-default" - } - fmt.Fprintf(&b, "voice=%s\n", voice) - fmt.Fprintf(&b, "speed=%.2f\n", config.GeminiSpeed) - default: - fmt.Fprintf(&b, "model=%s\n", config.OpenAIModel) - voice := strings.TrimSpace(config.OpenAIVoice) - if voice != "" { - fmt.Fprintf(&b, "voice=%s\n", voice) - } - fmt.Fprintf(&b, "speed=%.2f\n", config.OpenAISpeed) - if instruction := strings.TrimSpace(config.OpenAIInstruction); instruction != "" { - fmt.Fprintf(&b, "instruction=%s\n", instruction) - } - } - format := strings.TrimSpace(config.OutputFormat) - if format == "" { - format = p.effectiveAudioFormat() - } - fmt.Fprintf(&b, "format=%s\n", format) audioFileHint, audioFileBackHint := p.audioMetadataFileHints(audioFile) - if audioFileHint != "" { - fmt.Fprintf(&b, "audio_file=%s\n", filepath.Base(audioFileHint)) - } - if audioFileBackHint != "" { - fmt.Fprintf(&b, "audio_file_back=%s\n", filepath.Base(audioFileBackHint)) - } - - return b.String() + return audio.BuildSidecarMetadata(audio.SidecarMetadataParams{ + Provider: config.Provider, + OutputFormat: config.OutputFormat, + AudioFile: audioFileHint, + AudioFileBack: audioFileBackHint, + OpenAIModel: config.OpenAIModel, + OpenAIVoice: config.OpenAIVoice, + OpenAISpeed: config.OpenAISpeed, + OpenAIInstruction: config.OpenAIInstruction, + GeminiTTSModel: config.GeminiTTSModel, + GeminiVoice: config.GeminiVoice, + GeminiSpeed: config.GeminiSpeed, + }) } func (p *Processor) audioMetadataFileHints(audioFile string) (string, string) { diff --git a/internal/processor/processor_test.go b/internal/processor/processor_test.go index f658713..69250bf 100644 --- a/internal/processor/processor_test.go +++ b/internal/processor/processor_test.go @@ -436,6 +436,7 @@ func TestGenerateAudioBgBgUsesSharedOpenAIVoices(t *testing.T) { metadata := string(metadataData) for _, want := range []string{ "provider=openai", + "cardtype=bg-bg", "audio_file=audio_front.mp3", "audio_file_back=audio_back.mp3", } { @@ -443,6 +444,19 @@ func TestGenerateAudioBgBgUsesSharedOpenAIVoices(t *testing.T) { t.Fatalf("metadata = %q, missing %q", metadata, want) } } + + for i, outputFile := range fakeProvider.outputFiles { + attrPath := audio.AttributionPath(outputFile) + attributionData, err := os.ReadFile(attrPath) + if err != nil { + t.Fatalf("expected attribution file %q: %v", attrPath, err) + } + attribution := string(attributionData) + wantText := []string{"ябълка...", "круша..."}[i] + if !strings.Contains(attribution, "Processed text sent to TTS: "+wantText) { + t.Fatalf("bg-bg attribution missing processed text %q: %q", wantText, attribution) + } + } } func TestGenerateAudioProviderFactoryError(t *testing.T) { @@ -548,11 +562,22 @@ func TestGenerateAudioUsesConfiguredGeminiVoiceAndModel(t *testing.T) { "speed=1.00", "format=wav", "audio_file=audio.wav", + "cardtype=en-bg", } { if !strings.Contains(metadata, want) { t.Fatalf("metadata = %q, missing %q", metadata, want) } } + + attrPath := audio.AttributionPath(fakeProvider.lastOutputFile) + 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("gemini attribution missing processed text: %q", attribution) + } } func TestGenerateAudioUsesConfiguredAudioFormatWhenOpenAIConfigIsSetOnly(t *testing.T) { @@ -612,11 +637,22 @@ func TestGenerateAudioUsesConfiguredAudioFormatWhenOpenAIConfigIsSetOnly(t *test "model=gpt-4o-mini-tts", "voice=alloy", "format=mp3", + "cardtype=en-bg", } { if !strings.Contains(metadata, want) { t.Fatalf("metadata = %q, missing %q", metadata, want) } } + + attrPath := audio.AttributionPath(fakeProvider.lastOutputFile) + 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 TestGenerateAnkiFileUsesEffectiveAudioFormatForGemini(t *testing.T) { -- cgit v1.2.3