import streamlit as st import os import json import uuid from datetime import datetime, timezone from pathlib import Path from providers.pollinations_provider import PollinationsProvider from providers.hf_provider import HuggingFaceProvider from providers.stub_provider import StubProvider from dotenv import load_dotenv load_dotenv() # ============================================== # PATHS & CONFIGURATION # ============================================== BASE_DIR = Path(__file__).parent STORAGE_DIR = BASE_DIR / "storage" PROJECTS_DIR = BASE_DIR / "projects" PROMPT_HISTORY = BASE_DIR / "prompt_history.json" FEEDBACK_DB = BASE_DIR / "feedback_db.json" # Create directories STORAGE_DIR.mkdir(exist_ok=True) PROJECTS_DIR.mkdir(exist_ok=True) # Initialize JSON files if not PROMPT_HISTORY.exists(): PROMPT_HISTORY.write_text("[]") if not FEEDBACK_DB.exists(): FEEDBACK_DB.write_text("[]") # ============================================== # PROVIDERS CONFIGURATION # ============================================== PROVIDERS = { "FLUX (Pollinations - FREE)": ("pollinations", "flux"), "Turbo (Pollinations - FREE)": ("pollinations", "turbo"), "Stable Diffusion 1.4 (HF)": ("huggingface", "CompVis/stable-diffusion-v1-4"), "Stable Diffusion 1.5 (HF)": ("huggingface", "runwayml/stable-diffusion-v1-5"), "Demo Mode": ("stub", "demo"), } VIDEO_PROVIDERS = { "RunwayML Gen-2 — Demo": ("stub", "runway_gen2"), "Stable Video Diffusion — Demo": ("stub", "svd"), "Pika Labs — Coming Soon": ("stub", "pika"), } MODEL_3D_PROVIDERS = { "Shap-E (OpenAI) — Demo": ("stub", "shap_e"), "Point-E — Demo": ("stub", "point_e"), "DreamFusion — Coming Soon": ("stub", "dreamfusion"), } def get_provider(kind, model): """Factory function to get appropriate provider""" if kind == "huggingface": return HuggingFaceProvider(model=model) elif kind == "pollinations": return PollinationsProvider(model=model) elif kind == "stub": return StubProvider(name=model) else: return StubProvider(name="Unknown") # ============================================== # HELPER FUNCTIONS # ============================================== def save_to_history(asset_type, prompt, negative_prompt, provider, model_name, results, metadata=None): """Save generation to history""" try: history_content = PROMPT_HISTORY.read_text() history = json.loads(history_content) if history_content else [] except json.JSONDecodeError: history = [] entry = { "id": uuid.uuid4().hex, "asset_type": asset_type, "prompt": prompt, "negative_prompt": negative_prompt, "provider": provider, "model_name": model_name, "timestamp": datetime.now(timezone.utc).isoformat(), "results": results, "metadata": metadata or {}, "feedback": [] } history.insert(0, entry) PROMPT_HISTORY.write_text(json.dumps(history, indent=2)) return entry["id"] def add_feedback(entry_id, feedback_text, rating): """Add feedback to a generation""" try: history = json.loads(PROMPT_HISTORY.read_text()) except json.JSONDecodeError: return for entry in history: if entry["id"] == entry_id: entry["feedback"].append({ "text": feedback_text, "rating": rating, "timestamp": datetime.now(timezone.utc).isoformat() }) PROMPT_HISTORY.write_text(json.dumps(history, indent=2)) def create_project(name, brief): """Create a new project""" project = { "id": uuid.uuid4().hex, "name": name, "brief": brief, "created_at": datetime.now(timezone.utc).isoformat(), "assets": [] } project_file = PROJECTS_DIR / f"{project['id']}.json" project_file.write_text(json.dumps(project, indent=2)) return project # ============================================== # STREAMLIT UI CONFIGURATION # ============================================== st.set_page_config( page_title="AI Studio — Creative Workflow Platform", layout="wide", initial_sidebar_state="expanded" ) # Custom CSS st.markdown(""" """, unsafe_allow_html=True) # ============================================== # SIDEBAR # ============================================== st.sidebar.title("🎨 AI Studio Platform") st.sidebar.markdown("### Creative Workflow Builder") # Project selection st.sidebar.markdown("---") st.sidebar.subheader("Current Project") project_name = st.sidebar.text_input("Project Name", value="Luxury Real Estate Campaign") project_brief = st.sidebar.text_area("Creative Brief", value="High-end real estate marketing campaign targeting luxury buyers. Focus on premium aesthetics, lifestyle, and architectural excellence.") if st.sidebar.button("Create New Project"): proj = create_project(project_name, project_brief) st.sidebar.success(f"Project created: {proj['name']}") # ============================================== # EXCEL UPLOAD MODULE # ============================================== st.sidebar.markdown("---") st.sidebar.subheader("📊 Excel Batch Import") uploaded_file = st.sidebar.file_uploader("Upload Excel", type=['xlsx', 'xls']) if uploaded_file is not None: try: from utils.prompt_enhancer import load_excel_to_projects # Save uploaded file temporarily temp_path = STORAGE_DIR / "temp_upload.xlsx" with open(temp_path, "wb") as f: f.write(uploaded_file.getbuffer()) # Load projects projects = load_excel_to_projects(temp_path) st.session_state['uploaded_projects'] = projects st.sidebar.success(f"✅ Loaded {len(projects)} projects") # Project selector if projects: project_names = [f"{p.get('org_name', 'Unknown')} - {p.get('asset_type', 'N/A')}" for p in projects] selected_idx = st.sidebar.selectbox( "Select Project", range(len(project_names)), format_func=lambda x: project_names[x] ) st.session_state['selected_project'] = projects[selected_idx] st.sidebar.info(f"**Prompt:** {projects[selected_idx].get('prompt', '')[:50]}...") except Exception as e: st.sidebar.error(f"Error loading Excel: {e}") # ============================================== # MAIN TABS # ============================================== tab1, tab2, tab3, tab4, tab5, tab6, tab7 = st.tabs([ "🎯 Ideation Board", "🖼️ Image Generator", "🎬 Video Generator", "🧱 3D Creator", "📁 Library", "📜 History & Feedback", "🔄 Pipeline & Export" ]) # ============================================== # TAB 1: IDEATION BOARD # ============================================== with tab1: st.header("🎯 Ideation & Prompt Board") st.markdown("**Brainstorm and refine prompts before generation**") col1, col2 = st.columns([2, 1]) with col1: st.subheader("Prompt Ideas") # Preset templates for luxury real estate template = st.selectbox("Load Template", [ "Custom", "Luxury Villa Exterior", "Penthouse Interior", "Aerial Property Tour", "3D Furniture Asset" ]) if template == "Luxury Villa Exterior": idea_prompt = "Ultra-modern luxury villa at golden hour, infinity pool with crystal reflections, sleek glass facade, lush tropical landscaping, ocean view, photorealistic, architectural photography" elif template == "Penthouse Interior": idea_prompt = "Spacious penthouse interior, floor-to-ceiling windows, panoramic city skyline, designer furniture, marble floors, ambient lighting, wide-angle, luxury lifestyle" elif template == "Aerial Property Tour": idea_prompt = "Cinematic aerial drone footage of modern beachfront estate, golden hour lighting, smooth gimbal motion, reveal shot from ocean to property" elif template == "3D Furniture Asset": idea_prompt = "High-detail 3D model of contemporary outdoor lounge chair, teak wood frame, white weather-resistant cushions, for architectural visualization" else: idea_prompt = "" # Check if project is selected from Excel if 'selected_project' in st.session_state: proj = st.session_state['selected_project'] idea_prompt = proj.get('prompt', idea_prompt) st.info(f"📋 Auto-filled from: {proj.get('org_name', 'Excel')}") idea_text = st.text_area("Prompt Concept", value=idea_prompt, height=150, key="idea_text") # PROMPT ENHANCER if idea_text and len(idea_text) > 10: st.markdown("### 🚀 Prompt Enhancer") # Helper to simulate enhancer if utils missing try: from utils.prompt_enhancer import enhance_prompt enhanced = enhance_prompt(idea_text) except ImportError: enhanced = { "Cinematic": f"Cinematic shot of {idea_text}, 8k, highly detailed", "Photorealistic": f"Photorealistic photo of {idea_text}, canon eos r5, 50mm", "Artistic": f"Digital art of {idea_text}, trending on artstation" } enhanced_choice = st.radio( "Select Enhanced Version:", list(enhanced.keys()), key="idea_enhancer" ) st.text_area( "Enhanced Prompt Preview", value=enhanced[enhanced_choice], height=100, disabled=True, key="idea_enhanced_preview" ) st.session_state['ideation_final_prompt'] = enhanced[enhanced_choice] # Prompt versioning st.markdown("**Prompt Versions**") version = st.number_input("Version", min_value=1, value=1, step=1) if st.button("Save to Prompt Library"): st.success("Prompt saved to library with version tracking!") with col2: st.subheader("Prompt Strategy") st.markdown(""" **Elements to Include:** - Subject/Scene - Lighting conditions - Camera angle - Style keywords - Quality modifiers **Best Practices:** - Be specific about materials - Mention atmosphere/mood - Include technical details - Specify use case """) st.markdown("---") st.info("💡 **Tip:** Test prompts with image generator first before scaling to video/3D") # ============================================== # TAB 2: IMAGE GENERATOR # ============================================== with tab2: st.header("🖼️ Image Generator") col1, col2 = st.columns([3, 1]) with col1: selected_model = st.selectbox("Select Model", list(PROVIDERS.keys()), key="img_model") kind, model_name = PROVIDERS[selected_model] # Auto-fill from Excel if available default_prompt = "Ultra-modern luxury villa at sunset, glass façade, infinity pool, palm trees, ocean view, photorealistic, high detail" if 'selected_project' in st.session_state: proj = st.session_state['selected_project'] if proj.get('asset_type', '').lower() in ['image', 'img']: default_prompt = proj.get('prompt', default_prompt) st.info(f"📋 Auto-filled from: {proj.get('org_name', 'Excel')}") prompt = st.text_area( "Image Prompt", height=120, value=default_prompt, key="img_prompt" ) # PROMPT ENHANCER - MANDATORY final_prompt = prompt # Default if prompt and len(prompt) > 10: st.markdown("### 🚀 Select Enhanced Prompt (Required)") try: from utils.prompt_enhancer import enhance_prompt enhanced = enhance_prompt(prompt) except ImportError: enhanced = { "Cinematic": f"Cinematic shot of {prompt}, 8k, highly detailed", "Photorealistic": f"Photorealistic photo of {prompt}, canon eos r5, 50mm", "Artistic": f"Digital art of {prompt}, trending on artstation" } # Show all three options with their full text st.markdown("**Choose one enhancement:**") for idx, (title, enhanced_text) in enumerate(enhanced.items(), 1): st.markdown(f"**Option {idx}: {title}**") st.info(enhanced_text) enhanced_choice = st.radio( "Select your preferred version:", list(enhanced.keys()), key="img_enhancer", label_visibility="collapsed" ) final_prompt = enhanced[enhanced_choice] st.success(f"✅ Selected: **{enhanced_choice}**") col_neg, col_num = st.columns(2) with col_neg: negative_prompt = st.text_input("Negative Prompt", value="low quality, blurry, distorted", key="img_neg") with col_num: n_images = st.slider("Number of Images", 1, 4, 2, key="img_count") # Advanced settings with st.expander("Advanced Settings"): col_adv1, col_adv2 = st.columns(2) with col_adv1: steps = st.slider("Inference Steps", 20, 50, 30) guidance = st.slider("Guidance Scale", 5.0, 15.0, 7.5, 0.5) with col_adv2: width = st.selectbox("Width", [512, 768, 1024], index=2) height = st.selectbox("Height", [512, 768, 1024], index=2) if st.button("🎨 Generate Images", type="primary", key="gen_img"): provider = get_provider(kind, model_name) try: with st.spinner(f"Generating images using {selected_model}..."): outputs = provider.generate( final_prompt, negative_prompt, n_images=n_images, steps=steps, guidance=guidance ) # Save generated images saved = [] for img in outputs: fname = f"img_{uuid.uuid4().hex[:8]}.png" fpath = STORAGE_DIR / fname img.save(fpath) saved.append(str(fpath)) # Save to history entry_id = save_to_history( "image", final_prompt, negative_prompt, selected_model, model_name, saved, {"steps": steps, "guidance": guidance, "resolution": f"{width}x{height}"} ) st.success(f"✅ Generated {len(saved)} images!") # Display results cols = st.columns(min(len(saved), 3)) for idx, (c, path) in enumerate(zip(cols, saved)): with c: # FIXED: Changed use_container_width to use_column_width c.image(path, use_column_width=True) c.download_button( "Download", data=open(path, "rb"), file_name=f"image_{idx+1}.png", mime="image/png", key=f"dl_img_{idx}" ) except Exception as e: st.error(f"❌ Generation failed: {e}") with col2: st.subheader("Quick Guide") st.markdown(""" **Image Prompt Tips:** 1. **Composition** - Camera angle - Framing - Rule of thirds 2. **Lighting** - Time of day - Light direction - Atmosphere 3. **Style** - Photorealistic - Architectural - Cinematic 4. **Quality** - High detail - 8K resolution - Professional """) # ============================================== # TAB 3: VIDEO GENERATOR # ============================================== with tab3: st.header("🎬 Video Generator") st.info("🚧 Video generation pipeline - Currently in demo mode") col1, col2 = st.columns([3, 1]) with col1: video_model = st.selectbox("Select Video Model", list(VIDEO_PROVIDERS.keys())) # Auto-fill from Excel if available default_video_prompt = "Aerial drone video fly-through of modern seaside estate, golden hour, lush landscaping, cinematic, smooth motion, 10 seconds" if 'selected_project' in st.session_state: proj = st.session_state['selected_project'] if proj.get('asset_type', '').lower() == 'video': default_video_prompt = proj.get('prompt', default_video_prompt) st.info(f"📋 Auto-filled from: {proj.get('org_name', 'Excel')}") video_prompt = st.text_area( "Video Prompt", height=120, value=default_video_prompt, key="vid_prompt" ) # PROMPT ENHANCER - MANDATORY final_video_prompt = video_prompt # Default if video_prompt and len(video_prompt) > 10: st.markdown("### 🚀 Select Enhanced Prompt (Required)") try: from utils.prompt_enhancer import enhance_prompt enhanced = enhance_prompt(video_prompt) except ImportError: enhanced = { "Cinematic Motion": f"{video_prompt}, cinematic camera movement, smooth stabilization", "Dynamic Action": f"{video_prompt}, dynamic angle, fast paced", "Slow Mo": f"{video_prompt}, slow motion, 60fps, detailed" } enhanced_choice = st.radio( "Choose one enhancement:", list(enhanced.keys()), key="vid_enhancer" ) final_video_prompt = enhanced[enhanced_choice] st.text_area( "Final Prompt to Generate", value=final_video_prompt, height=80, disabled=True, key="vid_final_preview" ) col_dur, col_fps = st.columns(2) with col_dur: duration = st.slider("Duration (seconds)", 5, 30, 10) with col_fps: fps = st.selectbox("FPS", [24, 30, 60], index=1) motion_intensity = st.slider("Motion Intensity", 1, 10, 5) if st.button("🎬 Generate Video", type="primary"): st.warning("Video generation requires RunwayML or Stable Video Diffusion API. Demo mode active.") st.info(f"**Would generate with prompt:** {final_video_prompt}") with col2: st.subheader("Video Strategy") st.markdown(""" **Key Elements:** - Camera movement - Shot duration - Transitions - Pacing **Types:** - Property walkthrough - Aerial tour - Detail shots - Time-lapse """) # ============================================== # TAB 4: 3D CREATOR # ============================================== with tab4: st.header("🧱 3D Asset Creator") st.info("🚧 3D generation pipeline - Currently in demo mode") col1, col2 = st.columns([3, 1]) with col1: model_3d = st.selectbox("Select 3D Model", list(MODEL_3D_PROVIDERS.keys())) asset_prompt = st.text_area( "3D Asset Prompt", height=120, value="High-detail 3D model of contemporary outdoor lounge chair, teak wood and white cushions, clean geometry, for architectural visualization", key="3d_prompt" ) col_poly, col_tex = st.columns(2) with col_poly: poly_count = st.selectbox("Poly Count", ["Low (5K)", "Medium (20K)", "High (50K)"]) with col_tex: texture_res = st.selectbox("Texture Resolution", ["1K", "2K", "4K"]) export_format = st.multiselect( "Export Formats", ["FBX", "OBJ", "GLTF", "USD", "Blender (.blend)"], default=["FBX", "Blender (.blend)"] ) if st.button("🧱 Generate 3D Asset", type="primary"): st.warning("3D generation requires Shap-E or similar 3D generation API. Demo mode active.") with st.spinner("Simulating 3D generation..."): import time time.sleep(2) st.success("✅ 3D asset generation placeholder complete!") st.info("**Blender Integration:** Assets can be exported to `.blend` format for direct import") with col2: st.subheader("3D Workflow") st.markdown(""" **Pipeline:** 1. Generate base mesh 2. Apply textures 3. Optimize topology 4. Export to Blender 5. Final rendering **Blender Integration:** - Direct .blend export - Material nodes - Scene composition """) # ============================================== # TAB 5: LIBRARY # ============================================== with tab5: st.header("📁 Project Library & Asset Management") # Filter options col_f1, col_f2, col_f3 = st.columns(3) with col_f1: filter_type = st.selectbox("Asset Type", ["All", "Images", "Videos", "3D Models"]) with col_f2: sort_by = st.selectbox("Sort By", ["Newest First", "Oldest First", "Most Feedback"]) with col_f3: st.write("") # Spacer show_grid = st.checkbox("Grid View", value=True) files = sorted(STORAGE_DIR.glob("*.png"), reverse=True) if not files: st.info("No generated assets yet. Start creating in the generator tabs!") else: if show_grid: # Grid layout cols_per_row = 3 for i in range(0, len(files), cols_per_row): cols = st.columns(cols_per_row) for idx, f in enumerate(files[i:i+cols_per_row]): with cols[idx]: # FIXED: Changed use_container_width to use_column_width st.image(str(f), use_column_width=True) st.caption(f.name) col_dl, col_fb = st.columns(2) with col_dl: st.download_button( "⬇️", data=open(f, "rb"), file_name=f.name, mime="image/png", key=f"lib_dl_{f.name}" ) with col_fb: if st.button("💬", key=f"lib_fb_{f.name}"): st.info("Feedback panel would open here") else: # List layout for f in files: col1, col2, col3 = st.columns([3, 1, 1]) with col1: # FIXED: Changed width parameter logic for compatibility st.image(str(f), width=300) with col2: st.write(f"**{f.name}**") st.caption(f"Size: {f.stat().st_size // 1024}KB") with col3: st.download_button( "Download", data=open(f, "rb"), file_name=f.name, mime="image/png", key=f"list_dl_{f.name}" ) # ============================================== # TAB 6: HISTORY & FEEDBACK # ============================================== with tab6: st.header("📜 Prompt History & Feedback System") try: history = json.loads(PROMPT_HISTORY.read_text()) except json.JSONDecodeError: history = [] if not history: st.info("No generation history yet.") else: for item in history: # Handle both old and new format asset_type = item.get('asset_type', 'image').upper() with st.expander(f"{asset_type}: {item['prompt'][:80]}...", expanded=False): col1, col2 = st.columns([2, 1]) with col1: st.markdown(f"**Prompt:** {item['prompt']}") st.caption(f"Provider: {item['provider']} | {item['timestamp']}") if item.get("negative_prompt"): st.markdown(f"**Negative:** {item['negative_prompt']}") # Show results if item["results"]: result_cols = st.columns(min(len(item['results']), 3)) for idx, (col, path) in enumerate(zip(result_cols, item['results'])): if Path(path).exists(): # FIXED: Changed use_container_width to use_column_width col.image(path, use_column_width=True) with col2: st.markdown("**Feedback & Iteration**") # Add feedback rating = st.select_slider( "Quality Rating", options=[1, 2, 3, 4, 5], value=3, key=f"rating_{item['id']}" ) feedback_text = st.text_area( "Feedback Notes", placeholder="What would improve this?", key=f"feedback_{item['id']}", height=100 ) if st.button("Submit Feedback", key=f"submit_{item['id']}"): add_feedback(item['id'], feedback_text, rating) st.success("Feedback saved!") # Show existing feedback if item.get("feedback"): st.markdown("**Previous Feedback:**") for fb in item["feedback"]: st.caption(f"⭐ {fb['rating']}/5: {fb['text']}") st.markdown("---") # ============================================== # TAB 7: PIPELINE & EXPORT # ============================================== with tab7: st.header("🔄 Integration Pipeline & Export") st.markdown(""" ## Workflow: Concept to Campaign-Ready Asset This pipeline shows how assets flow from ideation to final delivery: """) # Visual workflow st.markdown(""" ``` 1. Creative Brief Submission ↓ 2. Ideation Board → Prompt Refinement → Version Control ↓ 3. Asset Generation (Image/Video/3D) ↓ 4. Team Review & Feedback Loop ↓ 5. Iteration & Refinement ↓ 6. Export & Integration: • Images → Marketing materials • 3D Assets → Blender for rendering • 3D Assets → Unity for interactive tours • Videos → Campaign distribution ↓ 7. Final Review & Campaign Launch ``` """) st.markdown("---") # Export options st.subheader("Export & Integration Tools") col1, col2 = st.columns(2) with col1: st.markdown("### Blender Integration") st.code(""" # Python script for Blender export import bpy def export_to_blender(asset_path): bpy.ops.import_scene.fbx(filepath=asset_path) # Set up materials, lighting bpy.ops.wm.save_as_mainfile( filepath="scene_output.blend" ) """, language="python") if st.button("Generate Blender Export Script"): st.download_button( "Download Script", data="""import bpy\n# Your export script""", file_name="blender_export.py", mime="text/plain" ) with col2: st.markdown("### Unity Integration") st.code(""" // C# script for Unity import using UnityEngine; public class AssetImporter : MonoBehaviour { void ImportAsset(string path) { GameObject asset = AssetDatabase.LoadAssetAtPath(path); Instantiate(asset); } } """, language="csharp") if st.button("Generate Unity Script"): st.download_button( "Download Script", data="""using UnityEngine;\n// Your import script""", file_name="AssetImporter.cs", mime="text/plain" ) st.markdown("---") # API Integration pseudocode st.subheader("API Workflow (Pseudocode)") st.code(""" def generate_campaign_asset(brief, prompt_type, iterations=3): \"\"\" Complete workflow from brief to final asset \"\"\" # Step 1: Create prompt from brief prompt = create_prompt(brief, prompt_type) # Step 2: Generate initial asset ai_output = call_ai_tool(prompt) # Step 3: Iteration loop with feedback for i in range(iterations): feedback = get_team_feedback(ai_output) if feedback['approved']: break prompt = refine_prompt(prompt, feedback) ai_output = call_ai_tool(prompt) # Step 4: Export based on asset type if prompt_type == "3D": exported = import_to_blender(ai_output) elif prompt_type == "video": exported = save_to_library(ai_output) elif prompt_type == "image": exported = optimize_for_web(ai_output) # Step 5: Save to project save_to_project(brief['project_id'], exported) return exported # Example usage brief = { 'project_id': 'luxury_realestate_q4', 'description': 'High-end villa marketing campaign', 'target_audience': 'Ultra-high-net-worth individuals', 'style': 'Photorealistic, cinematic' } image = generate_campaign_asset(brief, 'image') video = generate_campaign_asset(brief, 'video') model_3d = generate_campaign_asset(brief, '3D') """, language="python") st.markdown("---") st.success("✅ Complete workflow pipeline ready for presentation!") # ============================================== # FOOTER # ============================================== st.sidebar.markdown("---") st.sidebar.markdown("### 📊 System Status") st.sidebar.success("✅ Image Generation: Active") st.sidebar.warning("⚠️ Video Generation: Demo Mode") st.sidebar.warning("⚠️ 3D Generation: Demo Mode") st.sidebar.info("💡 Tip: Complete image pipeline first, then add video/3D APIs")