summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-07-18 19:45:35 +0300
committerPaul Buetow <paul@buetow.org>2025-07-18 19:45:35 +0300
commite1b3ba69499547424502cc7e4dacf1e10d5efde9 (patch)
tree17110ac06239ff24150cd87df7df1729f7a285ae
parentcd3b1e5b2fab8075303c064ba33996a0250cd6a6 (diff)
fix: ensure single directory per card with consistent ID
- Fixed issue where multiple directories were created for the same word - Added findCardDirectory to check for existing directories before creating new ones - Updated all file creation functions to reuse existing card directories - Applied fix to both GUI and CLI code - Added phonetic fetching to processing counter in generateMaterials - Now each card gets one directory with all its files 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
-rw-r--r--cmd/totalrecall/main.go119
-rw-r--r--internal/gui/app.go88
-rw-r--r--internal/gui/generator.go48
3 files changed, 181 insertions, 74 deletions
diff --git a/cmd/totalrecall/main.go b/cmd/totalrecall/main.go
index 8258050..35b56c2 100644
--- a/cmd/totalrecall/main.go
+++ b/cmd/totalrecall/main.go
@@ -338,18 +338,22 @@ func generateAudioWithVoice(word, voice string) error {
// Generate audio file
ctx := context.Background()
- cardID := internal.GenerateCardID(word)
- // Create subdirectory for this word
- wordDir := filepath.Join(outputDir, cardID)
- if err := os.MkdirAll(wordDir, 0755); err != nil {
- return fmt.Errorf("failed to create word directory: %w", err)
- }
-
- // Save word metadata if not already present
- metadataFile := filepath.Join(wordDir, "word.txt")
- if _, err := os.Stat(metadataFile); os.IsNotExist(err) {
- os.WriteFile(metadataFile, []byte(word), 0644)
+ // Find existing card directory or create new one
+ wordDir := findCardDirectory(word)
+ if wordDir == "" {
+ // No existing directory, create new one with card ID
+ cardID := internal.GenerateCardID(word)
+ wordDir = filepath.Join(outputDir, cardID)
+ if err := os.MkdirAll(wordDir, 0755); err != nil {
+ return fmt.Errorf("failed to create word directory: %w", err)
+ }
+
+ // Save word metadata
+ metadataFile := filepath.Join(wordDir, "word.txt")
+ if err := os.WriteFile(metadataFile, []byte(word), 0644); err != nil {
+ return fmt.Errorf("failed to save word metadata: %w", err)
+ }
}
// Add voice name to filename if generating multiple voices
@@ -423,17 +427,21 @@ func downloadImages(word string) error {
return fmt.Errorf("unknown image provider: %s", imageAPI)
}
- // Create subdirectory for this word
- cardID := internal.GenerateCardID(word)
- wordDir := filepath.Join(outputDir, cardID)
- if err := os.MkdirAll(wordDir, 0755); err != nil {
- return fmt.Errorf("failed to create word directory: %w", err)
- }
-
- // Save word metadata if not already present
- metadataFile := filepath.Join(wordDir, "word.txt")
- if _, err := os.Stat(metadataFile); os.IsNotExist(err) {
- os.WriteFile(metadataFile, []byte(word), 0644)
+ // Find existing card directory or create new one
+ wordDir := findCardDirectory(word)
+ if wordDir == "" {
+ // No existing directory, create new one with card ID
+ cardID := internal.GenerateCardID(word)
+ wordDir = filepath.Join(outputDir, cardID)
+ if err := os.MkdirAll(wordDir, 0755); err != nil {
+ return fmt.Errorf("failed to create word directory: %w", err)
+ }
+
+ // Save word metadata
+ metadataFile := filepath.Join(wordDir, "word.txt")
+ if err := os.WriteFile(metadataFile, []byte(word), 0644); err != nil {
+ return fmt.Errorf("failed to save word metadata: %w", err)
+ }
}
// Create downloader
@@ -667,19 +675,23 @@ func translateWord(word string) (string, error) {
}
func saveTranslation(word, translation string) error {
- // Save translation to a text file
- cardID := internal.GenerateCardID(word)
- wordDir := filepath.Join(outputDir, cardID)
-
- // Ensure directory exists
- if err := os.MkdirAll(wordDir, 0755); err != nil {
- return fmt.Errorf("failed to create word directory: %w", err)
- }
-
- // Save word metadata if not already present
- metadataFile := filepath.Join(wordDir, "word.txt")
- if _, err := os.Stat(metadataFile); os.IsNotExist(err) {
- os.WriteFile(metadataFile, []byte(word), 0644)
+ // Find existing card directory or create new one
+ wordDir := findCardDirectory(word)
+ if wordDir == "" {
+ // No existing directory, create new one with card ID
+ cardID := internal.GenerateCardID(word)
+ wordDir = filepath.Join(outputDir, cardID)
+
+ // Ensure directory exists
+ if err := os.MkdirAll(wordDir, 0755); err != nil {
+ return fmt.Errorf("failed to create word directory: %w", err)
+ }
+
+ // Save word metadata
+ metadataFile := filepath.Join(wordDir, "word.txt")
+ if err := os.WriteFile(metadataFile, []byte(word), 0644); err != nil {
+ return fmt.Errorf("failed to save word metadata: %w", err)
+ }
}
outputFile := filepath.Join(wordDir, "translation.txt")
@@ -696,6 +708,43 @@ func saveTranslation(word, translation string) error {
// Global map to store translations for Anki export
var wordTranslations = make(map[string]string)
+// findCardDirectory finds the directory for a given Bulgarian word
+func findCardDirectory(word string) string {
+ entries, err := os.ReadDir(outputDir)
+ if err != nil {
+ return ""
+ }
+
+ // Look through all directories to find one with matching word.txt
+ for _, entry := range entries {
+ if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") {
+ continue
+ }
+
+ dirPath := filepath.Join(outputDir, entry.Name())
+ wordFile := filepath.Join(dirPath, "word.txt")
+
+ // Read the word file to check if it matches
+ if data, err := os.ReadFile(wordFile); err == nil {
+ storedWord := strings.TrimSpace(string(data))
+ if storedWord == word {
+ return dirPath
+ }
+ } else {
+ // Try old format with underscore for backward compatibility
+ wordFile = filepath.Join(dirPath, "_word.txt")
+ if data, err := os.ReadFile(wordFile); err == nil {
+ storedWord := strings.TrimSpace(string(data))
+ if storedWord == word {
+ return dirPath
+ }
+ }
+ }
+ }
+
+ return ""
+}
+
func saveAudioAttribution(word, audioFile string, config *audio.Config) error {
// Create attribution text
attribution := fmt.Sprintf("Audio generated by OpenAI TTS\n\n")
diff --git a/internal/gui/app.go b/internal/gui/app.go
index c4b609e..28abb7b 100644
--- a/internal/gui/app.go
+++ b/internal/gui/app.go
@@ -507,9 +507,17 @@ func (a *Application) generateMaterials(word string) {
// Save translation to disk regardless
if translation != "" {
- cardID := internal.GenerateCardID(word)
- wordDir := filepath.Join(a.config.OutputDir, cardID)
- os.MkdirAll(wordDir, 0755) // Ensure directory exists
+ // Find existing card directory first
+ wordDir := a.findCardDirectory(word)
+ if wordDir == "" {
+ // No existing directory, create new one with card ID
+ cardID := internal.GenerateCardID(word)
+ wordDir = filepath.Join(a.config.OutputDir, cardID)
+ os.MkdirAll(wordDir, 0755) // Ensure directory exists
+ // Save word metadata
+ metadataFile := filepath.Join(wordDir, "word.txt")
+ os.WriteFile(metadataFile, []byte(word), 0644)
+ }
translationFile := filepath.Join(wordDir, "translation.txt")
content := fmt.Sprintf("%s = %s\n", word, translation)
os.WriteFile(translationFile, []byte(content), 0644)
@@ -580,6 +588,36 @@ func (a *Application) generateMaterials(word string) {
a.mu.Unlock()
}
+ // Fetch phonetic information in a separate goroutine
+ go func() {
+ fyne.Do(func() {
+ a.incrementProcessing() // Phonetic processing starts
+ })
+
+ phoneticInfo, err := a.getPhoneticInfo(word)
+ if err != nil {
+ // Log error but don't fail - phonetic info is optional
+ fmt.Printf("Warning: Failed to get phonetic info: %v\n", err)
+ phoneticInfo = "Failed to fetch phonetic information"
+ }
+
+ // Update UI with phonetic info if this is still the current word
+ a.mu.Lock()
+ if a.currentWord == word {
+ fyne.Do(func() {
+ a.phoneticDisplay.SetText(phoneticInfo)
+ })
+ }
+ a.mu.Unlock()
+
+ // Save phonetic info to disk
+ if phoneticInfo != "" && phoneticInfo != "Failed to fetch phonetic information" {
+ a.savePhoneticInfoForWord(word, phoneticInfo)
+ }
+
+ a.decrementProcessing() // Phonetic processing ends
+ }()
+
// Enable action buttons
fyne.Do(func() {
a.hideProgress()
@@ -1114,12 +1152,15 @@ func (a *Application) processWordJob(job *WordJob) {
// Save translation to disk immediately for this specific word
if translation != "" {
- cardID := internal.GenerateCardID(job.Word)
- wordDir := filepath.Join(a.config.OutputDir, cardID)
- os.MkdirAll(wordDir, 0755) // Ensure directory exists
- // Save word metadata if not already present
- metadataFile := filepath.Join(wordDir, "word.txt")
- if _, err := os.Stat(metadataFile); os.IsNotExist(err) {
+ // Find existing card directory first
+ wordDir := a.findCardDirectory(job.Word)
+ if wordDir == "" {
+ // No existing directory, create new one with card ID
+ cardID := internal.GenerateCardID(job.Word)
+ wordDir = filepath.Join(a.config.OutputDir, cardID)
+ os.MkdirAll(wordDir, 0755) // Ensure directory exists
+ // Save word metadata
+ metadataFile := filepath.Join(wordDir, "word.txt")
os.WriteFile(metadataFile, []byte(job.Word), 0644)
}
translationFile := filepath.Join(wordDir, "translation.txt")
@@ -1155,12 +1196,15 @@ func (a *Application) processWordJob(job *WordJob) {
// Save phonetic info to disk immediately for this specific word
if phoneticInfo != "" && phoneticInfo != "Failed to fetch phonetic information" {
- cardID := internal.GenerateCardID(job.Word)
- wordDir := filepath.Join(a.config.OutputDir, cardID)
- os.MkdirAll(wordDir, 0755) // Ensure directory exists
- // Save word metadata if not already present
- metadataFile := filepath.Join(wordDir, "word.txt")
- if _, err := os.Stat(metadataFile); os.IsNotExist(err) {
+ // Find existing card directory first
+ wordDir := a.findCardDirectory(job.Word)
+ if wordDir == "" {
+ // No existing directory, create new one with card ID
+ cardID := internal.GenerateCardID(job.Word)
+ wordDir = filepath.Join(a.config.OutputDir, cardID)
+ os.MkdirAll(wordDir, 0755) // Ensure directory exists
+ // Save word metadata
+ metadataFile := filepath.Join(wordDir, "word.txt")
os.WriteFile(metadataFile, []byte(job.Word), 0644)
}
phoneticFile := filepath.Join(wordDir, "phonetic.txt")
@@ -1585,9 +1629,17 @@ func (a *Application) savePhoneticInfoForWord(word, phoneticText string) {
if word != "" && phoneticText != "" &&
phoneticText != "Failed to fetch phonetic information" &&
phoneticText != "Phonetic information will appear here..." {
- cardID := internal.GenerateCardID(word)
- wordDir := filepath.Join(a.config.OutputDir, cardID)
- os.MkdirAll(wordDir, 0755) // Ensure directory exists
+ // Find existing card directory first
+ wordDir := a.findCardDirectory(word)
+ if wordDir == "" {
+ // No existing directory, create new one with card ID
+ cardID := internal.GenerateCardID(word)
+ wordDir = filepath.Join(a.config.OutputDir, cardID)
+ os.MkdirAll(wordDir, 0755) // Ensure directory exists
+ // Save word metadata
+ metadataFile := filepath.Join(wordDir, "word.txt")
+ os.WriteFile(metadataFile, []byte(word), 0644)
+ }
phoneticFile := filepath.Join(wordDir, "phonetic.txt")
os.WriteFile(phoneticFile, []byte(phoneticText), 0644)
}
diff --git a/internal/gui/generator.go b/internal/gui/generator.go
index f3b9563..ca9d854 100644
--- a/internal/gui/generator.go
+++ b/internal/gui/generator.go
@@ -100,17 +100,21 @@ func (a *Application) generateAudio(word string) (string, error) {
return "", err
}
- // Create subdirectory for this word using card ID
- cardID := internal.GenerateCardID(word)
- wordDir := filepath.Join(a.config.OutputDir, cardID)
- if err := os.MkdirAll(wordDir, 0755); err != nil {
- return "", fmt.Errorf("failed to create word directory: %w", err)
- }
-
- // Save the original Bulgarian word in a metadata file
- metadataFile := filepath.Join(wordDir, "word.txt")
- if err := os.WriteFile(metadataFile, []byte(word), 0644); err != nil {
- return "", fmt.Errorf("failed to save word metadata: %w", err)
+ // Find existing card directory or create new one
+ wordDir := a.findCardDirectory(word)
+ if wordDir == "" {
+ // No existing directory, create new one with card ID
+ cardID := internal.GenerateCardID(word)
+ wordDir = filepath.Join(a.config.OutputDir, cardID)
+ if err := os.MkdirAll(wordDir, 0755); err != nil {
+ return "", fmt.Errorf("failed to create word directory: %w", err)
+ }
+
+ // Save the original Bulgarian word in a metadata file
+ metadataFile := filepath.Join(wordDir, "word.txt")
+ if err := os.WriteFile(metadataFile, []byte(word), 0644); err != nil {
+ return "", fmt.Errorf("failed to save word metadata: %w", err)
+ }
}
// Generate filename in subdirectory
@@ -163,16 +167,18 @@ func (a *Application) generateImagesWithPrompt(word string, customPrompt string,
return "", fmt.Errorf("unknown image provider: %s", a.config.ImageProvider)
}
- // Create subdirectory for this word using card ID
- cardID := internal.GenerateCardID(word)
- wordDir := filepath.Join(a.config.OutputDir, cardID)
- if err := os.MkdirAll(wordDir, 0755); err != nil {
- return "", fmt.Errorf("failed to create word directory: %w", err)
- }
-
- // Save the original Bulgarian word in a metadata file if not already present
- metadataFile := filepath.Join(wordDir, "word.txt")
- if _, err := os.Stat(metadataFile); os.IsNotExist(err) {
+ // Find existing card directory or create new one
+ wordDir := a.findCardDirectory(word)
+ if wordDir == "" {
+ // No existing directory, create new one with card ID
+ cardID := internal.GenerateCardID(word)
+ wordDir = filepath.Join(a.config.OutputDir, cardID)
+ if err := os.MkdirAll(wordDir, 0755); err != nil {
+ return "", fmt.Errorf("failed to create word directory: %w", err)
+ }
+
+ // Save the original Bulgarian word in a metadata file
+ metadataFile := filepath.Join(wordDir, "word.txt")
if err := os.WriteFile(metadataFile, []byte(word), 0644); err != nil {
return "", fmt.Errorf("failed to save word metadata: %w", err)
}