Download app.py from areebsa/upsclr: direct link, hf CLI and curl.
- Browser
- Download file 22.1 kB
-
https://huggingface.co/spaces/areebsa/upsclr/resolve/main/app.py
- Command line
-
hf download hf://spaces/areebsa/upsclr/app.py
-
curl -L -o app.py https://huggingface.co/spaces/areebsa/upsclr/resolve/main/app.py
22.1 kB
| """4K Upscaler Pro — Premium AI-powered upscaler with Lumina Synth design.""" | |
| # ── Monkey-patch gradio_client to fix bool schema crash ───────────── | |
| # Gradio 5.x generates JSON schemas with `additionalProperties: False` | |
| # (a boolean). The gradio_client parser tries `"const" in schema` on it, | |
| # which crashes with TypeError because you can't do `"x" in False`. | |
| 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" | |
| # Guard against additionalProperties being bool | |
| 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 | |
| # ── End monkey-patch ──────────────────────────────────────────────── | |
| import gradio as gr | |
| from core import upscale_image, upscale_video | |
| # ───────────────────────────────────────────────────────────────────── | |
| # Lumina Synth Design System — Generated by Stitch | |
| # ───────────────────────────────────────────────────────────────────── | |
| 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; | |
| } | |
| """ | |
| # ───────────────────────────────────────────────────────────────────── | |
| # Build Gradio UI | |
| # ───────────────────────────────────────────────────────────────────── | |
| 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: | |
| # ── Hero Section ── | |
| 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(): | |
| # ━━━━━━━━━━━━ IMAGE TAB ━━━━━━━━━━━━ | |
| 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], | |
| ) | |
| # ━━━━━━━━━━━━ VIDEO TAB ━━━━━━━━━━━━ | |
| 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], | |
| ) | |
| # ━━━━━━━━━━━━ API DOCS TAB ━━━━━━━━━━━━ | |
| 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>` | |
| """) | |
| # ── Launch ── | |
| if __name__ == "__main__": | |
| demo.queue(max_size=5).launch( | |
| ssr_mode=False, | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| ) | |