Implement advanced AI enhancements UI tracking, styling, requirements, and documentation modal

This commit is contained in:
2026-06-24 13:45:34 -04:00
parent 212291ea94
commit 7b7cb2866a
7 changed files with 236 additions and 16 deletions
+54 -6
View File
@@ -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();
+60
View File
@@ -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">&times;</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>
+3
View File
@@ -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;