Commit ·
fd65e8f
1
Parent(s): 59cf51d
Simplify Story Reels: topic + image_style + voice only (remove character profile)
Browse files
modules/story_reels/router.py
CHANGED
|
@@ -49,8 +49,7 @@ async def generate_video(request: GenerateVideoRequest):
|
|
| 49 |
|
| 50 |
job_id = story_creator.add_to_queue(
|
| 51 |
topic=request.topic,
|
| 52 |
-
|
| 53 |
-
character_profile=request.character_profile,
|
| 54 |
voice=request.voice
|
| 55 |
)
|
| 56 |
|
|
|
|
| 49 |
|
| 50 |
job_id = story_creator.add_to_queue(
|
| 51 |
topic=request.topic,
|
| 52 |
+
image_style=request.image_style.value,
|
|
|
|
| 53 |
voice=request.voice
|
| 54 |
)
|
| 55 |
|
modules/story_reels/schemas.py
CHANGED
|
@@ -80,13 +80,9 @@ class GeneratedScene(BaseModel):
|
|
| 80 |
# ===================
|
| 81 |
|
| 82 |
class GenerateVideoRequest(BaseModel):
|
| 83 |
-
"""Main video generation request"""
|
| 84 |
-
topic: str = Field(..., description="Video topic/title")
|
| 85 |
-
|
| 86 |
-
character_profile: Optional[CharacterProfile] = Field(
|
| 87 |
-
default=None,
|
| 88 |
-
description="Character profile for consistency (optional)"
|
| 89 |
-
)
|
| 90 |
voice: str = Field("af_heart", description="TTS voice")
|
| 91 |
|
| 92 |
|
|
|
|
| 80 |
# ===================
|
| 81 |
|
| 82 |
class GenerateVideoRequest(BaseModel):
|
| 83 |
+
"""Main video generation request - simplified"""
|
| 84 |
+
topic: str = Field(..., description="Video topic/title (include any specific requirements here)")
|
| 85 |
+
image_style: StyleEnum = Field(StyleEnum.semi_realistic, description="Image generation style")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
voice: str = Field("af_heart", description="TTS voice")
|
| 87 |
|
| 88 |
|
modules/story_reels/services/script_generator.py
CHANGED
|
@@ -173,7 +173,7 @@ Return ONLY valid JSON array, no markdown, no explanation:
|
|
| 173 |
self,
|
| 174 |
full_script: str,
|
| 175 |
chunks: list,
|
| 176 |
-
|
| 177 |
max_batch: int = 30
|
| 178 |
) -> list:
|
| 179 |
"""
|
|
@@ -192,18 +192,8 @@ Return ONLY valid JSON array, no markdown, no explanation:
|
|
| 192 |
user_prompt = f"""FULL STORY SCRIPT:
|
| 193 |
{full_script}
|
| 194 |
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
user_prompt += f"""CHARACTER PROFILE:
|
| 198 |
-
- Name: {character_profile.get('name', 'Main character')}
|
| 199 |
-
- Age: {character_profile.get('age', '25')}
|
| 200 |
-
- Gender: {character_profile.get('gender', 'male')}
|
| 201 |
-
- Hair: {character_profile.get('hair', 'short black hair')}
|
| 202 |
-
- Skin: {character_profile.get('skin', 'light skin')}
|
| 203 |
-
- Clothes: {character_profile.get('clothes', 'casual clothes')}
|
| 204 |
-
- Style: {character_profile.get('style', 'semi-realistic')}
|
| 205 |
-
|
| 206 |
-
IMPORTANT: Include this character description in EVERY prompt!
|
| 207 |
|
| 208 |
"""
|
| 209 |
|
|
|
|
| 173 |
self,
|
| 174 |
full_script: str,
|
| 175 |
chunks: list,
|
| 176 |
+
image_style: str = "semi-realistic",
|
| 177 |
max_batch: int = 30
|
| 178 |
) -> list:
|
| 179 |
"""
|
|
|
|
| 192 |
user_prompt = f"""FULL STORY SCRIPT:
|
| 193 |
{full_script}
|
| 194 |
|
| 195 |
+
IMAGE STYLE: {image_style}
|
| 196 |
+
(Apply this style consistently to ALL images: {image_style}, high quality, detailed, cinematic lighting)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
"""
|
| 199 |
|
modules/story_reels/services/story_creator.py
CHANGED
|
@@ -11,7 +11,6 @@ from typing import Dict, List, Optional
|
|
| 11 |
from datetime import datetime
|
| 12 |
|
| 13 |
from ..schemas import (
|
| 14 |
-
CharacterProfile,
|
| 15 |
SceneInput,
|
| 16 |
JobStatus,
|
| 17 |
GeneratedScene
|
|
@@ -60,8 +59,7 @@ class StoryCreator:
|
|
| 60 |
def add_to_queue(
|
| 61 |
self,
|
| 62 |
topic: str,
|
| 63 |
-
|
| 64 |
-
character_profile: Optional[CharacterProfile] = None,
|
| 65 |
voice: str = "af_heart"
|
| 66 |
) -> str:
|
| 67 |
"""
|
|
@@ -75,8 +73,7 @@ class StoryCreator:
|
|
| 75 |
job = {
|
| 76 |
"id": job_id,
|
| 77 |
"topic": topic,
|
| 78 |
-
"
|
| 79 |
-
"character": character_profile,
|
| 80 |
"voice": voice,
|
| 81 |
"status": JobStatus.queued,
|
| 82 |
"progress": 0,
|
|
@@ -210,25 +207,12 @@ class StoryCreator:
|
|
| 210 |
job["status"] = JobStatus.generating_images
|
| 211 |
logger.info(f"[{job_id}] Generating AI-powered image prompts...")
|
| 212 |
|
| 213 |
-
#
|
| 214 |
-
|
| 215 |
-
if job["character"]:
|
| 216 |
-
char_dict = {
|
| 217 |
-
"name": job["character"].name,
|
| 218 |
-
"age": job["character"].age,
|
| 219 |
-
"gender": job["character"].gender,
|
| 220 |
-
"hair": job["character"].hair,
|
| 221 |
-
"skin": job["character"].skin,
|
| 222 |
-
"clothes": job["character"].clothes,
|
| 223 |
-
"style": job["character"].style.value if hasattr(job["character"].style, 'value') else str(job["character"].style)
|
| 224 |
-
}
|
| 225 |
-
|
| 226 |
-
# Generate all image prompts at once using Gemini
|
| 227 |
-
# Input: Full script (context) + 2s chunks → Output: JSON array of prompts
|
| 228 |
ai_prompts = self.script_gen.generate_image_prompts(
|
| 229 |
full_script=script,
|
| 230 |
chunks=image_chunks,
|
| 231 |
-
|
| 232 |
)
|
| 233 |
|
| 234 |
logger.info(f"[{job_id}] AI generated {len(ai_prompts)} image prompts")
|
|
@@ -241,7 +225,7 @@ class StoryCreator:
|
|
| 241 |
# If both APIs available: split images 50/50 for 2x speed
|
| 242 |
# 1 second delay between each request (rate limit safe)
|
| 243 |
|
| 244 |
-
seed =
|
| 245 |
|
| 246 |
# Build prompts list from AI-generated prompts
|
| 247 |
prompts_list = []
|
|
|
|
| 11 |
from datetime import datetime
|
| 12 |
|
| 13 |
from ..schemas import (
|
|
|
|
| 14 |
SceneInput,
|
| 15 |
JobStatus,
|
| 16 |
GeneratedScene
|
|
|
|
| 59 |
def add_to_queue(
|
| 60 |
self,
|
| 61 |
topic: str,
|
| 62 |
+
image_style: str = "semi-realistic",
|
|
|
|
| 63 |
voice: str = "af_heart"
|
| 64 |
) -> str:
|
| 65 |
"""
|
|
|
|
| 73 |
job = {
|
| 74 |
"id": job_id,
|
| 75 |
"topic": topic,
|
| 76 |
+
"image_style": image_style,
|
|
|
|
| 77 |
"voice": voice,
|
| 78 |
"status": JobStatus.queued,
|
| 79 |
"progress": 0,
|
|
|
|
| 207 |
job["status"] = JobStatus.generating_images
|
| 208 |
logger.info(f"[{job_id}] Generating AI-powered image prompts...")
|
| 209 |
|
| 210 |
+
# Generate all image prompts at once using AI
|
| 211 |
+
# Input: Full script (context) + 2s chunks + image_style → Output: JSON array of prompts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
ai_prompts = self.script_gen.generate_image_prompts(
|
| 213 |
full_script=script,
|
| 214 |
chunks=image_chunks,
|
| 215 |
+
image_style=job["image_style"]
|
| 216 |
)
|
| 217 |
|
| 218 |
logger.info(f"[{job_id}] AI generated {len(ai_prompts)} image prompts")
|
|
|
|
| 225 |
# If both APIs available: split images 50/50 for 2x speed
|
| 226 |
# 1 second delay between each request (rate limit safe)
|
| 227 |
|
| 228 |
+
seed = 432891 # Fixed seed for consistency
|
| 229 |
|
| 230 |
# Build prompts list from AI-generated prompts
|
| 231 |
prompts_list = []
|
static/index.html
CHANGED
|
@@ -283,84 +283,35 @@
|
|
| 283 |
<form id="storyForm">
|
| 284 |
<div class="form-group">
|
| 285 |
<label>Topic / Idea *</label>
|
| 286 |
-
<
|
| 287 |
-
|
|
|
|
|
|
|
|
|
|
| 288 |
</div>
|
| 289 |
|
| 290 |
-
<div class="form-
|
| 291 |
-
<label>Script (optional - AI will generate if empty)</label>
|
| 292 |
-
<textarea id="storyScript"
|
| 293 |
-
placeholder="Leave empty for AI-generated script, or write your own..."></textarea>
|
| 294 |
-
</div>
|
| 295 |
-
|
| 296 |
-
<!-- Character Profile -->
|
| 297 |
-
<div class="character-section">
|
| 298 |
-
<h3>👤 Character Profile (for consistency)</h3>
|
| 299 |
-
|
| 300 |
-
<div class="form-row">
|
| 301 |
-
<div class="form-group">
|
| 302 |
-
<label>Name</label>
|
| 303 |
-
<input type="text" id="charName" placeholder="Rafi" value="Rafi">
|
| 304 |
-
</div>
|
| 305 |
-
<div class="form-group">
|
| 306 |
-
<label>Age</label>
|
| 307 |
-
<input type="text" id="charAge" placeholder="16" value="16">
|
| 308 |
-
</div>
|
| 309 |
-
</div>
|
| 310 |
-
|
| 311 |
-
<div class="form-row">
|
| 312 |
-
<div class="form-group">
|
| 313 |
-
<label>Gender</label>
|
| 314 |
-
<select id="charGender">
|
| 315 |
-
<option value="male">Male</option>
|
| 316 |
-
<option value="female">Female</option>
|
| 317 |
-
</select>
|
| 318 |
-
</div>
|
| 319 |
-
<div class="form-group">
|
| 320 |
-
<label>Style</label>
|
| 321 |
-
<select id="charStyle">
|
| 322 |
-
<option value="semi-realistic">Semi-Realistic</option>
|
| 323 |
-
<option value="anime">Anime</option>
|
| 324 |
-
<option value="cartoon">Cartoon</option>
|
| 325 |
-
<option value="realistic">Realistic</option>
|
| 326 |
-
</select>
|
| 327 |
-
</div>
|
| 328 |
-
</div>
|
| 329 |
-
|
| 330 |
-
<div class="form-row">
|
| 331 |
-
<div class="form-group">
|
| 332 |
-
<label>Hair</label>
|
| 333 |
-
<input type="text" id="charHair" placeholder="short black hair"
|
| 334 |
-
value="short black hair">
|
| 335 |
-
</div>
|
| 336 |
-
<div class="form-group">
|
| 337 |
-
<label>Skin</label>
|
| 338 |
-
<input type="text" id="charSkin" placeholder="light brown" value="light brown">
|
| 339 |
-
</div>
|
| 340 |
-
</div>
|
| 341 |
-
|
| 342 |
<div class="form-group">
|
| 343 |
-
<label>
|
| 344 |
-
<
|
| 345 |
-
value="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
</div>
|
| 347 |
-
|
| 348 |
<div class="form-group">
|
| 349 |
-
<label>
|
| 350 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
</div>
|
| 352 |
</div>
|
| 353 |
|
| 354 |
-
<div class="form-group">
|
| 355 |
-
<label>Voice</label>
|
| 356 |
-
<select id="storyVoice">
|
| 357 |
-
<option value="af_heart">Female - Heart</option>
|
| 358 |
-
<option value="am_adam">Male - Adam</option>
|
| 359 |
-
<option value="af_bella">Female - Bella</option>
|
| 360 |
-
<option value="am_michael">Male - Michael</option>
|
| 361 |
-
</select>
|
| 362 |
-
</div>
|
| 363 |
-
|
| 364 |
<button type="submit" class="btn btn-primary">🚀 Generate Story Reel</button>
|
| 365 |
</form>
|
| 366 |
|
|
@@ -439,18 +390,8 @@
|
|
| 439 |
|
| 440 |
const data = {
|
| 441 |
topic: document.getElementById('storyTopic').value,
|
| 442 |
-
|
| 443 |
-
voice: document.getElementById('storyVoice').value
|
| 444 |
-
character_profile: {
|
| 445 |
-
name: document.getElementById('charName').value,
|
| 446 |
-
age: document.getElementById('charAge').value,
|
| 447 |
-
gender: document.getElementById('charGender').value,
|
| 448 |
-
style: document.getElementById('charStyle').value,
|
| 449 |
-
hair: document.getElementById('charHair').value,
|
| 450 |
-
skin: document.getElementById('charSkin').value,
|
| 451 |
-
clothes: document.getElementById('charClothes').value,
|
| 452 |
-
seed: parseInt(document.getElementById('charSeed').value)
|
| 453 |
-
}
|
| 454 |
};
|
| 455 |
|
| 456 |
try {
|
|
|
|
| 283 |
<form id="storyForm">
|
| 284 |
<div class="form-group">
|
| 285 |
<label>Topic / Idea *</label>
|
| 286 |
+
<textarea id="storyTopic" rows="3"
|
| 287 |
+
placeholder="e.g., A 16-year-old boy named Rafi going to school on his first day. He has short black hair, light brown skin, wearing casual clothes."
|
| 288 |
+
required></textarea>
|
| 289 |
+
<small style="color: var(--text-secondary);">Include character details, setting, mood -
|
| 290 |
+
everything in the topic!</small>
|
| 291 |
</div>
|
| 292 |
|
| 293 |
+
<div class="form-row">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
<div class="form-group">
|
| 295 |
+
<label>Image Style</label>
|
| 296 |
+
<select id="storyStyle">
|
| 297 |
+
<option value="semi-realistic">Semi-Realistic</option>
|
| 298 |
+
<option value="anime">Anime</option>
|
| 299 |
+
<option value="cartoon">Cartoon</option>
|
| 300 |
+
<option value="realistic">Realistic</option>
|
| 301 |
+
<option value="watercolor">Watercolor</option>
|
| 302 |
+
</select>
|
| 303 |
</div>
|
|
|
|
| 304 |
<div class="form-group">
|
| 305 |
+
<label>Voice</label>
|
| 306 |
+
<select id="storyVoice">
|
| 307 |
+
<option value="af_heart">Female - Heart</option>
|
| 308 |
+
<option value="am_adam">Male - Adam</option>
|
| 309 |
+
<option value="af_bella">Female - Bella</option>
|
| 310 |
+
<option value="am_michael">Male - Michael</option>
|
| 311 |
+
</select>
|
| 312 |
</div>
|
| 313 |
</div>
|
| 314 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
<button type="submit" class="btn btn-primary">🚀 Generate Story Reel</button>
|
| 316 |
</form>
|
| 317 |
|
|
|
|
| 390 |
|
| 391 |
const data = {
|
| 392 |
topic: document.getElementById('storyTopic').value,
|
| 393 |
+
image_style: document.getElementById('storyStyle').value,
|
| 394 |
+
voice: document.getElementById('storyVoice').value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
};
|
| 396 |
|
| 397 |
try {
|