Spaces:
Runtime error
Runtime error
| #!/usr/bin/env python3 | |
| import gradio as gr | |
| import numpy as np | |
| from PIL import Image, ImageDraw, ImageFont | |
| import cv2 | |
| import os | |
| import json | |
| import hashlib | |
| import time | |
| import random | |
| from datetime import datetime | |
| from pathlib import Path | |
| import warnings | |
| warnings.filterwarnings("ignore") | |
| # All heavy imports happen inside button handlers | |
| print("App loaded successfully") | |
| class SafetyFramework: | |
| BLOCKED = ["child","minor","underage","kid","children","non-consensual","revenge", | |
| "hidden camera","spy","torture","gore","snuff","beheading","execution", | |
| "terrorist","bomb making","how to kill"] | |
| WARN = ["real person","celebrity","famous","actor","actress","public figure","politician","named individual"] | |
| def check_prompt(self, text): | |
| if not text: return True, "ok", "" | |
| text_lower = text.lower() | |
| for kw in self.BLOCKED: | |
| if kw in text_lower: | |
| return False, "blocked", f"Blocked term: '{kw}'" | |
| wf = [k for k in self.WARN if k in text_lower] | |
| if wf: | |
| return True, "warning", f"Warning: references real individuals ({', '.join(wf)})" | |
| return True, "ok", "" | |
| def watermark(self, image, meta=None): | |
| if image is None: return image | |
| img = image.copy().convert("RGBA") | |
| w, h = img.size | |
| overlay = Image.new("RGBA", img.size, (0,0,0,0)) | |
| draw = ImageDraw.Draw(overlay) | |
| try: font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", max(10, h//80)) | |
| except: font = ImageFont.load_default() | |
| wm = "AI GENERATED - Fictional Content" | |
| ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC") | |
| try: | |
| bx = draw.textbbox((0,0), wm, font=font) | |
| tw, th = bx[2]-bx[0], bx[3]-bx[1] | |
| except: | |
| tw, th = len(wm)*6, 12 | |
| x, y = w-tw-15, h-th-15 | |
| draw.rectangle([x-5, y-5, x+tw+5, y+th+5], fill=(0,0,0,80)) | |
| draw.text((x,y), wm, fill=(255,255,255,180), font=font) | |
| draw.rectangle([x-5, y-th-7, x+len(ts)*6+5, y-5], fill=(0,0,0,80)) | |
| draw.text((x, y-th-5), ts, fill=(200,200,200,150), font=font) | |
| return Image.alpha_composite(img, overlay).convert("RGB") | |
| SAFETY = SafetyFramework() | |
| def placeholder(prompt, w, h, label="", sub=""): | |
| img = Image.new("RGB", (int(w), int(h)), (30,30,40)) | |
| d = ImageDraw.Draw(img) | |
| try: | |
| fl = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 24) | |
| fs = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14) | |
| except: | |
| fl = fs = ImageFont.load_default() | |
| t = "AI Creative Suite" | |
| d.text(((int(w)-len(t)*14)//2, 20), t, fill=(200,200,255), font=fl) | |
| p = prompt[:60] + "..." if len(prompt)>60 else prompt | |
| d.text(((int(w)-len(p)*8)//2, int(h)//2-30), p, fill=(180,180,200), font=fs) | |
| if sub: d.text(((int(w)-len(sub)*8)//2, int(h)//2), sub, fill=(150,150,180), font=fs) | |
| if label: d.text(((int(w)-len(label)*8)//2, int(h)//2+30), label, fill=(150,150,180), font=fs) | |
| return SAFETY.watermark(img) | |
| class ImageGen: | |
| def __init__(self): | |
| self._pipe = None | |
| def _init(self): | |
| if self._pipe is not None: return True | |
| try: | |
| import torch | |
| from diffusers import DiffusionPipeline | |
| dev = "cuda" if torch.cuda.is_available() else "cpu" | |
| self._pipe = DiffusionPipeline.from_pretrained( | |
| "stabilityai/stable-diffusion-xl-base-1.0", | |
| torch_dtype=torch.float16 if dev=="cuda" else torch.float32, | |
| use_safetensors=True, variant="fp16" if dev=="cuda" else None) | |
| if dev=="cuda": | |
| self._pipe = self._pipe.to(dev) | |
| self._pipe.enable_model_cpu_offload() | |
| self._dev = dev | |
| return True | |
| except Exception as e: | |
| self._err = str(e) | |
| return False | |
| def generate(self, prompt, neg, width, height, steps, guidance, seed, num): | |
| ok, lvl, msg = SAFETY.check_prompt(prompt) | |
| if not ok: | |
| return [placeholder(prompt, width, height, "BLOCKED", msg)]*int(num), msg | |
| if not self._init(): | |
| return [placeholder(prompt, width, height, "LOADING", self._err)]*int(num), f"Model not ready: {self._err}" | |
| import torch | |
| s = int(seed) if int(seed)!=-1 else random.randint(0, 2**32) | |
| gen = torch.Generator(device=self._dev).manual_seed(s) | |
| out = [] | |
| for i in range(int(num)): | |
| g = gen.manual_seed(s+i) | |
| try: | |
| r = self._pipe(prompt=prompt, negative_prompt=neg, width=int(width), height=int(height), | |
| num_inference_steps=int(steps), guidance_scale=float(guidance), generator=g).images[0] | |
| out.append(SAFETY.watermark(r, {"seed": s+i})) | |
| except Exception as e: | |
| out.append(placeholder(prompt, width, height, "ERROR", str(e)[:50])) | |
| return out, msg if lvl=="warning" else "" | |
| class VideoGen: | |
| def __init__(self): self._pipe = None | |
| def _init(self): | |
| if self._pipe is not None: return True | |
| try: | |
| import torch | |
| from diffusers import CogVideoXPipeline | |
| self._pipe = CogVideoXPipeline.from_pretrained("THUDM/CogVideoX-2b", | |
| torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32) | |
| if torch.cuda.is_available(): self._pipe.enable_model_cpu_offload() | |
| return True | |
| except Exception as e: | |
| self._err = str(e) | |
| return False | |
| def generate(self, prompt, num_frames, fps, seed): | |
| ok, lvl, msg = SAFETY.check_prompt(prompt) | |
| if not ok: return self._fallback(prompt, num_frames, fps, "BLOCKED"), msg | |
| if self._init(): | |
| try: | |
| import torch | |
| s = int(seed) if int(seed)!=-1 else random.randint(0, 2**32) | |
| gen = torch.Generator(device="cuda" if torch.cuda.is_available() else "cpu").manual_seed(s) | |
| v = self._pipe(prompt=prompt, num_frames=int(num_frames), num_inference_steps=50, | |
| guidance_scale=6.0, generator=gen).frames[0] | |
| path = f"/tmp/video_{int(time.time())}.mp4" | |
| from diffusers.utils import export_to_video | |
| export_to_video(v, path, fps=int(fps)) | |
| return path, msg if lvl=="warning" else "" | |
| except Exception as e: | |
| return self._fallback(prompt, num_frames, fps, str(e)[:50]), str(e) | |
| return self._fallback(prompt, num_frames, fps, self._err), self._err | |
| def _fallback(self, prompt, nf, fps, status=""): | |
| path = f"/tmp/vid_{int(time.time())}.mp4" | |
| frames = [] | |
| w, h = 512, 512 | |
| try: | |
| f = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 20) | |
| fs = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14) | |
| except: | |
| f = fs = ImageFont.load_default() | |
| for i in range(int(nf)): | |
| img = Image.new("RGB", (w,h), (20,20,30)) | |
| d = ImageDraw.Draw(img) | |
| off = int(15*np.sin(i*2*3.14159/max(nf,1))) | |
| d.text((w//2-100+off, 30), "AI Creative Suite - Video", fill=(200,200,255), font=f) | |
| p = prompt[:50]+"..." if len(prompt)>50 else prompt | |
| d.text((w//2-len(p)*4, h//2-20), p, fill=(180,180,200), font=fs) | |
| if status: d.text((20, h//2+10), f"Status: {status}", fill=(255,100,100), font=fs) | |
| bw = 300; bx = (w-bw)//2; by = h//2+40 | |
| pr = (i+1)/max(nf,1); fw = int(bw*pr) | |
| d.rectangle([bx,by,bx+bw,by+20], outline=(100,100,150), width=2) | |
| d.rectangle([bx+2,by+2,bx+fw-2,by+18], fill=(100,150,255)) | |
| frames.append(np.array(img)) | |
| try: | |
| import imageio | |
| imageio.mimsave(path, frames, fps=int(fps)) | |
| except: | |
| img = Image.new("RGB",(w,h),(20,20,30)); ImageDraw.Draw(img).text((50,200),"Video error",fill=(200,200,255)); img.save(path) | |
| return path | |
| class AudioGen: | |
| def __init__(self): | |
| self._music = None; self._sfx = None | |
| def _init_music(self, size): | |
| try: | |
| from transformers import AutoProcessor, MusicgenForConditionalGeneration | |
| p = AutoProcessor.from_pretrained(f"facebook/musicgen-{size}") | |
| m = MusicgenForConditionalGeneration.from_pretrained(f"facebook/musicgen-{size}") | |
| self._music = (p, m); return True | |
| except Exception as e: self._music_err = str(e); return False | |
| def _init_sfx(self): | |
| try: | |
| from transformers import AutoProcessor, AutoModelForTextToWaveform | |
| p = AutoProcessor.from_pretrained("facebook/audiogen-medium") | |
| m = AutoModelForTextToWaveform.from_pretrained("facebook/audiogen-medium") | |
| self._sfx = (p, m); return True | |
| except Exception as e: self._sfx_err = str(e); return False | |
| def generate_music(self, prompt, duration, seed, size): | |
| ok, lvl, msg = SAFETY.check_prompt(prompt) | |
| if not ok: return self._placeholder(float(duration), "BLOCKED"), msg | |
| if not self._music and not self._init_music(size): | |
| return self._placeholder(float(duration), self._music_err), self._music_err | |
| try: | |
| import torch | |
| p, m = self._music | |
| inputs = p(text=[prompt], padding=True, return_tensors="pt") | |
| mt = min(int(float(duration)*50), 1500) | |
| av = m.generate(**inputs, max_new_tokens=mt, do_sample=True, guidance_scale=3.0) | |
| path = f"/tmp/mus_{int(time.time())}.wav" | |
| import scipy.io.wavfile | |
| scipy.io.wavfile.write(path, rate=m.config.audio_encoder.sampling_rate, data=av[0,0].cpu().numpy()) | |
| return path, msg if lvl=="warning" else "" | |
| except Exception as e: return self._placeholder(float(duration), str(e)[:60]), str(e) | |
| def generate_sfx(self, prompt, duration, seed): | |
| ok, lvl, msg = SAFETY.check_prompt(prompt) | |
| if not ok: return self._placeholder(float(duration), "BLOCKED"), msg | |
| if not self._sfx and not self._init_sfx(): | |
| return self._placeholder(float(duration), self._sfx_err), self._sfx_err | |
| try: | |
| p, m = self._sfx | |
| inputs = p(text=[prompt], return_tensors="pt") | |
| mt = min(int(float(duration)*50), 1000) | |
| av = m.generate(**inputs, max_new_tokens=mt, do_sample=True) | |
| path = f"/tmp/sfx_{int(time.time())}.wav" | |
| import scipy.io.wavfile | |
| scipy.io.wavfile.write(path, rate=16000, data=av[0,0].cpu().numpy()) | |
| return path, msg if lvl=="warning" else "" | |
| except Exception as e: return self._placeholder(float(duration), str(e)[:60]), str(e) | |
| def _placeholder(self, dur, label=""): | |
| sr = 16000; s = np.zeros(int(sr*dur), np.float32) | |
| t = np.linspace(0,dur,len(s)) | |
| s += 0.1*np.sin(2*3.14159*440*t)*(t<0.5) | |
| s += 0.05*np.sin(2*3.14159*880*t)*((t>0.5)&(t<1.0)) | |
| path = f"/tmp/ph_{int(time.time())}.wav" | |
| try: | |
| import scipy.io.wavfile | |
| scipy.io.wavfile.write(path, rate=sr, data=s) | |
| except: | |
| with open(path, "wb") as f: pass | |
| return path | |
| class Enhancer: | |
| def upscale(self, image, scale): | |
| if image is None: return None | |
| w, h = image.size | |
| ns = (int(w*float(scale)), int(h*float(scale))) | |
| return SAFETY.watermark(image.resize(ns, Image.Resampling.LANCZOS)) | |
| def skin(self, image, strength): | |
| if image is None: return None | |
| img = np.array(image).astype(np.float32) | |
| sm = cv2.bilateralFilter(img.astype(np.uint8), 9, 75, 75) | |
| en = img*(1-float(strength)) + sm.astype(np.float32)*float(strength) | |
| k = np.array([[-1,-1,-1],[-1,9,-1],[-1,-1,-1]])*0.3 + np.array([[0,0,0],[0,1,0],[0,0,0]])*0.7 | |
| en = cv2.filter2D(en.astype(np.uint8), -1, k) | |
| return SAFETY.watermark(Image.fromarray(en)) | |
| class PlotGen: | |
| def __init__(self): | |
| self.tmpl = { | |
| "romance": {"scenes":["Meeting","Connection","Conflict","Resolution","Intimacy"], | |
| "beats":["first glance","shared secret","external obstacle","emotional breakthrough","physical closeness"]}, | |
| "adventure": {"scenes":["Departure","Trials","Discovery","Confrontation","Return"], | |
| "beats":["call to action","overcoming fear","hidden truth","final battle","changed perspective"]}, | |
| "mystery": {"scenes":["Incident","Investigation","Twist","Confrontation","Revelation"], | |
| "beats":["unexplained event","clue gathering","false lead","accusation","truth uncovered"]}, | |
| "fantasy": {"scenes":["Ordinary World","Crossing","Allies","Ordeal","Mastery"], | |
| "beats":["mundane life","portal opens","unlikely friendship","greatest fear","new power"]}, | |
| "thriller": {"scenes":["Calm","Disturbance","Escalation","Crisis","Aftermath"], | |
| "beats":["peaceful moment","unusual detail","stakes rise","point of no return","new normal"]}, | |
| "sci-fi": {"scenes":["Present","Anomaly","Exploration","Revelation","Transformation"], | |
| "beats":["technological world","strange signal","unknown territory","alien truth","human evolution"]} | |
| } | |
| self.emos = {"passionate":["intense gaze","trembling touch","racing heartbeats","heated whisper","burning desire"], | |
| "tense":["clenched jaw","narrowed eyes","heavy silence","shallow breathing","coiled energy"], | |
| "joyful":["bright laughter","warm embrace","sparkling eyes","relaxed posture","genuine smile"], | |
| "mysterious":["shadowed face","half-smile","glance over shoulder","unspoken knowledge","concealed intention"], | |
| "dark":["haunted expression","clenched fists","distant stare","sharp movements","controlled rage"]} | |
| def generate(self, genre, theme, tone, num_scenes, setting, chars): | |
| t = self.tmpl.get(genre, self.tmpl["romance"]) | |
| e = self.emos.get(tone, self.emos["passionate"]) | |
| n = min(int(num_scenes), len(t["scenes"])) | |
| out = f"# {genre.upper()} PLOT: {theme or 'Untitled'}\n\n" | |
| out += f"**Tone:** {tone} | **Setting:** {setting or 'Various'} | **Characters:** {chars or '2'}\n\n---\n\n" | |
| for i in range(n): | |
| sn = t["scenes"][i]; bt = t["beats"][i] if i<len(t["beats"]) else "development" | |
| em = random.choice(e) | |
| out += f"## Scene {i+1}: {sn}\n\n" | |
| out += f"**Theme:** {theme or 'journey'} | **Beat:** {bt} | **Emotion:** {em}\n\n" | |
| out += f"**Description:** {self._desc(sn, bt, tone, em, theme, setting, chars)}\n\n" | |
| out += f"**Actions:** {self._act(sn)}\n\n" | |
| out += f"**Visual Prompt:** {self._vis(sn, tone, theme, setting, em)}\n\n" | |
| out += f"**Audio:** {self._aud(sn, tone, em)}\n\n" | |
| out += f"**Duration:** {random.choice([3,5,8,10])}s\n\n---\n\n" | |
| out += "## Production Notes\n\n" | |
| out += f"- Runtime: ~{sum([random.choice([3,5,8,10]) for _ in range(n)])}s\n" | |
| out += f"- Color: {random.choice(['warm golds','cool blues','high contrast','muted earth','neon accents'])}\n" | |
| out += f"- Camera: {random.choice(['steady wides','intimate close-ups','handheld doc','sweeping drones'])}\n" | |
| out += f"- Pacing: {random.choice(['slow contemplative','fast cuts','builds to climax','rhythmic'])}\n" | |
| return out | |
| def _desc(self, s, b, t, e, th, st, c): | |
| defs = {k: [f"The {c or 'two'} in {st or 'the scene'}, {e}, {t} atmosphere."] for k,v in self.tmpl["romance"]["scenes"].items() if False} | |
| # default | |
| return f"The {t} scene unfolds in {st or 'the moment'}, carrying {th or 'the story'} with {e}." | |
| def _act(self, s): | |
| acts = {"Meeting":"exchange glances, approach cautiously, initiate conversation", | |
| "Connection":"lean in closer, share a secret, laugh together, touch hands", | |
| "Conflict":"turn away sharply, raise voice, clench fists, walk out", | |
| "Resolution":"embrace tightly, whisper apologies, hold each other", | |
| "Intimacy":"caress gently, move together, express love verbally"} | |
| return acts.get(s, "interact meaningfully, exchange looks") | |
| def _vis(self, s, t, th, st, e): | |
| v = {"Meeting":f"cinematic shot, characters first meeting in {st or 'elegant location'}, {t} atmosphere, {e}, dramatic lighting", | |
| "Connection":f"intimate scene, bonding, {t} mood, {st or 'soft lighting'}, {e}, emotional depth", | |
| "Conflict":f"dramatic confrontation, {t} tension, {st or 'dramatic location'}, {e}, high stakes", | |
| "Resolution":f"emotional resolution, {t} mood, {st or 'golden hour'}, {e}, beautiful cinematography", | |
| "Intimacy":f"intimate moment, {t} atmosphere, {st or 'warm space'}, {e}, tender connection, soft focus"} | |
| return v.get(s, f"cinematic scene, {t} mood, {st or 'dramatic lighting'}, {e}") | |
| def _aud(self, s, t, e): | |
| a = {"Meeting":f"soft ambient building to strings, {t} undertones, {e} harmonies", | |
| "Connection":f"gentle piano with emotional resonance, {t} harmonies, {e} textures", | |
| "Conflict":f"rising tension with percussion, {t} dissonance, {e} crescendo", | |
| "Resolution":f"orchestral triumph with brass, {t} resolution, {e} release", | |
| "Intimacy":f"soft sensual ambient pads, {t} warmth, {e} intimacy"} | |
| return a.get(s, f"{t} ambient soundtrack with {e} textures") | |
| class FilmEd: | |
| def __init__(self): self._proj = {} | |
| def create(self, name): | |
| if not name: return "Provide project name" | |
| self._proj[name] = {"scenes": [], "created": datetime.utcnow().isoformat()} | |
| return f"Project '{name}' created" | |
| def add(self, proj, img, dur, aud): | |
| if proj not in self._proj: return f"Create '{proj}' first" | |
| if img is None: return "Provide image" | |
| sid = len(self._proj[proj]["scenes"]) | |
| self._proj[proj]["scenes"].append({"id":sid, "image":img, "duration":float(dur), "audio":aud}) | |
| return f"Scene {sid} added. Total: {sid+1}" | |
| def render(self, proj, fps, trans): | |
| if proj not in self._proj: return None, f"Create '{proj}' first" | |
| sc = self._proj[proj]["scenes"] | |
| if not sc: return None, "No scenes" | |
| frames = [] | |
| for s in sc: | |
| img = s["image"].copy().convert("RGB").resize((512,512), Image.Resampling.LANCZOS) | |
| ov = Image.new("RGBA", img.size, (0,0,0,0)) | |
| d = ImageDraw.Draw(ov) | |
| try: fnt = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 16) | |
| except: fnt = ImageFont.load_default() | |
| d.rectangle([5,5,200,30], fill=(0,0,0,150)) | |
| d.text((10,8), f"Scene {s['id']} | {s['duration']}s", fill=(255,255,255), font=fnt) | |
| if trans != "none": | |
| tt = f"Transition: {trans}" | |
| d.rectangle([512-len(tt)*9-20,5,507,30], fill=(0,0,0,150)) | |
| d.text((512-len(tt)*9-15,8), tt, fill=(200,200,255), font=fnt) | |
| d.rectangle([5,487,120,507], fill=(0,0,0,150)) | |
| d.text((10,490), "AI GENERATED", fill=(255,255,255), font=fnt) | |
| img = Image.alpha_composite(img.convert("RGBA"), ov).convert("RGB") | |
| for _ in range(int(s["duration"]*int(fps))): frames.append(np.array(img)) | |
| path = f"/tmp/film_{proj}_{int(time.time())}.mp4" | |
| try: | |
| import imageio | |
| imageio.mimsave(path, frames, fps=int(fps)) | |
| except Exception as e: | |
| img = Image.new("RGB",(512,512),(20,20,30)); ImageDraw.Draw(img).text((50,200),f"Film: {proj}",fill=(200,200,255)); ImageDraw.Draw(img).text((50,250),f"Err: {str(e)[:40]}",fill=(150,150,180)); img.save(path) | |
| return path, f"Film '{proj}' rendered: {len(sc)} scenes at {fps} fps" | |
| class PoseEd: | |
| def extract(self, image): | |
| if image is None: return None | |
| try: | |
| from controlnet_aux import OpenposeDetector | |
| return OpenposeDetector.from_pretrained("lllyasviel/ControlNet")(image) | |
| except: | |
| e = cv2.Canny(np.array(image.convert("L")), 100, 200) | |
| return Image.fromarray(cv2.cvtColor(e, cv2.COLOR_GRAY2RGB)) | |
| def views(self, image, prompt, n): | |
| if image is None: return [], "No image" | |
| ok, lvl, msg = SAFETY.check_prompt(prompt) | |
| if not ok: return [placeholder(prompt, image.size[0], image.size[1], "BLOCKED")]*int(n), msg | |
| v = ["front view","side profile","three-quarter view","back view","high angle","low angle","close-up","wide shot"] | |
| r = [] | |
| for a in v[:int(n)]: | |
| img = image.copy().convert("RGB") | |
| ov = Image.new("RGBA", img.size, (0,0,0,0)) | |
| d = ImageDraw.Draw(ov) | |
| try: fnt = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 32) | |
| except: fnt = ImageFont.load_default() | |
| d.rectangle([img.size[0]//2-60,20,img.size[0]//2+60,60], fill=(0,0,0,180)) | |
| d.text((img.size[0]//2-50,25), a.upper(), fill=(255,200,100), font=fnt) | |
| r.append(SAFETY.watermark(Image.alpha_composite(img.convert("RGBA"), ov).convert("RGB"), {"view":a})) | |
| return r, msg if lvl=="warning" else "" | |
| class VarGen: | |
| def gen(self, image, prompt, n, seed): | |
| if image is None: return [], "No image" | |
| ok, lvl, msg = SAFETY.check_prompt(prompt) | |
| if not ok: return [placeholder(prompt, image.size[0], image.size[1], "BLOCKED")]*int(n), msg | |
| styles = ["cinematic","oil painting","digital art","watercolor","film noir","neon glow","golden hour","studio lighting"] | |
| r = [] | |
| for i, s in enumerate(styles[:int(n)]): | |
| img = image.copy().convert("RGB") | |
| if "cinematic" in s or "film" in s: img = img.filter(ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3)) | |
| elif "oil" in s: img = img.filter(ImageFilter.MedianFilter(size=5)) | |
| elif "watercolor" in s: img = img.filter(ImageFilter.SMOOTH_MORE) | |
| elif "neon" in s: | |
| try: | |
| from PIL import ImageEnhance | |
| img = ImageEnhance.Color(img).enhance(2.0) | |
| except: pass | |
| elif "golden" in s: | |
| img = Image.blend(img, Image.new("RGB", img.size, (255,200,100)), 0.15) | |
| ov = Image.new("RGBA", img.size, (0,0,0,0)) | |
| d = ImageDraw.Draw(ov) | |
| try: fnt = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 28) | |
| except: fnt = ImageFont.load_default() | |
| l = f"Var {i+1}: {s}" | |
| d.rectangle([10,img.size[1]-50,10+len(l)*14+20,img.size[1]-10], fill=(0,0,0,180)) | |
| d.text((20,img.size[1]-45), l, fill=(200,255,200), font=fnt) | |
| r.append(SAFETY.watermark(Image.alpha_composite(img.convert("RGBA"), ov).convert("RGB"), {"style":s})) | |
| return r, msg if lvl=="warning" else "" | |
| def build_ui(): | |
| with gr.Blocks(title="AI Creative Production Suite", css=""" | |
| .safety-notice { background:#fff3cd; border-left:4px solid #ffc107; padding:12px; margin:10px 0; border-radius:4px; } | |
| .info-box { background:#e7f3ff; border-left:4px solid #2196F3; padding:12px; margin:10px 0; border-radius:4px; } | |
| """) as demo: | |
| gr.Markdown(""" | |
| # 🎬 AI Creative Production Suite | |
| <div class="safety-notice"> | |
| ⚠️ <strong>Safety:</strong> Watermarked outputs. Fictional characters only. No face-swap or voice cloning. | |
| </div> | |
| """) | |
| with gr.Tab("Image Generator"): | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gp = gr.Textbox(label="Prompt", placeholder="cinematic portrait of a fantasy warrior, dramatic lighting", lines=3) | |
| gn = gr.Textbox(label="Negative Prompt", value="blurry, low quality, distorted, deformed, extra limbs", lines=2) | |
| with gr.Row(): | |
| gw = gr.Slider(512, 1536, 1024, 64, label="Width") | |
| gh = gr.Slider(512, 1536, 1024, 64, label="Height") | |
| with gr.Row(): | |
| gs = gr.Slider(10, 100, 30, 1, label="Steps") | |
| gg = gr.Slider(1, 20, 7.5, 0.5, label="Guidance") | |
| with gr.Row(): | |
| gd = gr.Number(-1, label="Seed (-1=random)", precision=0) | |
| gn_img = gr.Slider(1, 4, 1, 1, label="Num Images") | |
| gb = gr.Button("Generate", variant="primary") | |
| gw_txt = gr.Textbox(label="Safety Check", interactive=False) | |
| with gr.Column(scale=3): | |
| go = gr.Gallery(label="Generated Images", columns=2, rows=2) | |
| with gr.Tab("Image Enhancer"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| ei = gr.Image(label="Input Image", type="pil") | |
| es = gr.Slider(1, 4, 2, 0.5, label="Upscale Factor") | |
| esk = gr.Slider(0, 1, 0.5, 0.1, label="Skin Texture Strength") | |
| with gr.Row(): | |
| ebu = gr.Button("Upscale") | |
| ebs = gr.Button("Enhance Texture") | |
| with gr.Column(): | |
| eo = gr.Image(label="Enhanced Result") | |
| with gr.Tab("Pose Editor"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| pi = gr.Image(label="Character Reference", type="pil") | |
| pp = gr.Textbox(label="Character Description", placeholder="female warrior with silver armor", lines=2) | |
| pn = gr.Slider(1, 8, 4, 1, label="Number of Views") | |
| with gr.Row(): | |
| pbx = gr.Button("Extract Pose") | |
| pbg = gr.Button("Generate Views", variant="primary") | |
| pw = gr.Textbox(label="Safety Check", interactive=False) | |
| with gr.Column(): | |
| po = gr.Gallery(label="Generated Views", columns=2, rows=2) | |
| with gr.Tab("Image Variations"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| vi = gr.Image(label="Reference Image", type="pil") | |
| vp = gr.Textbox(label="Base Prompt", placeholder="portrait of a character", lines=2) | |
| vn = gr.Slider(1, 8, 4, 1, label="Variations") | |
| vs = gr.Number(-1, label="Seed", precision=0) | |
| vb = gr.Button("Generate Variations", variant="primary") | |
| vw = gr.Textbox(label="Safety Check", interactive=False) | |
| with gr.Column(): | |
| vo = gr.Gallery(label="Style Variations", columns=2, rows=2) | |
| with gr.Tab("Video Generator"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| vdp = gr.Textbox(label="Video Prompt", placeholder="slow motion ocean waves, golden hour, cinematic", lines=3) | |
| with gr.Row(): | |
| vdf = gr.Slider(16, 49, 25, 1, label="Frames") | |
| vdfps = gr.Slider(4, 30, 8, 1, label="FPS") | |
| vds = gr.Number(-1, label="Seed", precision=0) | |
| vdb = gr.Button("Generate Video", variant="primary") | |
| vdw = gr.Textbox(label="Safety Check", interactive=False) | |
| with gr.Column(): | |
| vdo = gr.Video(label="Generated Video") | |
| with gr.Tab("Audio Generator"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("#### Music") | |
| amp = gr.Textbox(label="Music Prompt", placeholder="romantic orchestral music, soft piano", lines=2) | |
| with gr.Row(): | |
| amd = gr.Slider(5, 60, 10, 5, label="Duration (s)") | |
| amsz = gr.Radio(["small","medium","large"], value="small", label="Size") | |
| ams = gr.Number(-1, label="Seed", precision=0) | |
| amb = gr.Button("Generate Music", variant="primary") | |
| amw = gr.Textbox(label="Safety Check", interactive=False) | |
| with gr.Column(): | |
| amo = gr.Audio(label="Generated Music", type="filepath") | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("#### Sound Effects") | |
| asp = gr.Textbox(label="SFX Prompt", placeholder="rain on window, footsteps on gravel", lines=2) | |
| with gr.Row(): | |
| asd = gr.Slider(1, 30, 5, 1, label="Duration (s)") | |
| ass = gr.Number(-1, label="Seed", precision=0) | |
| asb = gr.Button("Generate SFX") | |
| asw = gr.Textbox(label="Safety Check", interactive=False) | |
| with gr.Column(): | |
| aso = gr.Audio(label="Generated SFX", type="filepath") | |
| gr.Markdown("<div class='safety-notice'>⚠️ Instrumental music and environmental sounds only. No voice cloning.</div>") | |
| with gr.Tab("Plot Generator"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| plg = gr.Dropdown(["romance","adventure","mystery","fantasy","thriller","sci-fi"], value="romance", label="Genre") | |
| plt = gr.Textbox(label="Theme/Topic", placeholder="forbidden love, time travel") | |
| plto = gr.Dropdown(["passionate","tense","joyful","mysterious","dark"], value="passionate", label="Tone") | |
| with gr.Row(): | |
| pln = gr.Slider(3, 10, 5, 1, label="Scenes") | |
| plc = gr.Textbox(label="Characters", value="2") | |
| pls = gr.Textbox(label="Setting", placeholder="Victorian mansion, futuristic city") | |
| plb = gr.Button("Generate Plot", variant="primary") | |
| with gr.Column(scale=2): | |
| plo = gr.Textbox(label="Generated Plot", lines=40) | |
| with gr.Tab("Film Editor"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("#### Project") | |
| fpn = gr.Textbox(label="Project Name", value="my_film") | |
| with gr.Row(): | |
| fpc = gr.Button("Create Project") | |
| fps = gr.Textbox(label="Status", interactive=False) | |
| gr.Markdown("---") | |
| gr.Markdown("#### Add Scene") | |
| fsi = gr.Image(label="Scene Image", type="pil") | |
| with gr.Row(): | |
| fsd = gr.Slider(1, 30, 5, 1, label="Duration (s)") | |
| fsa = gr.Audio(label="Audio (optional)", type="filepath") | |
| fpa = gr.Button("Add Scene") | |
| gr.Markdown("---") | |
| gr.Markdown("#### Render") | |
| with gr.Row(): | |
| fpf = gr.Slider(12, 60, 24, 1, label="FPS") | |
| fpt = gr.Dropdown(["none","fade","cut","wipe"], value="fade", label="Transition") | |
| fpr = gr.Button("Render Film", variant="primary") | |
| with gr.Column(): | |
| fpo = gr.Video(label="Rendered Film") | |
| fprs = gr.Textbox(label="Render Status", interactive=False) | |
| with gr.Tab("Prompt Helper"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| phb = gr.Textbox(label="Basic Prompt", placeholder="portrait of a warrior", lines=2) | |
| with gr.Row(): | |
| phst = gr.Dropdown(["cinematic","oil painting","digital art","anime","photorealistic","fantasy art","watercolor","film noir"], value="cinematic", label="Style") | |
| phl = gr.Dropdown(["golden hour","dramatic lighting","soft natural","neon","moonlight","studio lighting","volumetric fog"], value="dramatic lighting", label="Lighting") | |
| phq = gr.Dropdown(["masterpiece","highly detailed","8k resolution","concept art","trending on artstation","award winning"], value="masterpiece, highly detailed", label="Quality") | |
| phc = gr.Dropdown(["close-up portrait","wide shot","medium shot","extreme close-up","overhead shot","low angle"], value="close-up portrait", label="Camera") | |
| phbtn = gr.Button("Enhance Prompt", variant="primary") | |
| with gr.Column(): | |
| pho = gr.Textbox(label="Enhanced Prompt", lines=6) | |
| phn = gr.Textbox(label="Negative Prompt", value="blurry, low quality, distorted, deformed, bad anatomy, extra limbs, watermark, signature, amateur, worst quality, low resolution", lines=2) | |
| # Handlers | |
| ig = ImageGen() | |
| gb.click(fn=lambda *a: (ig.generate(*a)[0], ig.generate(*a)[1]), | |
| inputs=[gp,gn,gw,gh,gs,gg,gd,gn_img], outputs=[go,gw_txt]) | |
| en = Enhancer() | |
| ebu.click(fn=en.upscale, inputs=[ei,es], outputs=eo) | |
| ebs.click(fn=en.skin, inputs=[ei,esk], outputs=eo) | |
| pe = PoseEd() | |
| pbx.click(fn=pe.extract, inputs=pi, outputs=po) | |
| pbg.click(fn=lambda i,p,n: pe.views(i,p,n), inputs=[pi,pp,pn], outputs=[po,pw]) | |
| vg = VarGen() | |
| vb.click(fn=lambda i,p,n,s: vg.gen(i,p,n,s), inputs=[vi,vp,vn,vs], outputs=[vo,vw]) | |
| vdg = VideoGen() | |
| vdb.click(fn=vdg.generate, inputs=[vdp,vdf,vdfps,vds], outputs=[vdo,vdw]) | |
| ag = AudioGen() | |
| amb.click(fn=ag.generate_music, inputs=[amp,amd,ams,amsz], outputs=[amo,amw]) | |
| asb.click(fn=ag.generate_sfx, inputs=[asp,asd,ass], outputs=[aso,asw]) | |
| pg = PlotGen() | |
| plb.click(fn=pg.generate, inputs=[plg,plt,plto,pln,pls,plc], outputs=plo) | |
| fe = FilmEd() | |
| fpc.click(fn=fe.create, inputs=fpn, outputs=fps) | |
| fpa.click(fn=fe.add, inputs=[fpn,fsi,fsd,fsa], outputs=fps) | |
| fpr.click(fn=lambda p,f,t: fe.render(p,f,t), inputs=[fpn,fpf,fpt], outputs=[fpo,fprs]) | |
| def ph_enh(basic, style, lighting, quality, camera): | |
| if not basic: return "","" | |
| return f"{camera}, {basic}, {style}, {lighting}, {quality}, best quality, sharp focus", "blurry, low quality, distorted, deformed, bad anatomy, extra limbs, watermark, signature, amateur, worst quality, low resolution" | |
| phbtn.click(fn=ph_enh, inputs=[phb,phst,phl,phq,phc], outputs=[pho,phn]) | |
| return demo | |
| if __name__ == "__main__": | |
| demo = build_ui() | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |