Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -85,6 +85,11 @@ current_model_name = None
|
|
| 85 |
current_pipe = None
|
| 86 |
model_lock = threading.Lock()
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
def load_model(model_name="dreamshaper-8"):
|
| 89 |
"""Thread-safe model loading with HIGH-QUALITY settings"""
|
| 90 |
global model_cache, current_model_name, current_pipe
|
|
@@ -202,8 +207,35 @@ def generate_high_quality_image(prompt, model_choice="dreamshaper-8", style="chi
|
|
| 202 |
print(f"β HQ Generation failed: {str(e)}")
|
| 203 |
raise
|
| 204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
def generate_test_image(prompt, model_choice, style_choice):
|
| 206 |
-
"""Generate a single image for testing
|
| 207 |
try:
|
| 208 |
if not prompt.strip():
|
| 209 |
return None, "β Please enter a prompt"
|
|
@@ -221,26 +253,28 @@ def generate_test_image(prompt, model_choice, style_choice):
|
|
| 221 |
negative_prompt
|
| 222 |
)
|
| 223 |
|
| 224 |
-
#
|
| 225 |
-
|
| 226 |
-
safe_prompt = "".join(c for c in prompt[:30] if c.isalnum() or c in (' ', '-', '_')).rstrip()
|
| 227 |
-
filename = f"test_{safe_prompt}_{timestamp}.png"
|
| 228 |
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
print(f"β οΈ Could not save temporarily: {e}")
|
| 236 |
|
| 237 |
status_msg = f"""β
Success! Generated: {prompt}
|
| 238 |
|
| 239 |
-
|
| 240 |
|
| 241 |
π¨ Enhanced prompt: {enhanced_prompt}
|
| 242 |
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
|
| 245 |
return image, status_msg
|
| 246 |
|
|
@@ -341,11 +375,38 @@ async def get_job_status_endpoint(job_id: str):
|
|
| 341 |
updated_at=job_data["updated_at"]
|
| 342 |
)
|
| 343 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 344 |
# GRADIO INTERFACE
|
| 345 |
def create_test_interface():
|
| 346 |
with gr.Blocks(title="High-Quality Image Generator", theme="soft") as demo:
|
| 347 |
gr.Markdown("# π¨ High-Quality Image Generator")
|
| 348 |
-
gr.Markdown("**Generate studio-quality images
|
| 349 |
|
| 350 |
with gr.Row():
|
| 351 |
with gr.Column(scale=1):
|
|
@@ -369,11 +430,12 @@ def create_test_interface():
|
|
| 369 |
|
| 370 |
generate_btn = gr.Button("β¨ Generate Image", variant="primary", size="lg")
|
| 371 |
|
| 372 |
-
gr.Markdown("### π‘
|
| 373 |
-
gr.Markdown("""
|
| 374 |
-
|
| 375 |
-
-
|
| 376 |
-
-
|
|
|
|
| 377 |
""")
|
| 378 |
|
| 379 |
with gr.Column(scale=2):
|
|
@@ -384,8 +446,8 @@ def create_test_interface():
|
|
| 384 |
show_share_button=True
|
| 385 |
)
|
| 386 |
status_output = gr.Textbox(
|
| 387 |
-
label="Status",
|
| 388 |
-
lines=
|
| 389 |
show_copy_button=True
|
| 390 |
)
|
| 391 |
|
|
@@ -404,10 +466,42 @@ def create_test_interface():
|
|
| 404 |
outputs=prompt_input
|
| 405 |
)
|
| 406 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
generate_btn.click(
|
| 408 |
fn=generate_test_image,
|
| 409 |
inputs=[prompt_input, model_dropdown, style_dropdown],
|
| 410 |
outputs=[image_output, status_output]
|
|
|
|
|
|
|
|
|
|
| 411 |
)
|
| 412 |
|
| 413 |
return demo
|
|
@@ -419,10 +513,32 @@ gradio_app = create_test_interface()
|
|
| 419 |
async def root():
|
| 420 |
return {"message": "Storybook Generator API + UI is running!"}
|
| 421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
# For Hugging Face Spaces
|
| 423 |
def get_app():
|
| 424 |
return app
|
| 425 |
|
| 426 |
if __name__ == "__main__":
|
| 427 |
print("π Starting High-Quality Storybook Generator...")
|
|
|
|
|
|
|
| 428 |
gradio_app.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 85 |
current_pipe = None
|
| 86 |
model_lock = threading.Lock()
|
| 87 |
|
| 88 |
+
# Create persistent directory for test images
|
| 89 |
+
PERSISTENT_IMAGE_DIR = "generated_test_images"
|
| 90 |
+
os.makedirs(PERSISTENT_IMAGE_DIR, exist_ok=True)
|
| 91 |
+
print(f"π Created persistent image directory: {PERSISTENT_IMAGE_DIR}")
|
| 92 |
+
|
| 93 |
def load_model(model_name="dreamshaper-8"):
|
| 94 |
"""Thread-safe model loading with HIGH-QUALITY settings"""
|
| 95 |
global model_cache, current_model_name, current_pipe
|
|
|
|
| 207 |
print(f"β HQ Generation failed: {str(e)}")
|
| 208 |
raise
|
| 209 |
|
| 210 |
+
def save_image_to_persistent_storage(image, prompt, subfolder=""):
|
| 211 |
+
"""Save image to persistent storage that appears in Files tab"""
|
| 212 |
+
try:
|
| 213 |
+
# Create filename
|
| 214 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 215 |
+
safe_prompt = "".join(c for c in prompt[:50] if c.isalnum() or c in (' ', '-', '_')).rstrip()
|
| 216 |
+
filename = f"image_{safe_prompt}_{timestamp}.png"
|
| 217 |
+
|
| 218 |
+
# Create subfolder if specified
|
| 219 |
+
if subfolder:
|
| 220 |
+
save_dir = os.path.join(PERSISTENT_IMAGE_DIR, subfolder)
|
| 221 |
+
os.makedirs(save_dir, exist_ok=True)
|
| 222 |
+
filepath = os.path.join(save_dir, filename)
|
| 223 |
+
else:
|
| 224 |
+
filepath = os.path.join(PERSISTENT_IMAGE_DIR, filename)
|
| 225 |
+
|
| 226 |
+
# Save the image
|
| 227 |
+
image.save(filepath)
|
| 228 |
+
print(f"πΎ Image saved to persistent storage: {filepath}")
|
| 229 |
+
|
| 230 |
+
# Return relative path for display
|
| 231 |
+
return filepath
|
| 232 |
+
|
| 233 |
+
except Exception as e:
|
| 234 |
+
print(f"β Failed to save to persistent storage: {e}")
|
| 235 |
+
return None
|
| 236 |
+
|
| 237 |
def generate_test_image(prompt, model_choice, style_choice):
|
| 238 |
+
"""Generate a single image for testing and save to persistent storage"""
|
| 239 |
try:
|
| 240 |
if not prompt.strip():
|
| 241 |
return None, "β Please enter a prompt"
|
|
|
|
| 253 |
negative_prompt
|
| 254 |
)
|
| 255 |
|
| 256 |
+
# Save to persistent storage with style-based subfolder
|
| 257 |
+
saved_path = save_image_to_persistent_storage(image, prompt, style_choice)
|
|
|
|
|
|
|
| 258 |
|
| 259 |
+
if saved_path:
|
| 260 |
+
# Get just the filename for display
|
| 261 |
+
filename = os.path.basename(saved_path)
|
| 262 |
+
save_info = f"πΎ **Auto-saved to Files tab:** `{PERSISTENT_IMAGE_DIR}/{style_choice}/{filename}`"
|
| 263 |
+
else:
|
| 264 |
+
save_info = "β οΈ Could not auto-save to persistent storage"
|
|
|
|
| 265 |
|
| 266 |
status_msg = f"""β
Success! Generated: {prompt}
|
| 267 |
|
| 268 |
+
{save_info}
|
| 269 |
|
| 270 |
π¨ Enhanced prompt: {enhanced_prompt}
|
| 271 |
|
| 272 |
+
π **Location in Files tab:**
|
| 273 |
+
- Navigate to: `{PERSISTENT_IMAGE_DIR}/`
|
| 274 |
+
- Then go to: `{style_choice}/` folder
|
| 275 |
+
- Find your image: `{os.path.basename(saved_path) if saved_path else 'filename'}`
|
| 276 |
+
|
| 277 |
+
π‘ You can also use the download button below the image!"""
|
| 278 |
|
| 279 |
return image, status_msg
|
| 280 |
|
|
|
|
| 375 |
updated_at=job_data["updated_at"]
|
| 376 |
)
|
| 377 |
|
| 378 |
+
@app.get("/api/list-test-images")
|
| 379 |
+
async def list_test_images():
|
| 380 |
+
"""API endpoint to list all generated test images"""
|
| 381 |
+
try:
|
| 382 |
+
images_list = []
|
| 383 |
+
for root, dirs, files in os.walk(PERSISTENT_IMAGE_DIR):
|
| 384 |
+
for file in files:
|
| 385 |
+
if file.endswith(('.png', '.jpg', '.jpeg')):
|
| 386 |
+
full_path = os.path.join(root, file)
|
| 387 |
+
rel_path = os.path.relpath(full_path, PERSISTENT_IMAGE_DIR)
|
| 388 |
+
images_list.append({
|
| 389 |
+
"filename": file,
|
| 390 |
+
"path": rel_path,
|
| 391 |
+
"full_path": full_path,
|
| 392 |
+
"size": os.path.getsize(full_path) if os.path.exists(full_path) else 0,
|
| 393 |
+
"created": os.path.getctime(full_path) if os.path.exists(full_path) else 0
|
| 394 |
+
})
|
| 395 |
+
|
| 396 |
+
return {
|
| 397 |
+
"status": "success",
|
| 398 |
+
"image_count": len(images_list),
|
| 399 |
+
"image_directory": PERSISTENT_IMAGE_DIR,
|
| 400 |
+
"images": sorted(images_list, key=lambda x: x["created"], reverse=True)
|
| 401 |
+
}
|
| 402 |
+
except Exception as e:
|
| 403 |
+
return {"status": "error", "message": str(e)}
|
| 404 |
+
|
| 405 |
# GRADIO INTERFACE
|
| 406 |
def create_test_interface():
|
| 407 |
with gr.Blocks(title="High-Quality Image Generator", theme="soft") as demo:
|
| 408 |
gr.Markdown("# π¨ High-Quality Image Generator")
|
| 409 |
+
gr.Markdown("**Generate studio-quality images that auto-save to your Files tab!**")
|
| 410 |
|
| 411 |
with gr.Row():
|
| 412 |
with gr.Column(scale=1):
|
|
|
|
| 430 |
|
| 431 |
generate_btn = gr.Button("β¨ Generate Image", variant="primary", size="lg")
|
| 432 |
|
| 433 |
+
gr.Markdown("### π‘ Auto-Save Feature")
|
| 434 |
+
gr.Markdown(f"""
|
| 435 |
+
**All images are automatically saved to:**
|
| 436 |
+
- `{PERSISTENT_IMAGE_DIR}/[style]/` in your Files tab
|
| 437 |
+
- Organized by art style for easy browsing
|
| 438 |
+
- Also available via download button below
|
| 439 |
""")
|
| 440 |
|
| 441 |
with gr.Column(scale=2):
|
|
|
|
| 446 |
show_share_button=True
|
| 447 |
)
|
| 448 |
status_output = gr.Textbox(
|
| 449 |
+
label="Status & File Location",
|
| 450 |
+
lines=5,
|
| 451 |
show_copy_button=True
|
| 452 |
)
|
| 453 |
|
|
|
|
| 466 |
outputs=prompt_input
|
| 467 |
)
|
| 468 |
|
| 469 |
+
# Image browser section
|
| 470 |
+
with gr.Accordion("π Browse Generated Images", open=False):
|
| 471 |
+
gr.Markdown(f"### Images saved in `{PERSISTENT_IMAGE_DIR}/`")
|
| 472 |
+
refresh_btn = gr.Button("π Refresh File List")
|
| 473 |
+
file_list = gr.File(
|
| 474 |
+
label="Generated Images",
|
| 475 |
+
file_count="multiple",
|
| 476 |
+
type="filepath",
|
| 477 |
+
height=300
|
| 478 |
+
)
|
| 479 |
+
|
| 480 |
+
def refresh_file_list():
|
| 481 |
+
"""Refresh the list of generated images"""
|
| 482 |
+
try:
|
| 483 |
+
image_files = []
|
| 484 |
+
for root, dirs, files in os.walk(PERSISTENT_IMAGE_DIR):
|
| 485 |
+
for file in files:
|
| 486 |
+
if file.endswith(('.png', '.jpg', '.jpeg')):
|
| 487 |
+
full_path = os.path.join(root, file)
|
| 488 |
+
image_files.append(full_path)
|
| 489 |
+
return image_files
|
| 490 |
+
except Exception as e:
|
| 491 |
+
print(f"Error refreshing file list: {e}")
|
| 492 |
+
return []
|
| 493 |
+
|
| 494 |
+
refresh_btn.click(fn=refresh_file_list, outputs=file_list)
|
| 495 |
+
# Auto-refresh on load
|
| 496 |
+
demo.load(fn=refresh_file_list, outputs=file_list)
|
| 497 |
+
|
| 498 |
generate_btn.click(
|
| 499 |
fn=generate_test_image,
|
| 500 |
inputs=[prompt_input, model_dropdown, style_dropdown],
|
| 501 |
outputs=[image_output, status_output]
|
| 502 |
+
).then(
|
| 503 |
+
fn=refresh_file_list,
|
| 504 |
+
outputs=file_list
|
| 505 |
)
|
| 506 |
|
| 507 |
return demo
|
|
|
|
| 513 |
async def root():
|
| 514 |
return {"message": "Storybook Generator API + UI is running!"}
|
| 515 |
|
| 516 |
+
@app.get("/files")
|
| 517 |
+
async def files_info():
|
| 518 |
+
"""Endpoint to check file structure"""
|
| 519 |
+
try:
|
| 520 |
+
file_structure = {}
|
| 521 |
+
for root, dirs, files in os.walk(PERSISTENT_IMAGE_DIR):
|
| 522 |
+
rel_root = os.path.relpath(root, PERSISTENT_IMAGE_DIR)
|
| 523 |
+
file_structure[rel_root] = {
|
| 524 |
+
"files": files,
|
| 525 |
+
"file_count": len(files)
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
return {
|
| 529 |
+
"persistent_directory": PERSISTENT_IMAGE_DIR,
|
| 530 |
+
"exists": os.path.exists(PERSISTENT_IMAGE_DIR),
|
| 531 |
+
"file_structure": file_structure
|
| 532 |
+
}
|
| 533 |
+
except Exception as e:
|
| 534 |
+
return {"error": str(e)}
|
| 535 |
+
|
| 536 |
# For Hugging Face Spaces
|
| 537 |
def get_app():
|
| 538 |
return app
|
| 539 |
|
| 540 |
if __name__ == "__main__":
|
| 541 |
print("π Starting High-Quality Storybook Generator...")
|
| 542 |
+
print(f"π Persistent image directory: {PERSISTENT_IMAGE_DIR}")
|
| 543 |
+
print("β
Images will auto-save to your Files tab")
|
| 544 |
gradio_app.launch(server_name="0.0.0.0", server_port=7860)
|