diff options
| -rw-r--r-- | README.md | 29 | ||||
| -rw-r--r-- | cmd/totalrecall/main.go | 1 | ||||
| -rw-r--r-- | internal/cli/command.go | 3 | ||||
| -rw-r--r-- | internal/cli/flags.go | 1 | ||||
| -rw-r--r-- | internal/story/artist.go | 4 | ||||
| -rw-r--r-- | internal/story/runner.go | 9 | ||||
| -rw-r--r-- | internal/version.go | 2 |
7 files changed, 37 insertions, 12 deletions
@@ -46,8 +46,9 @@ It has mainly been vibe coded using Claude Code CLI. - Art style override: `--story-style` replaces the random pick for both modes - **Iterative character consistency**: each page is generated with the cover + previous page as pixel references so characters stay visually consistent - **Character bible**: Gemini generates a detailed visual guide (age, clothing, colours) used in every prompt - - **Cinematic narration** via Gemini TTS (`<slug>_narration.mp3`) — Bulgarian phonology, dramatic pacing, random voice from a curated pool; override with `--narrator-voice` - - Intro teaser (15 s, different voice) + main story chunks (~100 words each) + epilogue with ambient music + - **Cinematic narration** via Gemini TTS (opt-in with `--narrate`; off by default to save quota) + - Output: `<slug>_narration.mp3` — Bulgarian phonology, dramatic pacing, random voice from a curated pool + - Override voice with `--narrator-voice`; intro teaser + main story chunks (~100 words each) + epilogue - Falls back to `<slug>_tts_todo.txt` if narration fails - **Vocabulary learning file** (`<slug>_comic_vocabulary.txt`) — word list with translations + full story text - **Theme file** (`<slug>_theme.txt`) — records the `--story-theme` used for easy reproduction @@ -189,10 +190,10 @@ Key features: - `<slug>_gallery_1.png` … `<slug>_gallery_5.png` — five close-up character gallery pages - `<slug>_back.png` — back cover with blurb - `<slug>.pdf` — all pages assembled into a single PDF - - `<slug>_narration.mp3` — cinematic Gemini TTS narration with intro, story chunks, and epilogue + - `<slug>_narration.mp3` — cinematic Gemini TTS narration (only when `--narrate` is passed) + - `<slug>_tts_todo.txt` — written if `--narrate` was given but narration failed - `<slug>_comic_vocabulary.txt` — vocabulary words + full story text for learning - `<slug>_theme.txt` — records `--story-theme` for easy reproduction - - `<slug>_tts_todo.txt` — written instead of MP3 only if narration fails Customise the story: ```bash @@ -206,13 +207,19 @@ Key features: # Force standard comic style (default is random 50/50 between ultra-realistic and standard) totalrecall --story words.txt --no-ultra-realistic - # Choose narrator voice (default: random from pool) - totalrecall --story words.txt --narrator-voice Charon # deep, authoritative - totalrecall --story words.txt --narrator-voice Fenrir # strong, resonant - totalrecall --story words.txt --narrator-voice Enceladus # breathy, intimate - totalrecall --story words.txt --narrator-voice Algieba # smooth, warm - totalrecall --story words.txt --narrator-voice Aoede # breezy, expressive - totalrecall --story words.txt --narrator-voice Schedar # steady, grounded + # Enable cinematic narration (default: off — use --narrate to opt in) + totalrecall --story words.txt --narrate + + # Choose narrator voice (default: random from pool; only relevant with --narrate) + totalrecall --story words.txt --narrate --narrator-voice Charon # deep, authoritative + totalrecall --story words.txt --narrate --narrator-voice Fenrir # strong, resonant + totalrecall --story words.txt --narrate --narrator-voice Enceladus # breathy, intimate + totalrecall --story words.txt --narrate --narrator-voice Algieba # smooth, warm + totalrecall --story words.txt --narrate --narrator-voice Aoede # breezy, expressive + totalrecall --story words.txt --narrate --narrator-voice Schedar # steady, grounded + + # Repair a partial run (skip existing pages, regenerate missing ones) + totalrecall --story words.txt --story-slug my-comic-slug ``` #### Optional Gallery Videos (Veo) diff --git a/cmd/totalrecall/main.go b/cmd/totalrecall/main.go index 13fa1bd..c991378 100644 --- a/cmd/totalrecall/main.go +++ b/cmd/totalrecall/main.go @@ -115,6 +115,7 @@ func runCommand(cmd *cobra.Command, args []string, flags *cli.Flags) error { Theme: flags.StoryTheme, UltraRealistic: storyUltraRealistic(flags.StoryNoUltraRealistic), NarratorVoice: flags.NarratorVoice, + NarrateEnabled: flags.NarrateEnabled, Slug: flags.StorySlug, }) if err := runner.Run(flags.StoryFile); err != nil { diff --git a/internal/cli/command.go b/internal/cli/command.go index c9dd889..ad62272 100644 --- a/internal/cli/command.go +++ b/internal/cli/command.go @@ -75,6 +75,9 @@ func setupFlags(cmd *cobra.Command, flags *Flags) { cmd.Flags().StringVar(&flags.NarratorVoice, "narrator-voice", "", "Gemini voice for cinematic story narration (default: random from cinematic pool). "+ "Valid values: Charon, Fenrir, Enceladus, Algieba, Aoede, Schedar") + cmd.Flags().BoolVar(&flags.NarrateEnabled, "narrate", false, + "Generate a cinematic MP3 narration of the story after --story completes (default false). "+ + "Requires GOOGLE_API_KEY. Use --narrator-voice to pick a specific voice.") cmd.Flags().BoolVar(&flags.VideoEnabled, "video", flags.VideoEnabled, "Prompt to generate Veo videos after comic generation (default true; use --video=false to skip)") cmd.Flags().BoolVar(&flags.SkipAudio, "skip-audio", false, "Skip audio generation") diff --git a/internal/cli/flags.go b/internal/cli/flags.go index 678b8ab..7e1cdbc 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -26,6 +26,7 @@ type Flags struct { StoryNoUltraRealistic bool // --no-ultra-realistic: disable photorealistic rendering requirement StorySlug string // --story-slug: force a specific output slug/directory (empty = auto from title) NarratorVoice string // --narrator-voice: Gemini voice for cinematic narration (empty = random) + NarrateEnabled bool // --narrate: generate cinematic MP3 narration after --story (default false) VideoEnabled bool // --video: whether to prompt for Veo video generation after --story completes SkipAudio bool SkipImages bool diff --git a/internal/story/artist.go b/internal/story/artist.go index 3e6b191..e3a0fb6 100644 --- a/internal/story/artist.go +++ b/internal/story/artist.go @@ -550,6 +550,10 @@ func buildStoryPagePrompt(section string, pageNum, totalPages int, style, bible "Each panel is separated by a thin black gutter line. "+ "All 4 panels must be clearly distinct scenes — NOT one continuous image. "+ "The full image area must be covered by the 4 panels with no empty space.\n"+ + "VARIETY MANDATE — every panel MUST differ from the others in at least 3 of these dimensions: "+ + "camera angle (close-up, medium shot, wide shot, bird's-eye, low angle), "+ + "character pose or action, location or background detail, lighting or time-of-day, "+ + "and foreground objects. Repeating the same angle or composition across panels is FORBIDDEN.\n"+ renderReq+ "STRICT CONSISTENCY RULES — apply to every single panel:\n"+ " • Human characters: identical face, AGE APPEARANCE, hair colour/style, and clothing "+ diff --git a/internal/story/runner.go b/internal/story/runner.go index 3399bb4..e5e9aa5 100644 --- a/internal/story/runner.go +++ b/internal/story/runner.go @@ -50,6 +50,9 @@ type RunnerConfig struct { // uses this value directly. Combine with loadOrGenerate's skip-existing logic // to repair a partial comic run without regenerating already-present pages. Slug string + // NarrateEnabled controls whether a cinematic MP3 narration is generated + // after comic creation. Defaults to false — narration is opt-in via --narrate. + NarrateEnabled bool } // Runner orchestrates the full pipeline: text → image → narration. @@ -172,6 +175,12 @@ func (r *Runner) Run(batchFile string) error { r.drawComicPages(result.StoryText, result.Bible, slug, entries) + // Narration is opt-in (--narrate flag). Skip entirely when not requested + // so runs complete faster and don't consume TTS quota unnecessarily. + if r.config != nil && !r.config.NarrateEnabled { + fmt.Println("Narration skipped (use --narrate to enable).") + return nil + } return r.handleNarration(result.StoryText, slug, comicsDir) } diff --git a/internal/version.go b/internal/version.go index 70d8b6e..51e50c3 100644 --- a/internal/version.go +++ b/internal/version.go @@ -1,3 +1,3 @@ package internal -const Version = "0.22.0" +const Version = "0.23.0" |
