Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -973,4 +973,315 @@ def create_gradio_interface():
|
|
| 973 |
|
| 974 |
except Exception as e:
|
| 975 |
error_msg = f"β Generation failed: {str(e)}"
|
| 976 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 973 |
|
| 974 |
except Exception as e:
|
| 975 |
error_msg = f"β Generation failed: {str(e)}"
|
| 976 |
+
print(error_msg)
|
| 977 |
+
return None, error_msg, None
|
| 978 |
+
|
| 979 |
+
with gr.Blocks(title="Premium Children's Book Illustrator with Dynamic Character Consistency", theme="soft") as demo:
|
| 980 |
+
gr.Markdown("# π¨ Premium Children's Book Illustrator")
|
| 981 |
+
gr.Markdown("Generate **studio-quality** storybook images with **dynamic character consistency**")
|
| 982 |
+
|
| 983 |
+
# Storage info display
|
| 984 |
+
storage_info = gr.Textbox(
|
| 985 |
+
label="π Local Storage Information",
|
| 986 |
+
interactive=False,
|
| 987 |
+
lines=2
|
| 988 |
+
)
|
| 989 |
+
|
| 990 |
+
def update_storage_info():
|
| 991 |
+
info = get_local_storage_info()
|
| 992 |
+
if "error" not in info:
|
| 993 |
+
return f"π Local Storage: {info['total_files']} images, {info['total_size_mb']} MB used"
|
| 994 |
+
return "π Local Storage: Unable to calculate"
|
| 995 |
+
|
| 996 |
+
with gr.Row():
|
| 997 |
+
with gr.Column(scale=1):
|
| 998 |
+
gr.Markdown("### π― Quality Settings")
|
| 999 |
+
|
| 1000 |
+
model_dropdown = gr.Dropdown(
|
| 1001 |
+
label="AI Model",
|
| 1002 |
+
choices=list(MODEL_CHOICES.keys()),
|
| 1003 |
+
value="dreamshaper-8"
|
| 1004 |
+
)
|
| 1005 |
+
|
| 1006 |
+
style_dropdown = gr.Dropdown(
|
| 1007 |
+
label="Art Style",
|
| 1008 |
+
choices=["childrens_book", "realistic", "fantasy", "anime"],
|
| 1009 |
+
value="childrens_book"
|
| 1010 |
+
)
|
| 1011 |
+
|
| 1012 |
+
# Dynamic character input for testing
|
| 1013 |
+
character_names_input = gr.Textbox(
|
| 1014 |
+
label="Character Names (comma-separated)",
|
| 1015 |
+
placeholder="Enter character names: Sparkle the Star Cat, Benny the Bunny, Tilly the Turtle",
|
| 1016 |
+
info="Enter character names to test consistency features",
|
| 1017 |
+
lines=2
|
| 1018 |
+
)
|
| 1019 |
+
|
| 1020 |
+
prompt_input = gr.Textbox(
|
| 1021 |
+
label="Scene Description",
|
| 1022 |
+
placeholder="Describe your scene with character interactions...\nExample: Sparkle the Star Cat chasing butterflies while Benny the Bunny watches",
|
| 1023 |
+
lines=3
|
| 1024 |
+
)
|
| 1025 |
+
|
| 1026 |
+
generate_btn = gr.Button("β¨ Generate Premium Image", variant="primary")
|
| 1027 |
+
|
| 1028 |
+
# Current image management
|
| 1029 |
+
current_file_path = gr.State()
|
| 1030 |
+
delete_btn = gr.Button("ποΈ Delete This Image", variant="stop")
|
| 1031 |
+
delete_status = gr.Textbox(label="Delete Status", interactive=False, lines=2)
|
| 1032 |
+
|
| 1033 |
+
gr.Markdown("### π API Usage for n8n")
|
| 1034 |
+
gr.Markdown("""
|
| 1035 |
+
**For complete storybooks (OCI bucket):**
|
| 1036 |
+
- Endpoint: `POST /api/generate-storybook`
|
| 1037 |
+
- Input: `story_title`, `scenes[]`, `characters[]`
|
| 1038 |
+
- Output: Saves to OCI bucket with dynamic character consistency
|
| 1039 |
+
""")
|
| 1040 |
+
|
| 1041 |
+
with gr.Column(scale=2):
|
| 1042 |
+
image_output = gr.Image(label="Generated Image", height=500, show_download_button=True)
|
| 1043 |
+
status_output = gr.Textbox(label="Status", interactive=False, lines=4)
|
| 1044 |
+
|
| 1045 |
+
# Dynamic character guidance section
|
| 1046 |
+
with gr.Accordion("π₯ Dynamic Character Guidance", open=False):
|
| 1047 |
+
gr.Markdown("""
|
| 1048 |
+
### How to Use Dynamic Characters from n8n:
|
| 1049 |
+
|
| 1050 |
+
**n8n Payload Structure:**
|
| 1051 |
+
```json
|
| 1052 |
+
{
|
| 1053 |
+
"story_title": "Your Story Title",
|
| 1054 |
+
"characters": [
|
| 1055 |
+
{
|
| 1056 |
+
"name": "Character Name",
|
| 1057 |
+
"description": "Character description...",
|
| 1058 |
+
"visual_prompt": "Detailed visual description", // Optional
|
| 1059 |
+
"key_features": ["feature1", "feature2"] // Optional
|
| 1060 |
+
}
|
| 1061 |
+
],
|
| 1062 |
+
"scenes": [
|
| 1063 |
+
{
|
| 1064 |
+
"visual": "Scene description with characters...",
|
| 1065 |
+
"text": "Scene text...",
|
| 1066 |
+
"characters_present": ["Character Name"] // Optional
|
| 1067 |
+
}
|
| 1068 |
+
]
|
| 1069 |
+
}
|
| 1070 |
+
```
|
| 1071 |
+
|
| 1072 |
+
**Features:**
|
| 1073 |
+
- β
Dynamic character processing from n8n
|
| 1074 |
+
- β
Automatic visual prompt generation
|
| 1075 |
+
- β
Key feature extraction
|
| 1076 |
+
- β
Cross-scene consistency
|
| 1077 |
+
- β
Flexible character numbers and types
|
| 1078 |
+
""")
|
| 1079 |
+
|
| 1080 |
+
# Examples section
|
| 1081 |
+
with gr.Accordion("π‘ Prompt Examples & Tips", open=False):
|
| 1082 |
+
gr.Markdown("""
|
| 1083 |
+
## π¨ Professional Prompt Examples with Dynamic Characters:
|
| 1084 |
+
|
| 1085 |
+
**Best Results with Dynamic Characters:**
|
| 1086 |
+
- "Sparkle the Star Cat chasing butterflies in a sunny meadow"
|
| 1087 |
+
- "Benny the Bunny and Tilly the Turtle having a picnic"
|
| 1088 |
+
- "Multiple characters discovering a magical portal together"
|
| 1089 |
+
|
| 1090 |
+
## β‘ Dynamic Character Consistency Tips:
|
| 1091 |
+
1. **Always mention character names** in your prompts
|
| 1092 |
+
2. **n8n will send character details** automatically
|
| 1093 |
+
3. **The system processes any number** of characters dynamically
|
| 1094 |
+
4. **Consistency is maintained** across all scenes automatically
|
| 1095 |
+
""")
|
| 1096 |
+
|
| 1097 |
+
# Local file management section
|
| 1098 |
+
with gr.Accordion("π Manage Local Test Images", open=True):
|
| 1099 |
+
gr.Markdown("### Locally Saved Images")
|
| 1100 |
+
|
| 1101 |
+
with gr.Row():
|
| 1102 |
+
refresh_btn = gr.Button("π Refresh List")
|
| 1103 |
+
clear_all_btn = gr.Button("ποΈ Clear All Images", variant="stop")
|
| 1104 |
+
|
| 1105 |
+
file_gallery = gr.Gallery(
|
| 1106 |
+
label="Local Images",
|
| 1107 |
+
show_label=True,
|
| 1108 |
+
elem_id="gallery",
|
| 1109 |
+
columns=4,
|
| 1110 |
+
height="auto"
|
| 1111 |
+
)
|
| 1112 |
+
|
| 1113 |
+
clear_status = gr.Textbox(label="Clear Status", interactive=False)
|
| 1114 |
+
|
| 1115 |
+
# Debug section
|
| 1116 |
+
with gr.Accordion("π§ Advanced Settings", open=False):
|
| 1117 |
+
debug_btn = gr.Button("π Check System Status", variant="secondary")
|
| 1118 |
+
debug_output = gr.Textbox(label="System Info", interactive=False, lines=4)
|
| 1119 |
+
|
| 1120 |
+
def check_system_status():
|
| 1121 |
+
"""Check system status"""
|
| 1122 |
+
active_jobs = len(job_storage)
|
| 1123 |
+
return f"""**System Status:**
|
| 1124 |
+
- Model: {current_model_name}
|
| 1125 |
+
- Dynamic Character Processing: β
Enabled
|
| 1126 |
+
- Fallback Templates: {len(FALLBACK_CHARACTER_TEMPLATES)} available
|
| 1127 |
+
- OCI API: {OCI_API_BASE_URL}
|
| 1128 |
+
- Local Storage: {get_local_storage_info().get('total_files', 0)} images
|
| 1129 |
+
- Active Jobs: {active_jobs}
|
| 1130 |
+
- Ready for dynamic character consistency generation!"""
|
| 1131 |
+
|
| 1132 |
+
# Connect buttons to functions
|
| 1133 |
+
generate_btn.click(
|
| 1134 |
+
fn=generate_test_image_with_characters,
|
| 1135 |
+
inputs=[prompt_input, model_dropdown, style_dropdown, character_names_input],
|
| 1136 |
+
outputs=[image_output, status_output, current_file_path]
|
| 1137 |
+
).then(
|
| 1138 |
+
fn=refresh_local_images,
|
| 1139 |
+
outputs=file_gallery
|
| 1140 |
+
).then(
|
| 1141 |
+
fn=update_storage_info,
|
| 1142 |
+
outputs=storage_info
|
| 1143 |
+
)
|
| 1144 |
+
|
| 1145 |
+
delete_btn.click(
|
| 1146 |
+
fn=delete_current_image,
|
| 1147 |
+
inputs=current_file_path,
|
| 1148 |
+
outputs=[delete_status, image_output, status_output, file_gallery]
|
| 1149 |
+
).then(
|
| 1150 |
+
fn=update_storage_info,
|
| 1151 |
+
outputs=storage_info
|
| 1152 |
+
)
|
| 1153 |
+
|
| 1154 |
+
refresh_btn.click(
|
| 1155 |
+
fn=refresh_local_images,
|
| 1156 |
+
outputs=file_gallery
|
| 1157 |
+
).then(
|
| 1158 |
+
fn=update_storage_info,
|
| 1159 |
+
outputs=storage_info
|
| 1160 |
+
)
|
| 1161 |
+
|
| 1162 |
+
clear_all_btn.click(
|
| 1163 |
+
fn=clear_all_images,
|
| 1164 |
+
outputs=[clear_status, file_gallery]
|
| 1165 |
+
).then(
|
| 1166 |
+
fn=update_storage_info,
|
| 1167 |
+
outputs=storage_info
|
| 1168 |
+
)
|
| 1169 |
+
|
| 1170 |
+
debug_btn.click(
|
| 1171 |
+
fn=check_system_status,
|
| 1172 |
+
inputs=None,
|
| 1173 |
+
outputs=debug_output
|
| 1174 |
+
)
|
| 1175 |
+
|
| 1176 |
+
# Initialize on load
|
| 1177 |
+
demo.load(fn=refresh_local_images, outputs=file_gallery)
|
| 1178 |
+
demo.load(fn=update_storage_info, outputs=storage_info)
|
| 1179 |
+
|
| 1180 |
+
return demo
|
| 1181 |
+
|
| 1182 |
+
# Create enhanced Gradio app
|
| 1183 |
+
demo = create_gradio_interface()
|
| 1184 |
+
|
| 1185 |
+
# Enhanced root endpoint that explains the API structure
|
| 1186 |
+
@app.get("/")
|
| 1187 |
+
async def root():
|
| 1188 |
+
return {
|
| 1189 |
+
"message": "Storybook Generator API with Dynamic Character Consistency is running!",
|
| 1190 |
+
"api_endpoints": {
|
| 1191 |
+
"health_check": "GET /api/health",
|
| 1192 |
+
"generate_storybook": "POST /api/generate-storybook",
|
| 1193 |
+
"check_job_status": "GET /api/job-status/{job_id}",
|
| 1194 |
+
"local_images": "GET /api/local-images"
|
| 1195 |
+
},
|
| 1196 |
+
"features": {
|
| 1197 |
+
"dynamic_characters": "β
Enabled",
|
| 1198 |
+
"character_consistency": "β
Enabled",
|
| 1199 |
+
"flexible_storytelling": "β
Enabled",
|
| 1200 |
+
"n8n_integration": "β
Enabled"
|
| 1201 |
+
},
|
| 1202 |
+
"web_interface": "GET /ui",
|
| 1203 |
+
"note": "Use API endpoints for programmatic access with dynamic characters from n8n"
|
| 1204 |
+
}
|
| 1205 |
+
|
| 1206 |
+
# Add a simple test endpoint
|
| 1207 |
+
@app.get("/api/test")
|
| 1208 |
+
async def test_endpoint():
|
| 1209 |
+
return {
|
| 1210 |
+
"status": "success",
|
| 1211 |
+
"message": "API with dynamic character consistency is working correctly",
|
| 1212 |
+
"dynamic_processing": "β
Enabled",
|
| 1213 |
+
"fallback_templates": len(FALLBACK_CHARACTER_TEMPLATES),
|
| 1214 |
+
"timestamp": datetime.now().isoformat()
|
| 1215 |
+
}
|
| 1216 |
+
|
| 1217 |
+
# For Hugging Face Spaces deployment
|
| 1218 |
+
def get_app():
|
| 1219 |
+
return app
|
| 1220 |
+
|
| 1221 |
+
if __name__ == "__main__":
|
| 1222 |
+
import uvicorn
|
| 1223 |
+
import os
|
| 1224 |
+
|
| 1225 |
+
# Check if we're running on Hugging Face Spaces
|
| 1226 |
+
HF_SPACE = os.environ.get('SPACE_ID') is not None
|
| 1227 |
+
|
| 1228 |
+
if HF_SPACE:
|
| 1229 |
+
print("π Running on Hugging Face Spaces - Integrated Mode")
|
| 1230 |
+
print("π API endpoints available at: /api/*")
|
| 1231 |
+
print("π¨ Web interface available at: /ui")
|
| 1232 |
+
print("π₯ Dynamic character consistency features enabled")
|
| 1233 |
+
print("π Both API and UI running on same port")
|
| 1234 |
+
|
| 1235 |
+
# Mount Gradio without reassigning app
|
| 1236 |
+
gr.mount_gradio_app(app, demo, path="/ui")
|
| 1237 |
+
|
| 1238 |
+
# Run the combined app
|
| 1239 |
+
uvicorn.run(
|
| 1240 |
+
app,
|
| 1241 |
+
host="0.0.0.0",
|
| 1242 |
+
port=7860,
|
| 1243 |
+
log_level="info"
|
| 1244 |
+
)
|
| 1245 |
+
else:
|
| 1246 |
+
# Local development - run separate servers
|
| 1247 |
+
print("π Running locally - Separate API and UI servers")
|
| 1248 |
+
print("π API endpoints: http://localhost:8000/api/*")
|
| 1249 |
+
print("π¨ Web interface: http://localhost:7860/ui")
|
| 1250 |
+
print("π₯ Dynamic character consistency features enabled")
|
| 1251 |
+
|
| 1252 |
+
def run_fastapi():
|
| 1253 |
+
"""Run FastAPI on port 8000 for API calls"""
|
| 1254 |
+
uvicorn.run(
|
| 1255 |
+
app,
|
| 1256 |
+
host="0.0.0.0",
|
| 1257 |
+
port=8000,
|
| 1258 |
+
log_level="info",
|
| 1259 |
+
access_log=False
|
| 1260 |
+
)
|
| 1261 |
+
|
| 1262 |
+
def run_gradio():
|
| 1263 |
+
"""Run Gradio on port 7860 for web interface"""
|
| 1264 |
+
demo.launch(
|
| 1265 |
+
server_name="0.0.0.0",
|
| 1266 |
+
server_port=7860,
|
| 1267 |
+
share=False,
|
| 1268 |
+
show_error=True,
|
| 1269 |
+
quiet=True
|
| 1270 |
+
)
|
| 1271 |
+
|
| 1272 |
+
# Start both servers in separate threads
|
| 1273 |
+
api_thread = threading.Thread(target=run_fastapi, daemon=True)
|
| 1274 |
+
ui_thread = threading.Thread(target=run_gradio, daemon=True)
|
| 1275 |
+
|
| 1276 |
+
api_thread.start()
|
| 1277 |
+
print("β
FastAPI server started on port 8000")
|
| 1278 |
+
|
| 1279 |
+
ui_thread.start()
|
| 1280 |
+
print("β
Gradio server started on port 7860")
|
| 1281 |
+
|
| 1282 |
+
# Keep the main thread alive
|
| 1283 |
+
try:
|
| 1284 |
+
while True:
|
| 1285 |
+
time.sleep(1)
|
| 1286 |
+
except KeyboardInterrupt:
|
| 1287 |
+
print("π Shutting down servers...")
|