diff options
| author | Paul Buetow <paul@buetow.org> | 2025-07-19 17:06:36 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-07-19 17:06:36 +0300 |
| commit | d663a312ff5f45513a23adf9eb9ffb29377bbad9 (patch) | |
| tree | f88d4beb3905cab49887081ff5559b2db7ea1c33 | |
| parent | 77538381c28e4de8fa7d3f3731412c97699bb889 (diff) | |
feat: improve export functionality and fix configuration issues
- Add custom export directory selection in GUI export dialog
- Change default export directory from ~/.local/state/totalrecall to ~/Downloads
- Remove broken voice configuration from config file (voice is always random)
- Fix discrepancies between code defaults and example config
- Remove unimplemented image cache settings from example config
The voice configuration was not working due to a bug where it only read from config if
the command-line flag was set to "nova" (but default was empty string). Since voices
are randomly selected anyway, this configuration option has been removed.
π€ Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
| -rw-r--r-- | .totalrecall.yaml.example | 9 | ||||
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | cmd/totalrecall/main.go | 8 | ||||
| -rw-r--r-- | internal/gui/app.go | 43 |
4 files changed, 44 insertions, 17 deletions
diff --git a/.totalrecall.yaml.example b/.totalrecall.yaml.example index 9685b95..b5c7cbd 100644 --- a/.totalrecall.yaml.example +++ b/.totalrecall.yaml.example @@ -12,8 +12,7 @@ audio: # OpenAI TTS settings openai_key: ${OPENAI_API_KEY} # Can also use environment variable openai_model: gpt-4o-mini-tts # Options: tts-1, tts-1-hd, gpt-4o-mini-tts - openai_voice: nova # Options: alloy, ash, ballad, coral, echo, fable, onyx, nova, sage, shimmer, verse - openai_speed: 0.8 # Range: 0.25 to 4.0 (may be ignored by gpt-4o-mini models) + openai_speed: 0.9 # Range: 0.25 to 4.0 (may be ignored by gpt-4o-mini models) # Voice instructions for gpt-4o-mini-tts model # This allows you to customize how the AI speaks @@ -38,11 +37,7 @@ image: openai_size: 1024x1024 # Options vary by model openai_quality: standard # Options: standard, hd (dall-e-3 only) openai_style: natural # Options: natural, vivid (dall-e-3 only) - - # Caching - enable_cache: true - cache_dir: ./.image_cache # Output configuration output: - directory: ./anki_cards + directory: ~/Downloads @@ -149,7 +149,6 @@ audio: # OpenAI settings openai_key: "sk-..." # Your OpenAI API key openai_model: "gpt-4o-mini-tts" # Model: tts-1, tts-1-hd, or gpt-4o-mini-tts - openai_voice: "nova" # Voice: alloy, ash, ballad, coral, echo, fable, onyx, nova, sage, shimmer, verse openai_speed: 0.8 # Speed: 0.25 to 4.0 (may be ignored by gpt-4o-mini models) openai_instruction: "You are speaking Bulgarian language (Π±ΡΠ»Π³Π°ΡΡΠΊΠΈ Π΅Π·ΠΈΠΊ). Pronounce the Bulgarian text with authentic Bulgarian phonetics, not Russian." # For gpt-4o-mini models only diff --git a/cmd/totalrecall/main.go b/cmd/totalrecall/main.go index f8039a8..f50750f 100644 --- a/cmd/totalrecall/main.go +++ b/cmd/totalrecall/main.go @@ -73,9 +73,9 @@ func init() { // Initialize random number generator rand.Seed(time.Now().UnixNano()) - // Set default output directory + // Set default output directory to Downloads home, _ := os.UserHomeDir() - defaultOutputDir := filepath.Join(home, ".local", "state", "totalrecall") + defaultOutputDir := filepath.Join(home, "Downloads") // Global flags rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.totalrecall.yaml)") @@ -384,9 +384,7 @@ func generateAudioWithVoice(word, voice string) error { if openAIModel == "gpt-4o-mini-tts" && viper.IsSet("audio.openai_model") { providerConfig.OpenAIModel = viper.GetString("audio.openai_model") } - if openAIVoice == "nova" && viper.IsSet("audio.openai_voice") { - providerConfig.OpenAIVoice = viper.GetString("audio.openai_voice") - } + // Voice is always random or specified via command line, not from config if openAISpeed == 0.9 && viper.IsSet("audio.openai_speed") { providerConfig.OpenAISpeed = viper.GetFloat64("audio.openai_speed") } diff --git a/internal/gui/app.go b/internal/gui/app.go index 2226eb1..7e63ab8 100644 --- a/internal/gui/app.go +++ b/internal/gui/app.go @@ -14,6 +14,7 @@ import ( "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/layout" + "fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" fynetooltip "github.com/dweymouth/fyne-tooltip" @@ -94,8 +95,11 @@ type Config struct { // DefaultConfig returns default GUI configuration func DefaultConfig() *Config { + homeDir, _ := os.UserHomeDir() + outputDir := filepath.Join(homeDir, "Downloads") + return &Config{ - OutputDir: "./anki_cards", + OutputDir: outputDir, AudioFormat: "mp3", ImageProvider: "openai", } @@ -112,7 +116,7 @@ func New(config *Config) *Application { ctx, cancel := context.WithCancel(context.Background()) - myApp := app.New() + myApp := app.NewWithID("org.codeberg.snonux.totalrecall") myApp.SetIcon(GetAppIcon()) app := &Application{ @@ -893,12 +897,43 @@ func (a *Application) onExportToAnki() { deckNameEntry := widget.NewEntry() deckNameEntry.SetPlaceHolder("Bulgarian Vocabulary") + // Export directory selection + homeDir, _ := os.UserHomeDir() + defaultExportDir := filepath.Join(homeDir, "Downloads") + selectedDir := defaultExportDir + + dirLabel := widget.NewLabel(selectedDir) + + dirButton := widget.NewButton("Browse...", func() { + folderDialog := dialog.NewFolderOpen(func(dir fyne.ListableURI, err error) { + if err != nil || dir == nil { + return + } + selectedDir = dir.Path() + dirLabel.SetText(selectedDir) + }, a.window) + + // Try to set initial directory + if uri, err := storage.ParseURI("file://" + selectedDir); err == nil { + if listableURI, ok := uri.(fyne.ListableURI); ok { + folderDialog.SetLocation(listableURI) + } + } + + folderDialog.Show() + }) + + dirContainer := container.NewBorder(nil, nil, nil, dirButton, dirLabel) + content := container.NewVBox( widget.NewLabel("Export Format:"), formatSelect, widget.NewSeparator(), widget.NewLabel("Deck Name:"), deckNameEntry, + widget.NewSeparator(), + widget.NewLabel("Export Directory:"), + dirContainer, widget.NewLabel(""), widget.NewRichTextFromMarkdown("**APKG**: Complete package with media files included\n**CSV**: Text only, requires manual media copy"), ) @@ -924,7 +959,7 @@ func (a *Application) onExportToAnki() { if isAPKG { filename = fmt.Sprintf("%s.apkg", internal.SanitizeFilename(deckName)) - outputPath = filepath.Join(a.config.OutputDir, filename) + outputPath = filepath.Join(selectedDir, filename) // Generate APKG from all cards in directory gen := anki.NewGenerator(nil) @@ -948,7 +983,7 @@ func (a *Application) onExportToAnki() { total, outputPath, withAudio, withImages)) } else { filename = "anki_import.csv" - outputPath = filepath.Join(a.config.OutputDir, filename) + outputPath = filepath.Join(selectedDir, filename) // Generate CSV from all cards in directory gen := anki.NewGenerator(&anki.GeneratorOptions{ |
