Spaces:
Running
Running
File size: 11,552 Bytes
12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab 12ef355 ac9beab |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video Translator & Dubber</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
}
.loader {
border-top-color: #3498db;
-webkit-animation: spin 1s linear infinite;
animation: spin 1s linear infinite;
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.form-container {
transition: opacity 0.5s ease-in-out;
}
</style>
</head>
<body class="bg-gray-100 text-gray-800">
<div class="container mx-auto p-4 sm:p-6 lg:p-8 max-w-4xl">
<header class="text-center mb-8">
<h1 class="text-3xl sm:text-4xl font-bold text-gray-900">Video Translator & Dubber</h1>
<p class="mt-2 text-md text-gray-600">Powered by Gemini & MoviePy</p>
</header>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="mb-6">
{% for category, message in messages %}
<div class="p-4 rounded-md {% if category == 'error' %} bg-red-100 text-red-700 {% elif category == 'warning' %} bg-yellow-100 text-yellow-800 {% else %} bg-blue-100 text-blue-700 {% endif %}" role="alert">
<strong class="font-bold">{% if category == 'error' %}Error!{% elif category == 'warning' %}Warning:{% else %}Info:{% endif %}</strong>
<span class="block sm:inline">{{ message }}</span>
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% if not api_key_set %}
<div class="p-4 mb-6 rounded-md bg-red-100 text-red-700" role="alert">
<strong class="font-bold">Configuration Error!</strong>
<span class="block sm:inline">The `GEMINI_API_KEY` environment variable is not set. This application will not work until it is configured correctly on the server.</span>
</div>
{% endif %}
{% if imagemagick_missing %}
<div class="p-4 mb-6 rounded-md bg-yellow-100 text-yellow-800" role="alert">
<strong class="font-bold">Configuration Warning!</strong>
<span class="block sm:inline">ImageMagick is not detected. The feature to add burned-in subtitles to the final video is disabled. The rest of the features will work normally.</span>
</div>
{% endif %}
<div id="form-container" class="form-container bg-white p-6 sm:p-8 rounded-xl shadow-lg">
<form action="{{ url_for('process') }}" method="post" enctype="multipart/form-data" id="video-form">
<!-- File Upload -->
<div class="mb-6">
<label for="video" class="block text-lg font-medium text-gray-700 mb-2">1. Upload Your Video</label>
<input type="file" name="video" id="video" accept="video/*" required class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100 transition duration-150">
</div>
<!-- Language Selection -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div>
<label for="source_language" class="block text-lg font-medium text-gray-700 mb-2">2. Original Language</label>
<select name="source_language" id="source_language" class="w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500">
{% for lang in supported_languages %}
<option value="{{ lang }}">{{ lang }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="translate_to" class="block text-lg font-medium text-gray-700 mb-2">3. Translate To (Optional)</label>
<select name="translate_to" id="translate_to" class="w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500">
<option value="None">None</option>
{% for lang in supported_languages %}
{% if lang != 'Auto Detect' %}
<option value="{{ lang }}">{{ lang }}</option>
{% endif %}
{% endfor %}
</select>
</div>
</div>
<!-- Processing Options -->
<div class="mb-8">
<label class="block text-lg font-medium text-gray-700 mb-3">4. Choose Output Options</label>
<div class="space-y-3">
<div class="flex items-center">
<input id="add_subtitles" name="add_subtitles" type="checkbox" class="h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" {% if imagemagick_missing %}disabled{% endif %}>
<label for="add_subtitles" class="ml-3 block text-sm font-medium text-gray-700">Add burned-in subtitles to video</label>
</div>
<div class="flex items-center">
<input id="add_tts" name="add_tts" type="checkbox" class="h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
<label for="add_tts" class="ml-3 block text-sm font-medium text-gray-700">Replace audio with AI-generated voice (dubbing)</label>
</div>
</div>
</div>
<!-- Submit Button -->
<div class="text-center">
<button type="submit" id="submit-btn" class="w-full sm:w-auto inline-flex justify-center items-center px-8 py-3 border border-transparent text-base font-medium rounded-full shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition duration-150 disabled:opacity-50" {% if not api_key_set %}disabled{% endif %}>
Process Video
</button>
</div>
</form>
</div>
<!-- Loading Spinner -->
<div id="loader-container" class="text-center my-10 hidden">
<div class="loader ease-linear rounded-full border-8 border-t-8 border-gray-200 h-32 w-32 mx-auto"></div>
<h2 class="text-2xl font-semibold mt-6 text-gray-700">Processing your video...</h2>
<p class="text-gray-500 mt-2">This may take several minutes depending on the video length.</p>
</div>
<!-- Results Section -->
{% if results %}
<div id="results-section" class="mt-10 bg-white p-6 sm:p-8 rounded-xl shadow-lg">
<h2 class="text-2xl font-bold text-gray-800 mb-6 border-b pb-3">Processing Complete!</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
{% if results.output_video_file %}
<div class="md:col-span-2 bg-gray-50 p-4 rounded-lg">
<h3 class="font-semibold text-lg text-gray-700 mb-3">Final Processed Video</h3>
<video class="w-full rounded-lg shadow-md" controls>
<source src="{{ url_for('download_file', session_id=session_id, filename=results.output_video_file) }}" type="video/mp4">
Your browser does not support the video tag.
</video>
<a href="{{ url_for('download_file', session_id=session_id, filename=results.output_video_file) }}" class="mt-4 inline-block w-full text-center px-6 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">Download Final Video (.mp4)</a>
</div>
{% endif %}
{% if results.original_srt_file or results.translated_srt_file or results.tts_audio_file %}
<div class="md:col-span-2">
<h3 class="font-semibold text-lg text-gray-700 mb-3">Downloadable Assets</h3>
<div class="space-y-3">
{% if results.original_srt_file %}
<a href="{{ url_for('download_file', session_id=session_id, filename=results.original_srt_file) }}" class="block bg-blue-50 hover:bg-blue-100 p-3 rounded-md transition duration-150">
<span class="font-medium text-blue-800">Original Subtitles (.srt)</span>
</a>
{% endif %}
{% if results.translated_srt_file %}
<a href="{{ url_for('download_file', session_id=session_id, filename=results.translated_srt_file) }}" class="block bg-purple-50 hover:bg-purple-100 p-3 rounded-md transition duration-150">
<span class="font-medium text-purple-800">Translated Subtitles (.srt)</span>
</a>
{% endif %}
{% if results.tts_audio_file %}
<a href="{{ url_for('download_file', session_id=session_id, filename=results.tts_audio_file) }}" class="block bg-teal-50 hover:bg-teal-100 p-3 rounded-md transition duration-150">
<span class="font-medium text-teal-800">Generated Audio (Dub) (.mp3)</span>
</a>
{% endif %}
</div>
</div>
{% endif %}
</div>
<div class="text-center mt-8">
<a href="{{ url_for('index') }}" class="text-blue-600 hover:text-blue-800 font-medium">Process another video →</a>
</div>
</div>
{% endif %}
</div>
<footer class="text-center p-4 mt-8">
<p class="text-sm text-gray-500">A project by the open-source community. Please use responsibly.</p>
</footer>
<script>
const form = document.getElementById('video-form');
const submitBtn = document.getElementById('submit-btn');
const formContainer = document.getElementById('form-container');
const loaderContainer = document.getElementById('loader-container');
form.addEventListener('submit', function() {
// Check if form is valid before proceeding
if(form.checkValidity()){
submitBtn.disabled = true;
submitBtn.innerHTML = 'Processing...';
formContainer.style.opacity = '0';
setTimeout(() => {
formContainer.classList.add('hidden');
loaderContainer.classList.remove('hidden');
}, 500);
}
});
</script>
</body>
</html>
|