summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-07-19 17:06:36 +0300
committerPaul Buetow <paul@buetow.org>2025-07-19 17:06:36 +0300
commitd663a312ff5f45513a23adf9eb9ffb29377bbad9 (patch)
treef88d4beb3905cab49887081ff5559b2db7ea1c33
parent77538381c28e4de8fa7d3f3731412c97699bb889 (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.example9
-rw-r--r--README.md1
-rw-r--r--cmd/totalrecall/main.go8
-rw-r--r--internal/gui/app.go43
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
diff --git a/README.md b/README.md
index 828d991..1fd7701 100644
--- a/README.md
+++ b/README.md
@@ -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{