1015 lines
72 KiB
HTML
1015 lines
72 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Antigravity AI Video Upscaler</title>
|
|
<link rel="stylesheet" href="styles.css">
|
|
<!-- FontAwesome for modern icons -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
</head>
|
|
<body>
|
|
<div class="app-container">
|
|
<!-- Dashboard Header -->
|
|
<header>
|
|
<div class="logo-container">
|
|
<div class="logo-icon">▲</div>
|
|
<div class="logo-text">
|
|
<h1>ANTIGRAVITY // UPSCALE</h1>
|
|
<p>Open Source AI Video Enhancer</p>
|
|
</div>
|
|
</div>
|
|
<div class="header-actions">
|
|
<button type="button" id="docs-btn" class="btn btn-secondary" style="height: 38px; border-radius: 50px; padding: 0 1.25rem; display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; box-shadow: none; border: 1px solid var(--border-color);">
|
|
<i class="fa-solid fa-book-open"></i> Docs
|
|
</button>
|
|
<div class="theme-selector">
|
|
<label for="theme-select"><i class="fa-solid fa-palette"></i> Theme</label>
|
|
<select id="theme-select">
|
|
<option value="cyberpunk" selected>Cyberpunk Neon</option>
|
|
<option value="oled">OLED Obsidian</option>
|
|
<option value="slate">Slate Gray</option>
|
|
</select>
|
|
</div>
|
|
<div class="gpu-badge">
|
|
<div class="gpu-dot"></div>
|
|
<span id="gpu-info">GPU Active: Detecting... (Vulkan)</span>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main>
|
|
<!-- Step 1: Upload Video -->
|
|
<div id="step-upload" class="step-container active">
|
|
<div class="glass-card">
|
|
<input type="file" id="file-input" accept="video/*" multiple style="display: none;">
|
|
<div id="drop-zone" class="upload-area">
|
|
<span class="upload-icon"><i class="fa-solid fa-cloud-arrow-up"></i></span>
|
|
<h3>Drag & Drop Video File(s)</h3>
|
|
<p>Supports MP4, MKV, AVI, MOV, WEBM</p>
|
|
<div style="display: flex; gap: 1rem; justify-content: center; margin-top: 1rem; flex-wrap: wrap;">
|
|
<button type="button" class="btn" onclick="document.getElementById('file-input').click()">
|
|
<i class="fa-solid fa-cloud-arrow-up"></i> Upload From Device
|
|
</button>
|
|
<button type="button" class="btn btn-secondary" id="browse-server-btn">
|
|
<i class="fa-solid fa-server"></i> Choose From Server
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Previously Uploaded Files Browser -->
|
|
<div class="glass-card" style="margin-top: 2rem; padding: 1.5rem;">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-color); padding-bottom: 0.75rem; margin-bottom: 1rem;">
|
|
<h3 style="margin: 0; font-family: var(--font-heading); font-size: 1.25rem;"><i class="fa-solid fa-folder-tree" style="color: var(--primary); margin-right: 0.5rem;"></i> Previously Uploaded Source Videos</h3>
|
|
<button type="button" id="refresh-uploads-btn" class="btn btn-secondary btn-small">
|
|
<i class="fa-solid fa-arrows-rotate"></i> Refresh
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Batch Configuration Bar -->
|
|
<div id="batch-config-bar" style="display: none; background: rgba(var(--primary-rgb), 0.1); border: 1px solid var(--primary); border-radius: 8px; padding: 0.75rem 1rem; margin-bottom: 1rem; justify-content: space-between; align-items: center; gap: 1rem;">
|
|
<span style="font-size: 0.9rem; color: var(--primary); font-weight: bold;"><i class="fa-solid fa-square-check"></i> <span id="batch-selected-count">0</span> video(s) selected</span>
|
|
<button type="button" id="batch-configure-btn" class="btn btn-primary btn-small">
|
|
<i class="fa-solid fa-sliders"></i> Configure Batch Settings
|
|
</button>
|
|
</div>
|
|
|
|
<div id="uploads-browser-list" style="display: flex; flex-direction: column; gap: 0.75rem;">
|
|
<!-- Dynamically populated files -->
|
|
<div style="text-align: center; padding: 2rem; color: var(--text-muted);">
|
|
<i class="fa-solid fa-photo-film" style="font-size: 2rem; margin-bottom: 0.5rem; color: rgba(255,255,255,0.15);"></i>
|
|
<p>No uploaded files found. Upload a video above to get started!</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 2: Configuration & Preview -->
|
|
<div id="step-config" class="step-container">
|
|
<div class="config-layout">
|
|
<!-- Config Sidebar -->
|
|
<div class="config-sidebar">
|
|
<!-- Video Metadata Display -->
|
|
<div class="glass-card meta-panel">
|
|
<h4>Video Source Info</h4>
|
|
<div class="meta-grid">
|
|
<div class="meta-item">
|
|
<span>File Name</span>
|
|
<span id="meta-name" class="text-truncate">-</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span>Size</span>
|
|
<span id="meta-size">-</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Detailed Streams Accordion -->
|
|
<div class="streams-accordions">
|
|
<div class="accordion-item">
|
|
<div class="accordion-header" onclick="this.parentElement.classList.toggle('open')">
|
|
<span><i class="fa-solid fa-video"></i> Video Stream #0</span>
|
|
<i class="fa-solid fa-chevron-down toggle-icon"></i>
|
|
</div>
|
|
<div class="accordion-content">
|
|
<div class="secondary-grid">
|
|
<div class="meta-item">
|
|
<span>Codec</span>
|
|
<span id="meta-res-codec">-</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span>Resolution</span>
|
|
<span id="meta-res">-</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span>Frame Rate</span>
|
|
<span id="meta-fps">-</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span>Duration</span>
|
|
<span id="meta-duration">-</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span>Total Frames</span>
|
|
<span id="meta-total-frames">-</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span>Aspect Ratio</span>
|
|
<span id="meta-aspect-ratio">-</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="accordion-item">
|
|
<div class="accordion-header" onclick="this.parentElement.classList.toggle('open')">
|
|
<span><i class="fa-solid fa-volume-high"></i> Audio Stream #1</span>
|
|
<i class="fa-solid fa-chevron-down toggle-icon"></i>
|
|
</div>
|
|
<div class="accordion-content">
|
|
<div class="secondary-grid">
|
|
<div class="meta-item">
|
|
<span>Codec</span>
|
|
<span id="meta-audio-codec">-</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span>Channels</span>
|
|
<span id="meta-audio-channels">-</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span>Sample Rate</span>
|
|
<span id="meta-audio-samplerate">-</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span>Bitrate</span>
|
|
<span id="meta-audio-bitrate">-</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Trimming Controls Panel -->
|
|
<div class="glass-card trimming-panel">
|
|
<h4 style="font-family: var(--font-heading); color: var(--primary); font-size: 1.1rem; margin-bottom: 0.5rem;"><i class="fa-solid fa-scissors"></i> Video Trimming</h4>
|
|
<p style="font-size: 0.75rem; color: var(--text-muted); margin-bottom: 0.75rem;">Specify range to upscale only a portion of the video.</p>
|
|
|
|
<div class="form-group">
|
|
<label for="trim-start-range">Trim Start: <span id="trim-start-value" class="badge">0.00s</span></label>
|
|
<div class="range-slider">
|
|
<input type="range" id="trim-start-range" min="0" max="100" step="0.05" value="0">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group" style="margin-top: 1rem;">
|
|
<label for="trim-end-range">Trim End: <span id="trim-end-value" class="badge">10.00s</span></label>
|
|
<div class="range-slider">
|
|
<input type="range" id="trim-end-range" min="0" max="100" step="0.05" value="10">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Config Parameters Form -->
|
|
<div class="glass-card settings-panel">
|
|
<h4 style="font-family: var(--font-heading); color: var(--primary);">Upscaling Profile</h4>
|
|
|
|
<!-- Model Selection -->
|
|
<div class="form-group">
|
|
<label for="model-select">AI Upscaling Model</label>
|
|
<select id="model-select">
|
|
<option value="realesr-animevideov3">Real-ESRGAN Anime Video V3 (Fast / Anime & Video)</option>
|
|
<option value="realesrgan-x4plus" selected>Real-ESRGAN X4 Plus (Ultra Quality / Real World)</option>
|
|
<option value="realesrgan-x4plus-anime">Real-ESRGAN X4 Plus Anime (Ultra Quality / Illustration)</option>
|
|
<option value="realesrnet-x4plus">Real-ESRNet X4 Plus (Alternate / Real World)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Scale Factor -->
|
|
<div class="form-group">
|
|
<label for="scale-range">Scale Factor <span id="scale-value" class="badge">4x</span></label>
|
|
<div class="range-slider">
|
|
<input type="range" id="scale-range" min="2" max="4" step="1" value="4">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Advanced Settings Toggle Header -->
|
|
<div class="advanced-settings-header" onclick="this.classList.toggle('open'); document.getElementById('advanced-settings-content').classList.toggle('open')">
|
|
<span><i class="fa-solid fa-sliders"></i> Advanced Options</span>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</div>
|
|
|
|
<!-- Collapsible Content -->
|
|
<div id="advanced-settings-content" class="advanced-settings-content">
|
|
<!-- GPU Device Selection -->
|
|
<div class="form-group">
|
|
<label for="gpu-select">Vulkan GPU Selector</label>
|
|
<select id="gpu-select">
|
|
<option value="auto" selected>Auto Detect Device</option>
|
|
<option value="0">GPU Device #0</option>
|
|
<option value="1">GPU Device #1</option>
|
|
<option value="2">GPU Device #2</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- GPU Tile Size -->
|
|
<div class="form-group">
|
|
<label for="tile-range">GPU Tile Size <span id="tile-value" class="badge">256</span></label>
|
|
<div class="range-slider">
|
|
<input type="range" id="tile-range" min="64" max="512" step="64" value="256">
|
|
</div>
|
|
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 2px;">
|
|
Halves automatically in case of Vulkan VRAM allocation faults.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- CRF Quality Slider -->
|
|
<div class="form-group">
|
|
<label for="crf-range">Constant Rate Factor (CRF) <span id="crf-value" class="badge">18</span></label>
|
|
<div class="range-slider">
|
|
<input type="range" id="crf-range" min="0" max="51" step="1" value="18">
|
|
</div>
|
|
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 2px;">
|
|
0 (lossless) to 51 (worst). Default 18 (visually pristine).
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Speed/Quality Preset -->
|
|
<div class="form-group">
|
|
<label for="preset-select">FFmpeg H.264 Encoder Preset</label>
|
|
<select id="preset-select">
|
|
<option value="ultrafast">Ultrafast (High Speed / Large Size)</option>
|
|
<option value="superfast">Superfast</option>
|
|
<option value="veryfast">Veryfast</option>
|
|
<option value="faster">Faster</option>
|
|
<option value="fast">Fast</option>
|
|
<option value="medium" selected>Medium (Balanced)</option>
|
|
<option value="slow">Slow (Max H.264 Quality Compression)</option>
|
|
<option value="slower">Slower</option>
|
|
<option value="veryslow">Veryslow (Maximum Compression)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Output Video container -->
|
|
<div class="form-group">
|
|
<label for="transcode-select">Output Container Format</label>
|
|
<select id="transcode-select">
|
|
<option value="mp4" selected>MP4 (MPEG-4 H.264 Wrapper)</option>
|
|
<option value="mkv">MKV (Matroska Video Container)</option>
|
|
<option value="mov">MOV (QuickTime Video Wrapper)</option>
|
|
<option value="avi">AVI (Legacy Audio Video Interleave)</option>
|
|
<option value="webm">WebM (VP9 / Web Native HTML5)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Custom Temporary Directory -->
|
|
<div class="form-group">
|
|
<label for="temp-dir-input"><i class="fa-solid fa-folder-open" style="color: var(--primary); margin-right: 0.25rem;"></i> Custom Temporary Files Directory</label>
|
|
<div style="display: flex; gap: 0.5rem; align-items: stretch;">
|
|
<input type="text" id="temp-dir-input" placeholder="e.g. /mnt/fast_ssd/temp (Leave empty for default temp/)" style="flex: 1;">
|
|
<button type="button" id="browse-job-temp-btn" class="btn btn-secondary" style="padding: 0 1rem;" title="Browse files">
|
|
<i class="fa-solid fa-folder-open"></i>
|
|
</button>
|
|
</div>
|
|
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 4px;">
|
|
Specify a custom location for temporary image frames to prevent running out of primary drive space.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Custom Output Directory -->
|
|
<div class="form-group">
|
|
<label for="output-dir-input"><i class="fa-solid fa-folder-closed" style="color: var(--primary); margin-right: 0.25rem;"></i> Custom Output Directory</label>
|
|
<div style="display: flex; gap: 0.5rem; align-items: stretch;">
|
|
<input type="text" id="output-dir-input" placeholder="e.g. /mnt/fast_ssd/outputs (Leave empty for default outputs/)" style="flex: 1;">
|
|
<button type="button" id="browse-job-output-btn" class="btn btn-secondary" style="padding: 0 1rem;" title="Browse files">
|
|
<i class="fa-solid fa-folder-open"></i>
|
|
</button>
|
|
</div>
|
|
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 4px;">
|
|
Specify a custom location to save the final upscaled video file.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Pre-filters selection checkboxes -->
|
|
<div class="filter-group">
|
|
<span style="font-weight: 600; font-size: 0.85rem; display: block; color: var(--primary);">Pre-Filters & Enhancing</span>
|
|
<div class="filter-checkboxes">
|
|
<label class="checkbox-container">
|
|
<input type="checkbox" id="filter-denoise"> Denoise (hqdn3d)
|
|
</label>
|
|
<label class="checkbox-container">
|
|
<input type="checkbox" id="filter-sharpen"> Sharpen (unsharp)
|
|
</label>
|
|
<label class="checkbox-container">
|
|
<input type="checkbox" id="filter-fps"> Double FPS (60FPS Interpolate)
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- AI Enhancements Section -->
|
|
<div class="filter-group" style="margin-top: 1rem;">
|
|
<span style="font-weight: 600; font-size: 0.85rem; display: block; color: var(--primary);">Advanced AI Enhancements</span>
|
|
<div class="filter-checkboxes">
|
|
<label class="checkbox-container">
|
|
<input type="checkbox" id="ai-face-restoration"> AI Face Restoration (GFPGAN)
|
|
</label>
|
|
<label class="checkbox-container">
|
|
<input type="checkbox" id="ai-rife-interpolation"> AI Frame Interpolation (RIFE)
|
|
</label>
|
|
<label class="checkbox-container">
|
|
<input type="checkbox" id="ai-audio-denoise"> AI Audio Denoising (RNNoise)
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Webhook Notify Input -->
|
|
<div class="form-group">
|
|
<label for="webhook-url">Webhook Notification Callback</label>
|
|
<input type="text" id="webhook-url" placeholder="https://your-server.com/webhook" style="background: rgba(255, 255, 255, 0.04); border: 1px solid var(--border-color); color: var(--text-main); padding: 0.8rem 1rem; border-radius: 10px; font-family: var(--font-body); font-size: 0.95rem; outline: none; transition: border 0.3s; width: 100%;">
|
|
</div>
|
|
|
|
<!-- Custom Model Upload Input -->
|
|
<div class="form-group">
|
|
<label>Upload Custom Model (.param + .bin)</label>
|
|
<div class="model-upload-zone" onclick="document.getElementById('custom-model-input').click()">
|
|
<input type="file" id="custom-model-input" accept=".param,.bin" multiple style="display: none;">
|
|
<i class="fa-solid fa-cubes-stacked"></i>
|
|
<p>Select both model files to load into realesrgan</p>
|
|
<span id="model-upload-filename" style="font-size: 0.72rem; color: var(--primary); font-weight: bold; word-break: break-all;">No files selected</span>
|
|
</div>
|
|
<div class="upload-model-btn-container" style="display: none;" id="upload-model-btn-wrapper">
|
|
<button type="button" class="btn btn-secondary btn-small" id="upload-model-btn" style="width: 100%;">
|
|
<i class="fa-solid fa-cloud-arrow-up"></i> Upload Model Files
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Preserve Audio Toggle -->
|
|
<div class="toggle-container">
|
|
<div>
|
|
<span style="font-weight: 600; font-size: 0.9rem; display: block;">Preserve Audio</span>
|
|
<span style="font-size: 0.75rem; color: var(--text-muted);">Copy audio streams into upscaled video</span>
|
|
</div>
|
|
<label class="switch">
|
|
<input type="checkbox" id="audio-toggle" checked>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action -->
|
|
<button type="button" id="start-upscale-btn" class="btn" style="margin-top: 1rem;">
|
|
<i class="fa-solid fa-play"></i> Start Full Upscale
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Comparison Preview Area -->
|
|
<div class="glass-card preview-panel">
|
|
<div class="preview-header">
|
|
<h3>Clarity Preview</h3>
|
|
<button type="button" id="prev-btn" class="btn btn-secondary" onclick="window.location.reload()">
|
|
<i class="fa-solid fa-arrow-left"></i> Change Video
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Timeline Selection -->
|
|
<div class="timeline-picker">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; width: 100%; margin-bottom: 0.25rem;">
|
|
<label for="timeline-slider" style="margin: 0; padding-bottom: 0;">
|
|
Select Frame Position: <span id="timeline-time" style="color: var(--primary); font-weight: bold;">0.00s</span>
|
|
</label>
|
|
<div style="display: flex; gap: 0.5rem;">
|
|
<button type="button" id="generate-preview-btn" class="btn btn-secondary btn-small" style="padding: 0.4rem 1rem !important;">
|
|
<i class="fa-solid fa-wand-magic-sparkles"></i> Frame Preview
|
|
</button>
|
|
<button type="button" id="generate-video-preview-btn" class="btn btn-primary btn-small" style="padding: 0.4rem 1rem !important;">
|
|
<i class="fa-solid fa-video"></i> 5s Video Preview
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<input type="range" id="timeline-slider" min="0" max="100" step="0.1" value="10">
|
|
</div>
|
|
|
|
<!-- Interactive Comparison Container -->
|
|
<div style="position: relative;">
|
|
<!-- Placeholder -->
|
|
<div id="preview-placeholder-box" class="preview-placeholder">
|
|
<i class="fa-solid fa-image-portrait"></i>
|
|
<h4>Clarity Preview Frame</h4>
|
|
<p>Extracts a single frame at the chosen timestamp and runs the AI model to show side-by-side comparative clarity enhancement.</p>
|
|
<p style="font-size: 0.82rem; color: var(--text-muted); margin-top: 0.5rem;">Click "Render Preview Frame" above to generate the comparative view.</p>
|
|
</div>
|
|
|
|
<!-- Interactive Slider (Hidden by default) -->
|
|
<div id="comparison-slider-container" class="comparison-wrapper" style="display: none;">
|
|
<img id="img-original" class="comparison-image original" src="" alt="Before Upscaling" style="--zoom: 1; --pan-x: 0px; --pan-y: 0px;">
|
|
<div id="img-upscaled-wrapper" class="upscaled-wrapper">
|
|
<img id="img-upscaled" class="comparison-image upscaled" src="" alt="After Upscaling" style="--zoom: 1; --pan-x: 0px; --pan-y: 0px;">
|
|
</div>
|
|
|
|
<!-- Video Elements for Synced Video Preview -->
|
|
<video id="video-preview-original" class="comparison-image original" loop muted playsinline style="display: none; --zoom: 1; --pan-x: 0px; --pan-y: 0px;"></video>
|
|
<div id="video-preview-upscaled-wrapper" class="upscaled-wrapper" style="display: none;">
|
|
<video id="video-preview-upscaled" class="comparison-image upscaled" loop muted playsinline style="--zoom: 1; --pan-x: 0px; --pan-y: 0px;"></video>
|
|
</div>
|
|
|
|
<div id="slider-split" class="slider-bar">
|
|
<div class="slider-handle"></div>
|
|
</div>
|
|
|
|
<span class="badge-orig">Before (Low Res)</span>
|
|
<span class="badge-up">After (AI Upscaled)</span>
|
|
</div>
|
|
|
|
<!-- Zoom & Pan Controls -->
|
|
<div id="zoom-pan-controls" class="zoom-pan-bar" style="display: none;">
|
|
<div class="zoom-control-item">
|
|
<label for="zoom-slider">Zoom: <span id="zoom-val">1.0x</span></label>
|
|
<input type="range" id="zoom-slider" min="1" max="5" step="0.1" value="1">
|
|
</div>
|
|
<div class="zoom-control-item">
|
|
<label for="pan-x-slider">Pan X: <span id="pan-x-val">0px</span></label>
|
|
<input type="range" id="pan-x-slider" min="-300" max="300" step="1" value="0">
|
|
</div>
|
|
<div class="zoom-control-item">
|
|
<label for="pan-y-slider">Pan Y: <span id="pan-y-val">0px</span></label>
|
|
<input type="range" id="pan-y-slider" min="-300" max="300" step="1" value="0">
|
|
</div>
|
|
<button type="button" id="reset-zoom-pan-btn" class="btn btn-secondary btn-small">
|
|
<i class="fa-solid fa-arrows-to-eye"></i> Reset
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Video Preview Playback Controls -->
|
|
<div id="video-preview-controls" class="zoom-pan-bar" style="display: none; justify-content: center; align-items: center; gap: 1rem; margin-top: 1rem;">
|
|
<button type="button" id="vprep-play-btn" class="btn btn-primary btn-circular">
|
|
<i class="fa-solid fa-play"></i>
|
|
</button>
|
|
<div class="time-scrubber-container">
|
|
<span id="vprep-time-current">0:00</span>
|
|
<input type="range" id="vprep-scrubber" min="0" max="5" step="0.05" value="0">
|
|
<span id="vprep-time-duration">0:05</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Loader overlay -->
|
|
<div id="preview-loader" class="preview-loading">
|
|
<div class="spinner" style="margin-bottom: 1rem;"></div>
|
|
<h4 id="preview-loader-title" style="font-family: var(--font-heading); margin-bottom: 0.5rem;">Orchestrating Vulkan GPU...</h4>
|
|
<div id="preview-loader-progress-container" style="width: 100%; max-width: 250px; height: 6px; background: rgba(255,255,255,0.1); border-radius: 3px; overflow: hidden; margin: 0.5rem 0; display: none;">
|
|
<div id="preview-loader-progress-bar" style="width: 0%; height: 100%; background: var(--primary); transition: width 0.3s ease;"></div>
|
|
</div>
|
|
<p id="preview-loader-text" style="color: var(--text-muted); font-size: 0.85rem;">Extracting and upscaling frame. Please wait...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 3: Processing Progress -->
|
|
<div id="step-progress" class="step-container">
|
|
<div class="glass-card progress-card">
|
|
<div class="progress-header">
|
|
<div>
|
|
<h2 style="font-family: var(--font-heading); margin-bottom: 0.5rem;">Upscaling Video Pipeline</h2>
|
|
<p style="color: var(--text-muted); font-size: 0.9rem;">Job ID: <span id="job-id-display" style="font-family: monospace; color: var(--text-main);">-</span></p>
|
|
</div>
|
|
<span id="status-badge" class="status-badge queued">Queued</span>
|
|
</div>
|
|
|
|
<!-- Progress Bar -->
|
|
<div>
|
|
<div style="display: flex; justify-content: space-between; font-weight: bold; margin-bottom: 0.5rem; font-size: 0.95rem;">
|
|
<span>Progress</span>
|
|
<span id="progress-percent" style="color: var(--primary);">0%</span>
|
|
</div>
|
|
<div class="bar-container">
|
|
<div id="progress-bar-fill" class="bar-fill"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics grid -->
|
|
<div class="progress-stats">
|
|
<div class="stat-card">
|
|
<span>Status</span>
|
|
<span id="stat-status">Queued</span>
|
|
</div>
|
|
<div class="stat-card">
|
|
<span>Processed Frames</span>
|
|
<span id="stat-frames">0 / 0</span>
|
|
</div>
|
|
<div class="stat-card">
|
|
<span>Estimated ETA</span>
|
|
<span id="stat-eta" style="color: var(--primary);">Calculating...</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Realtime Logs Panel -->
|
|
<div>
|
|
<h4 style="font-family: var(--font-heading); margin-bottom: 0.5rem; color: var(--text-muted); font-size: 0.9rem;">Pipeline Live Activity Logs</h4>
|
|
<div id="log-terminal" class="log-panel">
|
|
<div class="log-line">Waiting for worker thread...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action control -->
|
|
<div class="action-buttons-row">
|
|
<button type="button" id="back-to-workspace-btn" class="btn btn-secondary">
|
|
<i class="fa-solid fa-arrow-left"></i> Add Another Job
|
|
</button>
|
|
<button type="button" id="cancel-upscale-btn" class="btn btn-danger">
|
|
<i class="fa-solid fa-circle-stop"></i> Abort Upscaling Job
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 4: Finished/Completed -->
|
|
<div id="step-finished" class="step-container">
|
|
<div class="glass-card finished-card">
|
|
<div class="success-icon">
|
|
<i class="fa-solid fa-circle-check"></i>
|
|
</div>
|
|
<h2>Video Enhancement Completed!</h2>
|
|
<p style="color: var(--text-muted); max-width: 500px; margin: 0 auto;">
|
|
Your video was successfully upscaled and optimized using the Real-ESRGAN Vulkan backend model. Original audio streams have been merged.
|
|
</p>
|
|
|
|
<div class="action-buttons">
|
|
<a id="download-link" href="#" class="btn">
|
|
<i class="fa-solid fa-download"></i> Download Upscaled Video
|
|
</a>
|
|
<button type="button" id="finished-play-comparison-btn" class="btn btn-secondary">
|
|
<i class="fa-solid fa-film"></i> Compare Playback
|
|
</button>
|
|
<button type="button" class="btn btn-secondary" onclick="window.location.reload()">
|
|
<i class="fa-solid fa-rotate-left"></i> Process Another File
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Active Jobs & Queue Section -->
|
|
<section class="queue-section glass-card" style="margin-top: 3rem;">
|
|
<div class="queue-header">
|
|
<h2><i class="fa-solid fa-list-check"></i> Active Jobs & Pipeline Queue</h2>
|
|
<button type="button" id="refresh-queue-btn" class="btn btn-secondary btn-small">
|
|
<i class="fa-solid fa-arrows-rotate"></i> Refresh Queue
|
|
</button>
|
|
</div>
|
|
<p style="color: var(--text-muted); font-size: 0.9rem; margin-bottom: 1.5rem;">Monitor running and queued jobs in the queue pipeline. Preview jobs are marked as well.</p>
|
|
|
|
<div id="queue-list" class="queue-list" style="display: flex; flex-direction: column; gap: 0.75rem;">
|
|
<!-- Dynamically populated queue items -->
|
|
<div class="queue-empty-state" style="text-align: center; padding: 2rem; border: 1px dashed var(--border-color); border-radius: 12px; color: var(--text-muted);">
|
|
<i class="fa-solid fa-list-ul" style="font-size: 2rem; margin-bottom: 0.5rem; color: var(--primary);"></i>
|
|
<p>No active or queued jobs at the moment.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Temporary Storage & Maintenance Section -->
|
|
<section class="maintenance-section glass-card" style="margin-top: 3rem;">
|
|
<div class="queue-header">
|
|
<h2><i class="fa-solid fa-hard-drive"></i> Temporary Storage & Maintenance</h2>
|
|
<div style="display: flex; gap: 0.5rem;">
|
|
<button type="button" id="refresh-temp-btn" class="btn btn-secondary btn-small">
|
|
<i class="fa-solid fa-arrows-rotate"></i> Refresh
|
|
</button>
|
|
<button type="button" id="cleanup-temp-btn" class="btn btn-danger btn-small">
|
|
<i class="fa-solid fa-broom"></i> Clean Up Inactive Temp
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<p style="color: var(--text-muted); font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
Monitor temporary image frames created during upscaling. Running jobs cannot be cleared.
|
|
</p>
|
|
|
|
<!-- Default Temp Directory Setting -->
|
|
<div class="temp-dir-setting" style="margin-bottom: 1.5rem; padding: 1.25rem; border-radius: 12px; background: rgba(255,255,255,0.02); border: 1px solid var(--border-color);">
|
|
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem;">
|
|
<i class="fa-solid fa-folder-gear" style="color: var(--primary); font-size: 1.1rem;"></i>
|
|
<span style="font-weight: 600; font-size: 0.95rem;">Default Temp Directory</span>
|
|
</div>
|
|
<p style="font-size: 0.82rem; color: var(--text-muted); margin-bottom: 0.75rem;">
|
|
Set a persistent default directory for all upscale job temp files. This is applied automatically unless overridden per-job in Advanced Options. Use a fast SSD or a drive with plenty of space.
|
|
</p>
|
|
<div style="display: flex; gap: 0.5rem; align-items: stretch;">
|
|
<input type="text" id="default-temp-dir-input"
|
|
placeholder="e.g. /mnt/fast_ssd/upscaler_temp (Leave empty for project default temp/)"
|
|
style="flex: 1; padding: 0.6rem 0.85rem; border-radius: 8px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-primary); font-size: 0.88rem; font-family: 'JetBrains Mono', monospace;">
|
|
<button type="button" id="browse-default-temp-btn" class="btn btn-secondary" style="padding: 0 1rem;" title="Browse files">
|
|
<i class="fa-solid fa-folder-open"></i>
|
|
</button>
|
|
<button type="button" id="save-temp-dir-btn" class="btn btn-primary btn-small" style="white-space: nowrap; padding: 0.6rem 1.2rem;">
|
|
<i class="fa-solid fa-floppy-disk"></i> Save
|
|
</button>
|
|
</div>
|
|
<div id="temp-dir-status" style="font-size: 0.78rem; margin-top: 0.5rem; min-height: 1.2em;"></div>
|
|
</div>
|
|
|
|
<!-- Default Output Directory Setting -->
|
|
<div class="output-dir-setting" style="margin-bottom: 1.5rem; padding: 1.25rem; border-radius: 12px; background: rgba(255,255,255,0.02); border: 1px solid var(--border-color);">
|
|
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem;">
|
|
<i class="fa-solid fa-folder-closed" style="color: var(--primary); font-size: 1.1rem;"></i>
|
|
<span style="font-weight: 600; font-size: 0.95rem;">Default Output Directory</span>
|
|
</div>
|
|
<p style="font-size: 0.82rem; color: var(--text-muted); margin-bottom: 0.75rem;">
|
|
Set a persistent default directory where completed upscale videos will be saved. This is applied automatically unless overridden per-job in Advanced Options.
|
|
</p>
|
|
<div style="display: flex; gap: 0.5rem; align-items: stretch;">
|
|
<input type="text" id="default-output-dir-input"
|
|
placeholder="e.g. /mnt/fast_ssd/upscaled_outputs (Leave empty for default outputs/)"
|
|
style="flex: 1; padding: 0.6rem 0.85rem; border-radius: 8px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-primary); font-size: 0.88rem; font-family: 'JetBrains Mono', monospace;">
|
|
<button type="button" id="browse-default-output-btn" class="btn btn-secondary" style="padding: 0 1rem;" title="Browse files">
|
|
<i class="fa-solid fa-folder-open"></i>
|
|
</button>
|
|
<button type="button" id="save-output-dir-btn" class="btn btn-primary btn-small" style="white-space: nowrap; padding: 0.6rem 1.2rem;">
|
|
<i class="fa-solid fa-floppy-disk"></i> Save
|
|
</button>
|
|
</div>
|
|
<div id="output-dir-status" style="font-size: 0.78rem; margin-top: 0.5rem; min-height: 1.2em;"></div>
|
|
</div>
|
|
|
|
<!-- Cluster & Network Settings -->
|
|
<div class="cluster-settings" style="margin-bottom: 1.5rem; padding: 1.25rem; border-radius: 12px; background: rgba(255,255,255,0.02); border: 1px solid var(--border-color);">
|
|
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem;">
|
|
<i class="fa-solid fa-network-wired" style="color: var(--primary); font-size: 1.1rem;"></i>
|
|
<span style="font-weight: 600; font-size: 0.95rem;">Distributed Processing & Node Roles</span>
|
|
</div>
|
|
<p style="font-size: 0.82rem; color: var(--text-muted); margin-bottom: 0.75rem;">
|
|
Link multiple video upscaler servers together to accelerate upscaling tasks by distributing frame processing workload.
|
|
</p>
|
|
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1rem;">
|
|
<div class="form-group" style="margin-bottom: 0;">
|
|
<label for="node-role-select" style="font-size: 0.8rem; font-weight: 600; display: block; margin-bottom: 0.25rem;">Server Node Role</label>
|
|
<select id="node-role-select" style="padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); font-size: 0.85rem; width: 100%;">
|
|
<option value="standalone">Standalone Node (Default)</option>
|
|
<option value="coordinator">Coordinator Node (Distribute tasks)</option>
|
|
<option value="worker">Worker Node (upscale assigned chunks)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group" id="coord-url-group" style="display: none; margin-bottom: 0;">
|
|
<label for="coordinator-url-input" style="font-size: 0.8rem; font-weight: 600; display: block; margin-bottom: 0.25rem;">Coordinator API URL</label>
|
|
<input type="text" id="coordinator-url-input" placeholder="e.g. http://192.168.1.100:8000" style="padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); font-size: 0.85rem; width: 100%;">
|
|
</div>
|
|
|
|
<div class="form-group" id="worker-url-group" style="display: none; margin-bottom: 0;">
|
|
<label for="worker-url-input" style="font-size: 0.8rem; font-weight: 600; display: block; margin-bottom: 0.25rem;">My Public Worker URL</label>
|
|
<input type="text" id="worker-url-input" placeholder="e.g. http://192.168.1.101:8000" style="padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); font-size: 0.85rem; width: 100%;">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Shared Storage toggle -->
|
|
<div style="margin-bottom: 1rem; padding: 0.75rem; border-radius: 8px; background: rgba(255,255,255,0.01); border: 1px solid var(--border-color);">
|
|
<label class="checkbox-container" style="font-size: 0.85rem; display: flex; align-items: center; gap: 0.5rem; cursor: pointer; user-select: none;">
|
|
<input type="checkbox" id="use-shared-storage-checkbox"> Use Shared Network Storage (Fastest — skips uploads/downloads)
|
|
</label>
|
|
<div id="shared-storage-path-group" style="display: none; margin-top: 0.75rem; display: flex; gap: 0.5rem; align-items: stretch;">
|
|
<input type="text" id="shared-storage-path-input" placeholder="Local path to mounted share, e.g. /mnt/shared_temp" style="flex: 1; padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); font-size: 0.85rem; font-family: monospace;">
|
|
<button type="button" id="browse-shared-storage-btn" class="btn btn-secondary" style="padding: 0 0.75rem;" title="Browse files">
|
|
<i class="fa-solid fa-folder-open"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SMB Shares section -->
|
|
<div style="margin-bottom: 1.25rem;">
|
|
<h4 style="font-size: 0.85rem; margin-bottom: 0.5rem; display: flex; justify-content: space-between; align-items: center;">
|
|
<span><i class="fa-solid fa-share-nodes"></i> SMB Network Shares Mapping</span>
|
|
<button type="button" id="add-smb-share-btn" class="btn btn-secondary btn-small" style="padding: 0.25rem 0.5rem; font-size: 0.75rem;">
|
|
<i class="fa-solid fa-plus"></i> Add Share
|
|
</button>
|
|
</h4>
|
|
<div id="smb-shares-list" style="display: flex; flex-direction: column; gap: 0.5rem;">
|
|
<!-- Mapped shares render dynamically -->
|
|
<div style="text-align: center; padding: 1rem; border: 1px dashed var(--border-color); border-radius: 8px; color: var(--text-muted); font-size: 0.8rem;">
|
|
No mapped network shares.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" id="save-cluster-settings-btn" class="btn btn-primary btn-small" style="padding: 0.6rem 1.5rem;">
|
|
<i class="fa-solid fa-floppy-disk"></i> Save Cluster Settings
|
|
</button>
|
|
<div id="cluster-settings-status" style="font-size: 0.78rem; margin-top: 0.5rem; min-height: 1.2em;"></div>
|
|
</div>
|
|
|
|
<div class="temp-stats-summary" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1.5rem;">
|
|
<div class="glass-card" style="padding: 1rem; background: rgba(255,255,255,0.01); text-align: center;">
|
|
<span style="font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; display: block;">Total Temp Size</span>
|
|
<strong id="temp-total-size" style="font-size: 1.5rem; color: var(--primary);">0 Bytes</strong>
|
|
</div>
|
|
<div class="glass-card" style="padding: 1rem; background: rgba(255,255,255,0.01); text-align: center;">
|
|
<span style="font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; display: block;">Temp Folders Count</span>
|
|
<strong id="temp-folder-count" style="font-size: 1.5rem; color: var(--primary);">0</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="temp-files-list" style="display: flex; flex-direction: column; gap: 0.75rem;">
|
|
<!-- Temp files populated dynamically -->
|
|
<div style="text-align: center; padding: 2rem; border: 1px dashed var(--border-color); border-radius: 12px; color: var(--text-muted);">
|
|
<p>No temporary directories found.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Completed Video Gallery -->
|
|
<section class="gallery-section glass-card" style="margin-top: 3rem;">
|
|
<div class="gallery-header">
|
|
<h2><i class="fa-solid fa-photo-film"></i> Completed Enhancements</h2>
|
|
<div style="display: flex; gap: 0.5rem;">
|
|
<button type="button" id="refresh-gallery-btn" class="btn btn-secondary btn-small">
|
|
<i class="fa-solid fa-arrows-rotate"></i> Refresh
|
|
</button>
|
|
<button type="button" id="purge-all-btn" class="btn btn-danger btn-small">
|
|
<i class="fa-solid fa-trash-can-arrow-up"></i> Purge All Data
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<p style="color: var(--text-muted); font-size: 0.9rem; margin-bottom: 1.5rem;">Access all your upscaled videos, play them side-by-side with original sources, or manage the storage.</p>
|
|
|
|
<div id="gallery-grid" class="gallery-grid">
|
|
<!-- Dynamically populated completed items -->
|
|
<div class="gallery-empty-state">
|
|
<i class="fa-solid fa-photo-film"></i>
|
|
<p>No completed jobs found. Start upscaling a video above to see it in the gallery!</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
|
|
<!-- Synced Video Playback Modal -->
|
|
<div id="video-modal" class="modal-overlay" style="display: none;">
|
|
<div class="modal-card">
|
|
<div class="modal-header">
|
|
<h3><i class="fa-solid fa-film"></i> Side-by-Side Synced Video Comparison</h3>
|
|
<button type="button" class="close-btn" id="modal-close-btn">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="video-compare-wrapper">
|
|
<div class="video-container">
|
|
<h4>Original (Before)</h4>
|
|
<video id="modal-video-original" playsinline></video>
|
|
</div>
|
|
<div class="video-container">
|
|
<h4>Upscaled (AI Enhanced)</h4>
|
|
<video id="modal-video-upscaled" playsinline></video>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Synced Controls -->
|
|
<div class="video-sync-controls">
|
|
<button type="button" id="modal-play-btn" class="btn btn-circular">
|
|
<i class="fa-solid fa-play"></i>
|
|
</button>
|
|
<div class="time-scrubber-container">
|
|
<span id="modal-time-current">00:00</span>
|
|
<input type="range" id="modal-time-slider" min="0" max="100" value="0" step="0.01">
|
|
<span id="modal-time-duration">00:00</span>
|
|
</div>
|
|
<button type="button" id="modal-mute-btn" class="btn btn-secondary btn-circular">
|
|
<i class="fa-solid fa-volume-high"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Documentation Modal -->
|
|
<div id="docs-modal" class="modal-overlay" style="display: none;">
|
|
<div class="modal-card" style="max-width: 800px; max-height: 85vh; display: flex; flex-direction: column;">
|
|
<div class="modal-header">
|
|
<h3><i class="fa-solid fa-book-open"></i> Help & System Documentation</h3>
|
|
<button type="button" class="close-btn" id="docs-modal-close-btn">×</button>
|
|
</div>
|
|
<div class="modal-body" style="overflow-y: auto; padding: 2rem; font-family: var(--font-body); line-height: 1.6; color: var(--text-main);">
|
|
<div class="docs-markdown-body">
|
|
<h2>🎬 AI Video Upscaler Guide</h2>
|
|
<p>Welcome to the AI Video Upscaler! This application leverages neural network models running locally to upscale and enhance video footage.</p>
|
|
|
|
<h3>💡 Quick Start</h3>
|
|
<ol>
|
|
<li><strong>Upload:</strong> Drag & drop a video file onto the upload zone, or browse to select one.</li>
|
|
<li><strong>Configure:</strong> Choose your AI model, scale factor, and adjust parameters.</li>
|
|
<li><strong>Preview:</strong> Select a frame using the timeline and render a single-frame preview or a 5-second video preview to test your options.</li>
|
|
<li><strong>Run:</strong> Click "Start Full Upscale" to add the job to the GPU queue.</li>
|
|
</ol>
|
|
|
|
<h3>⚙️ AI Enhancement Features</h3>
|
|
<ul>
|
|
<li><strong>AI Face Restoration (GFPGAN):</strong> Restores human faces in blurry or low-res footage. Highly recommended for family home videos or film restoration. Requires python <code>gfpgan</code> package.</li>
|
|
<li><strong>AI Frame Interpolation (RIFE):</strong> Smooths motion by inserting high-quality AI generated frames (e.g. converting 24/30 FPS to 60 FPS). Requires <code>rife-ncnn-vulkan</code> binary.</li>
|
|
<li><strong>AI Audio Denoising (RNNoise):</strong> Eliminates steady background noise, wind, and camera hiss directly from audio tracks using a deep learning RNN model.</li>
|
|
</ul>
|
|
|
|
<h3>📊 Advanced Settings</h3>
|
|
<ul>
|
|
<li><strong>CRF (Constant Rate Factor):</strong> Controls output quality/compression. Lower values (like 18) are near-lossless, higher values compress more.</li>
|
|
<li><strong>Tile Size:</strong> Decreasing this (e.g., to 128 or 64) helps run upscaling on low-end GPUs with limited VRAM.</li>
|
|
<li><strong>Video Trimming:</strong> Trim start and end times to only upscale the parts you need.</li>
|
|
</ul>
|
|
|
|
<h3>🔄 Job Resumption</h3>
|
|
<p>If the application stops or the server crashes while upscaling, the job will be marked as <strong>interrupted</strong> in the queue. Simply click <strong>Resume</strong> to pick up exactly where you left off. Already upscaled frames will be skipped, saving time and energy.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Folder Browser Modal -->
|
|
<div id="folder-modal" class="modal-overlay" style="display: none; z-index: 1100;">
|
|
<div class="modal-card" style="max-width: 600px; display: flex; flex-direction: column; max-height: 80vh;">
|
|
<div class="modal-header">
|
|
<h3><i class="fa-solid fa-folder-open"></i> Select Directory</h3>
|
|
<button type="button" class="close-btn" id="folder-modal-close-btn">×</button>
|
|
</div>
|
|
<div class="modal-body" style="overflow-y: hidden; display: flex; flex-direction: column; height: 60vh; padding: 1.5rem; gap: 1rem;">
|
|
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
|
<button type="button" id="folder-up-btn" class="btn btn-secondary btn-small" style="padding: 0.5rem 0.75rem;">
|
|
<i class="fa-solid fa-arrow-up"></i> Up
|
|
</button>
|
|
<input type="text" id="folder-current-path" style="flex: 1; padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-primary); font-family: monospace; font-size: 0.85rem;" readonly>
|
|
</div>
|
|
|
|
<div id="folder-list" style="flex: 1; overflow-y: auto; border: 1px solid var(--border-color); border-radius: 8px; background: rgba(0, 0, 0, 0.2); padding: 0.5rem;">
|
|
<!-- Folder items list -->
|
|
</div>
|
|
|
|
<div style="display: flex; gap: 0.5rem; align-items: center; border-top: 1px solid var(--border-color); padding-top: 1rem;">
|
|
<input type="text" id="new-folder-name" placeholder="New folder name" style="flex: 1; padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-primary); font-size: 0.88rem;">
|
|
<button type="button" id="create-folder-btn" class="btn btn-secondary btn-small" style="padding: 0.5rem 1rem;">
|
|
<i class="fa-solid fa-folder-plus"></i> Create Folder
|
|
</button>
|
|
</div>
|
|
|
|
<div id="folder-modal-status" style="font-size: 0.78rem; color: var(--danger); min-height: 1.2em;"></div>
|
|
|
|
<div style="display: flex; justify-content: flex-end; gap: 0.75rem; border-top: 1px solid var(--border-color); padding-top: 1rem;">
|
|
<button type="button" id="folder-cancel-btn" class="btn btn-secondary">Cancel</button>
|
|
<button type="button" id="folder-select-btn" class="btn btn-primary">Select Current Directory</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Server File Browser Modal -->
|
|
<div id="file-browser-modal" class="modal-overlay" style="display: none; z-index: 1100;">
|
|
<div class="modal-card" style="max-width: 700px; display: flex; flex-direction: column; max-height: 80vh;">
|
|
<div class="modal-header">
|
|
<h3><i class="fa-solid fa-server"></i> Choose Video Files from Server</h3>
|
|
<button type="button" class="close-btn" id="file-browser-modal-close-btn">×</button>
|
|
</div>
|
|
<div class="modal-body" style="overflow-y: hidden; display: flex; flex-direction: column; height: 60vh; padding: 1.5rem; gap: 1rem;">
|
|
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
|
<button type="button" id="file-browser-up-btn" class="btn btn-secondary btn-small" style="padding: 0.5rem 0.75rem;">
|
|
<i class="fa-solid fa-arrow-up"></i> Up
|
|
</button>
|
|
<input type="text" id="file-browser-current-path" style="flex: 1; padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-primary); font-family: monospace; font-size: 0.85rem;" readonly>
|
|
</div>
|
|
|
|
<div id="file-browser-list" style="flex: 1; overflow-y: auto; border: 1px solid var(--border-color); border-radius: 8px; background: rgba(0, 0, 0, 0.2); padding: 0.5rem;">
|
|
<!-- File items list -->
|
|
</div>
|
|
|
|
<div id="file-browser-selected-info" style="font-size: 0.88rem; color: var(--text-muted); display: flex; justify-content: space-between; align-items: center;">
|
|
<span>Selected: <strong id="file-browser-selected-count" style="color: var(--primary);">0</strong> video file(s)</span>
|
|
</div>
|
|
|
|
<div id="file-browser-modal-status" style="font-size: 0.78rem; color: var(--danger); min-height: 1.2em;"></div>
|
|
|
|
<div style="display: flex; justify-content: flex-end; gap: 0.75rem; border-top: 1px solid var(--border-color); padding-top: 1rem;">
|
|
<button type="button" id="file-browser-cancel-btn" class="btn btn-secondary">Cancel</button>
|
|
<button type="button" id="file-browser-select-btn" class="btn btn-primary" disabled>Select File(s)</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- SMB Share Modal -->
|
|
<div id="smb-modal" class="modal-overlay" style="display: none; z-index: 1200;">
|
|
<div class="modal-card" style="max-width: 500px;">
|
|
<div class="modal-header">
|
|
<h3><i class="fa-solid fa-folder-plus"></i> Map SMB Network Share</h3>
|
|
<button type="button" class="close-btn" id="smb-modal-close-btn">×</button>
|
|
</div>
|
|
<div class="modal-body" style="padding: 1.5rem; display: flex; flex-direction: column; gap: 1rem;">
|
|
<div class="form-group">
|
|
<label for="smb-name" style="font-size: 0.85rem; font-weight: 600;">Share Name (Identifier)</label>
|
|
<input type="text" id="smb-name" placeholder="e.g. video_share" style="padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); width: 100%;">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="smb-server" style="font-size: 0.85rem; font-weight: 600;">SMB Server (IP or Hostname)</label>
|
|
<input type="text" id="smb-server" placeholder="e.g. 192.168.1.10" style="padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); width: 100%;">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="smb-share-name" style="font-size: 0.85rem; font-weight: 600;">Share Folder Name</label>
|
|
<input type="text" id="smb-share-name" placeholder="e.g. videos" style="padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); width: 100%;">
|
|
</div>
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 0;">
|
|
<div class="form-group" style="margin-bottom: 0;">
|
|
<label for="smb-username" style="font-size: 0.85rem; font-weight: 600;">Username (Optional)</label>
|
|
<input type="text" id="smb-username" placeholder="Guest" style="padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); width: 100%;">
|
|
</div>
|
|
<div class="form-group" style="margin-bottom: 0;">
|
|
<label for="smb-password" style="font-size: 0.85rem; font-weight: 600;">Password (Optional)</label>
|
|
<input type="password" id="smb-password" placeholder="••••••••" style="padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); width: 100%;">
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="smb-mount-path" style="font-size: 0.85rem; font-weight: 600;">Local Mount Path</label>
|
|
<div style="display: flex; gap: 0.5rem; align-items: stretch;">
|
|
<input type="text" id="smb-mount-path" placeholder="e.g. /home/david/smb_mount/videos" style="flex: 1; padding: 0.5rem; border-radius: 6px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); font-family: monospace; font-size: 0.85rem;">
|
|
<button type="button" id="browse-smb-mount-btn" class="btn btn-secondary" style="padding: 0 0.75rem;" title="Browse files">
|
|
<i class="fa-solid fa-folder-open"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="smb-modal-status" style="font-size: 0.78rem; min-height: 1.2em;"></div>
|
|
|
|
<div style="display: flex; justify-content: flex-end; gap: 0.75rem; border-top: 1px solid var(--border-color); padding-top: 1rem;">
|
|
<button type="button" id="smb-cancel-btn" class="btn btn-secondary">Cancel</button>
|
|
<button type="button" id="smb-save-btn" class="btn btn-primary">Map & Save Share</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Re-import Modal -->
|
|
<div id="reimport-modal" class="modal-overlay" style="display: none;">
|
|
<div class="modal-card glass-card" style="max-width: 480px;">
|
|
<div class="modal-header">
|
|
<h3><i class="fa-solid fa-cloud-arrow-down"></i> Re-import & Resume Job</h3>
|
|
<button type="button" id="reimport-modal-close-btn" class="close-btn">×</button>
|
|
</div>
|
|
<div class="modal-body" style="padding: 1.5rem 0;">
|
|
<form id="reimport-form">
|
|
<input type="hidden" id="reimport-job-id">
|
|
<input type="hidden" id="reimport-temp-dir">
|
|
|
|
<div class="form-group" style="margin-bottom: 1.25rem;">
|
|
<label for="reimport-video-select" style="display: block; margin-bottom: 0.5rem; font-weight: 600; font-size: 0.85rem;">Select Video File:</label>
|
|
<select id="reimport-video-select" class="form-control" required style="width: 100%; background: var(--bg-card); color: var(--text-main); border: 1px solid var(--border-color); padding: 0.5rem; border-radius: 6px;">
|
|
<!-- Dynamically populated -->
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group" style="margin-bottom: 1.25rem;">
|
|
<label for="reimport-model-select" style="display: block; margin-bottom: 0.5rem; font-weight: 600; font-size: 0.85rem;">Upscale Model:</label>
|
|
<select id="reimport-model-select" class="form-control" style="width: 100%; background: var(--bg-card); color: var(--text-main); border: 1px solid var(--border-color); padding: 0.5rem; border-radius: 6px;">
|
|
<option value="realesrgan-x4plus">Real-ESRGAN x4 Plus (Universal)</option>
|
|
<option value="realesr-animevideov3">Real-ESRGAN Anime Video v3</option>
|
|
<option value="realesrgan-x4plus-anime">Real-ESRGAN Anime x4</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div style="display: grid; grid-template-columns: 1fr; gap: 0.75rem; margin-bottom: 1.5rem;">
|
|
<label class="checkbox-label" style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer; font-size: 0.85rem;">
|
|
<input type="checkbox" id="reimport-face-restoration" style="width: 16px; height: 16px; accent-color: var(--primary);"> Enable Face Restoration (GFPGAN)
|
|
</label>
|
|
<label class="checkbox-label" style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer; font-size: 0.85rem;">
|
|
<input type="checkbox" id="reimport-rife-interpolation" style="width: 16px; height: 16px; accent-color: var(--primary);"> Enable Frame Interpolation (RIFE)
|
|
</label>
|
|
<label class="checkbox-label" style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer; font-size: 0.85rem;">
|
|
<input type="checkbox" id="reimport-audio-denoise" style="width: 16px; height: 16px; accent-color: var(--primary);"> Enable Audio Denoising
|
|
</label>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary" style="width: 100%; justify-content: center; padding: 0.75rem;">
|
|
<i class="fa-solid fa-play"></i> Import & Resume Job
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Javascript Orchestration -->
|
|
<script src="app.js"></script>
|
|
</body>
|
|
</html>
|