import os import json from core import utils from ai import models as ai_models def generate_blurb(bp, folder): utils.log("MARKETING", "Generating blurb...") meta = bp.get('book_metadata', {}) beats = bp.get('plot_beats', []) beats_text = "\n".join(f" - {b}" for b in beats[:6]) if beats else " - (no beats provided)" chars = bp.get('characters', []) protagonist = next((c for c in chars if 'protagonist' in c.get('role', '').lower()), None) protagonist_desc = f"{protagonist['name']} — {protagonist.get('description', '')}" if protagonist else "the protagonist" prompt = f""" ROLE: Marketing Copywriter TASK: Write a compelling back-cover blurb for a {meta.get('genre', 'fiction')} novel. BOOK DETAILS: - TITLE: {meta.get('title')} - GENRE: {meta.get('genre')} - AUDIENCE: {meta.get('target_audience', 'General')} - PROTAGONIST: {protagonist_desc} - LOGLINE: {bp.get('manual_instruction', '(none)')} - KEY PLOT BEATS: {beats_text} BLURB STRUCTURE: 1. HOOK (1-2 sentences): Open with the protagonist's world and the inciting disruption. Make it urgent. 2. STAKES (2-3 sentences): Raise the central conflict. What does the protagonist stand to lose? 3. TENSION (1-2 sentences): Hint at the impossible choice or escalating danger without revealing the resolution. 4. HOOK CLOSE (1 sentence): End with a tantalising question or statement that demands the reader open the book. RULES: - 150-200 words total. - DO NOT reveal the ending or resolution. - Match the genre's marketing tone ({meta.get('genre', 'fiction')}: e.g. thriller = urgent/terse, romance = emotionally charged, fantasy = epic/wondrous, horror = dread-laden). - Use present tense for the blurb voice. - No "Blurb:", no title prefix, no labels — marketing copy only. """ try: response = ai_models.model_writer.generate_content(prompt) utils.log_usage(folder, ai_models.model_writer.name, response.usage_metadata) blurb = response.text.strip() # Trim to 220 words if model overshot the 150-200 word target words = blurb.split() if len(words) > 220: blurb = " ".join(words[:220]) # End at the last sentence boundary within those 220 words for end_ch in ['.', '!', '?']: last_sent = blurb.rfind(end_ch) if last_sent > len(blurb) // 2: blurb = blurb[:last_sent + 1] break utils.log("MARKETING", f" -> Blurb trimmed to {len(blurb.split())} words.") with open(os.path.join(folder, "blurb.txt"), "w", encoding='utf-8') as f: f.write(blurb) with open(os.path.join(folder, "back_cover.txt"), "w", encoding='utf-8') as f: f.write(blurb) utils.log("MARKETING", f" -> Blurb: {len(blurb.split())} words.") except Exception as e: utils.log("MARKETING", f"Failed to generate blurb: {e}")