| Age | Commit message (Collapse) | Author |
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
Duplicate each 16-bit PCM sample into both L/R channels so all
audio output uses both speakers, and retroactively converted the
88 existing card MP3s to stereo via ffmpeg.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Copy *_gallery_*.png into comics/gallery after each --story run
- Expand ultra-realistic rendering instructions in image prompts
- Add gallery copy test; document comics/ in gitignore; sample vocab batch asset
Made-with: Cursor
|
|
Use named return and defer like anki/generator GenerateCSV so a failed
out.Close() is returned when io.Copy already succeeded.
Made-with: Cursor
|
|
Use make([]WordEntry, 0, len(lines)) after splitting normalized content
to avoid repeated growth on append. Return nil when no entries are
parsed so empty batch files still yield a nil slice like before.
Made-with: Cursor
|
|
Introduce internal/apicircuit with sony/gobreaker: trip after five
consecutive failures, 45s open state, 2m count reset, half-open trial cap.
context.Canceled is not counted as a failure for breaker stats.
Wrap OpenAI TTS, Gemini TTS, OpenAI DALL-E/chat (image path), and Nano Banana
Gemini GenerateContent calls. HTTP timeouts remain unchanged in httpctx.
Made-with: Cursor
|
|
GetActiveJobs previously drained and refilled q.jobs while holding RLock,
racing with AddWordWithPrompt and ProcessNextJob on the same channel.
Derive queued jobs from q.results (StatusQueued) and processing jobs from
q.processing, matching the authoritative state already updated under the
mutex elsewhere.
Made-with: Cursor
|
|
Move cobra flag registration, viper binding, and explicit-flag tracking into
flags.go alongside Flags and NewFlags. Add config.go for InitConfig and
apikeys.go for GetOpenAIKey/GetGoogleAPIKey. command.go now only defines
CreateRootCommand.
Made-with: Cursor
|
|
Add internal/config/defaults.go with shared model IDs, output format, OpenAI/Gemini
audio literals, and speed defaults. Wire audio.DefaultProviderConfig, CLI flags,
GUI defaulting paths, and image package re-exports to these constants. Update
command_test to use the public config identifiers.
Made-with: Cursor
|
|
CardTemplate
Extract SQLite schema and inserts into SQLiteSchemer, zip creation into
ZipPackager, and embedded HTML/CSS templates into CardTemplate with
external .html and .css files. APKGGenerator orchestrates copy, media
mapping, DB build, and packaging.
Made-with: Cursor
|
|
KeyboardShortcuts
Move navigation, export dialog, queue processing, and keyboard wiring out of
Application into focused types with app *Application for shared state.
Add ensureHandlers() for lazy init so tests that build Application literals
still work. Wire queue callbacks to QueueManager; keep thin Application
delegates for entry points used across the GUI.
Made-with: Cursor
|
|
Use time.NewTimer with defer Stop in the delayed tooltip goroutine so the
timer is not left pending when ctx wins the select. After the delay, check
ctx before and inside fyne.Do so SetToolTip never runs after shutdown
cancels the application context.
Made-with: Cursor
|
|
Extract CLIConfigResolver for flag/config precedence and GUI wiring; embed
it on Processor so audio and image helpers use promoted accessors.
Move batch file flow to BatchProcessor and Anki generation to AnkiExporter;
Processor delegates while keeping per-word processing and translation cache.
Made-with: Cursor
|
|
Define Archiver, ModelLister, and StoryRunner interfaces in their packages.
gui.New returns App; Application carries an injectable Archiver from Config.
cmd/totalrecall wires default implementations via runDeps for tests and DI.
Made-with: Cursor
|
|
- Add newProcessor() and newProcessorConfig in processor_config.go; newStoryRunner
and storyUltraRealistic in story.go; main.go focuses on runCommand wiring.
- Move gallery prompts, GenerateSelectedVideos, and RunStoryVideos into
internal/video; cli.GenerateSelectedVideos delegates for GUI compatibility.
- Remove t.Parallel from Veo tests that patch newGenaiClient (race with globals).
Made-with: Cursor
|
|
Add internal/registry generic Registry[K,T] for keyed factory registration.
Wire audio.NewProvider via registered per-provider constructors; GUI and
processor newImageSearcher use registries of *Orchestrator/*Processor methods.
Export image.ImageProviderOpenAI and ImageProviderNanoBanana from search.go
and use them across gui to avoid duplicate string constants.
Made-with: Cursor
|
|
Introduce internal/httpctx with non-zero http.Client timeouts for go-openai
and google.golang.org/genai, shared image download client, and
WithTimeoutUnlessSet for operation-level deadlines when callers use Background.
Wire NewOpenAIClient/NewGenAIClient everywhere clients are constructed.
Apply Search timeouts for DALL-E and Nano Banana, provider audio timeouts,
model-list timeouts, Veo operation timeouts, story page download context,
and single-word CLI processing cap.
Made-with: Cursor
|
|
Split orchestrator audio resolution, voice selection, and parallel material
generation into dedicated types in separate files. GenerationOrchestrator
wires AudioConfigResolver and VoiceSelector from NewGenerationOrchestrator
and delegates GenerateMaterials to ParallelRunner; public behaviour unchanged.
Made-with: Cursor
|
|
Return errors from saveAudioAttribution (including audio_metadata.txt),
image prompt persistence (callback + saveImagePrompt), and
ProcessWordWithTranslationAndType (card type, translation, phonetic)
instead of only logging warnings so callers see partial failures.
Made-with: Cursor
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Two-part fix for missing dialogue bubbles:
1. Panel script prompt: each panel description must include quoted Bulgarian
dialogue for the character to speak/think. At least 3 of 4 panels per page
must have speech or thought text in the description.
2. Image prompt: added MANDATORY SPEECH BUBBLES block requiring at least 3 of 4
panels to contain a bubble, describing exact white-background/black-outline
style for speech and cloud-shape for thought bubbles, all text Bulgarian Cyrillic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Previously story text was split mechanically into 5 sections and each panel
was guessed from raw prose — causing repeated compositions and incoherent plots.
Now GenerateFull requests a 20-entry panel visual script (P1-A…P5-D) from Gemini
in the same API call as the story and bible. Each panel gets an explicit 1-2 sentence
description of WHO/WHAT/WHERE/expression, written in chronological story order.
buildStoryPagePrompt now uses these descriptions to drive the image model directly
("Draw each panel EXACTLY as described") instead of "scene N from the excerpt".
Falls back to excerpt-driven prompts when the script is absent or incomplete.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Previously only --no-ultra-realistic existed (to force standard comic style).
--ultra-realistic now forces photorealistic panels instead of the random 50/50 pick.
storyUltraRealistic() updated to handle three states: forced-on, forced-off, random.
README updated with --ultra-realistic example.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Add --narrate flag (default false); narration is now opt-in to save TTS quota
- Add VARIETY MANDATE to story page prompts: each panel must differ in camera
angle, pose, location, lighting, and foreground — prevents repeated compositions
- Wire NarrateEnabled through RunnerConfig and main.go
- Update README: --narrate flag, updated output file table, narrator voice examples
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
with clear message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
veo.go
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- internal/video/veo_test.go: add pageNumFromPath table test, loadGalleryImage
multiple-matches edge case, saveMP4 fallback name, and NewVeoGenerator success
path with mock client injection
- internal/cli/prompts_test.go: add parseSelection edge cases — zero max with
"all", page number exceeding max, and whitespace-only input
- internal/cli/video_runner_test.go: new file covering GenerateSelectedVideos
for empty/nil paths (early return) and empty/whitespace API key (error path)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Add a new subsection under the --story CLI section explaining the
interactive Veo video-generation prompt that runs after comic creation,
including the model name (veo-2.0-generate-001), MP4 output paths, the
billing/cost note, and the --video=false skip flag.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Adds showGalleryVideoDialog in internal/gui/dialogs.go: a multi-checkbox
list (all pre-selected) lets the user choose which gallery PNGs to animate,
then calls cli.GenerateSelectedVideos in a goroutine while a progress dialog
indicates background work.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
PromptForGalleryVideos now returns []string paths (from recursive walk)
instead of []int page numbers, so GenerateSelectedVideos always has the
exact path to each gallery PNG regardless of which comics/<slug>/
subdirectory it lives in.
Previously, GenerateSelectedVideos passed "." as galleryPath and called
loadGalleryImage with a non-recursive filepath.Glob that could not find
PNGs in subdirectories, causing video generation to always fail with
"no gallery image found".
Additional changes:
- Add VeoGenerator.GenerateVideoFromPath that accepts a full image path
and writes the MP4 next to the source PNG
- Make runStoryVideos non-fatal: video errors print a warning and return
nil so comic/PDF/narration outputs are never invalidated by Veo errors
- Add filterPathsByPages helper and tests for the new behaviour
- Add TestFindGalleryPages_Recursive to cover the comics/<slug>/ layout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Add VideoEnabled bool (default true) to Flags, register --video bool flag in
command.go, export PromptForGalleryVideos/GenerateSelectedVideos, and wire them
into main.go via runStoryVideos — skipped when --video=false. Also update
findGalleryPages to walk subdirectories recursively so gallery PNGs inside
comics/<slug>/ are discovered from the CWD search root.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Implements the shared CLI runner that calls video.VeoGenerator for each
selected gallery page number sequentially, printing progress and the saved
MP4 path per page. Lives in internal/cli/video_runner.go.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Implements promptForGalleryVideos(outputDir) which lists *_gallery_*.png
files, asks the user y/n, then prompts for page selection (1,3,5 or all),
returning a sorted slice of chosen page numbers. Also adds the pure helper
parseSelection(input, max) with full unit tests covering all branches.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Introduces internal/video/veo.go with NewVeoGenerator and
GenerateVideoFromGallery. Reads gallery PNG pages, submits them to
the Veo API (veo-2.0-generate-001) as image-to-video with an 8-second
16:9 clip request, polls the long-running operation every 15 s, then
saves the resulting MP4 to the output directory.
Unit tests in veo_test.go cover key error paths and helper functions
using mocks — no real API calls required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Fire-and-forget goroutines in the tooltip setup, word-change handler, and
audio playback could write to freed Fyne widgets after the window was closed.
All four patterns are now fixed:
- setupTooltips() and the secondary-toolbar tooltip block: replaced
time.AfterFunc(500ms) with wg-tracked goroutines using select/ctx.Done().
- handleWordChange(): replaced time.AfterFunc(100ms) with the same pattern.
- onWindowClosed(): added wordChangeTimer.Stop() to prevent its AfterFunc
callback from firing after context cancellation.
- AudioPlayer: added ctx context.Context field + SetContext(); the
post-playback goroutine now guards fyne.Do with ctx.Err() == nil, and the
auto-play AfterFunc is replaced with a ctx-aware goroutine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
FindCardDirectory, FindOrCreateCardDirectory, GenerateCardID and the
ScanWords helper previously existed in both internal/utils.go (as
standalone functions) and were partially duplicated in
internal/gui/card_service.go (readWordFromDir, ScanExistingWords).
Introduce internal/store.CardStore as the single source of truth for
all on-disk card-directory operations. Both internal/processor and
internal/gui now hold a *store.CardStore field and delegate to it,
removing the last copy of the directory-scanning loop from
card_service.go. internal/utils.go keeps thin forwarding wrappers for
callers that import the root internal package.
Also adds table-driven unit tests for the new package covering
FindCardDirectory (including legacy _word.txt fallback),
FindOrCreateCardDirectory, and CardStore.ScanWords.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Define audio.ProviderFactory, image.PromptAwareClient,
image.OpenAIClientFactory, image.NanoBananaClientFactory, and
image.ClientFactories as the single source of truth for the three injectable
factory signatures that were previously duplicated across processor.Processor,
gui.Application, and gui.GenerationOrchestrator. Replace all three separate
function-type fields with imageFactories image.ClientFactories +
newAudioProvider audio.ProviderFactory, eliminating the parallel field
declarations and the local promptAwareImageClient interface in gui/generator.go.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Resolve all Viper config values once at startup in cmd/totalrecall/main.go
via the new newProcessorConfig() helper. The exported processor.Config struct
replaces the internal viperConfig type, and NewProcessor now accepts *Config
instead of querying Viper internally. The processor package no longer imports
Viper at all. Tests are updated to pass Config values directly, eliminating
the viper.Set/Reset boilerplate that coupled tests to the global singleton.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Extract CardService (file I/O, persistence, card directory management) and
GenerationOrchestrator (audio/image/phonetics generation) from the 2852-line
Application struct. Application is now thin UI event-wiring. Also split all
functions over 50 lines into focused helpers throughout app.go.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Extract audio coordination (voice selection, config assembly, attribution
writing) into audio_coordinator.go, card directory management into
card_store.go, and image downloading/searcher construction into
image_downloader.go. processor.go shrinks from ~1119 to ~575 lines,
each file now has a single clear responsibility. Also apply go fmt to
all touched files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Replace fixed 10s retry pause with pageRetryBase × attempt (15s→30s→45s→60s)
and increase max retries from 3 to 5. Extracted shared retryPage() helper used
by both generateStoryPage and generatePageWithRetry to keep retry logic DRY.
Gallery pages that fail due to Gemini rate limits now recover automatically
instead of being silently skipped after just 30 seconds of retries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- loadOrGenerate() in artist.go skips API calls for pages already on disk,
loading bytes for the reference chain. Re-runs now only generate missing
pages instead of regenerating the whole comic.
- --story-slug flag forces the output directory slug so a repair run writes
to the same directory as the original (e.g. --story-slug ai-jungle-quest).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
The strict LOCATION rule (all 4 panels must show the exact same location)
prevented natural scene progression within a page's story excerpt. Removed
so characters can move through the environment across panels on a single page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Ultra-realistic mode now picks from a dedicated realisticStyles pool
(DSLR photography, cinematic stills, hyper-realistic) instead of the
comicStyles pool. This prevents "comic strip" language from dominating
the model's output when photorealistic rendering is requested.
README updated: 12-page comic count (5 gallery pages), rendering mode
description clarified to show both pools and the 50/50 random default.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Add explicit LOCATION rule to story page prompt: all panels must show only
the location described in that page's excerpt, not any other story location
- Strengthen excerpt label so model illustrates only the current excerpt
- Bump version to 0.16.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|