arcanus commited on
Commit
2e32df7
·
verified ·
1 Parent(s): b71047b

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +128 -896
templates/index.html CHANGED
@@ -1,908 +1,140 @@
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/[email protected]/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/[email protected]/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>
 
1
+ {% extends "layout.html" %}
2
+
3
+ {% block content %}
4
+ <div id="loader" class="loader-container">
5
+ <div class="loader"></div>
6
+ </div>
7
+ <div class="container-fluid">
8
+ <div class="row row justify-content-center">
9
+ <div class="col-8 col-lg-6">
10
+ <div class="video-container">
11
+ <video
12
+ id="video-player"
13
+ class="video-js vjs-default-skin vjs-big-play-centered"
14
+ controls
15
+ preload="auto"
16
+ width="640"
17
+ height="360"
18
+ data-setup='{"controls": true, "autoplay": false, "preload": "auto"}'>
19
+ {% if video_path %}
20
+ <source src="{{ video_path }}" type="video/mp4">
21
+ {% endif %}
22
+ <p class="vjs-no-js">
23
+ To view this video please enable JavaScript, or consider upgrading to a
24
+ web browser that supports HTML5 video
25
+ </p>
26
+ </video>
27
+ </div>
28
+ <div class="timeline-container">
29
+ <div id="timeline" class="provereni">
30
+ {% if subtitle_content %}
31
+ <script id="initial-subtitles" type="text/plain">{{ subtitle_content | safe }}</script>
32
+ {% else %}
33
+ <script>
34
+ // Refresh stránky po 2 sekundách (můžeš změnit čas)
35
+ setTimeout(() => {
36
+ location.reload();
37
+ }, 2000);
38
+ </script>
39
+
40
+ {% endif %}
41
+ <div id="timeline-content"></div>
42
+ </div>
43
+
44
+ </div>
45
+ <div id="timeline-controls">
46
+ <button class="btn btn-sm btn-primary" id="zoom-in">
47
+ <i class="fas fa-search-minus"></i>
48
+ </button>
49
+ <button class="btn btn-sm btn-primary" id="zoom-out">
50
+ <i class="fas fa-search-plus"></i>
51
+ </button>
52
+ <button class="btn btn-sm btn-success" id="exportTranslation">
53
+ <i class="fas fa-save"></i> Aktualizovat překlad
54
+ </button>
55
+ <button style="display:none;"class="btn btn-sm btn-primary" id="createDubbing">
56
+ <i class="fas fa-microphone"></i> Vytvořit dabing z titulků
57
+ </button>
58
+ </div>
59
+ </div>
60
+ <div class="col-12 col-lg-4">
61
+ <div class="editor-panel">
62
+ <div class="card">
63
+ <div class="card-header">
64
+ <h5>Upload Files</h5>
65
+ </div>
66
+ <div class="card-body">
67
+ <div class="mb-3">
68
+ <label class="form-label">Video File</label>
69
+ <input type="file" class="form-control" id="video-upload" accept="video/*">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  </div>
71
+ <div class="mb-3">
72
+ <label class="form-label">Subtitle File</label>
73
+ <input type="file" class="form-control" id="subtitle-upload" accept=".srt,.vtt">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  </div>
75
  </div>
76
+ </div>
77
+
78
+ <div class="card mt-3">
79
+ <div class="card-header">
80
+ <h5>Subtitle Editor</h5>
81
+ </div>
82
+ <div class="card-body">
83
+ <div class="mb-3">
84
+ <button class="btn btn-primary" id="add-subtitle">
85
+ <i class="fas fa-plus"></i> Add Subtitle
86
+ </button>
87
+ <button class="btn btn-success" id="generate-subtitles">
88
+ <i class="fas fa-magic"></i> Generate Subtitles
89
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  </div>
91
+ <div class="subtitle-list" id="subtitle-list">
92
+ <!-- Subtitles will be listed here -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  </div>
94
  </div>
95
+ </div>
96
 
97
+ <div class="card mt-3">
98
+ <div class="card-header">
99
+ <h5>Export</h5>
100
  </div>
101
+ <div class="card-body">
102
+ <button class="btn btn-success" id="export-vtt">
103
+ <i class="fas fa-download"></i> Export VTT
104
+ </button>
105
+ <button class="btn btn-success" id="export-srt">
106
+ <i class="fas fa-download"></i> Export SRT
107
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  </div>
109
+ </div>
 
 
 
 
110
  </div>
111
  </div>
112
  </div>
113
+ </div>
114
+
115
+ <!-- Modal pro editaci titulku -->
116
+ <div class="modal fade" id="editSubtitleModal" tabindex="-1" aria-hidden="true">
117
+ <div class="modal-dialog">
118
+ <div class="modal-content">
119
+ <div class="modal-header">
120
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
121
+ </div>
122
+ <div class="modal-body">
123
+ <div class="mb-3">
124
+ <textarea class="form-control" id="subtitleText" rows="3"></textarea>
125
+ </div>
126
+ </div>
127
+ <div class="modal-footer">
128
+ <button type="button" class="btn btn-danger" id="deleteSubtitle"><i class="fa-solid fa-xmark"></i></button>
129
+ <button type="button" class="btn btn-success" id="saveSubtitle"><i class="fa-solid fa-check"></i></button>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ {% endblock %}
135
+
136
+ {% block scripts %}
137
+ <script src="{{ url_for('static', filename='js/subtitle.js') }}"></script>
138
+ <script src="{{ url_for('static', filename='js/timeline.js') }}"></script>
139
+ <script src="{{ url_for('static', filename='js/editor.js') }}"></script>
140
+ {% endblock %}