| """4K Upscaler Pro — Premium AI-powered upscaler with Lumina Synth design.""" |
|
|
| |
| |
| |
| |
| import gradio_client.utils as _gc_utils |
|
|
| _original_json_schema_to_python_type = _gc_utils._json_schema_to_python_type |
|
|
|
|
| def _patched_json_schema_to_python_type(schema, defs=None): |
| """Safe wrapper that handles bool schemas.""" |
| if isinstance(schema, bool): |
| return "Any" |
| |
| if isinstance(schema, dict) and "additionalProperties" in schema: |
| if isinstance(schema["additionalProperties"], bool): |
| schema = {k: v for k, v in schema.items() if k != "additionalProperties"} |
| return _original_json_schema_to_python_type(schema, defs) |
|
|
|
|
| _gc_utils._json_schema_to_python_type = _patched_json_schema_to_python_type |
|
|
| _original_get_type = _gc_utils.get_type |
|
|
|
|
| def _patched_get_type(schema): |
| """Safe wrapper that handles bool schemas in get_type.""" |
| if isinstance(schema, bool): |
| return "Any" |
| if not isinstance(schema, dict): |
| return "Any" |
| return _original_get_type(schema) |
|
|
|
|
| _gc_utils.get_type = _patched_get_type |
| |
|
|
| import gradio as gr |
| from core import upscale_image, upscale_video |
|
|
| |
| |
| |
| CSS = """ |
| @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap'); |
| |
| /* ── Design Tokens ── */ |
| :root { |
| --bg: #0e0e13; |
| --surface: #131319; |
| --surface-high: #1f1f26; |
| --surface-highest: #25252d; |
| --surface-bright: #2c2b33; |
| --primary: #ba9eff; |
| --primary-dim: #8455ef; |
| --secondary: #53ddfc; |
| --secondary-dim: #40ceed; |
| --tertiary: #ec63ff; |
| --on-surface: #f9f5fd; |
| --on-surface-variant: #acaab1; |
| --outline: #76747b; |
| --outline-variant: #48474d; |
| --error: #ff6e84; |
| --gradient-primary: linear-gradient(135deg, #8455ef, #53ddfc); |
| --gradient-glow: linear-gradient(135deg, rgba(132,85,239,0.4), rgba(83,221,252,0.4)); |
| --glass-bg: rgba(19, 19, 25, 0.75); |
| --glass-border: rgba(72, 71, 77, 0.25); |
| --radius-md: 16px; |
| --radius-lg: 24px; |
| --radius-full: 9999px; |
| --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| } |
| |
| /* ── Global Reset ── */ |
| *, *::before, *::after { box-sizing: border-box; } |
| |
| body, .gradio-container { |
| background: var(--bg) !important; |
| font-family: 'Inter', -apple-system, sans-serif !important; |
| color: var(--on-surface) !important; |
| min-height: 100vh; |
| } |
| |
| .gradio-container { |
| max-width: 1400px !important; |
| margin: 0 auto !important; |
| padding: 0 24px !important; |
| } |
| |
| /* ── Ambient Background Glow ── */ |
| .gradio-container::before { |
| content: ''; |
| position: fixed; |
| top: -50%; |
| left: -50%; |
| width: 200%; |
| height: 200%; |
| background: radial-gradient(ellipse at 30% 20%, rgba(132,85,239,0.08) 0%, transparent 50%), |
| radial-gradient(ellipse at 70% 80%, rgba(83,221,252,0.05) 0%, transparent 50%); |
| pointer-events: none; |
| z-index: 0; |
| } |
| |
| /* ── Hero Title ── */ |
| .hero-title { |
| text-align: center; |
| padding: 48px 0 8px; |
| } |
| |
| .hero-title h1 { |
| font-family: 'Space Grotesk', sans-serif !important; |
| font-size: 3.2rem !important; |
| font-weight: 700 !important; |
| letter-spacing: -0.03em !important; |
| background: var(--gradient-primary) !important; |
| -webkit-background-clip: text !important; |
| -webkit-text-fill-color: transparent !important; |
| background-clip: text !important; |
| line-height: 1.1 !important; |
| margin: 0 !important; |
| padding: 0 !important; |
| } |
| |
| .hero-subtitle p { |
| text-align: center !important; |
| color: var(--on-surface-variant) !important; |
| font-size: 1.05rem !important; |
| font-weight: 400 !important; |
| letter-spacing: 0.01em !important; |
| margin-top: 8px !important; |
| padding-bottom: 24px !important; |
| } |
| |
| /* ── Badge ── */ |
| .status-badge p { |
| display: inline-block !important; |
| background: rgba(132, 85, 239, 0.15) !important; |
| border: 1px solid rgba(186, 158, 255, 0.2) !important; |
| color: var(--primary) !important; |
| font-family: 'Inter', sans-serif !important; |
| font-size: 0.7rem !important; |
| font-weight: 600 !important; |
| text-transform: uppercase !important; |
| letter-spacing: 0.08em !important; |
| padding: 4px 14px !important; |
| border-radius: var(--radius-full) !important; |
| text-align: center !important; |
| width: auto !important; |
| margin: 0 auto !important; |
| } |
| |
| /* ── Tabs — Glowing Indicators ── */ |
| .tab-nav { |
| display: flex !important; |
| justify-content: center !important; |
| gap: 4px !important; |
| padding: 6px !important; |
| background: var(--surface) !important; |
| border-radius: var(--radius-full) !important; |
| border: 1px solid var(--glass-border) !important; |
| margin-bottom: 32px !important; |
| width: fit-content !important; |
| margin-left: auto !important; |
| margin-right: auto !important; |
| } |
| |
| .tab-nav button { |
| font-family: 'Inter', sans-serif !important; |
| font-size: 0.875rem !important; |
| font-weight: 500 !important; |
| color: var(--on-surface-variant) !important; |
| background: transparent !important; |
| border: none !important; |
| padding: 10px 28px !important; |
| border-radius: var(--radius-full) !important; |
| cursor: pointer !important; |
| transition: var(--transition) !important; |
| position: relative !important; |
| } |
| |
| .tab-nav button:hover { |
| color: var(--on-surface) !important; |
| background: rgba(132, 85, 239, 0.08) !important; |
| } |
| |
| .tab-nav button.selected { |
| background: var(--gradient-primary) !important; |
| color: #fff !important; |
| font-weight: 600 !important; |
| box-shadow: 0 0 20px rgba(132, 85, 239, 0.4), |
| 0 0 60px rgba(83, 221, 252, 0.15) !important; |
| } |
| |
| /* ── Glass Panels ── */ |
| .glass-panel { |
| background: var(--glass-bg) !important; |
| backdrop-filter: blur(24px) !important; |
| -webkit-backdrop-filter: blur(24px) !important; |
| border-radius: var(--radius-lg) !important; |
| border: 1px solid var(--glass-border) !important; |
| padding: 28px !important; |
| transition: var(--transition) !important; |
| position: relative !important; |
| overflow: hidden !important; |
| } |
| |
| .glass-panel::before { |
| content: ''; |
| position: absolute; |
| top: 0; left: 0; right: 0; |
| height: 1px; |
| background: linear-gradient(90deg, transparent, rgba(186,158,255,0.3), transparent); |
| pointer-events: none; |
| } |
| |
| .glass-panel:hover { |
| border-color: rgba(132, 85, 239, 0.2) !important; |
| box-shadow: 0 0 40px rgba(132, 85, 239, 0.06) !important; |
| } |
| |
| /* ── Section Labels ── */ |
| .section-label p { |
| font-family: 'Inter', sans-serif !important; |
| font-size: 0.7rem !important; |
| font-weight: 600 !important; |
| text-transform: uppercase !important; |
| letter-spacing: 0.1em !important; |
| color: var(--on-surface-variant) !important; |
| margin-bottom: 12px !important; |
| } |
| |
| /* ── Input Components ── */ |
| .gradio-image, .gradio-video { |
| border-radius: var(--radius-md) !important; |
| overflow: hidden !important; |
| } |
| |
| .gradio-image .image-container, |
| .gradio-image .upload-container { |
| background: var(--surface) !important; |
| border: 1.5px dashed var(--outline-variant) !important; |
| border-radius: var(--radius-md) !important; |
| min-height: 280px !important; |
| transition: var(--transition) !important; |
| } |
| |
| .gradio-image .upload-container:hover { |
| border-color: var(--secondary) !important; |
| background: var(--surface-high) !important; |
| box-shadow: 0 0 30px rgba(83, 221, 252, 0.08) !important; |
| } |
| |
| /* ── Radio Buttons — Pill Style ── */ |
| .gradio-radio label { |
| background: var(--surface-high) !important; |
| border: 1px solid transparent !important; |
| border-radius: var(--radius-full) !important; |
| padding: 8px 20px !important; |
| color: var(--on-surface-variant) !important; |
| font-size: 0.85rem !important; |
| font-weight: 500 !important; |
| cursor: pointer !important; |
| transition: var(--transition) !important; |
| } |
| |
| .gradio-radio label:hover { |
| background: var(--surface-bright) !important; |
| color: var(--on-surface) !important; |
| } |
| |
| .gradio-radio label.selected { |
| background: var(--primary-dim) !important; |
| color: #fff !important; |
| border-color: var(--primary) !important; |
| box-shadow: 0 0 15px rgba(132, 85, 239, 0.3) !important; |
| } |
| |
| /* ── Dropdown ── */ |
| .gradio-dropdown { |
| border-radius: var(--radius-md) !important; |
| } |
| |
| .gradio-dropdown select, .gradio-dropdown input { |
| background: var(--surface-high) !important; |
| border: 1px solid var(--outline-variant) !important; |
| border-radius: var(--radius-md) !important; |
| color: var(--on-surface) !important; |
| padding: 10px 16px !important; |
| font-size: 0.875rem !important; |
| transition: var(--transition) !important; |
| } |
| |
| .gradio-dropdown select:focus, .gradio-dropdown input:focus { |
| border-color: var(--primary) !important; |
| box-shadow: 0 0 0 3px rgba(132, 85, 239, 0.15) !important; |
| } |
| |
| /* ── Primary Button — Gradient with Glow ── */ |
| .gr-button-primary, button.primary { |
| background: var(--gradient-primary) !important; |
| border: none !important; |
| border-radius: var(--radius-full) !important; |
| color: #fff !important; |
| font-family: 'Inter', sans-serif !important; |
| font-weight: 600 !important; |
| font-size: 0.95rem !important; |
| letter-spacing: 0.02em !important; |
| padding: 14px 32px !important; |
| cursor: pointer !important; |
| transition: var(--transition) !important; |
| position: relative !important; |
| overflow: hidden !important; |
| text-shadow: 0 1px 2px rgba(0,0,0,0.3) !important; |
| } |
| |
| .gr-button-primary::before, button.primary::before { |
| content: ''; |
| position: absolute; |
| inset: 0; |
| background: linear-gradient(135deg, rgba(255,255,255,0.15), transparent); |
| pointer-events: none; |
| } |
| |
| .gr-button-primary:hover, button.primary:hover { |
| transform: translateY(-1px) !important; |
| box-shadow: 0 8px 30px rgba(132, 85, 239, 0.4), |
| 0 0 60px rgba(83, 221, 252, 0.2) !important; |
| } |
| |
| .gr-button-primary:active, button.primary:active { |
| transform: translateY(0) !important; |
| } |
| |
| /* ── Secondary Button ── */ |
| .gr-button-secondary, button.secondary { |
| background: transparent !important; |
| border: 1px solid var(--outline-variant) !important; |
| border-radius: var(--radius-full) !important; |
| color: var(--secondary) !important; |
| font-weight: 500 !important; |
| padding: 12px 24px !important; |
| transition: var(--transition) !important; |
| } |
| |
| .gr-button-secondary:hover, button.secondary:hover { |
| border-color: var(--secondary) !important; |
| background: rgba(83, 221, 252, 0.08) !important; |
| box-shadow: 0 0 20px rgba(83, 221, 252, 0.15) !important; |
| } |
| |
| /* ── Info Textbox ── */ |
| .gradio-textbox textarea, .gradio-textbox input { |
| background: var(--surface) !important; |
| border: 1px solid var(--outline-variant) !important; |
| border-radius: var(--radius-md) !important; |
| color: var(--on-surface) !important; |
| font-family: 'Inter', monospace !important; |
| font-size: 0.85rem !important; |
| line-height: 1.6 !important; |
| } |
| |
| /* ── File Download ── */ |
| .gradio-file { |
| border-radius: var(--radius-md) !important; |
| } |
| |
| /* ── Progress Bar — Neon Gradient ── */ |
| .progress-bar { |
| background: var(--surface) !important; |
| border-radius: var(--radius-full) !important; |
| overflow: hidden !important; |
| } |
| |
| .progress-bar > div { |
| background: var(--gradient-primary) !important; |
| box-shadow: 0 0 15px rgba(132, 85, 239, 0.5) !important; |
| transition: width 0.4s ease !important; |
| } |
| |
| /* Gradio's built-in progress */ |
| .progress-level { |
| background: var(--gradient-primary) !important; |
| } |
| |
| .progress-level span { |
| color: #fff !important; |
| font-family: 'Inter', sans-serif !important; |
| font-weight: 500 !important; |
| } |
| |
| .meta-text span { |
| color: var(--on-surface-variant) !important; |
| font-size: 0.85rem !important; |
| } |
| |
| /* ── Loading Shimmer Animation ── */ |
| @keyframes shimmer { |
| 0% { background-position: -200% 0; } |
| 100% { background-position: 200% 0; } |
| } |
| |
| @keyframes pulse-glow { |
| 0%, 100% { box-shadow: 0 0 20px rgba(132, 85, 239, 0.2); } |
| 50% { box-shadow: 0 0 40px rgba(132, 85, 239, 0.5), 0 0 80px rgba(83, 221, 252, 0.2); } |
| } |
| |
| @keyframes spin { |
| to { transform: rotate(360deg); } |
| } |
| |
| .generating { |
| animation: pulse-glow 2s ease-in-out infinite !important; |
| } |
| |
| /* ── Scrollbar ── */ |
| ::-webkit-scrollbar { width: 6px; } |
| ::-webkit-scrollbar-track { background: var(--bg); } |
| ::-webkit-scrollbar-thumb { |
| background: var(--outline-variant); |
| border-radius: 3px; |
| } |
| ::-webkit-scrollbar-thumb:hover { background: var(--outline); } |
| |
| /* ── Labels ── */ |
| label, .label-wrap { |
| color: var(--on-surface-variant) !important; |
| font-weight: 500 !important; |
| font-size: 0.85rem !important; |
| } |
| |
| /* ── Markdown in API tab ── */ |
| .prose { |
| color: var(--on-surface) !important; |
| } |
| |
| .prose h2, .prose h3 { |
| font-family: 'Space Grotesk', sans-serif !important; |
| color: var(--on-surface) !important; |
| } |
| |
| .prose code { |
| background: var(--surface-high) !important; |
| color: var(--secondary) !important; |
| padding: 2px 6px !important; |
| border-radius: 6px !important; |
| font-size: 0.85em !important; |
| } |
| |
| .prose pre { |
| background: var(--surface) !important; |
| border: 1px solid var(--outline-variant) !important; |
| border-radius: var(--radius-md) !important; |
| padding: 16px !important; |
| } |
| |
| .prose pre code { |
| background: transparent !important; |
| color: var(--on-surface) !important; |
| } |
| |
| .prose hr { |
| border-color: var(--outline-variant) !important; |
| opacity: 0.3 !important; |
| } |
| |
| .prose strong { |
| color: var(--primary) !important; |
| } |
| |
| /* ── Footer Hide ── */ |
| footer { display: none !important; } |
| |
| /* ── Result Card Styling ── */ |
| .result-card { |
| background: var(--surface) !important; |
| border: 1px solid var(--glass-border) !important; |
| border-radius: var(--radius-lg) !important; |
| padding: 20px !important; |
| position: relative !important; |
| } |
| |
| .result-card::after { |
| content: ''; |
| position: absolute; |
| bottom: 0; left: 0; right: 0; |
| height: 1px; |
| background: linear-gradient(90deg, transparent, rgba(83, 221, 252, 0.3), transparent); |
| pointer-events: none; |
| } |
| """ |
|
|
| |
| |
| |
|
|
| RESOLUTIONS = [ |
| "4K (3840×2160)", |
| "2K (2560×1440)", |
| "1080p (1920×1080)", |
| "2x Original", |
| ] |
|
|
| METHODS = ["Real-ESRGAN (AI)", "Lanczos (Fast)"] |
|
|
|
|
| def process_image(image_path, method, target, progress=gr.Progress()): |
| """Wrapper that bridges Gradio progress to core function.""" |
| return upscale_image(image_path, method, target, progress=progress) |
|
|
|
|
| def process_video(video_path, method, target, progress=gr.Progress()): |
| """Wrapper that bridges Gradio progress to core function.""" |
| return upscale_video(video_path, method, target, progress=progress) |
|
|
|
|
| with gr.Blocks( |
| css=CSS, |
| title="4K Upscaler Pro — AI Image & Video Enhancement", |
| theme=gr.themes.Base( |
| primary_hue=gr.themes.colors.purple, |
| secondary_hue=gr.themes.colors.cyan, |
| neutral_hue=gr.themes.colors.gray, |
| font=gr.themes.GoogleFont("Inter"), |
| font_mono=gr.themes.GoogleFont("Space Mono"), |
| ), |
| ) as demo: |
|
|
| |
| gr.Markdown("# ⬆ 4K Upscaler Pro", elem_classes=["hero-title"]) |
| gr.Markdown( |
| "AI-powered image & video enhancement • Up to 4K resolution • REST API ready", |
| elem_classes=["hero-subtitle"], |
| ) |
| gr.Markdown("✦ POWERED BY REAL-ESRGAN", elem_classes=["status-badge"]) |
|
|
| with gr.Tabs(): |
| |
| with gr.TabItem("🖼 Image Upscaler", id="image-tab"): |
| with gr.Row(equal_height=True): |
| with gr.Column(scale=1, elem_classes=["glass-panel"]): |
| gr.Markdown("**INPUT**", elem_classes=["section-label"]) |
| img_input = gr.Image( |
| type="filepath", |
| label="Upload Image", |
| height=300, |
| sources=["upload", "clipboard"], |
| ) |
| img_method = gr.Radio( |
| METHODS, |
| value="Lanczos (Fast)", |
| label="Upscaling Method", |
| ) |
| img_target = gr.Dropdown( |
| RESOLUTIONS, |
| value="4K (3840×2160)", |
| label="Target Resolution", |
| ) |
| img_btn = gr.Button( |
| "⬆ Upscale Image", |
| variant="primary", |
| size="lg", |
| ) |
|
|
| with gr.Column(scale=1, elem_classes=["glass-panel"]): |
| gr.Markdown("**RESULT**", elem_classes=["section-label"]) |
| img_output = gr.Image( |
| label="Upscaled Result", |
| height=300, |
| interactive=False, |
| ) |
| img_info = gr.Textbox( |
| label="Processing Details", |
| interactive=False, |
| lines=3, |
| ) |
| img_download = gr.File(label="⬇ Download PNG") |
|
|
| img_btn.click( |
| process_image, |
| inputs=[img_input, img_method, img_target], |
| outputs=[img_output, img_info, img_download], |
| ) |
|
|
| |
| with gr.TabItem("🎬 Video Upscaler", id="video-tab"): |
| with gr.Row(equal_height=True): |
| with gr.Column(scale=1, elem_classes=["glass-panel"]): |
| gr.Markdown("**INPUT**", elem_classes=["section-label"]) |
| vid_input = gr.Video(label="Upload Video", height=300) |
| vid_method = gr.Radio( |
| METHODS, |
| value="Lanczos (Fast)", |
| label="Upscaling Method", |
| ) |
| vid_target = gr.Dropdown( |
| RESOLUTIONS, |
| value="4K (3840×2160)", |
| label="Target Resolution", |
| ) |
| vid_btn = gr.Button( |
| "⬆ Upscale Video", |
| variant="primary", |
| size="lg", |
| ) |
|
|
| with gr.Column(scale=1, elem_classes=["glass-panel"]): |
| gr.Markdown("**RESULT**", elem_classes=["section-label"]) |
| vid_output = gr.Video( |
| label="Upscaled Result", |
| height=300, |
| interactive=False, |
| ) |
| vid_info = gr.Textbox( |
| label="Processing Details", |
| interactive=False, |
| lines=3, |
| ) |
|
|
| vid_btn.click( |
| process_video, |
| inputs=[vid_input, vid_method, vid_target], |
| outputs=[vid_output, vid_info], |
| ) |
|
|
| |
| with gr.TabItem("📡 API Docs", id="api-tab"): |
| gr.Markdown(""" |
| ## REST API — Use from n8n, curl, or any HTTP client |
| |
| Hugging Face Spaces expose a built-in API automatically. |
| |
| --- |
| |
| ### 🖼 Image Upscaling |
| |
| **POST** `https://areebsa-upsclr.hf.space/api/predict` |
| |
| ```json |
| { |
| "data": [ |
| "<base64_image_or_url>", |
| "Lanczos (Fast)", |
| "4K (3840×2160)" |
| ] |
| } |
| ``` |
| |
| **Response:** |
| ```json |
| { |
| "data": [ |
| "<upscaled_image_url>", |
| "✅ Upscaled: 1280×720 → 3840×2160", |
| "<download_url>" |
| ] |
| } |
| ``` |
| |
| --- |
| |
| ### 🎬 Video Upscaling |
| |
| **POST** `https://areebsa-upsclr.hf.space/api/predict` |
| |
| Use `fn_index: 1` for the video endpoint. |
| |
| ```json |
| { |
| "fn_index": 1, |
| "data": [ |
| "<video_url_or_path>", |
| "Lanczos (Fast)", |
| "4K (3840×2160)" |
| ] |
| } |
| ``` |
| |
| --- |
| |
| ### 🔗 n8n / Zapier Integration |
| |
| Use the **HTTP Request** node: |
| - **Method:** POST |
| - **URL:** `https://areebsa-upsclr.hf.space/api/predict` |
| - **Body:** JSON with `data` array (see above) |
| - **Headers:** `Content-Type: application/json` |
| |
| For private spaces, add: |
| - **Header:** `Authorization: Bearer <your_HF_token>` |
| """) |
|
|
| |
| if __name__ == "__main__": |
| demo.queue(max_size=5).launch( |
| ssr_mode=False, |
| server_name="0.0.0.0", |
| server_port=7860, |
| ) |
|
|