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 &rarr;</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>