summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-02 07:27:48 +0300
committerPaul Buetow <paul@buetow.org>2026-04-02 07:27:48 +0300
commitc1a2a5a1df54611437d36e1b2f448f78b32e8eb1 (patch)
tree504407e236c5192f0e2fb8eb3045669eccc6add0
parente96cc2578595a0ec22a07819cd0eb1b0b0c711f7 (diff)
Fix GUI voice-suffixed audio discovery
-rw-r--r--internal/gui/audio_paths.go59
-rw-r--r--internal/gui/audio_paths_test.go86
-rw-r--r--internal/gui/navigation.go32
3 files changed, 89 insertions, 88 deletions
diff --git a/internal/gui/audio_paths.go b/internal/gui/audio_paths.go
index baee232..42402da 100644
--- a/internal/gui/audio_paths.go
+++ b/internal/gui/audio_paths.go
@@ -1,26 +1,13 @@
package gui
-import (
- "os"
- "path/filepath"
- "strings"
- "time"
-)
-
-var supportedAudioExtensions = map[string]struct{}{
- ".aac": {},
- ".flac": {},
- ".mp3": {},
- ".opus": {},
- ".wav": {},
-}
+import "codeberg.org/snonux/totalrecall/internal/anki"
func (a *Application) resolveSingleAudioFile(wordDir string) string {
- return resolveAudioFileByBaseName(wordDir, "audio")
+ return anki.ResolveAudioFile(wordDir, "audio", "")
}
func (a *Application) resolveBgBgAudioFiles(wordDir string) (string, string) {
- return resolveAudioFileByBaseName(wordDir, "audio_front"), resolveAudioFileByBaseName(wordDir, "audio_back")
+ return anki.ResolveAudioFile(wordDir, "audio_front", ""), anki.ResolveAudioFile(wordDir, "audio_back", "")
}
func (a *Application) hasAnyAudioFile(wordDir string) bool {
@@ -32,43 +19,3 @@ func (a *Application) hasAnyAudioFile(wordDir string) bool {
front, back := a.resolveBgBgAudioFiles(wordDir)
return front != "" || back != ""
}
-
-func resolveAudioFileByBaseName(wordDir, baseName string) string {
- entries, err := os.ReadDir(wordDir)
- if err != nil {
- return ""
- }
-
- prefix := baseName + "."
- var resolved string
- var resolvedModTime time.Time
-
- for _, entry := range entries {
- if entry.IsDir() {
- continue
- }
-
- name := entry.Name()
- if !strings.HasPrefix(name, prefix) {
- continue
- }
-
- ext := strings.ToLower(filepath.Ext(name))
- if _, ok := supportedAudioExtensions[ext]; !ok {
- continue
- }
-
- info, err := entry.Info()
- if err != nil {
- continue
- }
-
- candidate := filepath.Join(wordDir, name)
- if resolved == "" || info.ModTime().After(resolvedModTime) || (info.ModTime().Equal(resolvedModTime) && candidate < resolved) {
- resolved = candidate
- resolvedModTime = info.ModTime()
- }
- }
-
- return resolved
-}
diff --git a/internal/gui/audio_paths_test.go b/internal/gui/audio_paths_test.go
index 1251c00..a61a24e 100644
--- a/internal/gui/audio_paths_test.go
+++ b/internal/gui/audio_paths_test.go
@@ -1,10 +1,15 @@
package gui
import (
+ "context"
"os"
"path/filepath"
"testing"
"time"
+
+ fyneapp "fyne.io/fyne/v2/app"
+ "fyne.io/fyne/v2/widget"
+ ttwidget "github.com/dweymouth/fyne-tooltip/widget"
)
func TestResolveSingleAudioFileFindsLegacyMp3WhenGuiDefaultIsWav(t *testing.T) {
@@ -29,38 +34,76 @@ func TestResolveSingleAudioFileFindsLegacyMp3WhenGuiDefaultIsWav(t *testing.T) {
}
}
-func TestResolveSingleAudioFilePrefersNewerOnDiskAudio(t *testing.T) {
+func TestGUIDiscoveryAndLoadingRecognizeVoiceSuffixedAudio(t *testing.T) {
+ fyneApp := fyneapp.New()
+ t.Cleanup(func() {
+ fyneApp.Quit()
+ })
+ ctx, cancel := context.WithCancel(context.Background())
+ t.Cleanup(cancel)
+
tempDir := t.TempDir()
- wordDir := filepath.Join(tempDir, "word")
+ wordDir := filepath.Join(tempDir, "word-card")
if err := os.MkdirAll(wordDir, 0755); err != nil {
t.Fatalf("failed to create word dir: %v", err)
}
- mp3Path := filepath.Join(wordDir, "audio.mp3")
- wavPath := filepath.Join(wordDir, "audio.wav")
- if err := os.WriteFile(mp3Path, []byte("mp3"), 0644); err != nil {
- t.Fatalf("failed to write mp3 file: %v", err)
+ word := "ябълка"
+ if err := os.WriteFile(filepath.Join(wordDir, "word.txt"), []byte(word), 0644); err != nil {
+ t.Fatalf("failed to write word file: %v", err)
+ }
+
+ voiceAudioPath := filepath.Join(wordDir, "audio_sentinel-gemini-voice.mp3")
+ if err := os.WriteFile(voiceAudioPath, []byte("voice-audio"), 0644); err != nil {
+ t.Fatalf("failed to write voice-suffixed audio file: %v", err)
}
- if err := os.WriteFile(wavPath, []byte("wav"), 0644); err != nil {
- t.Fatalf("failed to write wav file: %v", err)
+
+ app := &Application{
+ config: &Config{
+ OutputDir: tempDir,
+ AudioFormat: "wav",
+ },
+ ctx: ctx,
+ cancel: cancel,
+ queue: NewWordQueue(context.Background()),
+ wordInput: NewCustomEntry(),
+ audioPlayer: NewAudioPlayer(),
+ imageDisplay: NewImageDisplay(),
+ translationEntry: NewCustomEntry(),
+ cardTypeSelect: widget.NewSelect([]string{"English → Bulgarian", "Bulgarian → Bulgarian"}, nil),
+ imagePromptEntry: NewCustomMultiLineEntry(),
+ statusLabel: widget.NewLabel(""),
+ prevWordBtn: ttwidget.NewButton("", nil),
+ nextWordBtn: ttwidget.NewButton("", nil),
+ keepButton: ttwidget.NewButton("", nil),
+ regenerateImageBtn: ttwidget.NewButton("", nil),
+ regenerateRandomImageBtn: ttwidget.NewButton("", nil),
+ regenerateAudioBtn: ttwidget.NewButton("", nil),
+ regenerateAllBtn: ttwidget.NewButton("", nil),
+ deleteButton: ttwidget.NewButton("", nil),
}
- older := time.Now().Add(-time.Hour)
- newer := time.Now()
- if err := os.Chtimes(mp3Path, older, older); err != nil {
- t.Fatalf("failed to set mp3 file time: %v", err)
+ app.scanExistingWords()
+ if len(app.existingWords) != 1 || app.existingWords[0] != word {
+ t.Fatalf("scanExistingWords() = %v, want %q", app.existingWords, word)
}
- if err := os.Chtimes(wavPath, newer, newer); err != nil {
- t.Fatalf("failed to set wav file time: %v", err)
+
+ app.loadExistingFiles(word)
+ time.Sleep(50 * time.Millisecond)
+
+ if app.currentAudioFile != voiceAudioPath {
+ t.Fatalf("currentAudioFile = %q, want voice-suffixed path %q", app.currentAudioFile, voiceAudioPath)
}
- app := &Application{
- config: &Config{AudioFormat: "wav"},
+ if app.audioPlayer == nil {
+ t.Fatal("expected audio player to be initialized")
+ }
+ if app.audioPlayer.audioFile != voiceAudioPath {
+ t.Fatalf("audioPlayer.audioFile = %q, want %q", app.audioPlayer.audioFile, voiceAudioPath)
}
- got := app.resolveSingleAudioFile(wordDir)
- if got != wavPath {
- t.Fatalf("resolveSingleAudioFile() = %q, want newer wav %q", got, wavPath)
+ if app.currentCardType != "en-bg" {
+ t.Fatalf("currentCardType = %q, want %q", app.currentCardType, "en-bg")
}
}
@@ -80,11 +123,6 @@ func TestResolveBgBgAudioFilesFindLegacyMp3Files(t *testing.T) {
t.Fatalf("failed to write back file: %v", err)
}
- older := time.Now().Add(-time.Hour)
- if err := os.Chtimes(frontPath, older, older); err != nil {
- t.Fatalf("failed to set front file time: %v", err)
- }
-
app := &Application{
config: &Config{AudioFormat: "wav"},
}
diff --git a/internal/gui/navigation.go b/internal/gui/navigation.go
index cc1d9e6..4acbb50 100644
--- a/internal/gui/navigation.go
+++ b/internal/gui/navigation.go
@@ -444,9 +444,13 @@ func (a *Application) loadExistingFiles(word string) {
if frontAudio != "" {
a.currentAudioFile = frontAudio
fmt.Printf("DEBUG (loadExistingFiles): Found front audio: %s\n", frontAudio)
- fyne.Do(func() {
+ if a.window == nil {
a.audioPlayer.SetAudioFile(frontAudio)
- })
+ } else {
+ fyne.Do(func() {
+ a.audioPlayer.SetAudioFile(frontAudio)
+ })
+ }
} else {
fmt.Printf("DEBUG (loadExistingFiles): Front audio not found: %s\n", frontAudio)
}
@@ -454,9 +458,13 @@ func (a *Application) loadExistingFiles(word string) {
if backAudio != "" {
a.currentAudioFileBack = backAudio
fmt.Printf("DEBUG (loadExistingFiles): Found back audio: %s\n", backAudio)
- fyne.Do(func() {
+ if a.window == nil {
a.audioPlayer.SetBackAudioFile(backAudio)
- })
+ } else {
+ fyne.Do(func() {
+ a.audioPlayer.SetBackAudioFile(backAudio)
+ })
+ }
} else {
fmt.Printf("DEBUG (loadExistingFiles): Back audio not found: %s\n", backAudio)
}
@@ -467,18 +475,26 @@ func (a *Application) loadExistingFiles(word string) {
if audioFile != "" {
a.currentAudioFile = audioFile
fmt.Printf("DEBUG (loadExistingFiles): Found audio: %s\n", audioFile)
- fyne.Do(func() {
+ if a.window == nil {
a.audioPlayer.SetAudioFile(audioFile)
- })
+ } else {
+ fyne.Do(func() {
+ a.audioPlayer.SetAudioFile(audioFile)
+ })
+ }
} else {
fmt.Printf("DEBUG (loadExistingFiles): Audio not found: %s\n", audioFile)
}
// Hide back audio button for en-bg cards
a.currentAudioFileBack = ""
fmt.Printf("DEBUG (loadExistingFiles): Clearing back audio for en-bg card\n")
- fyne.Do(func() {
+ if a.window == nil {
a.audioPlayer.SetBackAudioFile("")
- })
+ } else {
+ fyne.Do(func() {
+ a.audioPlayer.SetBackAudioFile("")
+ })
+ }
}
// Load image file