Ai_lip_sync / templates /index.html
WebashalarForML's picture
Update templates/index.html
a9c1e99 verified
{% extends "base.html" %}
{% block title %}Dashboard | AI Avatar Creator{% endblock %}
{% block head_extra %}
<style>
.hero-title {
font-size: 2.8rem;
font-weight: 700;
letter-spacing: -1px;
margin-bottom: 40px;
line-height: 1.2;
color: var(--text-main);
}
.hero-title span { color: var(--text-muted); font-weight: 500; }
.cards-row {
display: flex;
gap: 24px;
margin-bottom: 40px;
flex-wrap: wrap;
}
.ui-card {
flex: 1;
min-width: 280px;
background: var(--card-bg);
border-radius: 24px;
padding: 32px;
box-shadow: var(--shadow-card);
border: 1px solid var(--card-border);
transition: transform 0.2s ease, box-shadow 0.2s ease;
position: relative;
}
.ui-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 30px rgba(0,0,0,0.05);
}
.card-icon {
width: 48px; height: 48px; border-radius: 14px;
display: flex; align-items: center; justify-content: center;
font-size: 24px; margin-bottom: 24px;
}
.card-icon.yellow { background: #fef3c7; color: #d97706; }
[data-theme="dark"] .card-icon.yellow { background: rgba(217, 119, 6, 0.2); }
.card-icon.red { background: #fee2e2; color: #dc2626; }
[data-theme="dark"] .card-icon.red { background: rgba(220, 38, 38, 0.2); }
.card-icon.blue { background: #e0f2fe; color: #0284c7; }
[data-theme="dark"] .card-icon.blue { background: rgba(2, 132, 199, 0.2); }
.ui-card h3 { font-size: 18px; margin: 0 0 10px 0; font-weight: 600; }
.ui-card p { font-size: 14px; color: var(--text-secondary); margin: 0 0 24px 0; line-height: 1.5; }
/* Custom File Input */
.custom-file-upload {
display: flex; align-items: center; justify-content: center; gap: 8px;
width: 100%; padding: 14px; background: var(--bg-body);
border: 1px dashed var(--text-muted); border-radius: 12px;
cursor: pointer; font-weight: 500; font-size: 14px;
color: var(--text-main); transition: 0.2s; box-sizing: border-box;
}
.custom-file-upload:hover { border-color: var(--accent-blue); background: rgba(59, 130, 246, 0.05); }
.custom-file-upload input { display: none; }
.filename-display {
margin-top: 10px; font-size: 12px; color: var(--text-secondary);
word-break: break-all; text-align: center; font-weight: 500;
}
/* Custom Select */
.custom-select {
width: 100%; padding: 14px; border-radius: 12px; border: 1px solid var(--card-border);
background: var(--bg-body); color: var(--text-main); font-size: 14px;
font-family: inherit; font-weight: 500; cursor: pointer; outline: none;
appearance: none; -webkit-appearance: none;
background-image: url('data:image/svg+xml;utf8,<svg fill="%236b7280" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 10l5 5 5-5z"/></svg>');
background-repeat: no-repeat; background-position: right 12px top 50%; background-size: 16px;
}
.custom-select:focus { border-color: var(--accent-blue); box-shadow: 0 0 0 3px rgba(59,130,246,0.1); }
/* Bottom Action Bar */
.action-bar {
background: var(--card-bg); border-radius: 20px; padding: 24px;
border: 1px solid var(--card-border); box-shadow: var(--shadow-card);
margin-bottom: 20px;
}
.action-bar-top {
display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;
font-size: 14px; color: var(--text-secondary);
}
.advanced-toggle {
display: flex; align-items: center; gap: 8px; cursor: pointer;
color: var(--text-muted); font-weight: 500;
}
.action-bar-main {
display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
background: var(--bg-body); padding: 12px; border-radius: 16px;
}
/* Input pills inside action bar */
.pill-input {
background: var(--card-bg); border: 1px solid var(--card-border);
padding: 10px 16px; border-radius: 30px; font-size: 13px; font-weight: 500;
display: inline-flex; align-items: center; gap: 8px; color: var(--text-main);
}
.pill-input input[type="number"] {
border: none; background: transparent; width: 40px; color: var(--text-main);
font-weight: 600; outline: none; text-align: center;
}
.pill-checkbox {
display: flex; align-items: center; gap: 6px; cursor: pointer;
}
.pill-checkbox input { accent-color: var(--btn-dark); width: 16px; height: 16px; cursor: pointer;}
.generate-btn {
background: var(--btn-dark); color: var(--bg-window);
padding: 12px 24px; border-radius: 30px; border: none; font-size: 14px;
font-weight: 600; cursor: pointer; transition: 0.2s; display: inline-flex;
align-items: center; gap: 8px; margin-left: auto;
}
.generate-btn:hover { background: var(--btn-dark-hover); transform: translateY(-1px); }
[data-theme="dark"] .generate-btn { color: #0f172a; }
/* Fun little floating bot from the reference image */
.floating-bot {
position: absolute; top: -30px; right: 20px; font-size: 45px;
filter: drop-shadow(0 4px 10px rgba(0,0,0,0.1));
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-8px); }
}
/* Loading Overlay */
.loading-overlay {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(255,255,255,0.7); backdrop-filter: blur(10px);
display: none; flex-direction: column; justify-content: center; align-items: center;
z-index: 9999; color: #111827; font-weight: 500;
}
[data-theme="dark"] .loading-overlay { background: rgba(15,23,42,0.8); color: white; }
.loader {
width: 48px; height: 48px; border: 4px solid var(--card-border);
border-bottom-color: var(--btn-dark); border-radius: 50%;
animation: spin 1s linear infinite; margin-bottom: 20px;
}
@keyframes spin { 100% { transform: rotate(360deg); } }
</style>
{% endblock %}
{% block content %}
<h1 class="hero-title">
Hi Creator, <span>Ready to</span><br>Achieve Great Things?
</h1>
<form action="{{ url_for('infer') }}" method="post" enctype="multipart/form-data" id="inferenceForm">
<div class="cards-row">
<!-- Card 1: Face -->
<div class="ui-card">
<div class="card-icon yellow"><i class="fas fa-layer-group"></i></div>
<h3>Source Material</h3>
<p>Upload the base image or video of the face you want to animate.</p>
<label class="custom-file-upload">
<input type="file" id="face_file" name="face_file" accept="video/*,image/*" required>
<i class="fas fa-video"></i> Select Face File
</label>
<div id="face_filename" class="filename-display"></div>
</div>
<!-- Card 2: Audio -->
<div class="ui-card">
<div class="floating-bot">🤖</div>
<div class="card-icon red"><i class="fab fa-slack"></i></div>
<h3>Voice Track</h3>
<p>Provide the audio track to generate perfect lip-syncing synchronization.</p>
<label class="custom-file-upload">
<input type="file" id="audio_file" name="audio_file" accept="audio/*,video/*" required>
<i class="fas fa-music"></i> Select Audio File
</label>
<div id="audio_filename" class="filename-display"></div>
</div>
<!-- Card 3: Model -->
<div class="ui-card">
<div class="card-icon blue"><i class="fas fa-calendar-alt"></i></div>
<h3>AI Model Settings</h3>
<p>Select your Wav2Lip checkpoint to process the avatar generation.</p>
<select id="model_select" name="model_select" class="custom-select" required>
<option value="">-- Choose a model --</option>
{% for model in models %}
<option value="{{ model }}">{{ model }}</option>
{% endfor %}
</select>
</div>
</div>
<!-- Action Bar (Mimicking Prompt Input Area) -->
<div class="action-bar">
<div class="action-bar-top">
<div class="advanced-toggle"><i class="fas fa-sparkles"></i> Unlock more with Advanced Options</div>
<div><i class="fas fa-cog"></i> Powered by Wav2Lip</div>
</div>
<div class="action-bar-main">
<!-- Inline settings styled as sleek pills -->
<label class="pill-input pill-checkbox">
<input type="checkbox" id="static_input" name="static_input">
Static Image
</label>
<div class="pill-input">
<i class="fas fa-tachometer-alt text-muted"></i> FPS
<input type="number" id="fps" name="fps" value="25" min="1" max="60">
</div>
<div class="pill-input">
<i class="fas fa-compress text-muted"></i> Resize
<input type="number" id="resize_factor" name="resize_factor" value="1" min="1" max="4">
</div>
<label class="pill-input pill-checkbox">
<input type="checkbox" id="rotate" name="rotate"> Rotate 90°
</label>
<label class="pill-input pill-checkbox">
<input type="checkbox" id="nosmooth" name="nosmooth"> No Smooth
</label>
<!-- Submit Button -->
<button type="submit" class="generate-btn">
<i class="fas fa-paper-plane"></i> Generate Output
</button>
</div>
</div>
</form>
<div class="loading-overlay" id="loadingOverlay">
<div class="loader"></div>
<h2>Processing your avatar...</h2>
<p style="color: var(--text-secondary); margin-top: 10px;">This may take a few minutes depending on file size.</p>
</div>
{% endblock %}
{% block scripts_extra %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// File upload name display
const faceInput = document.getElementById('face_file');
const faceDisplay = document.getElementById('face_filename');
faceInput.addEventListener('change', (e) => {
if(e.target.files[0]) faceDisplay.textContent = e.target.files[0].name;
else faceDisplay.textContent = '';
});
const audioInput = document.getElementById('audio_file');
const audioDisplay = document.getElementById('audio_filename');
audioInput.addEventListener('change', (e) => {
if(e.target.files[0]) audioDisplay.textContent = e.target.files[0].name;
else audioDisplay.textContent = '';
});
// Loading state
const form = document.getElementById('inferenceForm');
const overlay = document.getElementById('loadingOverlay');
const btn = document.querySelector('.generate-btn');
form.addEventListener('submit', function() {
overlay.style.display = 'flex';
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Initializing...';
});
window.addEventListener('pageshow', function() {
overlay.style.display = 'none';
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-paper-plane"></i> Generate Output';
});
});
</script>
{% endblock %}