Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Jigarzzz❤️ – Premium Video Suite</title> | |
| <meta name="description" content="AI-powered video merger, voiceover generator, and YouTube safe clipper. Free & professional."> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="css/style.css?v=9"> | |
| <style> | |
| /* ── Progress Bar ── */ | |
| .progress-bar-wrap { | |
| display: none; | |
| flex-direction: column; | |
| gap: 8px; | |
| margin-top: 16px; | |
| padding: 16px; | |
| background: rgba(0,0,0,0.25); | |
| border: 1px solid rgba(0,242,254,0.15); | |
| border-radius: var(--r-md); | |
| animation: fadeSlideUp 0.3s ease; | |
| } | |
| .progress-bar-wrap.visible { display: flex; } | |
| .progress-bar-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .progress-status { | |
| font-size: 0.82rem; | |
| font-weight: 700; | |
| color: var(--primary); | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .pulse-dot { | |
| width: 7px; | |
| height: 7px; | |
| background: var(--primary); | |
| border-radius: 50%; | |
| box-shadow: 0 0 8px var(--primary); | |
| animation: pulse-glow 1.1s ease-in-out infinite; | |
| } | |
| @keyframes pulse-glow { | |
| 0%,100% { opacity:1; transform:scale(1); } | |
| 50% { opacity:0.4; transform:scale(0.65); } | |
| } | |
| .progress-pct { | |
| font-size: 0.82rem; | |
| color: var(--text-muted); | |
| font-weight: 600; | |
| } | |
| .progress-track { | |
| height: 6px; | |
| background: rgba(255,255,255,0.07); | |
| border-radius: 99px; | |
| overflow: hidden; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| width: 0%; | |
| background: var(--grad-primary); | |
| border-radius: 99px; | |
| transition: width 0.5s ease; | |
| box-shadow: 0 0 10px rgba(0,242,254,0.5); | |
| } | |
| .progress-step-text { | |
| font-size: 0.75rem; | |
| color: var(--text-muted); | |
| } | |
| /* ── Stats Strip ── */ | |
| .hero-stats { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 14px; | |
| margin-bottom: 22px; | |
| } | |
| .stat-card { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border); | |
| border-radius: var(--r-lg); | |
| padding: 16px 18px; | |
| display: flex; | |
| align-items: center; | |
| gap: 13px; | |
| transition: all var(--t-fast); | |
| } | |
| .stat-card:hover { | |
| background: var(--bg-card-hover); | |
| border-color: var(--border-bright); | |
| transform: translateY(-2px); | |
| } | |
| .stat-icon-wrap { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 10px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-shrink: 0; | |
| font-size: 1.2rem; | |
| } | |
| .stat-icon-wrap.cyan { background: rgba(0,242,254,0.1); } | |
| .stat-icon-wrap.pink { background: rgba(244,114,182,0.1); } | |
| .stat-icon-wrap.purple{ background: rgba(167,139,250,0.1); } | |
| .stat-info { display: flex; flex-direction: column; gap: 2px; } | |
| .stat-value { font-size: 1.4rem; font-weight: 900; color: #fff; line-height: 1; } | |
| .stat-label { font-size: 0.72rem; color: var(--text-muted); font-weight: 600; letter-spacing: 0.3px; } | |
| /* ── Badge chip ── */ | |
| .badge { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 5px; | |
| background: rgba(0,242,254,0.08); | |
| border: 1px solid rgba(0,242,254,0.2); | |
| color: var(--primary); | |
| font-size: 0.71rem; | |
| font-weight: 700; | |
| padding: 4px 10px; | |
| border-radius: var(--r-full); | |
| letter-spacing: 0.4px; | |
| } | |
| .badge.red { background: rgba(255,45,85,0.08); border-color: rgba(255,45,85,0.25); color: #ff6b8a; } | |
| .badge.green { background: rgba(56,239,125,0.08); border-color: rgba(56,239,125,0.25); color: #38ef7d; } | |
| /* ── File count pill ── */ | |
| .file-count-pill { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 5px; | |
| background: rgba(0,242,254,0.1); | |
| color: var(--primary); | |
| border-radius: var(--r-full); | |
| padding: 3px 10px; | |
| font-size: 0.72rem; | |
| font-weight: 700; | |
| margin-left: 8px; | |
| vertical-align: middle; | |
| } | |
| /* ── Two-column ratio grid for clipper ── */ | |
| .ratio-3col { grid-template-columns: repeat(3,1fr) ; } | |
| /* ── Output file size info ── */ | |
| .output-info { | |
| display: flex; | |
| gap: 10px; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| margin-top: 8px; | |
| } | |
| /* ── Scrollable library ── */ | |
| .library-empty-art { font-size: 2.8rem; opacity: 0.18; line-height: 1; } | |
| /* ── Gradient border input ── */ | |
| .url-input-wrap { position: relative; } | |
| .url-input-wrap input { | |
| padding-left: 44px; | |
| } | |
| .url-input-icon { | |
| position: absolute; | |
| left: 13px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| width: 18px; | |
| height: 18px; | |
| color: var(--text-muted); | |
| pointer-events: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Ambient Glow Orbs --> | |
| <div class="glow-bg"></div> | |
| <div class="glow-bg-secondary"></div> | |
| <div class="app-container"> | |
| <!-- ═══════════════════════════════════════════ | |
| HEADER | |
| ════════════════════════════════════════════ --> | |
| <header class="app-header"> | |
| <div class="logo"> | |
| <div class="rocket-container"> | |
| <span class="fire-flame">🔥</span> | |
| <span class="rocket">🚀</span> | |
| </div> | |
| <div> | |
| <h1>Jigarzzz<span class="heart-red">❤️</span></h1> | |
| <div style="font-size:0.72rem; color:var(--text-muted); margin-top:1px; font-weight:500; letter-spacing:0.3px;">Premium Video Suite</div> | |
| </div> | |
| </div> | |
| <div class="eyecatchy-header"> | |
| <span>Unlimited Free Voice generation, video generation and Anti copyright yt</span> | |
| </div> | |
| </header> | |
| <!-- ═══════════════════════════════════════════ | |
| STATS STRIP | |
| ════════════════════════════════════════════ --> | |
| <div class="hero-stats"> | |
| <div class="stat-card"> | |
| <div class="stat-icon-wrap cyan">🎬</div> | |
| <div class="stat-info"> | |
| <span class="stat-value" id="stat-files">0</span> | |
| <span class="stat-label">Files Exported</span> | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-icon-wrap pink">🎙️</div> | |
| <div class="stat-info"> | |
| <span class="stat-value" id="stat-clips">0</span> | |
| <span class="stat-label">YT Clips Created</span> | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-icon-wrap purple">🔊</div> | |
| <div class="stat-info"> | |
| <span class="stat-value" id="stat-audio">0</span> | |
| <span class="stat-label">Audio Tracks Mixed</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ═══════════════════════════════════════════ | |
| MAIN WORKSPACE | |
| ════════════════════════════════════════════ --> | |
| <div class="workspace-grid"> | |
| <!-- ── LEFT: Controls Panel ── --> | |
| <main class="controls-panel"> | |
| <!-- Navigation Tabs --> | |
| <div class="tabs-nav"> | |
| <button class="tab-btn active" data-tab="merger-tab" id="tab-merger"> | |
| <svg class="tab-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h10M4 18h6"/> | |
| </svg> | |
| Video Merger & Overlay | |
| </button> | |
| <button class="tab-btn" data-tab="clipper-tab" id="tab-clipper"> | |
| <svg class="tab-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/> | |
| </svg> | |
| YouTube Safe Clipper | |
| </button> | |
| <button class="tab-btn" data-tab="ai-creator-tab" id="tab-ai-creator"> | |
| <svg class="tab-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="color: #a855f7;"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L5.05 15.15a2 2 0 00-1.022.547l-1.39 1.39a1 1 0 00.707 1.707h16.31a1 1 0 00.707-1.707l-1.39-1.39zM12 2v4M12 6L9.5 8.5M12 6l2.5 2.5"/> | |
| </svg> | |
| 🪄 AI Video Creator | |
| </button> | |
| </div> | |
| <!-- ═══════════════════════════════════ | |
| TAB 1 – VIDEO MERGER | |
| ════════════════════════════════════ --> | |
| <div id="merger-tab" class="tab-content active"> | |
| <form id="merge-form"> | |
| <!-- Step 1: Upload Clips --> | |
| <div class="input-section"> | |
| <h3 class="section-title"> | |
| 1. Upload Clips | |
| <span class="file-count-pill" id="video-count-pill" style="display:none">0 files</span> | |
| </h3> | |
| <p class="section-desc">Select any number of video clips — varying durations supported.</p> | |
| <div class="dropzone" id="video-dropzone"> | |
| <input type="file" id="videos" name="videos" multiple accept="video/*" class="file-input"> | |
| <svg class="dropzone-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/> | |
| </svg> | |
| <span>Drag & drop clips or <strong>browse files</strong></span> | |
| <span style="font-size:0.73rem; opacity:0.5;">MP4, MOV, AVI, MKV — any length</span> | |
| </div> | |
| <div id="video-queue" class="file-queue"></div> | |
| </div> | |
| <!-- Step 2: Aspect Ratio --> | |
| <div class="input-section"> | |
| <h3 class="section-title">2. Output Format</h3> | |
| <div class="ratio-options"> | |
| <label class="ratio-card"> | |
| <input type="radio" name="aspect_ratio" value="vertical" checked> | |
| <div class="ratio-box vertical-box"></div> | |
| <span class="ratio-label">Vertical (9:16)<small>Shorts · Reels · TikTok</small></span> | |
| </label> | |
| <label class="ratio-card"> | |
| <input type="radio" name="aspect_ratio" value="horizontal"> | |
| <div class="ratio-box horizontal-box"></div> | |
| <span class="ratio-label">Horizontal (16:9)<small>YouTube · Standard</small></span> | |
| </label> | |
| </div> | |
| </div> | |
| <!-- Step 3: Audio --> | |
| <div class="input-section"> | |
| <h3 class="section-title">3. Audio Overlay</h3> | |
| <div class="audio-toggle-container"> | |
| <button type="button" class="toggle-btn active" data-type="script" id="audio-toggle-script">🎙️ Generate TTS</button> | |
| <button type="button" class="toggle-btn" data-type="upload" id="audio-toggle-upload">📁 Upload Audio File</button> | |
| <button type="button" class="toggle-btn" data-type="none" id="audio-toggle-none">🚫 No Audio</button> | |
| </div> | |
| <!-- TTS Sub-Panel --> | |
| <div id="tts-panel" class="sub-panel active"> | |
| <div class="form-group"> | |
| <label for="script_text">Voiceover Script</label> | |
| <textarea id="script_text" name="script_text" | |
| placeholder="Type Urdu or English script... (e.g. 'آج ہم بات کریں گے...' or 'Welcome to our channel!')"></textarea> | |
| </div> | |
| <!-- Language + Voice Row --> | |
| <div class="form-row"> | |
| <div class="form-group"> | |
| <label for="tts-language">Language</label> | |
| <select id="tts-language"> | |
| <option value="ur-PK" selected>🇵🇰 Urdu (Pakistan)</option> | |
| <option value="ur-IN">🕊️ Punjabi / Urdu (India) — Gul & Salman</option> | |
| <option value="hi-IN">🇮🇳 Hindi (India)</option> | |
| <option value="pa-IN">🌾 Punjabi (India) — Native</option> | |
| <option value="en-US">🇺🇸 English (US)</option> | |
| <option value="en-GB">🇬🇧 English (UK)</option> | |
| <option value="en-AU">🇦🇺 English (Australian)</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="tts-voice">Voice Character</label> | |
| <div class="voice-picker-wrap"> | |
| <select id="tts-voice" name="voice"> | |
| <!-- Filled dynamically --> | |
| </select> | |
| <button type="button" class="preview-voice-btn" id="preview-voice-btn" title="Hear this voice"> | |
| ▶ Preview | |
| </button> | |
| </div> | |
| <!-- Live preview player --> | |
| <div class="voice-preview-player" id="voice-preview-player" style="display:none"> | |
| <audio id="preview-audio" controls style="width:100%;margin-top:8px;"></audio> | |
| <span class="preview-label">🎧 Sample — adjust mood & re-preview to compare</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Mood / Style Grid --> | |
| <div class="form-group" id="mood-group"> | |
| <label>🎭 Mood & Style <span class="mood-support-tag" id="tts-mood-support-tag"></span></label> | |
| <div class="mood-grid" id="tts-mood-grid"> | |
| <!-- Filled dynamically by JS --> | |
| </div> | |
| </div> | |
| <!-- Speed + Pitch Sliders --> | |
| <div class="form-row sliders-row"> | |
| <div class="form-group slider-group"> | |
| <label>🚀 Speed <span class="slider-val" id="tts-rate-val">Normal</span></label> | |
| <input type="range" id="tts-rate-slider" min="-50" max="50" value="0" step="5" class="style-slider"> | |
| <div class="slider-marks"><span>Slow</span><span>Normal</span><span>Fast</span></div> | |
| </div> | |
| <div class="form-group slider-group"> | |
| <label>🎵 Pitch <span class="slider-val" id="tts-pitch-val">Normal</span></label> | |
| <input type="range" id="tts-pitch-slider" min="-200" max="200" value="0" step="10" class="style-slider"> | |
| <div class="slider-marks"><span>Low</span><span>Normal</span><span>High</span></div> | |
| </div> | |
| </div> | |
| <!-- Age Group Selector --> | |
| <div class="form-group" id="age-group"> | |
| <label>🧑 Voice Age <span style="font-size:0.7rem;color:var(--text-muted);font-weight:400;">— adjusts pitch & speed to simulate age</span></label> | |
| <div class="age-grid" id="tts-age-grid"> | |
| <!-- Filled dynamically by JS --> | |
| </div> | |
| </div> | |
| <!-- Hidden fields passed to /api/merge --> | |
| <input type="hidden" id="tts-style" name="style" value=""> | |
| <input type="hidden" id="tts-style-degree" name="style_degree" value="1.0"> | |
| <input type="hidden" id="tts-rate" name="rate" value="+0%"> | |
| <input type="hidden" id="tts-pitch" name="pitch" value="+0Hz"> | |
| </div> | |
| <!-- Upload Audio Sub-Panel --> | |
| <div id="audio-upload-panel" class="sub-panel"> | |
| <div class="form-group"> | |
| <label for="audio_file">Audio File (MP3 / WAV / M4A)</label> | |
| <input type="file" id="audio_file" name="audio_file" accept="audio/*"> | |
| </div> | |
| </div> | |
| <!-- No Audio Sub-Panel --> | |
| <div id="no-audio-panel" class="sub-panel"> | |
| <p style="font-size:0.83rem; color:var(--text-muted); padding:12px 0;">No audio overlay will be added. Videos will be merged as-is.</p> | |
| </div> | |
| <!-- Background Music (Optional) --> | |
| <div class="optional-group"> | |
| <div class="form-group"> | |
| <label for="bg_music_file">🎵 Background Music <span style="opacity:0.5; font-weight:400">(optional — mixed at 15% vol)</span></label> | |
| <input type="file" id="bg_music_file" name="bg_music_file" accept="audio/*"> | |
| </div> | |
| <div class="checkbox-group"> | |
| <label class="checkbox-container"> | |
| <input type="checkbox" name="trim_audio" value="true" checked> | |
| <span class="checkmark"></span> | |
| Auto-trim audio to match total video duration | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Progress Bar --> | |
| <div class="progress-bar-wrap" id="merge-progress"> | |
| <div class="progress-bar-header"> | |
| <span class="progress-status"><span class="pulse-dot"></span> Processing…</span> | |
| <span class="progress-pct" id="merge-pct">0%</span> | |
| </div> | |
| <div class="progress-track"> | |
| <div class="progress-fill" id="merge-fill"></div> | |
| </div> | |
| <span class="progress-step-text" id="merge-step">Initialising…</span> | |
| </div> | |
| <button type="submit" class="submit-btn" id="merge-submit-btn"> | |
| <span class="btn-text">🎬 Generate Merged Video</span> | |
| <span class="btn-loader"></span> | |
| </button> | |
| </form> | |
| </div> | |
| <!-- ═══════════════════════════════════ | |
| TAB 2 – YOUTUBE CLIPPER | |
| ════════════════════════════════════ --> | |
| <div id="clipper-tab" class="tab-content"> | |
| <form id="clip-form"> | |
| <!-- Step 1: YouTube URL --> | |
| <div class="input-section"> | |
| <h3 class="section-title">1. YouTube Video URL</h3> | |
| <div class="form-group"> | |
| <div class="url-input-wrap"> | |
| <svg class="url-input-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/> | |
| </svg> | |
| <input type="text" id="url" name="url" placeholder="https://www.youtube.com/watch?v=..." required> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Step 2: Aspect Ratio --> | |
| <div class="input-section"> | |
| <h3 class="section-title">2. Output Format</h3> | |
| <p class="section-desc">Keep original or crop to a target platform ratio.</p> | |
| <div class="ratio-options ratio-3col"> | |
| <label class="ratio-card"> | |
| <input type="radio" name="aspect_ratio" value="original" checked> | |
| <div class="ratio-box" style="width:42px;height:28px;border:2px dashed rgba(255,255,255,0.2);border-radius:4px;"></div> | |
| <span class="ratio-label">Original<small>No Crop</small></span> | |
| </label> | |
| <label class="ratio-card"> | |
| <input type="radio" name="aspect_ratio" value="vertical"> | |
| <div class="ratio-box vertical-box"></div> | |
| <span class="ratio-label">Vertical (9:16)<small>Shorts · Reels</small></span> | |
| </label> | |
| <label class="ratio-card"> | |
| <input type="radio" name="aspect_ratio" value="horizontal"> | |
| <div class="ratio-box horizontal-box"></div> | |
| <span class="ratio-label">Horizontal (16:9)<small>YouTube · Standard</small></span> | |
| </label> | |
| </div> | |
| </div> | |
| <!-- Step 3: Slicing Mode --> | |
| <div class="input-section"> | |
| <h3 class="section-title">3. Slicing Method</h3> | |
| <div class="audio-toggle-container"> | |
| <button type="button" class="toggle-btn active" data-type="auto" id="clip-toggle-auto">⚡ Auto-Split</button> | |
| <button type="button" class="toggle-btn" data-type="timestamps" id="clip-toggle-ts">🕐 Custom Timestamps</button> | |
| </div> | |
| <!-- Auto Split Panel --> | |
| <div id="auto-clip-panel" class="sub-panel active"> | |
| <div class="form-group inline-group"> | |
| <label for="interval">Clip Duration per Segment:</label> | |
| <div class="number-input"> | |
| <input type="number" id="interval" name="interval" value="30" min="1" max="1800"> | |
| <span>seconds</span> | |
| </div> | |
| </div> | |
| <small class="help-text" style="display:block; margin-top:4px;">Each segment will be exactly this many seconds long.</small> | |
| </div> | |
| <!-- Custom Timestamps Panel --> | |
| <div id="timestamps-clip-panel" class="sub-panel"> | |
| <div class="form-group"> | |
| <label for="timestamps">Timestamp Ranges (seconds)</label> | |
| <input type="text" id="timestamps" name="timestamps" placeholder="e.g. 10-40, 55-85, 120-150"> | |
| <small class="help-text">Format: start-end pairs, comma-separated</small> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Step 4: Safety Filters --> | |
| <div class="input-section"> | |
| <h3 class="section-title">4. Safety Filters <span class="badge red" style="margin-left:6px; vertical-align:middle;">Anti-Copyright</span></h3> | |
| <p class="section-desc">Applies transformations to evade automated content matching.</p> | |
| <div class="filters-grid"> | |
| <label class="checkbox-container"> | |
| <input type="checkbox" name="mirror" value="true" checked> | |
| <span class="checkmark"></span> | |
| ↔ Horizontal Flip | |
| </label> | |
| <label class="checkbox-container"> | |
| <input type="checkbox" name="zoom" value="true" checked> | |
| <span class="checkmark"></span> | |
| 🔍 Edge Crop & Zoom (1.05x) | |
| </label> | |
| </div> | |
| <div class="form-row" style="margin-top:16px;"> | |
| <div class="form-group"> | |
| <label for="speed">Video Speed</label> | |
| <select id="speed" name="speed"> | |
| <option value="1.0">Standard (1.0×)</option> | |
| <option value="1.04" selected>Safe Speed-up (1.04×)</option> | |
| <option value="1.08">Fast Speed-up (1.08×)</option> | |
| <option value="0.95">Slight Slow-down (0.95×)</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="pitch_shift">Audio Pitch Shift</label> | |
| <select id="pitch_shift" name="pitch_shift"> | |
| <option value="0.0">Normal Pitch (0.0)</option> | |
| <option value="0.8" selected>Slight High (+0.8)</option> | |
| <option value="1.5">High Pitch (+1.5)</option> | |
| <option value="-0.8">Slight Low (-0.8)</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Progress Bar --> | |
| <div class="progress-bar-wrap" id="clip-progress"> | |
| <div class="progress-bar-header"> | |
| <span class="progress-status"><span class="pulse-dot"></span> Processing…</span> | |
| <span class="progress-pct" id="clip-pct">0%</span> | |
| </div> | |
| <div class="progress-track"> | |
| <div class="progress-fill" id="clip-fill"></div> | |
| </div> | |
| <span class="progress-step-text" id="clip-step">Downloading video…</span> | |
| </div> | |
| <button type="submit" class="submit-btn" id="clip-submit-btn" | |
| style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); box-shadow: 0 6px 30px rgba(245,87,108,0.35);"> | |
| <span class="btn-text">✂️ Download & Create Clips</span> | |
| <span class="btn-loader"></span> | |
| </button> | |
| </form> | |
| </div> | |
| <!-- ═══════════════════════════════════ | |
| TAB 3 – AI VIDEO CREATOR | |
| ════════════════════════════════════ --> | |
| <div id="ai-creator-tab" class="tab-content"> | |
| <form id="ai-video-form"> | |
| <!-- Step 1: Script Text --> | |
| <div class="input-section"> | |
| <h3 class="section-title">1. Write Video Script</h3> | |
| <p class="section-desc">Type your story or narration script. Each sentence will generate an animated visual slide matched to the voiceover.</p> | |
| <div class="form-group"> | |
| <textarea id="ai_script_text" name="script_text" required style="min-height:120px;" | |
| placeholder="e.g. Space is a mysterious place full of wonders. Today, we explore the secrets of distant galaxies. Stay with us!"></textarea> | |
| </div> | |
| </div> | |
| <!-- Step 2: Format & Theme --> | |
| <div class="input-section"> | |
| <h3 class="section-title">2. Choose Theme & Style</h3> | |
| <div class="form-row"> | |
| <div class="form-group"> | |
| <label for="ai_theme">Visual Theme / Imagery</label> | |
| <select id="ai_theme" name="theme"> | |
| <option value="ai_generative" selected>🤖 AI Video Gen (Flux/Sora Style)</option> | |
| <option value="auto">🔍 Auto-Keyword Match</option> | |
| <option value="space">🌌 Space & Cosmos</option> | |
| <option value="tech">💻 Technology & Cyberpunk</option> | |
| <option value="nature">🌿 Nature & Landscape</option> | |
| <option value="finance">📈 Finance & Wealth</option> | |
| <option value="city">🏙️ Modern City & Urban</option> | |
| <option value="abstract">🎨 Abstract & Gradients</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label>Aspect Ratio</label> | |
| <div class="ratio-options" style="margin-top:4px;"> | |
| <label class="ratio-card small-card"> | |
| <input type="radio" name="aspect_ratio" value="vertical" checked> | |
| <div class="ratio-box vertical-box"></div> | |
| <span class="ratio-label">Vertical (9:16)</span> | |
| </label> | |
| <label class="ratio-card small-card"> | |
| <input type="radio" name="aspect_ratio" value="horizontal"> | |
| <div class="ratio-box horizontal-box"></div> | |
| <span class="ratio-label">Horizontal (16:9)</span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Step 3: Voiceover --> | |
| <div class="input-section"> | |
| <h3 class="section-title">3. Narration Voiceover</h3> | |
| <div class="form-row"> | |
| <div class="form-group"> | |
| <label for="ai-language">Language</label> | |
| <select id="ai-language" name="language"> | |
| <!-- Filled dynamically --> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="ai-voice">Voice Character</label> | |
| <div class="voice-picker-wrap"> | |
| <select id="ai-voice" name="voice"> | |
| <!-- Filled dynamically --> | |
| </select> | |
| <button type="button" class="preview-voice-btn" id="ai-preview-voice-btn" title="Hear this voice"> | |
| ▶ Preview | |
| </button> | |
| </div> | |
| <!-- Live preview player --> | |
| <div class="voice-preview-player" id="ai-voice-preview-player" style="display:none"> | |
| <audio id="ai-preview-audio" controls style="width:100%;margin-top:8px;"></audio> | |
| <span class="preview-label">🎧 Sample — adjust settings & re-preview</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Mood / Style Grid --> | |
| <div class="form-group" id="ai-mood-group"> | |
| <label>🎭 Mood & Style <span class="mood-support-tag" id="ai-mood-support-tag"></span></label> | |
| <div class="mood-grid" id="ai-mood-grid"> | |
| <!-- Filled dynamically by JS --> | |
| </div> | |
| </div> | |
| <!-- Speed + Pitch Sliders --> | |
| <div class="form-row sliders-row"> | |
| <div class="form-group slider-group"> | |
| <label>🚀 Speed <span class="slider-val" id="ai-rate-val">Normal</span></label> | |
| <input type="range" id="ai-rate-slider" min="-50" max="50" value="0" step="5" class="style-slider"> | |
| <div class="slider-marks"><span>Slow</span><span>Normal</span><span>Fast</span></div> | |
| </div> | |
| <div class="form-group slider-group"> | |
| <label>🎵 Pitch <span class="slider-val" id="ai-pitch-val">Normal</span></label> | |
| <input type="range" id="ai-pitch-slider" min="-200" max="200" value="0" step="10" class="style-slider"> | |
| <div class="slider-marks"><span>Low</span><span>Normal</span><span>High</span></div> | |
| </div> | |
| </div> | |
| <!-- Age Group Selector --> | |
| <div class="form-group" id="ai-age-group"> | |
| <label>🧑 Voice Age <span style="font-size:0.7rem;color:var(--text-muted);font-weight:400;">— adjusts pitch & speed to simulate age</span></label> | |
| <div class="age-grid" id="ai-age-grid"> | |
| <!-- Filled dynamically by JS --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Step 4: Background Music --> | |
| <div class="input-section"> | |
| <h3 class="section-title">4. Background Music (Optional)</h3> | |
| <div class="form-row"> | |
| <div class="form-group"> | |
| <label for="ai_bg_music_file">🎵 Background Music <span style="opacity:0.5; font-weight:400">(optional — mixed at 15% vol)</span></label> | |
| <input type="file" id="ai_bg_music_file" name="bg_music_file" accept="audio/*"> | |
| </div> | |
| </div> | |
| <div class="checkbox-group" style="margin-top:10px;"> | |
| <label class="checkbox-container"> | |
| <input type="checkbox" name="trim_audio" value="true" checked> | |
| <span class="checkmark"></span> | |
| Auto-trim audio to match total video duration | |
| </label> | |
| </div> | |
| </div> | |
| <!-- Progress Bar --> | |
| <div class="progress-bar-wrap" id="ai-progress"> | |
| <div class="progress-bar-header"> | |
| <span class="progress-status"><span class="pulse-dot"></span> Generating AI Video…</span> | |
| <span class="progress-pct" id="ai-pct">0%</span> | |
| </div> | |
| <div class="progress-track"> | |
| <div class="progress-fill" id="ai-fill"></div> | |
| </div> | |
| <span class="progress-step-text" id="ai-step">Initialising…</span> | |
| </div> | |
| <button type="submit" class="submit-btn" id="ai-submit-btn" style="background: linear-gradient(135deg, #a855f7 0%, #6366f1 100%); box-shadow: 0 6px 30px rgba(99,102,241,0.35);"> | |
| <span class="btn-text">🪄 Create Complete AI Video</span> | |
| <span class="btn-loader"></span> | |
| </button> | |
| <!-- Hidden inputs --> | |
| <input type="hidden" id="ai-tts-style" name="style" value=""> | |
| <input type="hidden" id="ai-tts-style-degree" name="style_degree" value="1.0"> | |
| <input type="hidden" id="ai-tts-rate" name="rate" value="+0%"> | |
| <input type="hidden" id="ai-tts-pitch" name="pitch" value="+0Hz"> | |
| </form> | |
| </div> | |
| </main> | |
| <!-- ── RIGHT: Media Library ── --> | |
| <aside class="media-library"> | |
| <div class="library-header"> | |
| <h2>📂 Exported Library</h2> | |
| <div style="display:flex;gap:8px;align-items:center;"> | |
| <button class="clear-library-btn" id="clear-library-btn" title="Delete all exported files"> | |
| 🗑 Clear All | |
| </button> | |
| <button class="refresh-btn" id="refresh-gallery" title="Refresh"> | |
| <svg class="refresh-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 1121.22 8H18"/> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Search --> | |
| <div class="form-group" style="margin-bottom:16px; position:relative; z-index:1;"> | |
| <input type="text" id="library-search" placeholder="🔍 Search exported files…" style="font-size:0.83rem;"> | |
| </div> | |
| <div class="library-list" id="gallery-container"> | |
| <div class="empty-library"> | |
| <div class="library-empty-art">🎞️</div> | |
| <span>No files exported yet.<br>Run a merge or clipping operation to begin.</span> | |
| </div> | |
| </div> | |
| </aside> | |
| </div><!-- /workspace-grid --> | |
| <!-- ══════════════════════════════════════════ | |
| VIDEO PLAYER MODAL | |
| ═════════════════════════════════════════ --> | |
| <div class="modal" id="video-modal"> | |
| <div class="modal-overlay" id="modal-close"></div> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h3 id="modal-title">▶ Video Playback</h3> | |
| <button class="close-btn" id="modal-close-btn">×</button> | |
| </div> | |
| <div class="modal-body"> | |
| <video id="modal-player" controls></video> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Toast --> | |
| <div id="toast" class="toast"></div> | |
| </div><!-- /app-container --> | |
| <script src="js/app.js?v=9"></script> | |
| </body> | |
| </html> | |