Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -43,10 +43,7 @@ def ensure_outputs_dir():
|
|
| 43 |
# REPLICATE IMAGE HELPERS (MODEL GRATIS + FALLBACK)
|
| 44 |
# ============================
|
| 45 |
def generate_image_flux(prompt: str) -> str:
|
| 46 |
-
"""
|
| 47 |
-
Generate image URL using Flux Schnell FREE on Replicate (for cover).
|
| 48 |
-
Jika gagal (kredit / model / jaringan), return "".
|
| 49 |
-
"""
|
| 50 |
try:
|
| 51 |
output = replicate.run(
|
| 52 |
"black-forest-labs/flux-schnell-free",
|
|
@@ -59,10 +56,7 @@ def generate_image_flux(prompt: str) -> str:
|
|
| 59 |
return ""
|
| 60 |
|
| 61 |
def generate_image_sdxl(prompt: str) -> str:
|
| 62 |
-
"""
|
| 63 |
-
Generate image URL using SDXL LITE (gratis) on Replicate (for chapter illustrations).
|
| 64 |
-
Jika gagal, return "".
|
| 65 |
-
"""
|
| 66 |
try:
|
| 67 |
output = replicate.run(
|
| 68 |
"stability-ai/sdxl-lite",
|
|
@@ -75,10 +69,7 @@ def generate_image_sdxl(prompt: str) -> str:
|
|
| 75 |
return ""
|
| 76 |
|
| 77 |
def download_image(url: str, filename: str) -> str:
|
| 78 |
-
"""
|
| 79 |
-
Download image from URL to local file.
|
| 80 |
-
Jika gagal, return "".
|
| 81 |
-
"""
|
| 82 |
if not url:
|
| 83 |
return ""
|
| 84 |
ensure_outputs_dir()
|
|
@@ -105,10 +96,12 @@ def generate_ebook_json(username, tier,
|
|
| 105 |
style = normalize(style, "Storybook style")
|
| 106 |
platform = normalize(platform, "KDP")
|
| 107 |
length = normalize(length, "Medium (30–50 pages)")
|
|
|
|
| 108 |
try:
|
| 109 |
chapters = int(chapters)
|
| 110 |
-
except
|
| 111 |
chapters = 5
|
|
|
|
| 112 |
chapters = max(1, min(MAX_CHAPTERS, chapters))
|
| 113 |
|
| 114 |
chapter_titles = [f"Bab {i}: Judul Bab {i}" for i in range(1, chapters + 1)]
|
|
@@ -125,107 +118,6 @@ def generate_ebook_json(username, tier,
|
|
| 125 |
"length": length,
|
| 126 |
"goal": goal,
|
| 127 |
"chapters": chapter_titles,
|
| 128 |
-
"prompts": {
|
| 129 |
-
"title_prompt": (
|
| 130 |
-
f"Generate 10 title ideas for an {genre} ebook about '{goal}'. "
|
| 131 |
-
f"Tone: {tone}. Style: {style}. Platform: {platform}. "
|
| 132 |
-
"Titles should be short, catchy, and suitable for children."
|
| 133 |
-
),
|
| 134 |
-
"outline_prompt": (
|
| 135 |
-
f"Create a detailed chapter outline for an {genre} ebook about '{goal}'. "
|
| 136 |
-
f"Tone: {tone}. Style: {style}. Length: {length}. "
|
| 137 |
-
f"Use {chapters} chapters. Include chapter titles and 2–3 bullet points per chapter."
|
| 138 |
-
),
|
| 139 |
-
"chapter_prompt": (
|
| 140 |
-
f"Write Chapter 1 of an {genre} ebook about '{goal}'. "
|
| 141 |
-
f"Tone: {tone}. Style: {style}. Length: {length}. "
|
| 142 |
-
"Use simple language suitable for children and include dialogues."
|
| 143 |
-
)
|
| 144 |
-
}
|
| 145 |
-
}
|
| 146 |
-
|
| 147 |
-
return json.dumps(data, ensure_ascii=False, indent=2)
|
| 148 |
-
|
| 149 |
-
# ============================
|
| 150 |
-
# IMAGE DESIGN JSON GENERATOR
|
| 151 |
-
# ============================
|
| 152 |
-
def generate_image_json(username, tier,
|
| 153 |
-
goal, design_style, color_palette,
|
| 154 |
-
platform, aspect_ratio):
|
| 155 |
-
|
| 156 |
-
goal = normalize(goal, "Islamic children's story cover about kindness.")
|
| 157 |
-
design_style = normalize(design_style, "Pastel Islamic")
|
| 158 |
-
color_palette = normalize(color_palette, "Pastel")
|
| 159 |
-
platform = normalize(platform, "Midjourney")
|
| 160 |
-
aspect_ratio = normalize(aspect_ratio, "1:1")
|
| 161 |
-
|
| 162 |
-
prompt = (
|
| 163 |
-
f"{goal}, {design_style} style, {color_palette} colors, "
|
| 164 |
-
f"high quality, aspect ratio {aspect_ratio}, soft lighting, kid-friendly, Islamic theme."
|
| 165 |
-
)
|
| 166 |
-
|
| 167 |
-
negative_prompt = (
|
| 168 |
-
"no text, no watermark, no logo, no distortion, no blur, no low quality, no violence."
|
| 169 |
-
)
|
| 170 |
-
|
| 171 |
-
data = {
|
| 172 |
-
"type": "image_design",
|
| 173 |
-
"generated_at": now_iso(),
|
| 174 |
-
"user": username,
|
| 175 |
-
"tier": tier,
|
| 176 |
-
"platform": platform,
|
| 177 |
-
"goal": goal,
|
| 178 |
-
"style": design_style,
|
| 179 |
-
"color_palette": color_palette,
|
| 180 |
-
"aspect_ratio": aspect_ratio,
|
| 181 |
-
"prompt": prompt,
|
| 182 |
-
"negative_prompt": negative_prompt
|
| 183 |
-
}
|
| 184 |
-
|
| 185 |
-
return json.dumps(data, ensure_ascii=False, indent=2)
|
| 186 |
-
|
| 187 |
-
# ============================
|
| 188 |
-
# VIDEO AI JSON GENERATOR
|
| 189 |
-
# ============================
|
| 190 |
-
def generate_video_json(username, tier,
|
| 191 |
-
goal, video_style, camera_movement,
|
| 192 |
-
platform, duration):
|
| 193 |
-
|
| 194 |
-
goal = normalize(goal, "Islamic children's story animation about honesty.")
|
| 195 |
-
video_style = normalize(video_style, "Pastel Kids Animation")
|
| 196 |
-
camera_movement = normalize(camera_movement, "Slow Zoom")
|
| 197 |
-
platform = normalize(platform, "Pika Labs")
|
| 198 |
-
duration = normalize(duration, "10 seconds")
|
| 199 |
-
|
| 200 |
-
scenes = [
|
| 201 |
-
{
|
| 202 |
-
"scene": 1,
|
| 203 |
-
"description": (
|
| 204 |
-
f"A child reading an Islamic storybook about '{goal}' in a cozy room, "
|
| 205 |
-
f"{video_style.lower()}, soft pastel colors, warm lighting."
|
| 206 |
-
),
|
| 207 |
-
"camera": camera_movement
|
| 208 |
-
},
|
| 209 |
-
{
|
| 210 |
-
"scene": 2,
|
| 211 |
-
"description": (
|
| 212 |
-
"Close-up of the book pages glowing softly, Arabic patterns in the background, "
|
| 213 |
-
"gentle motion, peaceful atmosphere."
|
| 214 |
-
),
|
| 215 |
-
"camera": "Static Shot"
|
| 216 |
-
}
|
| 217 |
-
]
|
| 218 |
-
|
| 219 |
-
data = {
|
| 220 |
-
"type": "video_ai",
|
| 221 |
-
"generated_at": now_iso(),
|
| 222 |
-
"user": username,
|
| 223 |
-
"tier": tier,
|
| 224 |
-
"platform": platform,
|
| 225 |
-
"style": video_style,
|
| 226 |
-
"duration": duration,
|
| 227 |
-
"goal": goal,
|
| 228 |
-
"scenes": scenes
|
| 229 |
}
|
| 230 |
|
| 231 |
return json.dumps(data, ensure_ascii=False, indent=2)
|
|
@@ -248,6 +140,7 @@ class EbookPDF(FPDF):
|
|
| 248 |
|
| 249 |
def build_ebook_pdf_with_images(username, goal, genre, tone, style, length,
|
| 250 |
cover_brief, chapters_count):
|
|
|
|
| 251 |
ensure_outputs_dir()
|
| 252 |
pdf = EbookPDF(format="A4")
|
| 253 |
pdf.set_auto_page_break(auto=True, margin=15)
|
|
@@ -257,53 +150,57 @@ def build_ebook_pdf_with_images(username, goal, genre, tone, style, length,
|
|
| 257 |
cover_url = generate_image_flux(cover_prompt)
|
| 258 |
cover_path = download_image(cover_url, f"cover_{uuid.uuid4().hex}.png")
|
| 259 |
|
| 260 |
-
# COVER PAGE
|
| 261 |
pdf.add_page()
|
|
|
|
| 262 |
if cover_path:
|
| 263 |
pdf.image(cover_path, x=0, y=0, w=210)
|
| 264 |
-
pdf.
|
| 265 |
else:
|
| 266 |
pdf.set_fill_color(230, 240, 255)
|
| 267 |
pdf.rect(0, 0, 210, 297, "F")
|
|
|
|
| 268 |
|
| 269 |
-
pdf.set_y(220)
|
| 270 |
pdf.set_font("Helvetica", "B", 20)
|
| 271 |
-
pdf.set_text_color(20, 20, 40)
|
| 272 |
pdf.multi_cell(0, 10, goal, 0, "L")
|
|
|
|
| 273 |
pdf.set_font("Helvetica", "", 12)
|
| 274 |
pdf.multi_cell(0, 7, f"Genre: {genre}", 0, "L")
|
| 275 |
pdf.multi_cell(0, 7, f"Tone: {tone} | Style: {style}", 0, "L")
|
| 276 |
|
| 277 |
-
# TABLE OF CONTENTS
|
| 278 |
pdf.add_page()
|
| 279 |
pdf.set_font("Helvetica", "B", 18)
|
| 280 |
pdf.cell(0, 10, "Daftar Isi", 0, 1)
|
| 281 |
pdf.ln(5)
|
| 282 |
-
pdf.set_font("Helvetica", "", 12)
|
| 283 |
|
|
|
|
| 284 |
chapters_count = max(1, min(MAX_CHAPTERS, int(chapters_count)))
|
| 285 |
chapters = [f"Bab {i}: Peristiwa Penting {i}" for i in range(1, chapters_count + 1)]
|
|
|
|
| 286 |
for i, ch in enumerate(chapters, start=1):
|
| 287 |
pdf.cell(0, 8, f"{i}. {ch}", 0, 1)
|
| 288 |
|
| 289 |
-
# BODY
|
| 290 |
body_paragraph = (
|
| 291 |
f"Cerita ini mengisahkan tentang {goal.lower()}, disampaikan dengan gaya {style.lower()} "
|
| 292 |
-
f"dan nuansa {tone.lower()}.
|
| 293 |
"mengandung nilai-nilai kebaikan, dan mengajak pembaca untuk merenungkan makna di balik setiap peristiwa."
|
| 294 |
)
|
| 295 |
|
| 296 |
-
# CHAPTERS
|
| 297 |
for idx, ch in enumerate(chapters, start=1):
|
| 298 |
pdf.add_page()
|
| 299 |
pdf.set_font("Helvetica", "B", 16)
|
| 300 |
pdf.cell(0, 10, ch, 0, 1)
|
| 301 |
pdf.ln(4)
|
| 302 |
|
|
|
|
| 303 |
illus_prompt = (
|
| 304 |
f"Illustration for chapter {idx} of an Islamic children's story about {goal}. "
|
| 305 |
f"Scene: {ch}. Style: {style}, tone {tone}, pastel colors, kid-friendly, Islamic."
|
| 306 |
)
|
|
|
|
| 307 |
illus_url = generate_image_sdxl(illus_prompt)
|
| 308 |
illus_path = download_image(illus_url, f"chapter_{idx}_{uuid.uuid4().hex}.png")
|
| 309 |
|
|
@@ -347,7 +244,7 @@ with gr.Blocks() as demo:
|
|
| 347 |
login_btn = gr.Button("Login")
|
| 348 |
login_msg = gr.Markdown("")
|
| 349 |
|
| 350 |
-
# MAIN UI
|
| 351 |
with gr.Group(visible=False) as main_ui:
|
| 352 |
|
| 353 |
gr.Markdown("# 🌟 AIPromptLab — JSON & PDF E-Book Generator (Replicate FREE Models)")
|
|
@@ -431,122 +328,13 @@ with gr.Blocks() as demo:
|
|
| 431 |
)
|
| 432 |
|
| 433 |
ebook_generate_btn = gr.Button("🚀 Generate E-book JSON")
|
| 434 |
-
ebook_output = gr.Textbox(label="E-book JSON Output", lines=18
|
| 435 |
ebook_copy_btn = gr.Button("Copy E-book JSON")
|
| 436 |
|
| 437 |
gr.Markdown("### 📄 Generate PDF E-book (A4 + Cover + Ilustrasi Bab)")
|
| 438 |
ebook_pdf_btn = gr.Button("Generate PDF E-book")
|
| 439 |
ebook_pdf_file = gr.File(label="Download E-book PDF")
|
| 440 |
|
| 441 |
-
# ============================
|
| 442 |
-
# TAB 2: IMAGE DESIGN
|
| 443 |
-
# ============================
|
| 444 |
-
with gr.Tab("Image Design Prompt"):
|
| 445 |
-
|
| 446 |
-
img_goal = gr.Textbox(label="Goal / Ide Desain Gambar")
|
| 447 |
-
img_style = gr.Dropdown(
|
| 448 |
-
label="Design Style",
|
| 449 |
-
choices=[
|
| 450 |
-
"Pastel Islamic",
|
| 451 |
-
"Flat Illustration",
|
| 452 |
-
"3D Render",
|
| 453 |
-
"Watercolor",
|
| 454 |
-
"Minimalist",
|
| 455 |
-
"Cartoon Kids",
|
| 456 |
-
"Realistic Photography"
|
| 457 |
-
],
|
| 458 |
-
value="Pastel Islamic"
|
| 459 |
-
)
|
| 460 |
-
img_palette = gr.Dropdown(
|
| 461 |
-
label="Color Palette",
|
| 462 |
-
choices=[
|
| 463 |
-
"Pastel",
|
| 464 |
-
"Vibrant",
|
| 465 |
-
"Monochrome",
|
| 466 |
-
"Earth Tone",
|
| 467 |
-
"Neon"
|
| 468 |
-
],
|
| 469 |
-
value="Pastel"
|
| 470 |
-
)
|
| 471 |
-
img_platform = gr.Dropdown(
|
| 472 |
-
label="Target Platform",
|
| 473 |
-
choices=[
|
| 474 |
-
"Midjourney",
|
| 475 |
-
"DALL·E",
|
| 476 |
-
"Stable Diffusion",
|
| 477 |
-
"Canva Design Brief"
|
| 478 |
-
],
|
| 479 |
-
value="Midjourney"
|
| 480 |
-
)
|
| 481 |
-
img_ratio = gr.Dropdown(
|
| 482 |
-
label="Aspect Ratio",
|
| 483 |
-
choices=[
|
| 484 |
-
"1:1",
|
| 485 |
-
"9:16",
|
| 486 |
-
"16:9",
|
| 487 |
-
"4:5"
|
| 488 |
-
],
|
| 489 |
-
value="1:1"
|
| 490 |
-
)
|
| 491 |
-
|
| 492 |
-
img_generate_btn = gr.Button("🚀 Generate Image Design JSON")
|
| 493 |
-
img_output = gr.Textbox(label="Image Design JSON Output", lines=18, elem_id="image-json-output")
|
| 494 |
-
img_copy_btn = gr.Button("Copy Image JSON")
|
| 495 |
-
|
| 496 |
-
# ============================
|
| 497 |
-
# TAB 3: VIDEO AI
|
| 498 |
-
# ============================
|
| 499 |
-
with gr.Tab("Video AI Prompt"):
|
| 500 |
-
|
| 501 |
-
vid_goal = gr.Textbox(label="Goal / Ide Video AI")
|
| 502 |
-
vid_style = gr.Dropdown(
|
| 503 |
-
label="Video Style",
|
| 504 |
-
choices=[
|
| 505 |
-
"Cinematic",
|
| 506 |
-
"Pastel Kids Animation",
|
| 507 |
-
"Anime",
|
| 508 |
-
"Realistic",
|
| 509 |
-
"Claymation",
|
| 510 |
-
"Watercolor Motion"
|
| 511 |
-
],
|
| 512 |
-
value="Pastel Kids Animation"
|
| 513 |
-
)
|
| 514 |
-
vid_camera = gr.Dropdown(
|
| 515 |
-
label="Camera Movement",
|
| 516 |
-
choices=[
|
| 517 |
-
"Slow Zoom",
|
| 518 |
-
"Pan Left",
|
| 519 |
-
"Pan Right",
|
| 520 |
-
"Dolly In",
|
| 521 |
-
"Static Shot"
|
| 522 |
-
],
|
| 523 |
-
value="Slow Zoom"
|
| 524 |
-
)
|
| 525 |
-
vid_platform = gr.Dropdown(
|
| 526 |
-
label="Target Platform",
|
| 527 |
-
choices=[
|
| 528 |
-
"Pika Labs",
|
| 529 |
-
"Runway Gen-2",
|
| 530 |
-
"Kaiber",
|
| 531 |
-
"Luma Dream Machine"
|
| 532 |
-
],
|
| 533 |
-
value="Pika Labs"
|
| 534 |
-
)
|
| 535 |
-
vid_duration = gr.Dropdown(
|
| 536 |
-
label="Duration",
|
| 537 |
-
choices=[
|
| 538 |
-
"5 seconds",
|
| 539 |
-
"10 seconds",
|
| 540 |
-
"15 seconds",
|
| 541 |
-
"30 seconds"
|
| 542 |
-
],
|
| 543 |
-
value="10 seconds"
|
| 544 |
-
)
|
| 545 |
-
|
| 546 |
-
vid_generate_btn = gr.Button("🚀 Generate Video AI JSON")
|
| 547 |
-
vid_output = gr.Textbox(label="Video AI JSON Output", lines=18, elem_id="video-json-output")
|
| 548 |
-
vid_copy_btn = gr.Button("Copy Video JSON")
|
| 549 |
-
|
| 550 |
# LOGIN LOGIC
|
| 551 |
def handle_login(username, password):
|
| 552 |
ok, tier, msg = login(username, password)
|
|
@@ -600,56 +388,6 @@ with gr.Blocks() as demo:
|
|
| 600 |
ebook_pdf_file
|
| 601 |
)
|
| 602 |
|
| 603 |
-
# IMAGE GENERATE
|
| 604 |
-
def handle_image_generate(login_status, login_user, login_tier,
|
| 605 |
-
goal, design_style, color_palette, platform, aspect_ratio):
|
| 606 |
-
if not login_status:
|
| 607 |
-
return "❌ Anda belum login."
|
| 608 |
-
return generate_image_json(login_user, login_tier, goal, design_style, color_palette, platform, aspect_ratio)
|
| 609 |
-
|
| 610 |
-
img_generate_btn.click(
|
| 611 |
-
handle_image_generate,
|
| 612 |
-
[login_status, login_user, login_tier,
|
| 613 |
-
img_goal, img_style, img_palette, img_platform, img_ratio],
|
| 614 |
-
img_output
|
| 615 |
-
)
|
| 616 |
-
|
| 617 |
-
# VIDEO GENERATE
|
| 618 |
-
def handle_video_generate(login_status, login_user, login_tier,
|
| 619 |
-
goal, video_style, camera_movement, platform, duration):
|
| 620 |
-
if not login_status:
|
| 621 |
-
return "❌ Anda belum login."
|
| 622 |
-
return generate_video_json(login_user, login_tier, goal, video_style, camera_movement, platform, duration)
|
| 623 |
-
|
| 624 |
-
vid_generate_btn.click(
|
| 625 |
-
handle_video_generate,
|
| 626 |
-
[login_status, login_user, login_tier,
|
| 627 |
-
vid_goal, vid_style, vid_camera, vid_platform, vid_duration],
|
| 628 |
-
vid_output
|
| 629 |
-
)
|
| 630 |
-
|
| 631 |
-
# COPY BUTTONS (JS)
|
| 632 |
-
ebook_copy_btn.click(
|
| 633 |
-
None,
|
| 634 |
-
None,
|
| 635 |
-
None,
|
| 636 |
-
js="navigator.clipboard.writeText(document.getElementById('ebook-json-output').value)"
|
| 637 |
-
)
|
| 638 |
-
|
| 639 |
-
img_copy_btn.click(
|
| 640 |
-
None,
|
| 641 |
-
None,
|
| 642 |
-
None,
|
| 643 |
-
js="navigator.clipboard.writeText(document.getElementById('image-json-output').value)"
|
| 644 |
-
)
|
| 645 |
-
|
| 646 |
-
vid_copy_btn.click(
|
| 647 |
-
None,
|
| 648 |
-
None,
|
| 649 |
-
None,
|
| 650 |
-
js="navigator.clipboard.writeText(document.getElementById('video-json-output').value)"
|
| 651 |
-
)
|
| 652 |
-
|
| 653 |
# LAUNCH
|
| 654 |
demo.launch(
|
| 655 |
server_name="0.0.0.0",
|
|
|
|
| 43 |
# REPLICATE IMAGE HELPERS (MODEL GRATIS + FALLBACK)
|
| 44 |
# ============================
|
| 45 |
def generate_image_flux(prompt: str) -> str:
|
| 46 |
+
"""Flux Schnell FREE (cover)"""
|
|
|
|
|
|
|
|
|
|
| 47 |
try:
|
| 48 |
output = replicate.run(
|
| 49 |
"black-forest-labs/flux-schnell-free",
|
|
|
|
| 56 |
return ""
|
| 57 |
|
| 58 |
def generate_image_sdxl(prompt: str) -> str:
|
| 59 |
+
"""SDXL Lite FREE (chapter illustrations)"""
|
|
|
|
|
|
|
|
|
|
| 60 |
try:
|
| 61 |
output = replicate.run(
|
| 62 |
"stability-ai/sdxl-lite",
|
|
|
|
| 69 |
return ""
|
| 70 |
|
| 71 |
def download_image(url: str, filename: str) -> str:
|
| 72 |
+
"""Download image safely"""
|
|
|
|
|
|
|
|
|
|
| 73 |
if not url:
|
| 74 |
return ""
|
| 75 |
ensure_outputs_dir()
|
|
|
|
| 96 |
style = normalize(style, "Storybook style")
|
| 97 |
platform = normalize(platform, "KDP")
|
| 98 |
length = normalize(length, "Medium (30–50 pages)")
|
| 99 |
+
|
| 100 |
try:
|
| 101 |
chapters = int(chapters)
|
| 102 |
+
except:
|
| 103 |
chapters = 5
|
| 104 |
+
|
| 105 |
chapters = max(1, min(MAX_CHAPTERS, chapters))
|
| 106 |
|
| 107 |
chapter_titles = [f"Bab {i}: Judul Bab {i}" for i in range(1, chapters + 1)]
|
|
|
|
| 118 |
"length": length,
|
| 119 |
"goal": goal,
|
| 120 |
"chapters": chapter_titles,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
}
|
| 122 |
|
| 123 |
return json.dumps(data, ensure_ascii=False, indent=2)
|
|
|
|
| 140 |
|
| 141 |
def build_ebook_pdf_with_images(username, goal, genre, tone, style, length,
|
| 142 |
cover_brief, chapters_count):
|
| 143 |
+
|
| 144 |
ensure_outputs_dir()
|
| 145 |
pdf = EbookPDF(format="A4")
|
| 146 |
pdf.set_auto_page_break(auto=True, margin=15)
|
|
|
|
| 150 |
cover_url = generate_image_flux(cover_prompt)
|
| 151 |
cover_path = download_image(cover_url, f"cover_{uuid.uuid4().hex}.png")
|
| 152 |
|
| 153 |
+
# ===== COVER PAGE =====
|
| 154 |
pdf.add_page()
|
| 155 |
+
|
| 156 |
if cover_path:
|
| 157 |
pdf.image(cover_path, x=0, y=0, w=210)
|
| 158 |
+
pdf.set_y(210) # aman
|
| 159 |
else:
|
| 160 |
pdf.set_fill_color(230, 240, 255)
|
| 161 |
pdf.rect(0, 0, 210, 297, "F")
|
| 162 |
+
pdf.set_y(50)
|
| 163 |
|
|
|
|
| 164 |
pdf.set_font("Helvetica", "B", 20)
|
|
|
|
| 165 |
pdf.multi_cell(0, 10, goal, 0, "L")
|
| 166 |
+
|
| 167 |
pdf.set_font("Helvetica", "", 12)
|
| 168 |
pdf.multi_cell(0, 7, f"Genre: {genre}", 0, "L")
|
| 169 |
pdf.multi_cell(0, 7, f"Tone: {tone} | Style: {style}", 0, "L")
|
| 170 |
|
| 171 |
+
# ===== TABLE OF CONTENTS =====
|
| 172 |
pdf.add_page()
|
| 173 |
pdf.set_font("Helvetica", "B", 18)
|
| 174 |
pdf.cell(0, 10, "Daftar Isi", 0, 1)
|
| 175 |
pdf.ln(5)
|
|
|
|
| 176 |
|
| 177 |
+
pdf.set_font("Helvetica", "", 12)
|
| 178 |
chapters_count = max(1, min(MAX_CHAPTERS, int(chapters_count)))
|
| 179 |
chapters = [f"Bab {i}: Peristiwa Penting {i}" for i in range(1, chapters_count + 1)]
|
| 180 |
+
|
| 181 |
for i, ch in enumerate(chapters, start=1):
|
| 182 |
pdf.cell(0, 8, f"{i}. {ch}", 0, 1)
|
| 183 |
|
| 184 |
+
# ===== BODY TEMPLATE =====
|
| 185 |
body_paragraph = (
|
| 186 |
f"Cerita ini mengisahkan tentang {goal.lower()}, disampaikan dengan gaya {style.lower()} "
|
| 187 |
+
f"dan nuansa {tone.lower()}. Cerita dirancang agar mudah dipahami anak-anak, "
|
| 188 |
"mengandung nilai-nilai kebaikan, dan mengajak pembaca untuk merenungkan makna di balik setiap peristiwa."
|
| 189 |
)
|
| 190 |
|
| 191 |
+
# ===== CHAPTERS =====
|
| 192 |
for idx, ch in enumerate(chapters, start=1):
|
| 193 |
pdf.add_page()
|
| 194 |
pdf.set_font("Helvetica", "B", 16)
|
| 195 |
pdf.cell(0, 10, ch, 0, 1)
|
| 196 |
pdf.ln(4)
|
| 197 |
|
| 198 |
+
# ===== ILLUSTRATION =====
|
| 199 |
illus_prompt = (
|
| 200 |
f"Illustration for chapter {idx} of an Islamic children's story about {goal}. "
|
| 201 |
f"Scene: {ch}. Style: {style}, tone {tone}, pastel colors, kid-friendly, Islamic."
|
| 202 |
)
|
| 203 |
+
|
| 204 |
illus_url = generate_image_sdxl(illus_prompt)
|
| 205 |
illus_path = download_image(illus_url, f"chapter_{idx}_{uuid.uuid4().hex}.png")
|
| 206 |
|
|
|
|
| 244 |
login_btn = gr.Button("Login")
|
| 245 |
login_msg = gr.Markdown("")
|
| 246 |
|
| 247 |
+
# MAIN UI
|
| 248 |
with gr.Group(visible=False) as main_ui:
|
| 249 |
|
| 250 |
gr.Markdown("# 🌟 AIPromptLab — JSON & PDF E-Book Generator (Replicate FREE Models)")
|
|
|
|
| 328 |
)
|
| 329 |
|
| 330 |
ebook_generate_btn = gr.Button("🚀 Generate E-book JSON")
|
| 331 |
+
ebook_output = gr.Textbox(label="E-book JSON Output", lines=18)
|
| 332 |
ebook_copy_btn = gr.Button("Copy E-book JSON")
|
| 333 |
|
| 334 |
gr.Markdown("### 📄 Generate PDF E-book (A4 + Cover + Ilustrasi Bab)")
|
| 335 |
ebook_pdf_btn = gr.Button("Generate PDF E-book")
|
| 336 |
ebook_pdf_file = gr.File(label="Download E-book PDF")
|
| 337 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 338 |
# LOGIN LOGIC
|
| 339 |
def handle_login(username, password):
|
| 340 |
ok, tier, msg = login(username, password)
|
|
|
|
| 388 |
ebook_pdf_file
|
| 389 |
)
|
| 390 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 391 |
# LAUNCH
|
| 392 |
demo.launch(
|
| 393 |
server_name="0.0.0.0",
|