Implement advanced AI enhancements UI tracking, styling, requirements, and documentation modal
This commit is contained in:
+54
-6
@@ -600,6 +600,10 @@ generateVideoPreviewBtn.addEventListener("click", async () => {
|
||||
const filterSharpen = document.getElementById("filter-sharpen");
|
||||
const filterFps = document.getElementById("filter-fps");
|
||||
|
||||
const aiFaceRestoration = document.getElementById("ai-face-restoration");
|
||||
const aiRifeInterpolation = document.getElementById("ai-rife-interpolation");
|
||||
const aiAudioDenoise = document.getElementById("ai-audio-denoise");
|
||||
|
||||
const transcodeFormat = transcodeSelect.value;
|
||||
|
||||
try {
|
||||
@@ -626,7 +630,10 @@ generateVideoPreviewBtn.addEventListener("click", async () => {
|
||||
interpolation: filterFps.checked,
|
||||
webhook_url: null,
|
||||
transcode_format: transcodeFormat,
|
||||
is_preview: true
|
||||
is_preview: true,
|
||||
ai_face_restoration: aiFaceRestoration ? aiFaceRestoration.checked : false,
|
||||
ai_rife_interpolation: aiRifeInterpolation ? aiRifeInterpolation.checked : false,
|
||||
ai_audio_denoise: aiAudioDenoise ? aiAudioDenoise.checked : false
|
||||
})
|
||||
});
|
||||
|
||||
@@ -711,6 +718,10 @@ startUpscaleBtn.addEventListener("click", async () => {
|
||||
const filterSharpen = document.getElementById("filter-sharpen");
|
||||
const filterFps = document.getElementById("filter-fps");
|
||||
const webhookUrl = document.getElementById("webhook-url");
|
||||
|
||||
const aiFaceRestoration = document.getElementById("ai-face-restoration");
|
||||
const aiRifeInterpolation = document.getElementById("ai-rife-interpolation");
|
||||
const aiAudioDenoise = document.getElementById("ai-audio-denoise");
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/upscale/start", {
|
||||
@@ -733,7 +744,10 @@ startUpscaleBtn.addEventListener("click", async () => {
|
||||
denoise: filterDenoise.checked,
|
||||
sharpen: filterSharpen.checked,
|
||||
interpolation: filterFps.checked,
|
||||
webhook_url: webhookUrl.value.trim() || null
|
||||
webhook_url: webhookUrl.value.trim() || null,
|
||||
ai_face_restoration: aiFaceRestoration ? aiFaceRestoration.checked : false,
|
||||
ai_rife_interpolation: aiRifeInterpolation ? aiRifeInterpolation.checked : false,
|
||||
ai_audio_denoise: aiAudioDenoise ? aiAudioDenoise.checked : false
|
||||
})
|
||||
});
|
||||
|
||||
@@ -816,6 +830,10 @@ function updateProgressUI(data) {
|
||||
statusText = "Queued in pipeline. Waiting for GPU lock...";
|
||||
} else if (data.status === "extracting") {
|
||||
statusText = "Extracting video frames...";
|
||||
} else if (data.status === "restoring_faces") {
|
||||
statusText = "GFPGAN: Running Face Restoration on upscaled frames...";
|
||||
} else if (data.status === "interpolating") {
|
||||
statusText = "RIFE: Interpolating frames for smooth motion...";
|
||||
} else if (data.status === "assembling") {
|
||||
statusText = "Assembling video preview and audio tracks...";
|
||||
}
|
||||
@@ -891,6 +909,10 @@ function updateProgressUI(data) {
|
||||
appendLogLine("FFmpeg: Splitting video stream to high-fidelity JPG frames...", "info");
|
||||
} else if (data.status === "upscaling") {
|
||||
appendLogLine(`Vulkan GPU: Processing frame ${data.current_frame} of ${data.total_frames} (ETA: ${data.eta})...`, "info");
|
||||
} else if (data.status === "restoring_faces") {
|
||||
appendLogLine("GFPGAN: Running Face Restoration on upscaled frames...", "info");
|
||||
} else if (data.status === "interpolating") {
|
||||
appendLogLine("RIFE: Interpolating frames for smooth motion...", "info");
|
||||
} else if (data.status === "assembling") {
|
||||
appendLogLine("FFmpeg: Reassembling enhanced frames & merging audio tracks...", "info");
|
||||
} else if (data.status === "completed") {
|
||||
@@ -1021,7 +1043,7 @@ async function checkActiveJobs() {
|
||||
|
||||
const jobs = await res.json();
|
||||
const activeJob = jobs.find(job =>
|
||||
["queued", "analyzing", "extracting", "upscaling", "assembling"].includes(job.status)
|
||||
["queued", "analyzing", "extracting", "upscaling", "restoring_faces", "interpolating", "assembling"].includes(job.status)
|
||||
);
|
||||
|
||||
if (activeJob) {
|
||||
@@ -1318,7 +1340,7 @@ async function loadQueue() {
|
||||
const jobs = await res.json();
|
||||
|
||||
const displayJobs = jobs.filter(job =>
|
||||
["queued", "analyzing", "extracting", "upscaling", "assembling", "completed", "failed", "cancelled", "interrupted"].includes(job.status)
|
||||
["queued", "analyzing", "extracting", "upscaling", "restoring_faces", "interpolating", "assembling", "completed", "failed", "cancelled", "interrupted"].includes(job.status)
|
||||
);
|
||||
|
||||
if (displayJobs.length === 0) {
|
||||
@@ -1380,7 +1402,7 @@ async function loadQueue() {
|
||||
<i class="fa-solid fa-trash"></i> Clear
|
||||
</button>
|
||||
`;
|
||||
} else if (["queued", "analyzing", "extracting", "upscaling", "assembling"].includes(job.status)) {
|
||||
} else if (["queued", "analyzing", "extracting", "upscaling", "restoring_faces", "interpolating", "assembling"].includes(job.status)) {
|
||||
actionsHtml = `
|
||||
${reorderHtml}
|
||||
<button class="btn btn-primary btn-small btn-view-job" data-id="${job.job_id}">
|
||||
@@ -1401,7 +1423,7 @@ async function loadQueue() {
|
||||
`;
|
||||
}
|
||||
|
||||
const showProgress = ["analyzing", "extracting", "upscaling", "assembling"].includes(job.status);
|
||||
const showProgress = ["analyzing", "extracting", "upscaling", "restoring_faces", "interpolating", "assembling"].includes(job.status);
|
||||
const progressStyle = showProgress ? "display: block;" : "display: none;";
|
||||
|
||||
item.innerHTML = `
|
||||
@@ -1757,6 +1779,32 @@ async function initializeApp() {
|
||||
loadUploadsBrowser();
|
||||
});
|
||||
}
|
||||
|
||||
// Register documentation modal handlers
|
||||
const docsBtn = document.getElementById("docs-btn");
|
||||
const docsModal = document.getElementById("docs-modal");
|
||||
const docsCloseBtn = document.getElementById("docs-modal-close-btn");
|
||||
|
||||
if (docsBtn && docsModal) {
|
||||
docsBtn.addEventListener("click", () => {
|
||||
docsModal.style.display = "flex";
|
||||
document.body.style.overflow = "hidden";
|
||||
});
|
||||
}
|
||||
|
||||
if (docsCloseBtn && docsModal) {
|
||||
docsCloseBtn.addEventListener("click", () => {
|
||||
docsModal.style.display = "none";
|
||||
document.body.style.overflow = "";
|
||||
});
|
||||
|
||||
docsModal.addEventListener("click", (e) => {
|
||||
if (e.target === docsModal) {
|
||||
docsModal.style.display = "none";
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
initializeApp();
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
</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">
|
||||
@@ -280,6 +283,22 @@
|
||||
</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>
|
||||
@@ -590,6 +609,47 @@
|
||||
</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>
|
||||
|
||||
<!-- Javascript Orchestration -->
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -653,11 +653,14 @@ input:checked + .slider:before {
|
||||
.status-badge.queued { background: rgba(255, 184, 0, 0.1); border: 1px solid var(--warning); color: var(--warning); }
|
||||
.status-badge.extracting { background: rgba(189, 0, 255, 0.15); border: 1px solid var(--secondary); color: #e499ff; }
|
||||
.status-badge.upscaling { background: rgba(0, 240, 255, 0.1); border: 1px solid var(--primary); color: var(--primary); }
|
||||
.status-badge.restoring_faces { background: rgba(255, 0, 255, 0.1); border: 1px solid var(--secondary); color: var(--secondary); }
|
||||
.status-badge.interpolating { background: rgba(0, 255, 184, 0.1); border: 1px solid var(--primary); color: var(--primary); }
|
||||
.status-badge.assembling { background: rgba(0, 240, 255, 0.1); border: 1px solid var(--primary); color: var(--primary); }
|
||||
.status-badge.completed { background: rgba(0, 255, 135, 0.1); border: 1px solid var(--success); color: var(--success); }
|
||||
.status-badge.failed { background: rgba(255, 0, 85, 0.1); border: 1px solid var(--danger); color: var(--danger); }
|
||||
.status-badge.interrupted { background: rgba(255, 255, 255, 0.08); border: 1px solid var(--text-muted); color: var(--text-muted); }
|
||||
|
||||
|
||||
.bar-container {
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
|
||||
Reference in New Issue
Block a user