arcanus commited on
Commit
104dce2
·
1 Parent(s): 028a53c

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +898 -86
templates/index.html CHANGED
@@ -1,96 +1,908 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>AI DUB API</title>
7
- <link rel="stylesheet" href="/static/styles.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  <script>
9
- async function processVideo() {
10
- const formData = new FormData();
11
- const video = document.getElementById("video").files[0];
12
- const targetLanguage = document.getElementById("target_language").value;
13
-
14
- formData.append("video", video);
15
- formData.append("target_language", targetLanguage);
16
-
17
- const progressContainer = document.getElementById("progress-container");
18
- const progressBar = document.getElementById("progress-bar");
19
- const progressText = document.getElementById("progress-text");
20
- const output = document.getElementById("output");
21
-
22
- // Získání délky videa
23
- const videoElement = document.createElement("video");
24
- const url = URL.createObjectURL(video);
25
- videoElement.src = url;
26
-
27
- videoElement.onloadedmetadata = async () => {
28
- const videoDuration = videoElement.duration; // Délka videa v sekundách
29
- const processingTime = videoDuration / 2; // Odhadovaná doba zpracování (polovina délky)
30
-
31
- // Zobraz progress bar
32
- progressContainer.style.display = "block";
33
- progressBar.value = 0;
34
- progressText.textContent = "Processing...";
35
-
36
- let currentProgress = 0;
37
- const intervalTime = 100; // Interval aktualizace v milisekundách
38
- const step = (100 / (processingTime * 1000 / intervalTime)); // Krok pro progress bar
39
- const interval = setInterval(() => {
40
- if (currentProgress < 100) {
41
- currentProgress += step;
42
- progressBar.value = Math.min(100, currentProgress); // Omezit na 100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  } else {
44
- clearInterval(interval);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
- }, intervalTime);
47
-
48
- try {
49
- const response = await fetch("http://127.0.0.1:5000/process_video", {
50
- method: "POST",
51
- body: formData,
52
- });
53
-
54
- const result = await response.json();
55
-
56
- if (result.status === "success") {
57
- progressBar.value = 100; // Nastav na 100 %, pokud je hotovo
58
- progressText.textContent = "Completed!";
59
- output.textContent = `Success! File saved at: ${result.result}`;
60
- } else {
61
- progressText.textContent = "Error occurred!";
62
- output.textContent = `Error: ${result.message}`;
63
- }
64
- } catch (error) {
65
- progressText.textContent = "Failed!";
66
- output.textContent = `Error: ${error.message}`;
67
- } finally {
68
- clearInterval(interval); // Zastav interval
69
- setTimeout(() => {
70
- progressContainer.style.display = "none"; // Skryj progress bar
71
- }, 3000);
72
- }
73
- };
74
- }
75
  </script>
76
- </head>
77
- <body>
78
- <h1>AI DUB API</h1>
79
- <form onsubmit="event.preventDefault(); processVideo();">
80
- <label for="video">Upload Video:</label>
81
- <input type="file" id="video" name="video" required><br><br>
82
- <label for="target_language">Target Language:</label>
83
- <select id="target_language" name="target_language" required>
84
- <option value="Czech (cs)">Czech</option>
85
- <option value="English (en)">English</option>
86
- <option value="Spanish (es)">Spanish</option>
87
- </select><br><br>
88
- <button type="submit">Process</button>
89
- </form>
90
- <div id="output"></div>
91
- <div id="progress-container" style="display: none;">
92
- <p id="progress-text">Processing...</p>
93
- <progress id="progress-bar" value="0" max="100" style="width: 100%;"></progress>
94
- </div>
95
  </body>
96
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="cs">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title></title>
7
+ <link rel="stylesheet" href="/static/css/uikit.min.css" />
8
+ <script src="/static/js/uikit.min.js"></script>
9
+ <script src="/static/js/uikit-icons.min.js"></script>
10
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
11
+ <style>
12
+ .hidden { display: none; }
13
+ body { padding: 20px; }
14
+ .speaker-voice { margin-bottom: 15px; }
15
+
16
+ /* Styl pro plovoucí tlačítko */
17
+ .floating-button {
18
+ position: fixed;
19
+ bottom: 20px;
20
+ left: 20px;
21
+ z-index: 1000;
22
+ padding: 15px 30px;
23
+ background-color: #dc3545;
24
+ color: white;
25
+ border: none;
26
+ border-radius: 5px;
27
+ cursor: pointer;
28
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
29
+ transition: all 0.3s ease;
30
+ }
31
+
32
+ .floating-button:hover {
33
+ background-color: #c82333;
34
+ box-shadow: 0 4px 8px rgba(0,0,0,0.3);
35
+ transform: translateY(-2px);
36
+ }
37
+
38
+ .floating-button:active {
39
+ transform: translateY(0);
40
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
41
+ }
42
+
43
+ /* Styl pro toast notifikaci */
44
+ .toast-container {
45
+ position: fixed;
46
+ bottom: 80px;
47
+ left: 20px;
48
+ z-index: 1000;
49
+ }
50
+
51
+ .toast {
52
+ background-color: #28a745;
53
+ color: white;
54
+ padding: 15px 25px;
55
+ border-radius: 5px;
56
+ margin-bottom: 10px;
57
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
58
+ display: none;
59
+ }
60
+
61
+ .toast.error {
62
+ background-color: #dc3545;
63
+ }
64
+ </style>
65
+ </head>
66
+ <body>
67
+ <!-- Toast notifikace -->
68
+ <div class="toast-container">
69
+ <div id="toast" class="toast"></div>
70
+ </div>
71
+
72
+ <!-- Plovoucí tlačítko -->
73
+ <button id="purgeButton" class="floating-button">Vymazat cache</button>
74
+
75
+ <div class="container">
76
+ <h1 class="text-center mb-4"></h1>
77
+
78
+ <div class="card">
79
+ <div class="card-body">
80
+ <form id="translateForm" enctype="multipart/form-data">
81
+ <!-- Input Type Selection -->
82
+ <div class="uk-margin">
83
+ <label class="uk-form-label" for="project_name">Název projektu:</label>
84
+ <div class="uk-form-controls">
85
+ <input class="uk-input" type="text" id="project_name" name="project_name" placeholder="Zadejte název projektu">
86
+ </div>
87
+ </div>
88
+ <div class="mb-3">
89
+ <label class="form-label">Zdroj videa</label>
90
+ <select class="form-select" id="inputType" name="input_type">
91
+ <option value="file">Nahrát video</option>
92
+ <option value="url">URL adresa</option>
93
+ <option value="directory">Cesta k videu</option>
94
+ </select>
95
+ </div>
96
+
97
+ <!-- Input Fields -->
98
+ <div id="fileInput" class="mb-3">
99
+ <label class="form-label">Video soubor</label>
100
+ <input type="file" class="form-control" name="video" accept="video/*">
101
+ </div>
102
+
103
+ <div id="urlInput" class="mb-3 hidden">
104
+ <label class="form-label">URL adresa videa</label>
105
+ <input type="url" class="form-control" name="url" placeholder="Zadejte URL adresu videa" id="urlField">
106
+ <div id="youtubeProgress" class="progress mt-2 hidden">
107
+ <div class="progress-bar" role="progressbar" style="width: 0%"></div>
108
+ </div>
109
+ <div id="youtubeStatus" class="mt-2"></div>
110
+ </div>
111
+
112
+ <div id="directoryInput" class="mb-3 hidden">
113
+ <label class="form-label">Cesta k souboru</label>
114
+ <input type="text" class="form-control" name="directory" placeholder="Zadejte cestu k video souboru">
115
+ </div>
116
+
117
+ <!-- Language Selection -->
118
+ <div class="mb-3">
119
+ <label class="form-label">Zdrojový jazyk</label>
120
+ <select class="form-select" name="source_language" id="sourceLanguage">
121
+ <option value="Automatic detection">Automatická detekce</option>
122
+ {% for lang in languages %}
123
+ {% if lang != "Automatic detection" %}
124
+ <option value="{{ lang }}">{{ lang }}</option>
125
+ {% endif %}
126
+ {% endfor %}
127
+ </select>
128
+ </div>
129
+
130
+ <div class="mb-3">
131
+ <label class="form-label">Cílový jazyk</label>
132
+ <select class="form-select" name="target_language">
133
+ {% for lang in languages[1:] %}
134
+ {% if lang == "Czech (cs)" %}
135
+ <option value="{{ lang }}" selected>Čeština (cs)</option>
136
+ {% else %}
137
+ <option value="{{ lang }}">{{ lang }}</option>
138
+ {% endif %}
139
+ {% endfor %}
140
+ </select>
141
+ </div>
142
+
143
+ <!-- Speakers Configuration -->
144
+ <div class="mb-3">
145
+ <label class="form-label">Počet mluvčích</label>
146
+ <input type="range" class="form-range" id="maxSpeakers" name="max_speakers"
147
+ min="1" max="12" value="1" oninput="updateSpeakers(this.value)">
148
+ <span id="speakersValue">1</span> mluvčí
149
+ </div>
150
+
151
+ <!-- TTS Voice Selection -->
152
+ <div id="voiceSelectors">
153
+ {% for i in range(12) %}
154
+ <div class="speaker-voice" id="voice{{ '%02d' % i }}" {% if i >= 1 %}style="display:none"{% endif %}>
155
+ <label class="form-label">Hlas mluvčího {{ i + 1 }}</label>
156
+ <select class="form-select" name="tts_voice{{ '%02d' % i }}">
157
+ {% for voice in tts_voices %}
158
+ <option value="{{ voice }}" {% if i == 0 and voice == "cs-CZ-AntoninNeural-Male" %}selected{% endif %}>{{ voice }}</option>
159
+ {% endfor %}
160
+ </select>
161
+ </div>
162
+ {% endfor %}
163
+ </div>
164
+
165
+ <!-- Subtitle Settings -->
166
+ <div class="mb-3">
167
+ <div class="card">
168
+ <div class="card-header">
169
+ <h5 class="mb-0">
170
+ <button class="btn btn-link" type="button" data-bs-toggle="collapse" data-bs-target="#subtitleSettingsCollapse">
171
+ Nastavení titulků
172
+ </button>
173
+ </h5>
174
+ </div>
175
+ <div id="subtitleSettingsCollapse" class="collapse">
176
+ <div class="card-body">
177
+ <div class="form-group mb-3">
178
+ <label for="subtitleFormat">Formát titulků</label>
179
+ <select class="form-select" id="subtitleFormat" name="subtitle_format">
180
+ <option value="disable">Vypnuto</option>
181
+ <option value="srt" selected>SRT</option>
182
+ <option value="vtt">VTT</option>
183
+ <option value="ass">ASS</option>
184
+ <option value="txt">TXT</option>
185
+ <option value="tsv">TSV</option>
186
+ <option value="json">JSON</option>
187
+ <option value="aud">AUD</option>
188
+ </select>
189
+ </div>
190
+
191
+ <div class="form-check mb-3">
192
+ <input class="form-check-input" type="checkbox" id="softSubtitles" name="soft_subtitles">
193
+ <label class="form-check-label" for="softSubtitles">
194
+ Přidat měkké titulky do videa
195
+ </label>
196
+ </div>
197
+
198
+ <div class="form-check mb-3">
199
+ <input class="form-check-input" type="checkbox" id="burnSubtitles" name="burn_subtitles">
200
+ <label class="form-check-label" for="burnSubtitles">
201
+ Vypálit titulky do videa
202
+ </label>
203
+ </div>
204
+
205
+ <hr>
206
+ <h6>Nastavení Whisper</h6>
207
+
208
+ <div class="form-check mb-3">
209
+ <input class="form-check-input" type="checkbox" id="literalizeNumbers" name="literalize_numbers" checked>
210
+ <label class="form-check-label" for="literalizeNumbers">
211
+ Převádět čísla na slova
212
+ </label>
213
+ </div>
214
+
215
+ <div class="form-group mb-3">
216
+ <label for="diarizationModel">Model pro rozpoznání mluvčích</label>
217
+ <select class="form-select" id="diarizationModel" name="diarization_model">
218
+ <option value="pyannote_2.1" selected>Pyannote 2.1</option>
219
+ </select>
220
+ </div>
221
+
222
+ <div class="form-group mb-3">
223
+ <label for="segmentDuration">Doba trvání segmentu (sekundy)</label>
224
+ <input type="range" class="form-range" id="segmentDuration"
225
+ name="segment_duration" min="1" max="30" value="15">
226
+ <span id="segmentDurationValue">15</span>
227
+ </div>
228
+
229
+ <div class="form-group mb-3">
230
+ <label for="whisperModel">Model Whisper</label>
231
+ <select class="form-select" id="whisperModel" name="whisper_model">
232
+ <option value="large-v3">Large V3</option>
233
+ <option value="medium">Medium</option>
234
+ </select>
235
+ </div>
236
+
237
+ <div class="form-group mb-3">
238
+ <label for="computeType">Typ výpočtu</label>
239
+ <select class="form-select" id="computeType" name="compute_type">
240
+ <option value="float16">float16 (GPU)</option>
241
+ <option value="float32">float32 (CPU)</option>
242
+ </select>
243
+ </div>
244
+
245
+ <div class="form-group mb-3">
246
+ <label for="batchSize">Velikost dávky</label>
247
+ <input type="range" class="form-range" id="batchSize"
248
+ name="batch_size" min="1" max="32" value="8">
249
+ <span id="batchSizeValue">8</span>
250
+ </div>
251
+
252
+ <div class="form-group mb-3">
253
+ <label for="subtitleFile">Soubor titulků (volitelný)</label>
254
+ <input type="file" class="form-control" id="subtitleFile"
255
+ name="subtitle_file" accept=".srt,.ass,.vtt">
256
+ </div>
257
+
258
+ <hr>
259
+ <h6>Segmentace textu</h6>
260
+
261
+ <div class="form-group mb-3">
262
+ <label for="textSegmentation">Škála segmentace textu</label>
263
+ <select class="form-select" id="textSegmentation" name="text_segmentation">
264
+ <option value="sentence" selected>Věta</option>
265
+ <option value="word">Slovo</option>
266
+ <option value="character">Znak</option>
267
+ </select>
268
+ </div>
269
+
270
+ <div class="form-group mb-3">
271
+ <label for="divideTextBy">Rozdělit text podle</label>
272
+ <input type="text" class="form-control" id="divideTextBy"
273
+ name="divide_text_by" placeholder="Volitelný oddělovač">
274
+ </div>
275
+
276
+ <hr>
277
+ <h6>Rozpoznání mluvčích a překlad</h6>
278
+
279
+ <div class="form-group mb-3">
280
+ <label for="diarizationModel">Model pro rozpoznání mluvčích</label>
281
+ <select class="form-select" id="diarizationModel" name="diarization_model">
282
+ <option value="pyannote_2.1" selected>Pyannote 2.1</option>
283
+ </select>
284
+ </div>
285
+
286
+ <div class="form-group mb-3">
287
+ <label for="translationProcess">Proces překladu</label>
288
+ <select class="form-select" id="translationProcess" name="translation_process">
289
+ <option value="google_translator_batch" selected>Google Translator (Batch)</option>
290
+ </select>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ </div>
296
+
297
+ <!-- Output Settings -->
298
+ <div class="mb-3">
299
+ <div class="card">
300
+ <div class="card-header">
301
+ <h5 class="mb-0">
302
+ <button class="btn btn-link" type="button" data-bs-toggle="collapse" data-bs-target="#outputSettingsCollapse">
303
+ Nastavení výstupu
304
+ </button>
305
+ </h5>
306
+ </div>
307
+ <div id="outputSettingsCollapse" class="collapse">
308
+ <div class="card-body">
309
+ <div class="form-group mb-3">
310
+ <label for="outputType">Typ výstupu</label>
311
+ <select class="form-select" id="outputType" name="output_type">
312
+ <option value="video (mp4)" selected>Video (MP4)</option>
313
+ <option value="audio (wav)">Audio (WAV)</option>
314
+ </select>
315
+ </div>
316
+
317
+ <div class="form-group mb-3">
318
+ <label for="outputName">Název výstupu</label>
319
+ <input type="text" class="form-control" id="outputName"
320
+ name="output_name" placeholder="Volitelný název výstupu">
321
+ </div>
322
+
323
+ <div class="form-check mb-3">
324
+ <input class="form-check-input" type="checkbox" id="playSound"
325
+ name="play_sound" checked>
326
+ <label class="form-check-label" for="playSound">
327
+ Přehrát zvuk po dokončení úlohy
328
+ </label>
329
+ </div>
330
+
331
+ <div class="form-check mb-3">
332
+ <input class="form-check-input" type="checkbox" id="enableCache"
333
+ name="enable_cache" >
334
+ <label class="form-check-label" for="enableCache">
335
+ Povolit cache
336
+ </label>
337
+ </div>
338
+
339
+ <div class="form-check mb-3">
340
+ <input class="form-check-input" type="checkbox" id="preview"
341
+ name="preview">
342
+ <label class="form-check-label" for="preview">
343
+ Náhled
344
+ </label>
345
+ </div>
346
+ </div>
347
+ </div>
348
+ </div>
349
+ </div>
350
+
351
+ <!-- Voice Imitation Settings -->
352
+ <div class="mb-3">
353
+ <div class="card">
354
+ <div class="card-header">
355
+ <h5 class="mb-0">
356
+ <button class="btn btn-link" type="button" data-bs-toggle="collapse" data-bs-target="#voiceImitationCollapse">
357
+ Nastavení imitace hlasu
358
+ </button>
359
+ </h5>
360
+ </div>
361
+ <div id="voiceImitationCollapse" class="collapse">
362
+ <div class="card-body">
363
+ <div class="form-check mb-3">
364
+ <input class="form-check-input" type="checkbox" id="enableVoiceImitation" name="enable_voice_imitation">
365
+ <label class="form-check-label" for="enableVoiceImitation">
366
+ Povolit imitaci hlasu
367
+ </label>
368
+ </div>
369
+
370
+ <div id="voiceImitationOptions" style="display: none;">
371
+ <div class="form-group mb-3">
372
+ <label for="voiceImitationMethod">Metoda imitace hlasu</label>
373
+ <select class="form-select" id="voiceImitationMethod" name="voice_imitation_method">
374
+ <option value="RVC">RVC (Retrieval-based Voice Conversion)</option>
375
+ <option value="RVC2">RVC2 (Enhanced Voice Conversion)</option>
376
+ </select>
377
+ </div>
378
+
379
+ <div class="form-group mb-3">
380
+ <label for="voiceModel">Model hlasu</label>
381
+ <select class="form-select" id="voiceModel" name="voice_model">
382
+ <option value="">Vyberte model hlasu...</option>
383
+ </select>
384
+ </div>
385
+
386
+ <div class="form-group mb-3">
387
+ <label for="transposeValue">Transpozice</label>
388
+ <input type="range" class="form-range" id="transposeValue"
389
+ name="transpose_value" min="-12" max="12" value="0">
390
+ <span id="transposeValueDisplay">0</span>
391
+ </div>
392
+
393
+ <div class="form-group mb-3">
394
+ <label for="f0Method">Metoda extrakce F0</label>
395
+ <select class="form-select" id="f0Method" name="f0_method">
396
+ <option value="dio">DIO (Rychlá)</option>
397
+ <option value="harvest">Harvest (Pomalejší, ale stabilnější)</option>
398
+ <option value="crepe">Crepe (Dobrá, ale náročná na GPU)</option>
399
+ <option value="rmvpe">RMVPE (Nejlepší celkově)</option>
400
+ </select>
401
+ </div>
402
+
403
+ <div class="form-group mb-3">
404
+ <label for="indexRate">Index Rate</label>
405
+ <input type="range" class="form-range" id="indexRate"
406
+ name="index_rate" min="0" max="1" step="0.01" value="0.5">
407
+ <span id="indexRateDisplay">0.5</span>
408
+ </div>
409
+
410
+ <div class="form-group mb-3">
411
+ <label for="protectValue">Hodnota ochrany</label>
412
+ <input type="range" class="form-range" id="protectValue"
413
+ name="protect_value" min="0" max="0.5" step="0.01" value="0.33">
414
+ <span id="protectValueDisplay">0.33</span>
415
+ </div>
416
+
417
+ <div class="form-check mb-3">
418
+ <input class="form-check-input" type="checkbox" id="filterRadius" name="filter_radius" value="3">
419
+ <label class="form-check-label" for="filterRadius">
420
+ Povolit filtr radius (sníží šum)
421
+ </label>
422
+ </div>
423
+
424
+ <div class="form-check mb-3">
425
+ <input class="form-check-input" type="checkbox" id="rmsNormTarget" name="rms_norm_target" value="-16">
426
+ <label class="form-check-label" for="rmsNormTarget">
427
+ Povolit cílovou hodnotu RMS
428
+ </label>
429
+ </div>
430
+ </div>
431
+ </div>
432
+ </div>
433
+ </div>
434
+ </div>
435
+
436
+ <!-- Edit Subtitles Section -->
437
+ <div class="mb-3">
438
+ <div class="card">
439
+ <div class="card-header">
440
+ <h5 class="mb-0">
441
+ <button class="btn btn-link" type="button" data-bs-toggle="collapse" data-bs-target="#editSubtitlesCollapse">
442
+ Upravit vygenerované titulky
443
+ </button>
444
+ </h5>
445
+ </div>
446
+ <div id="editSubtitlesCollapse" class="collapse">
447
+ <div class="card-body">
448
+ <div class="form-group mb-3">
449
+ <label for="subtitlesEditor">Upravit titulky</label>
450
+ <textarea class="form-control" id="subtitlesEditor" name="edited_subtitles" rows="10" style="font-family: monospace;"></textarea>
451
+ </div>
452
+ <button type="button" class="btn btn-secondary" id="getSubtitlesBtn">Získat titulky a upravit</button>
453
+ </div>
454
+ </div>
455
+ </div>
456
+ </div>
457
+
458
+ <!-- Submit Button and Progress -->
459
+ <div class="mb-3">
460
+ <button type="submit" class="btn btn-primary" id="submitBtn">Přeložit</button>
461
+ </div>
462
+
463
+ <!-- Progress and Results -->
464
+ <div id="progress" class="alert alert-info mt-3 hidden">
465
+ <div id="progressText" class="text-center mb-2">Zpracování... Prosím čekejte.</div>
466
+ <div class="progress">
467
+ <div id="progressBar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
468
+ </div>
469
+ </div>
470
+ <div id="result" class="mt-3"></div>
471
+ <div id="videoResult" class="mt-3 hidden">
472
+ <div class="card">
473
+ <div class="card-body">
474
+ <div class="d-flex justify-content-between align-items-center mb-3">
475
+ <h5 class="card-title mb-0">Video výstup</h5>
476
+ <div class="btn-group" role="group" aria-label="Přepínač videa">
477
+ <button type="button" class="btn btn-primary active" id="translatedBtn" onclick="switchVideo('translated')">Přeložené</button>
478
+ <button type="button" class="btn btn-outline-primary" id="originalBtn" onclick="switchVideo('original')">Originál</button>
479
+ </div>
480
+ </div>
481
+ <video id="outputVideo" class="w-100 mb-3" controls>
482
+ <source src="" type="video/mp4">
483
+ Váš prohlížeč nepodporuje přehrávání videa.
484
+ </video>
485
+ <h6 class="mb-2">Soubory ke stažení:</h6>
486
+ <div id="downloadFiles" class="list-group">
487
+ </div>
488
+ </div>
489
+ </div>
490
+ </div>
491
+ <div id="outputFiles" class="list-group mt-3"></div>
492
+ </form>
493
+
494
+ <!-- Progress and Results -->
495
+
496
+ </div>
497
+ </div>
498
+ </div>
499
+
500
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
501
  <script>
502
+ // Globální proměnné pro YouTube video
503
+ let downloadedYoutubeFile = null;
504
+
505
+ // Sledování změn v URL poli
506
+ document.getElementById('urlField').addEventListener('input', async function(e) {
507
+ const url = e.target.value;
508
+ const youtubeProgress = document.getElementById('youtubeProgress');
509
+ const youtubeStatus = document.getElementById('youtubeStatus');
510
+ const submitBtn = document.getElementById('submitBtn');
511
+ const inputTypeSelect = document.getElementById('inputType');
512
+
513
+ // Reset stavu
514
+ downloadedYoutubeFile = null;
515
+ youtubeStatus.innerHTML = '';
516
+ youtubeProgress.classList.add('hidden');
517
+
518
+ // Kontrola YouTube URL
519
+ if (url.includes('youtube.com') || url.includes('youtu.be')) {
520
+ submitBtn.disabled = true;
521
+ youtubeProgress.classList.remove('hidden');
522
+ youtubeStatus.innerHTML = '<div class="alert alert-info">Stahuji video z YouTube...</div>';
523
+
524
+ try {
525
+ const response = await fetch('/download_youtube', {
526
+ method: 'POST',
527
+ headers: {
528
+ 'Content-Type': 'application/x-www-form-urlencoded',
529
+ },
530
+ body: new URLSearchParams({
531
+ url: url
532
+ })
533
+ });
534
+
535
+ const data = await response.json();
536
+
537
+ if (data.success) {
538
+ downloadedYoutubeFile = data.file_path;
539
+ youtubeStatus.innerHTML = '<div class="alert alert-success">Video úspěšně staženo!</div>';
540
+
541
+ // Přepnout na Submit Video
542
+ inputTypeSelect.value = 'file';
543
+ inputTypeSelect.disabled = true;
544
+
545
+ // Trigger změnu input type
546
+ const event = new Event('change');
547
+ inputTypeSelect.dispatchEvent(event);
548
+
549
+ // Nastavit video soubor
550
+ const fileInput = document.querySelector('input[type="file"]');
551
+ const dataTransfer = new DataTransfer();
552
+
553
+ // Vytvořit File objekt ze staženého souboru
554
+ const fileName = data.filename;
555
+ const response = await fetch('/' + data.file_path.split('\\').join('/'));
556
+ const blob = await response.blob();
557
+ const file = new File([blob], fileName, { type: 'video/mp4' });
558
+
559
+ dataTransfer.items.add(file);
560
+ fileInput.files = dataTransfer.files;
561
+ fileInput.disabled = true;
562
+
563
+ submitBtn.disabled = false;
564
+ } else {
565
+ youtubeStatus.innerHTML = `<div class="alert alert-danger">Chyba: ${data.error}</div>`;
566
+ submitBtn.disabled = true;
567
+ inputTypeSelect.disabled = false;
568
+ }
569
+ } catch (error) {
570
+ youtubeStatus.innerHTML = '<div class="alert alert-danger">Chyba při stahování videa</div>';
571
+ submitBtn.disabled = true;
572
+ inputTypeSelect.disabled = false;
573
+ }
574
+
575
+ youtubeProgress.classList.add('hidden');
576
+ }
577
+ });
578
+
579
+ // Handle input type switching
580
+ document.getElementById('inputType').addEventListener('change', function() {
581
+ const fileInput = document.getElementById('fileInput');
582
+ const urlInput = document.getElementById('urlInput');
583
+ const directoryInput = document.getElementById('directoryInput');
584
+
585
+ // Skrýt všechny inputy
586
+ fileInput.classList.add('hidden');
587
+ urlInput.classList.add('hidden');
588
+ directoryInput.classList.add('hidden');
589
+
590
+ // Zobrazit vybraný input
591
+ switch(this.value) {
592
+ case 'file':
593
+ fileInput.classList.remove('hidden');
594
+ break;
595
+ case 'url':
596
+ urlInput.classList.remove('hidden');
597
+ break;
598
+ case 'directory':
599
+ directoryInput.classList.remove('hidden');
600
+ break;
601
+ }
602
+ });
603
+
604
+ // Handle speakers range
605
+ function updateSpeakers(value) {
606
+ document.getElementById('speakersValue').textContent = value;
607
+ for (let i = 0; i < 12; i++) {
608
+ const voiceDiv = document.getElementById(`voice${String(i).padStart(2, '0')}`);
609
+ if (i < value) {
610
+ voiceDiv.style.display = 'block';
611
+ } else {
612
+ voiceDiv.style.display = 'none';
613
+ }
614
+ }
615
+ }
616
+ // Initialize with default value
617
+ window.addEventListener('load', function() {
618
+ updateSpeakers(1);
619
+ });
620
+ // Handle form submission
621
+ document.getElementById('translateForm').addEventListener('submit', async function(e) {
622
+ e.preventDefault();
623
+ const formData = new FormData(this);
624
+
625
+ // Přidání informace o staženém YouTube videu
626
+ if (downloadedYoutubeFile) {
627
+ formData.append('youtube_file', downloadedYoutubeFile);
628
+ }
629
+
630
+ // Add edited subtitles to form data if they exist
631
+ const subtitlesEditor = document.getElementById('subtitlesEditor');
632
+ if (subtitlesEditor && subtitlesEditor.value.trim()) {
633
+ formData.append('edited_subtitles', subtitlesEditor.value);
634
+ }
635
+
636
+ const progress = document.getElementById('progress');
637
+ const progressText = document.getElementById('progressText');
638
+ const progressBar = document.getElementById('progressBar');
639
+ const result = document.getElementById('result');
640
+ const submitBtn = document.getElementById('submitBtn');
641
+
642
+ // Reset progress state
643
+ progress.classList.remove('hidden');
644
+ progressBar.style.width = '0%';
645
+ progressBar.setAttribute('aria-valuenow', '0');
646
+ progressText.textContent = "Nahrávání souboru...";
647
+ result.innerHTML = '';
648
+ submitBtn.disabled = true;
649
+
650
+ // Progress states
651
+ const states = [
652
+ { text: "Nahrávání souboru...", progress: 20 },
653
+ { text: "Extrahování zvuku...", progress: 40 },
654
+ { text: "Převod řeči na text...", progress: 60 },
655
+ { text: "Překládání...", progress: 80 },
656
+ { text: "AI Dabing...", progress: 90 }
657
+ ];
658
+
659
+ let currentState = 0;
660
+ const updateProgress = () => {
661
+ if (currentState < states.length) {
662
+ const state = states[currentState];
663
+ progressText.textContent = state.text;
664
+ progressBar.style.width = state.progress + '%';
665
+ progressBar.setAttribute('aria-valuenow', state.progress);
666
+ currentState++;
667
+ }
668
+ };
669
+
670
+ // Start with first state and progress updates
671
+ updateProgress();
672
+ const progressInterval = setInterval(updateProgress, 2000);
673
+
674
+ try {
675
+ const response = await fetch('/translate', {
676
+ method: 'POST',
677
+ body: formData
678
+ });
679
+
680
+ const data = await response.json();
681
+ clearInterval(progressInterval);
682
+
683
+ if (data.success) {
684
+ progressBar.style.width = '100%';
685
+ progressBar.setAttribute('aria-valuenow', '100');
686
+ progressText.textContent = "Hotovo!";
687
+ setTimeout(() => {
688
+ result.innerHTML = `<div class="alert alert-success">Překlad byl úspěšně dokončen!</div>`;
689
+
690
+ // Zobrazení video přehrávače a nastavení zdrojů
691
+ const videoResult = document.getElementById('videoResult');
692
+ const outputVideo = document.getElementById('outputVideo');
693
+ const downloadFiles = document.getElementById('downloadFiles');
694
+
695
+ if (data.video) {
696
+ // Převést absolutní cesty na relativní URL
697
+ const videoUrl = '/' + data.video.split('outputs\\').pop().replace(/\\/g, '/');
698
+ const originalVideoUrl = data.original_video ? '/' + data.original_video.split('uploads\\').pop().replace(/\\/g, '/') : null;
699
+
700
+ // Uložit cesty k videím
701
+ currentVideoState.translatedVideo = videoUrl;
702
+ currentVideoState.originalVideo = originalVideoUrl;
703
+
704
+ // Nastavit výchozí (přeložené) video
705
+ outputVideo.src = videoUrl;
706
+ videoResult.classList.remove('hidden');
707
+
708
+ // Přidání souborů ke stažení
709
+ if (data.files && data.files.length > 0) {
710
+ downloadFiles.innerHTML = data.files.map(file => {
711
+ const fileName = file.split(/[\\/]/).pop();
712
+ const extension = fileName.split('.').pop().toUpperCase();
713
+ const fileUrl = '/' + file.split('outputs\\').pop().replace(/\\/g, '/');
714
+ return `
715
+ <a href="${fileUrl}" download class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
716
+ <span>${fileName}</span>
717
+ <span class="badge bg-primary">${extension}</span>
718
+ </a>
719
+ `;
720
+ }).join('');
721
+ }
722
+ }
723
+
724
+ progress.classList.add('hidden');
725
+ }, 1000);
726
+ } else {
727
+ result.innerHTML = `<div class="alert alert-danger">Error: ${data.error}</div>`;
728
+ progress.classList.add('hidden');
729
+ }
730
+ } catch (error) {
731
+ result.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`;
732
+ progress.classList.add('hidden');
733
+ } finally {
734
+ submitBtn.disabled = false;
735
+ }
736
+ });
737
+ // Update all range input values
738
+ document.getElementById('segmentDuration').addEventListener('input', function() {
739
+ document.getElementById('segmentDurationValue').textContent = this.value;
740
+ });
741
+
742
+ document.getElementById('batchSize').addEventListener('input', function() {
743
+ document.getElementById('batchSizeValue').textContent = this.value;
744
+ });
745
+ // Voice Imitation Settings Handlers
746
+ document.getElementById('enableVoiceImitation').addEventListener('change', function() {
747
+ const options = document.getElementById('voiceImitationOptions');
748
+ options.style.display = this.checked ? 'block' : 'none';
749
+ });
750
+
751
+ // Update range input displays
752
+ document.getElementById('transposeValue').addEventListener('input', function() {
753
+ document.getElementById('transposeValueDisplay').textContent = this.value;
754
+ });
755
+
756
+ document.getElementById('indexRate').addEventListener('input', function() {
757
+ document.getElementById('indexRateDisplay').textContent = this.value;
758
+ });
759
+
760
+ document.getElementById('protectValue').addEventListener('input', function() {
761
+ document.getElementById('protectValueDisplay').textContent = this.value;
762
+ });
763
+
764
+ // Load voice models based on method
765
+ document.getElementById('voiceImitationMethod').addEventListener('change', async function() {
766
+ const voiceModelSelect = document.getElementById('voiceModel');
767
+ voiceModelSelect.innerHTML = '<option value="">Loading models...</option>';
768
+
769
+ try {
770
+ const response = await fetch(`/get_voice_models?method=${this.value}`);
771
+ const data = await response.json();
772
+
773
+ if (data.success) {
774
+ voiceModelSelect.innerHTML = '<option value="">Select a voice model...</option>' +
775
+ data.models.map(model => `<option value="${model}">${model}</option>`).join('');
776
+ } else {
777
+ voiceModelSelect.innerHTML = '<option value="">Error loading models</option>';
778
+ }
779
+ } catch (error) {
780
+ voiceModelSelect.innerHTML = '<option value="">Error loading models</option>';
781
+ }
782
+ });
783
+ // Handle get subtitles button
784
+ document.getElementById('getSubtitlesBtn').addEventListener('click', async function() {
785
+ const progress = document.getElementById('progress');
786
+ const result = document.getElementById('result');
787
+ const submitBtn = document.getElementById('submitBtn');
788
+ const subtitlesEditor = document.getElementById('subtitlesEditor');
789
+
790
+ progress.classList.remove('hidden');
791
+ result.innerHTML = '';
792
+ this.disabled = true;
793
+ submitBtn.disabled = true;
794
+
795
+ try {
796
+ const formData = new FormData(document.getElementById('translateForm'));
797
+
798
+ const response = await fetch('/get_subtitles', {
799
+ method: 'POST',
800
+ body: formData
801
+ });
802
+
803
+ const data = await response.json();
804
+
805
+ if (data.success) {
806
+ result.innerHTML = `<div class="alert alert-success">Subtitles generated successfully! You can now edit them below.</div>`;
807
+ if (data.subtitles) {
808
+ subtitlesEditor.value = data.subtitles;
809
+ document.getElementById('editSubtitlesCollapse').classList.add('show');
810
+ subtitlesEditor.focus();
811
+ }
812
+ } else {
813
+ result.innerHTML = `<div class="alert alert-danger">Error: ${data.error}</div>`;
814
+ }
815
+ } catch (error) {
816
+ result.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`;
817
+ } finally {
818
+ progress.classList.add('hidden');
819
+ this.disabled = false;
820
+ submitBtn.disabled = false;
821
+ }
822
+ });
823
+ // Funkce pro zobrazení toast notifikace
824
+ function showToast(message, isError = false) {
825
+ const toast = document.getElementById('toast');
826
+ toast.textContent = message;
827
+ toast.style.display = 'block';
828
+
829
+ if (isError) {
830
+ toast.classList.add('error');
831
+ } else {
832
+ toast.classList.remove('error');
833
+ }
834
+
835
+ setTimeout(() => {
836
+ toast.style.display = 'none';
837
+ }, 3000);
838
+ }
839
+
840
+ // Handler pro PURGE tlačítko
841
+ document.getElementById('purgeButton').addEventListener('click', async function() {
842
+ if (!confirm('Opravdu chcete vymazat obsah složek outputs a audio?')) {
843
+ return;
844
+ }
845
+
846
+ try {
847
+ const response = await fetch('/purge', {
848
+ method: 'POST'
849
+ });
850
+
851
+ const data = await response.json();
852
+
853
+ if (data.success) {
854
+ showToast(data.message);
855
  } else {
856
+ showToast(data.error, true);
857
+ }
858
+ } catch (error) {
859
+ showToast('Došlo k chybě při mazání složek', true);
860
+ }
861
+ });
862
+ let currentVideoState = {
863
+ translatedVideo: '',
864
+ originalVideo: '',
865
+ currentTime: 0,
866
+ isPlaying: false
867
+ };
868
+
869
+ // Funkce pro přepínání mezi videi
870
+ function switchVideo(type) {
871
+ const video = document.getElementById('outputVideo');
872
+ const translatedBtn = document.getElementById('translatedBtn');
873
+ const originalBtn = document.getElementById('originalBtn');
874
+
875
+ // Uložit aktuální stav přehrávání
876
+ currentVideoState.currentTime = video.currentTime;
877
+ currentVideoState.isPlaying = !video.paused;
878
+
879
+ // Přepnout video zdroj
880
+ if (type === 'translated') {
881
+ video.src = currentVideoState.translatedVideo;
882
+ translatedBtn.classList.add('active');
883
+ translatedBtn.classList.remove('btn-outline-primary');
884
+ translatedBtn.classList.add('btn-primary');
885
+ originalBtn.classList.remove('active');
886
+ originalBtn.classList.add('btn-outline-primary');
887
+ originalBtn.classList.remove('btn-primary');
888
+ } else {
889
+ video.src = currentVideoState.originalVideo;
890
+ originalBtn.classList.add('active');
891
+ originalBtn.classList.remove('btn-outline-primary');
892
+ originalBtn.classList.add('btn-primary');
893
+ translatedBtn.classList.remove('active');
894
+ translatedBtn.classList.add('btn-outline-primary');
895
+ translatedBtn.classList.remove('btn-primary');
896
+ }
897
+
898
+ // Obnovit stav přehrávání
899
+ video.addEventListener('loadedmetadata', function() {
900
+ video.currentTime = currentVideoState.currentTime;
901
+ if (currentVideoState.isPlaying) {
902
+ video.play();
903
  }
904
+ }, { once: true });
905
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
906
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
907
  </body>
908
  </html>