koala2 / templates /index_new.html
arcanus's picture
Update templates/index_new.html
2c5e8ac verified
raw
history blame
112 kB
<!DOCTYPE html>
<html lang="cs" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Dubber</title>
<style></style>
<link rel="stylesheet" crossorigin href="/static/css/index-BtnDRx9Z.css">
<link rel="stylesheet" crossorigin href="/static/css/style.css">
<script>
sessionStorage.removeItem('titulkycesta');
sessionStorage.removeItem('video_path');
sessionStorage.removeItem('subtitles');
</script>
</head>
<body class="antialiased">
<!-- Status Container -->
<div id="statusContainer" class="fixed top-4 right-4 bg-card p-4" style="z-index: 1000;">
<div id="xttsStatus" class="mb-2"></div>
<div id="piperStatus" class="mb-2"></div>
<div id="ngrokStatus" class="mb-2"></div>
</div>
<!-- Log messages container -->
<div id="logContainer"
class="fixed bottom-4 right-4 max-w-md bg-card p-4 rounded-lg shadow-lg overflow-y-auto max-h-60"
style="z-index: 1000; display:none !important;">
<div class="text-sm font-mono" id="logMessages"></div>
</div>
<div class="min-h-screen bg-background flex items-center justify-center p-4 sm:p-8">
<div class="toast-container">
<div id="toast" class="toast"></div>
</div>
<!-- Plovoucí tlačítko -->
<button id="purgeButton" class=" floating-button"
style="float: left;top: 0;left: 0;position: absolute;font-size: 10px;text-transform: uppercase;padding: 5px 10px;/* background: #272a30; */border-radius: 5px; display: none !important;">Vymazat
cache</button>
<div class="w-full max-w-2xl" id="hlavniform" style="z-index: 99;">
<div class="rounded-xl border bg-card text-card-foreground shadow-xl">
<div class="p-6 flex flex-row items-center justify-between space-y-0 pb-7">
<div class="flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-sparkles h-6 w-6">
<path
d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z">
</path>
<path d="M20 3v4"></path>
<path d="M22 5h-4"></path>
<path d="M4 17v2"></path>
<path d="M5 18H3"></path>
</svg>
<h3 class="tracking-tight text-2xl font-bold">Nadabujte svůj obsah</h3>
</div>
<div class="flex items-center gap-4">
<div class="flex items-center space-x-2">
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" class="sr-only peer" id="interface-mode">
<div
class="w-9 h-5 bg-input peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-background after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-primary">
</div>
<span class="ms-3 text-sm font-medium cursor-pointer select-none"><span
class="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 w-9 hover:bg-accent hover:text-accent-foreground transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-sun h-5 w-5">
<circle cx="12" cy="12" r="4"></circle>
<path d="M12 2v2"></path>
<path d="M12 20v2"></path>
<path d="m4.93 4.93 1.41 1.41"></path>
<path d="m17.66 17.66 1.41 1.41"></path>
<path d="M2 12h2"></path>
<path d="M20 12h2"></path>
<path d="m6.34 17.66-1.41 1.41"></path>
<path d="m19.07 4.93-1.41 1.41"></path>
</svg>
</span></span>
</label>
</div>
</div>
</div>
<form id="translateForm" enctype="multipart/form-data" class="p-6 pt-0 space-y-8" method="post">
<div hidden>
<!-- Subtitle Settings -->
<div class="mb-3">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<button class="btn btn-link" type="button" data-bs-toggle="collapse"
data-bs-target="#subtitleSettingsCollapse">
Nastavení titulků
</button>
</h5>
</div>
<div id="subtitleSettingsCollapse" class="collapse">
<div class="card-body">
<div class="form-group mb-3">
<label for="subtitleFormat">Formát titulků</label>
<select class="form-select" id="subtitleFormat" name="subtitle_format">
<option value="disable">Vypnuto</option>
<option value="srt" selected>SRT</option>
<option value="vtt">VTT</option>
<option value="ass">ASS</option>
<option value="txt">TXT</option>
<option value="tsv">TSV</option>
<option value="json">JSON</option>
<option value="aud">AUD</option>
</select>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="softSubtitles"
name="soft_subtitles">
<label class="form-check-label" for="softSubtitles">
Přidat měkké titulky do videa
</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="burnSubtitles"
name="burn_subtitles">
<label class="form-check-label" for="burnSubtitles">
Vypálit titulky do videa
</label>
</div>
<hr>
<h6>Nastavení Whisper</h6>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="literalizeNumbers"
name="literalize_numbers" checked>
<label class="form-check-label" for="literalizeNumbers">
Převádět čísla na slova
</label>
</div>
<div class="form-group mb-3">
<label for="diarizationModel">Model pro rozpoznání mluvčích</label>
<select class="form-select" id="diarizationModel" name="diarization_model">
<option value="pyannote_3.1" selected>Pyannote 3.1</option>
<option value="pyannote_2.1">Pyannote 2.1</option>
</select>
</div>
<div class="form-group mb-3">
<label for="segmentDuration">Doba trvání segmentu (sekundy)</label>
<input type="range" class="form-range" id="segmentDuration"
name="segment_duration" min="1" max="30" value="15">
<span id="segmentDurationValue">15</span>
</div>
<div class="form-group mb-3">
<label for="whisperModel">Model Whisper</label>
<select class="form-select" id="whisperModel" name="whisper_model">
<option value="large-v3">Large V3</option>
<option value="medium">Medium</option>
</select>
</div>
<div class="form-group mb-3">
<label for="computeType">Typ výpočtu</label>
<select class="form-select" id="computeType" name="compute_type">
<option value="float16">float16 (GPU)</option>
<option value="float32" selected>float32 (CPU)</option>
</select>
</div>
<div class="form-group mb-3">
<label for="batchSize">Velikost dávky</label>
<input type="range" class="form-range" id="batchSize" name="batch_size"
min="1" max="32" value="8">
<span id="batchSizeValue">8</span>
</div>
<div class="form-group mb-3">
<label for="subtitleFile">Soubor titulků (volitelný)</label>
<input type="file" class="form-control" id="subtitleFile"
name="subtitle_file" accept=".srt,.ass,.vtt">
</div>
<hr>
<h6>Segmentace textu</h6>
<div class="form-group mb-3">
<label for="textSegmentation">Škála segmentace textu</label>
<select class="form-select" id="textSegmentation" name="text_segmentation">
<option value="sentence" selected>Věta</option>
<option value="word">Slovo</option>
<option value="character">Znak</option>
</select>
</div>
<div class="form-group mb-3">
<label for="divideTextBy">Rozdělit text podle</label>
<input type="text" class="form-control" id="divideTextBy"
name="divide_text_by" placeholder="Volitelný oddělovač">
</div>
<hr>
<h6>Rozpoznání mluvčích a překlad</h6>
<div class="form-group mb-3">
<label for="translationProcess">Proces překladu</label>
<select class="form-select" id="translationProcess"
name="translation_process">
<option value="google_translator_batch" selected>Google Translator
(Batch)</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- Output Settings -->
<div class="mb-3">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<button class="btn btn-link" type="button" data-bs-toggle="collapse"
data-bs-target="#outputSettingsCollapse">
Nastavení výstupu
</button>
</h5>
</div>
<div id="outputSettingsCollapse" class="collapse">
<div class="card-body">
<div class="form-group mb-3">
<label for="outputType">Typ výstupu</label>
<select class="form-select" id="outputType" name="output_type">
<option value="video (mp4)" selected>Video (MP4)</option>
<option value="audio (wav)">Audio (WAV)</option>
</select>
</div>
<div class="form-group mb-3">
<label for="outputName">Název výstupu</label>
<input type="text" class="form-control" id="outputName" name="output_name"
placeholder="Volitelný název výstupu">
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="playSound"
name="play_sound" checked>
<label class="form-check-label" for="playSound">
Přehrát zvuk po dokončení úlohy
</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="enableCache"
name="enable_cache">
<label class="form-check-label" for="enableCache">
Povolit cache
</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="preview" name="preview">
<label class="form-check-label" for="preview">
Náhled
</label>
</div>
</div>
</div>
</div>
</div>
<!-- Voice Imitation Settings -->
<div class="mb-3">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<button class="btn btn-link" type="button" data-bs-toggle="collapse"
data-bs-target="#voiceImitationCollapse">
Nastavení imitace hlasu
</button>
</h5>
</div>
<div id="voiceImitationCollapse" class="collapse">
<div class="card-body">
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="enableVoiceImitation"
checked name="enable_voice_imitation">
<label class="form-check-label" for="enableVoiceImitation">
Povolit imitaci hlasu
</label>
</div>
<div id="voiceImitationOptions" style="display: none;">
<div class="form-group mb-3">
<label for="voiceImitationMethod">Metoda imitace hlasu</label>
<select class="form-select" id="voiceImitationMethod"
name="voice_imitation_method">
<option value="RVC">RVC (Retrieval-based Voice Conversion)</option>
<option value="RVC2" selected>RVC2 (Enhanced Voice Conversion)
</option>
</select>
</div>
<div class="form-group mb-3">
<label for="voiceModel">Model hlasu</label>
<select class="form-select" id="voiceModel" name="voice_model">
<option value="openvoice_v2">openvoice_v2 </option>
</select>
</div>
<div class="form-group mb-3">
<label for="transposeValue">Transpozice</label>
<input type="range" class="form-range" id="transposeValue"
name="transpose_value" min="-12" max="12" value="0">
<span id="transposeValueDisplay">0</span>
</div>
<div class="form-group mb-3">
<label for="f0Method">Metoda extrakce F0</label>
<select class="form-select" id="f0Method" name="f0_method">
<option value="dio">DIO (Rychlá)</option>
<option value="harvest">Harvest (Pomalejší, ale stabilnější)
</option>
<option value="crepe">Crepe (Dobrá, ale náročná na GPU)</option>
<option selected value="rmvpe">RMVPE (Nejlepší celkově)</option>
</select>
</div>
<div class="form-group mb-3">
<label for="indexRate">Index Rate</label>
<input type="range" class="form-range" id="indexRate" name="index_rate"
min="0" max="1" step="0.01" value="0.5">
<span id="indexRateDisplay">0.5</span>
</div>
<div class="form-group mb-3">
<label for="protectValue">Hodnota ochrany</label>
<input type="range" class="form-range" id="protectValue"
name="protect_value" min="0" max="0.5" step="0.01" value="0.33">
<span id="protectValueDisplay">0.33</span>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="filterRadius"
name="filter_radius" value="3" checked>
<label class="form-check-label" for="filterRadius">
Povolit filtr radius (sníží šum)
</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="rmsNormTarget"
name="rms_norm_target" value="-16">
<label class="form-check-label" for="rmsNormTarget">
Povolit cílovou hodnotu RMS
</label>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Subtitles Section -->
<div class="mb-3">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<button class="btn btn-link" type="button" data-bs-toggle="collapse"
data-bs-target="#editSubtitlesCollapse">
Upravit vygenerované titulky
</button>
</h5>
</div>
<div id="editSubtitlesCollapse" class="collapse">
<div class="card-body">
<div class="form-group mb-3">
<label for="subtitlesEditor">Upravit titulky</label>
<textarea class="form-control" id="subtitlesEditor" name="edited_subtitles"
rows="10" style="font-family: monospace;"></textarea>
</div>
<button type="button" class="btn btn-secondary" id="getSubtitlesBtn">Získat
titulky a upravit</button>
</div>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-12 gap-6" id="zpracovani1">
<div class="col-span-4">
<div class="space-y-2">
<label class="text-sm font-medium text-foreground">Název projektu</label>
<input required id="project_name" name="project_name"
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
placeholder="Nepojmenovaný projekt">
</div>
</div>
<div class="col-span-4" style="display:none;">
<div class="space-y-2">
<label class="text-sm font-medium text-foreground">Počet hlasů</label>
<select id="maxSpeakers" name="max_speakers"
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
</div>
<div class="col-span-8">
<div class="space-y-2">
<label class="text-sm font-medium text-foreground">TTS model</label>
<div id="voiceSelectors">
{% for i in range(12) %}
<div class="speaker-voice" id="voice{{ '%02d' % i }}" {% if i>= 1
%}style="display:none"{% endif %}>
<select
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
name="tts_voice{{ '%02d' % i }}">
{% for voice in tts_voices %}
<option value="{{ voice }}" {% if i==0 and voice=="cs-CZ-AntoninNeural-Male"
%}selected{% endif %}>{{ voice }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
</div>
</div>
</div>
</div> <input type="range" class="form-range" id="maxSpeakers" name="max_speakers" min="1" max="12"
value="1" oninput="updateSpeakers(this.value)" hidden>
<div id="zpracovani2" class="grid grid-cols-1 sm:grid-cols-2 gap-6">
<div class="space-y-4">
<div class="space-y-2">
<label class="text-sm font-medium text-foreground">Zdrojový jazyk*</label>
<select
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring form-select"
name="source_language" id="sourceLanguage">
<option value="Automatic detection">Automatická detekce</option>
{% for lang in languages %}
{% if lang != "Automatic detection" %}
<option value="{{ lang }}">{{ lang }}</option>
{% endif %}
{% endfor %}
</select>
</div>
<div class="text-sm text-muted-foreground">Jazyk původního obsahu</div>
</div>
<div class="space-y-4">
<div class="space-y-2">
<label class="text-sm font-medium text-foreground">Cílové jazyky*</label>
<select
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring form-select"
name="target_language">
{% for lang in languages[1:] %}
{% if lang == "Česky (cs)" %}
<option value="{{ lang }}" selected>Česky (cs)</option>
{% else %}
<option value="{{ lang }}">{{ lang }}</option>
{% endif %}
{% endfor %}
</select>
</div>
<div class="text-sm text-muted-foreground">Jazyky pro překlad</div>
</div>
</div>
<div id="zpracovani3" class="space-y-4">
<label class="text-sm font-medium text-foreground">Zdroj zvuku nebo videa*</label>
<div id="konecvyberu"
class="h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground grid w-full grid-cols-2">
<button type="button" id="uploadBtn"
class="inline-flex items-center justify-center rounded-md px-3 py-1 text-sm font-medium bg-card text-foreground shadow transition-colors"
disabled>Nahrát soubor</button>
<button type="button" id="youtubeBtn"
class="inline-flex items-center justify-center rounded-md px-3 py-1 text-sm font-medium transition-colors"
disabled>YouTube</button>
</div>
<div class="mt-2">
<div
class="border-2 border-dashed rounded-lg p-8 text-center space-y-4 hover:border-primary transition-colors cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-upload mx-auto h-12 w-12 text-muted-foreground">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" x2="12" y1="3" y2="15"></line>
</svg>
<div>
<p id="chanana" class="text-primary cursor-pointer ">Klikněte nebo přetáhněte soubor
sem</p>
<p class="text-sm text-muted-foreground mt-2">Audio nebo video soubor, až 500MB nebo
45 minut</p>
</div><input id="inputType" type="file" class="form-control" name="video" hidden>
</div>
</div>
<div class="youtu mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50"
style=" height: 184px; text-align: center;">
<div class="space-y-4 p-4">
<div class="flex items-center gap-2 mb-2"><span style=" width: 100%; "
class="text-sm font-medium"><svg style=" display: inline-block; "
xmlns="http://www.w3.org/2000/svg" width="24" height="24"
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-youtube h-5 w-5 text-red-500">
<path
d="M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17">
</path>
<path d="m10 15 5-3-5-3z"></path>
</svg> Vložte YouTube URL</span></div><input type="url" name="url" id="urlField"
class="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 w-full"
placeholder="https://www.youtube.com/watch?v=..." style=" text-align: center; ">
<p class="text-sm text-muted-foreground">Vložte platnou URL adresu videa z YouTube</p>
</div>
</div>
</div>
<div id="youtubeProgress" class="progress mt-2 hidden">
<div class="progress-bar" role="progressbar" style="width: 0%"></div>
</div>
<div id="youtubeStatus" style=" text-align: center; margin-top: -20px; font-size: 14px; "
class="mt-2"></div>
<div id="youtubeStatusPRG"
style=" text-align: center; margin-top: 0px; font-size: 14px; opacity: 0.6;"
class="mt-2"></div>
<div style="display:block;" id="zpracovani4" class="flex items-center space-x-2"><button
type="button" role="switch" aria-checked="false" data-state="unchecked" value="on"
class="peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input"
id="translationCorrectionBtn"><span data-state="unchecked"
class="pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"></span></button><label
class="font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-sm cursor-pointer select-none"
for="translationCorrectionBtn">Manuální korekce překladu</label></div>
<button id="submitBtn"
class="inline-flex items-center justify-center text-sm font-medium transition-colors bg-primary text-primary-foreground shadow hover:bg-primary/90 h-10 rounded-md px-8 w-full mb-2">Vytvořit
dabing</button>
<button id="submitBtn2" style="display:none"
class="inline-flex items-center justify-center text-sm font-medium transition-colors bg-primary text-primary-foreground shadow hover:bg-primary/90 h-10 rounded-md px-8 w-full mb-2">Vytvořit
dabing (Upravený překlad)</button>
<p HIDDEN class="text-sm text-center font-medium">AI Video Dub</p>
<div id="progress" class="alert alert-info mt-3 hidden">
<div id="progressText" class="text-center mb-2">Zpracování... Prosím čekejte.</div>
<div class="progress">
<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>
</div>
</div>
<div id="result" class="mt-3"></div>
<div id="videoResult" style=" text-align: center; "
class="text-sm font-medium text-foreground mt-3 hidden">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 hidden class="card-title mb-0">Video výstup</h5>
<div class="h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground grid w-full grid-cols-2 "
role="group" aria-label="Přepínač videa">
<button type="button"
class="btn btn-primary active inline-flex items-center justify-center rounded-md px-3 py-1 text-sm font-medium bg-card text-foreground shadow transition-colors"
id="translatedBtn" onclick="switchVideo('translated')">Přeložené</button>
<button type="button" class="btn btn-outline-primary" id="originalBtn"
onclick="switchVideo('original')">Originál</button>
</div>
</div>
<video id="outputVideo" style=" margin-top: 10px; " class="w-100 mb-3" controls>
<source src="" type="video/mp4">
Váš prohlížeč nepodporuje přehrávání videa.
</video>
<h6 hidden class="mb-2">Soubory ke stažení:</h6>
<div id="downloadFiles" style="
padding: 40px 20px;
" class="list-group">
</div>
</div>
</div>
</div>
<div id="outputFiles" class="list-group mt-3"></div>
</form>
</div>
</div>
<div class="editortit ">
<iframe class="editortit-iframe" id="ifred">
</iframe>
</div>
<script>
document.getElementById("translateForm").addEventListener("input", function () {
const form = this; // Odkaz na formulář
const submitBtn = document.getElementById("submitBtn");
// Zkontrolujeme, zda všechna required pole jsou validní
const allFieldsFilled = Array.from(form.querySelectorAll("[required]")).every(input => input.value.trim() !== "");
// Povolit nebo zakázat tlačítko na základě výsledku
submitBtn.disabled = !allFieldsFilled;
});
document.getElementById("translationCorrectionBtn").addEventListener("click", function () {
const button = this;
const span = button.querySelector("span.pointer-events-none");
const hlavniForm = document.getElementById("hlavniform");
// Získání aktuálního stavu
const currentState = button.getAttribute("data-state");
if (currentState === "unchecked") {
// Změna na checked
button.style.backgroundColor = "hsl(0deg 0% 100%)";
span.style.setProperty("--tw-translate-x", "1rem");
button.setAttribute("data-state", "checked");
} else {
// Změna na unchecked
button.style.backgroundColor = "#2e3138";
span.style.setProperty("--tw-translate-x", "0rem");
button.setAttribute("data-state", "unchecked");
}
});
document.getElementById("submitBtn").addEventListener("click", async function () {
// Získání hodnoty z inputu s name="project_name"
submitBtn.style.display = 'none';
// Zobrazit tlačítko pro upravený překlad
const correctionBtn = document.getElementById("translationCorrectionBtn");
const hlavniForm = document.getElementById("hlavniform");
if (correctionBtn.getAttribute("data-state") === "checked") {
submitBtn2.style.display = 'inline-flex';
function jsonToSrtWithEstimation(jsonData) {
// Pomocná funkce pro převod času na SRT formát
function formatTime(seconds) {
const hours = Math.floor(seconds / 3600).toString().padStart(2, '0');
const minutes = Math.floor((seconds % 3600) / 60).toString().padStart(2, '0');
const secs = Math.floor(seconds % 60).toString().padStart(2, '0');
const millis = Math.floor((seconds % 1) * 1000).toString().padStart(3, '0');
return `${hours}:${minutes}:${secs},${millis}`;
}
// Funkce pro odhad délky na základě počtu slov
function estimateDuration(text) {
const words = text.split(' ').length; // Počet slov
const wordsPerSecond = 3.0; // Průměrně 2 slova za sekundu
return words / wordsPerSecond; // Odhad doby v sekundách
}
let srt = '';
for (let i = 0; i < jsonData.length; i++) {
const entry = jsonData[i];
const startTime = formatTime(entry.start);
// Odhad koncového času
const duration = estimateDuration(entry.text);
const endTime = formatTime(entry.start + duration);
// Sestavení SRT bloku
srt += `${i + 1}\n${startTime} --> ${endTime}\n${entry.text}\n\n`;
}
return srt.trim(); // Odstranění nadbytečných mezer na konci
}
const progress = document.getElementById('progress');
const result = document.getElementById('result');
const submitBtn = document.getElementById('submitBtn');
const subtitlesEditor = document.getElementById('subtitlesEditor');
progress.classList.remove('hidden');
result.innerHTML = '';
this.disabled = true;
submitBtn.disabled = true;
try {
const formData = new FormData(document.getElementById('translateForm'));
console.log('FormData obsah:');
for (let pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
const response = await fetch('/get_subtitles', {
method: 'POST',
body: formData
});
console.log("TITULKY");
const data = await response.json();
console.log(data);
if (data.success) {
result.innerHTML = `<div style="display:none;" class="alert alert-success">Překlad byl úspěšně dokončen! Podrobnosti jsou vygenerovány!</div>`;
// Store data in browser's session storage
if (data.subtitles) {
sessionStorage.setItem('subtitles', JSON.stringify(data.subtitles));
console.log("SESSION Subtitles saved to session storage");
}
if (data.video_path) {
sessionStorage.setItem('video_path', data.video_path);
console.log("SESSION Video path saved to session storage:", data.video_path);
}
if (data.video_url) {
sessionStorage.setItem('video_url', data.video_url);
console.log("SESSION Video URL saved to session storage:", data.video_url);
}
if (data.directory_path) {
sessionStorage.setItem('directory_path', data.directory_path);
console.log("SESSION Directory path saved to session storage:", data.directory_path);
}
if (data.subtitles) {
// Spuštění funkce
// Parsování stringu do JSON formátu
const inputDataJSSRT = JSON.parse(data.subtitles);
const outputSrt = jsonToSrtWithEstimation(inputDataJSSRT);
console.log(outputSrt);
// Save SRT to exports directory
const projectName = document.querySelector('input[name="project_name"]').value;
fetch('/save_srt', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
srt_content: outputSrt,
project_name: projectName
})
}).then(response => response.json())
.then(data => {
if (data.success) {
const formData = new FormData();
formData.append('project_name', projectName);
fetch('/editace', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(html => {
document.getElementById('ifred').innerHTML = html;
})
.catch(error => {
console.error('Failed to load editor:', error);
});
} else {
console.error('Failed to save SRT file');
}
})
.catch(error => console.error('Error saving SRT:', error));
subtitlesEditor.value = outputSrt;
document.getElementById('editSubtitlesCollapse').classList.add('show');
subtitlesEditor.focus();
}
} else {
result.innerHTML = `<div class="alert alert-danger">Error: ${data.error}</div>`;
}
} catch (error) {
result.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`;
} finally {
progress.classList.add('hidden');
this.disabled = false;
submitBtn.disabled = false;
}
// Kód, který se spustí, pokud je `translationCorrectionBtn` v "checked" stavu
hlavniForm.classList.add("shift-left");
const projectName = document.querySelector('input[name="project_name"]').value;
// Nastavení hodnoty jako src atributu pro iframe s id="ifred"
console.log(projectName);
const videoPath = sessionStorage.getItem('video_path');
const cleanedPath = videoPath.replace(/^exports[\\/]/, "").replace(/[\\/]+video\.mp4$/, "");
console.log("Cleaned Path:", cleanedPath);
const titulkysPath = videoPath.replace(/[\\/]+video\.mp4$/, "/titulky.srt");
console.log("SRT Path:", titulkysPath);
console.log("--------------------");
console.log(videoPath);
console.log(cleanedPath);
console.log(titulkysPath);
console.log("--------------------");
sessionStorage.setItem('titulkycesta', titulkysPath);
// Výpis všech klíčů a hodnot v sessionStorage
console.log("Všechny session:");
for (let i = 0; i < sessionStorage.length; i++) {
const key = sessionStorage.key(i);
const value = sessionStorage.getItem(key);
console.log(`${key}: ${value}`);
}
document.getElementById("ifred").setAttribute("src", "/editace?folder_id=" + cleanedPath);
const editor = document.querySelector(".editortit");
if (editor.style.display === "none" || !editor.style.display) {
// Zobrazit prvek
//editor.style.display = "block";
setTimeout(() => {
editor.classList.add("visible");
}, 10); // Krátké zpoždění pro správný efekt opacity
} else {
// Skrytí prvku
editor.classList.remove("visible");
setTimeout(() => {
editor.style.display = "none !important";
}, 1000); // Čekej 1s, dokud přechod neskončí
}
// Zde přidej svou logiku
} else {
//console.log("translationCorrectionBtn není checked.");
}
});
// Tab switching functionality
const uploadBtn = document.getElementById('uploadBtn');
const youtubeBtn = document.getElementById('youtubeBtn');
const uploadContent = document.querySelector('.border-dashed').parentElement;
const youtubeContent = document.querySelector('.youtu'); // Select the existing YouTube div
const zpracovani4 = document.getElementById('zpracovani4');
// Hide YouTube content initially
youtubeContent.style.display = 'none';
uploadBtn.addEventListener('click', () => {
uploadBtn.className = 'inline-flex items-center justify-center rounded-md px-3 py-1 text-sm font-medium bg-card text-foreground shadow transition-colors';
youtubeBtn.className = 'inline-flex items-center justify-center rounded-md px-3 py-1 text-sm font-medium transition-colors';
uploadContent.style.display = 'block';
youtubeContent.style.display = 'none';
zpracovani4.style.display = 'block';
});
youtubeBtn.addEventListener('click', () => {
youtubeBtn.className = 'inline-flex items-center justify-center rounded-md px-3 py-1 text-sm font-medium bg-card text-foreground shadow transition-colors';
uploadBtn.className = 'inline-flex items-center justify-center rounded-md px-3 py-1 text-sm font-medium transition-colors';
uploadContent.style.display = 'none';
youtubeContent.style.display = 'block';
zpracovani4.style.display = 'none';
});
// Dark mode toggle
const interfaceMode = document.getElementById('interface-mode');
const html = document.documentElement;
interfaceMode.addEventListener('change', function () {
if (this.checked) {
html.classList.remove('dark');
} else {
html.classList.add('dark');
}
});
// File upload handling
const dropZone = document.querySelector('.border-dashed');
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('border-primary');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('border-primary');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('border-primary');
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFile(files[0]);
}
});
dropZone.addEventListener('click', () => {
const input = document.getElementById('inputType');
input.type = 'file';
input.accept = 'audio/*,video/*';
input.onchange = (e) => {
if (e.target.files.length > 0) {
handleFile(e.target.files[0]);
}
};
input.click();
});
function handleFile(file) {
if (file.size > 500 * 1024 * 1024) { // 500MB limit
alert('Soubor je příliš velký. Maximální velikost je 500MB.');
return;
}
// Here you would typically upload the file to your server
console.log('Selected file:', file.name);
document.getElementById("chanana").textContent = file.name;
}
</script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Funkce pro kontrolu názvu projektu
function checkProjectName() {
const projectName = document.getElementById('project_name').value.trim();
const uploadBtn = document.getElementById('uploadBtn');
const youtubeBtn = document.getElementById('youtubeBtn');
const urlField = document.getElementById('urlField');
const inputType = document.getElementById('inputType');
if (projectName === '') {
uploadBtn.disabled = true;
youtubeBtn.disabled = true;
urlField.disabled = true;
inputType.disabled = true;
uploadBtn.title = 'Nejprve vyplňte název projektu';
youtubeBtn.title = 'Nejprve vyplňte název projektu';
} else {
uploadBtn.disabled = false;
youtubeBtn.disabled = false;
urlField.disabled = false;
inputType.disabled = false;
uploadBtn.title = '';
youtubeBtn.title = '';
}
}
// Přidat event listener pro input název projektu
document.getElementById('project_name').addEventListener('input', checkProjectName);
// Inicializovat stav při načtení stránky
document.addEventListener('DOMContentLoaded', function () {
checkProjectName();
});
// Globální proměnné pro YouTube video
let downloadedYoutubeFile = null;
// Sledování změn v URL poli
document.getElementById('urlField').addEventListener('input', async function (e) {
const url = e.target.value;
const youtubeProgress = document.getElementById('youtubeProgress');
const youtubeStatus = document.getElementById('youtubeStatus');
const submitBtn = document.getElementById('submitBtn');
const inputTypeSelect = document.getElementById('inputType');
const uploadBtn = document.getElementById('uploadBtn');
// Reset stavu
downloadedYoutubeFile = null;
youtubeStatus.innerHTML = '';
youtubeProgress.classList.add('hidden');
// Kontrola YouTube URL
if (url.includes('youtube.com') || url.includes('youtu.be')) {
submitBtn.disabled = true;
//youtubeProgress.classList.remove('hidden');
youtubeStatus.innerHTML = '<div class="alert alert-info">Stahuji <span class="loader"></span></div>';
try {
const response = await fetch('/download_youtube', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
url: url
})
});
const data = await response.json();
console.log(data);
if (data.success) {
downloadedYoutubeFile = data.file_path;
// Vytvořit HTML pro thumbnail
const thumbnailHTML = `
<div class="alert alert-success">
<div class="d-flex align-items-center">
<img src="${data.thumbnail_url}" alt="Video thumbnail" style="width: 210px; height: 120px; object-fit: cover; margin: 0 auto; border-radius: 9px; margin: 10px auto;">
<div>
<strong>Video úspěšně staženo!</strong>
<br>
<small class="text-muted">Připraveno ke zpracování</small>
</div>
</div>
</div>`;
youtubeStatus.innerHTML = thumbnailHTML;
document.getElementById('urlField').disabled = false;
submitBtn.disabled = false;
document.getElementById('uploadBtn').disabled = true;
} else {
youtubeStatus.innerHTML = `<div class="alert alert-danger">Error: ${data.error}</div>`;
}
} catch (error) {
youtubeStatus.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`;
}
youtubeProgress.classList.add('hidden');
}
});
// Handle input type switching
document.getElementById('inputType').addEventListener('change', function () {
const fileInput = document.getElementById('fileInput');
const urlInput = document.getElementById('urlInput');
const directoryInput = document.getElementById('directoryInput');
// Skrýt všechna inputy
// fileInput.classList.add('hidden');
//urlInput.classList.add('hidden');
// directoryInput.classList.add('hidden');
// Zobrazit vybraný input
switch (this.value) {
case 'file':
fileInput.classList.remove('hidden');
break;
case 'url':
urlInput.classList.remove('hidden');
break;
case 'directory':
directoryInput.classList.remove('hidden');
break;
}
});
// Handle speakers range
function updateSpeakers(value) {
//document.getElementById('speakersValue').textContent = value;
for (let i = 0; i < 12; i++) {
const voiceDiv = document.getElementById(`voice${String(i).padStart(2, '0')}`);
if (i < value) {
voiceDiv.style.display = 'block';
} else {
voiceDiv.style.display = 'none';
}
}
}
// Initialize with default value
window.addEventListener('load', function () {
updateSpeakers(1);
}); const correctionBtn = document.getElementById("translationCorrectionBtn");
// Handle form submission
document.getElementById('translateForm').addEventListener('submit', async function (e) {
e.preventDefault();
if (correctionBtn.getAttribute("data-state") !== "checked") {
console.log("Není zaškrtnuto.");
const formData = new FormData(this);
// Přidání informace o staženém YouTube videu
if (downloadedYoutubeFile) {
formData.append('youtube_file', downloadedYoutubeFile);
}
// Add edited subtitles to form data if they exist
const subtitlesEditor = document.getElementById('subtitlesEditor');
if (subtitlesEditor && subtitlesEditor.value.trim()) {
formData.append('edited_subtitles', subtitlesEditor.value);
}
const progress = document.getElementById('progress');
const progressText = document.getElementById('progressText');
const progressBar = document.getElementById('progressBar');
const result = document.getElementById('result');
const submitBtn = document.getElementById('submitBtn');
// Reset progress state
progress.classList.remove('hidden');
progressBar.style.width = '0%';
progressBar.setAttribute('aria-valuenow', '0');
progressText.textContent = "Nahrávání souboru...";
result.innerHTML = '';
submitBtn.disabled = true;
// Progress states
const states = [
{ text: "Nahrávání souboru...", progress: 20 },
{ text: "Extrahování zvuku...", progress: 40 },
{ text: "Převod řeči na text...", progress: 60 },
{ text: "Překládání...", progress: 80 },
{ text: "AI Dabing...", progress: 90 }
];
let currentState = 0;
const updateProgress = () => {
if (currentState < states.length) {
const state = states[currentState];
progressText.textContent = state.text;
progressBar.style.width = state.progress + '%';
progressBar.setAttribute('aria-valuenow', state.progress);
currentState++;
}
};
// Start with first state and progress updates
updateProgress();
const progressInterval = setInterval(updateProgress, 2000);
try {
console.log('FormData obsah:');
for (let pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
const response = await fetch('/translate', {
method: 'POST',
body: formData
});
console.log("translate");
const data = await response.json();
clearInterval(progressInterval);
if (data.success) {
progressBar.style.width = '100%';
progressBar.setAttribute('aria-valuenow', '100');
progressText.textContent = "Hotovo!";
setTimeout(() => {
result.innerHTML = `<div style="display:none;" class="alert alert-success">Překlad byl úspěšně dokončen!</div>`;
const ids = ["zpracovani1", "zpracovani2", "zpracovani3", "zpracovani4", "submitBtn"];
ids.forEach(id => {
const element = document.getElementById(id);
if (element) {
element.style.display = "none";
}
});
// Zobrazení video přehrávače a nastavení zdrojů
const videoResult = document.getElementById('videoResult');
const outputVideo = document.getElementById('outputVideo');
const downloadFiles = document.getElementById('downloadFiles');
if (data.video) {
// Použít cesty přímo z backendu
const videoUrl = data.video;
const originalVideoUrl = data.original_video;
// Uložit cesty k videím
currentVideoState.translatedVideo = videoUrl;
currentVideoState.originalVideo = originalVideoUrl;
// Nastavit video přehrávač
videoResult.style.display = 'block';
outputVideo.src = videoUrl;
outputVideo.load();
// Přidat odkazy na stažení
let downloadLinks = '<ul class="list-unstyled" style="display:none;">';
let counter = 0; // Čítač pro unikátní ID
data.files.forEach(file => {
const fileName = file.split('/').pop();
downloadLinks += `<li><a href="${file}" id="stahnuti${counter}" download class="btn btn-link">${fileName}</a></li>`;
counter++;
});
downloadLinks += '</ul>';
downloadLinks += '<button id="downloadButton3"><svg width="50px" height="50px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C22 4.92893 22 7.28595 22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12ZM12 6.25C12.4142 6.25 12.75 6.58579 12.75 7V12.1893L14.4697 10.4697C14.7626 10.1768 15.2374 10.1768 15.5303 10.4697C15.8232 10.7626 15.8232 11.2374 15.5303 11.5303L12.5303 14.5303C12.3897 14.671 12.1989 14.75 12 14.75C11.8011 14.75 11.6103 14.671 11.4697 14.5303L8.46967 11.5303C8.17678 11.2374 8.17678 10.7626 8.46967 10.4697C8.76256 10.1768 9.23744 10.1768 9.53033 10.4697L11.25 12.1893V7C11.25 6.58579 11.5858 6.25 12 6.25ZM8 16.25C7.58579 16.25 7.25 16.5858 7.25 17C7.25 17.4142 7.58579 17.75 8 17.75H16C16.4142 17.75 16.75 17.4142 16.75 17C16.75 16.5858 16.4142 16.25 16 16.25H8Z" fill="#1C274C" style=" fill: gainsboro;"></path> </svg></button><button id="refreshbrt"><svg width="50px" height="50px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M3.46447 3.46447C2 4.92893 2 7.28595 2 12C2 16.714 2 19.0711 3.46447 20.5355C4.92893 22 7.28595 22 12 22C16.714 22 19.0711 22 20.5355 20.5355C22 19.0711 22 16.714 22 12C22 7.28595 22 4.92893 20.5355 3.46447C19.0711 2 16.714 2 12 2C7.28595 2 4.92893 2 3.46447 3.46447ZM5.46058 11.0833C5.83333 7.79988 8.62406 5.25 12.0096 5.25C13.9916 5.25 15.7702 6.12471 16.9775 7.50653C17.25 7.81846 17.2181 8.29226 16.9061 8.56479C16.5942 8.83733 16.1204 8.80539 15.8479 8.49347C14.9136 7.42409 13.541 6.75 12.0096 6.75C9.45215 6.75 7.33642 8.63219 6.97332 11.0833H7.33654C7.63998 11.0833 7.91353 11.2662 8.02955 11.5466C8.14558 11.8269 8.08122 12.1496 7.86651 12.364L6.69825 13.5307C6.40544 13.8231 5.93113 13.8231 5.63832 13.5307L4.47005 12.364C4.25534 12.1496 4.19099 11.8269 4.30701 11.5466C4.42304 11.2662 4.69658 11.0833 5.00002 11.0833H5.46058ZM17.3018 10.4693C17.5947 10.1769 18.069 10.1769 18.3618 10.4693L19.53 11.636C19.7448 11.8504 19.8091 12.1731 19.6931 12.4534C19.5771 12.7338 19.3035 12.9167 19.0001 12.9167H18.5395C18.1668 16.2001 15.376 18.75 11.9905 18.75C10.0085 18.75 8.22995 17.8753 7.02263 16.4935C6.7501 16.1815 6.78203 15.7077 7.09396 15.4352C7.40589 15.1627 7.87968 15.1946 8.15222 15.5065C9.08654 16.5759 10.4591 17.25 11.9905 17.25C14.548 17.25 16.6637 15.3678 17.0268 12.9167H16.6636C16.3601 12.9167 16.0866 12.7338 15.9705 12.4534C15.8545 12.1731 15.9189 11.8504 16.1336 11.636L17.3018 10.4693Z" fill="#1C274C" style=" fill: gainsboro; "></path> </svg></button>';
downloadFiles.innerHTML = downloadLinks;
document.getElementById("refreshbrt").addEventListener("click", function () {
location.reload(); // Provede obnovu stránky
});
document.getElementById("downloadButton3").addEventListener("click", function () {
// Dynamické sestavení pole z atributů href odkazů
const files = [];
for (let i = 0; i <= 2; i++) {
const link = document.getElementById(`stahnuti${i}`);
if (link) {
files.push(link.href); // Přidáme URL z atributu href
}
}
// Pro každý soubor vytvoříme odkaz a automaticky jej stáhneme
files.forEach(file => {
const a = document.createElement("a");
a.href = file;
a.download = ""; // Pokud chceš specifický název, zadej např. "file1.pdf"
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
});
}
progress.classList.add('hidden');
}, 1000);
} else {
result.innerHTML = `<div class="alert alert-danger">Error: ${data.error}</div>`;
progress.classList.add('hidden');
}
} catch (error) {
result.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`;
progress.classList.add('hidden');
} finally {
submitBtn.disabled = false;
}
} else {
console.log("Je zaškrtnuto.");
const formData = new FormData(this);
const subtitles = sessionStorage.getItem('subtitles');
if (subtitles) {
// Vložit titulky do textboxu
const subtitlesEditor = document.getElementById('subtitlesEditor');
if (subtitlesEditor) {
subtitlesEditor.value = subtitles;
}
}
if (subtitlesEditor && subtitlesEditor.value.trim()) {
formData.append('edited_subtitles', subtitlesEditor.value);
console.log("až sem dobrý");
}
// Najít element s třídou "editortit visible" a odstranit třídu "visible"
const editorTit = document.querySelector('.editortit.visible');
if (editorTit) {
editorTit.classList.remove('visible');
}
// Najít element s třídou "w-full max-w-2xl shift-left" a odstranit třídu "shift-left"
const wfullElement = document.querySelector('.w-full.max-w-2xl.shift-left');
if (wfullElement) {
wfullElement.classList.remove('shift-left');
}
// Přidání informace o staženém YouTube videu
if (downloadedYoutubeFile) {
formData.append('youtube_file', downloadedYoutubeFile);
}
// Add edited subtitles to form data if they exist
const progress = document.getElementById('progress');
const progressText = document.getElementById('progressText');
const progressBar = document.getElementById('progressBar');
const result = document.getElementById('result');
const submitBtn = document.getElementById('submitBtn');
// Reset progress state
progress.classList.remove('hidden');
progressBar.style.width = '0%';
progressBar.setAttribute('aria-valuenow', '0');
progressText.textContent = "Nahrávání souboru...";
result.innerHTML = '';
submitBtn.disabled = true;
// Progress states
const states = [
{ text: "Nahrávání souboru...", progress: 20 },
{ text: "Extrahování zvuku...", progress: 40 },
{ text: "Převod řeči na text...", progress: 60 },
{ text: "Překládání...", progress: 80 },
{ text: "AI Dabing...", progress: 90 }
];
let currentState = 0;
const updateProgress = () => {
if (currentState < states.length) {
const state = states[currentState];
progressText.textContent = state.text;
progressBar.style.width = state.progress + '%';
progressBar.setAttribute('aria-valuenow', state.progress);
currentState++;
}
};
// Start with first state and progress updates
updateProgress();
const progressInterval = setInterval(updateProgress, 2000);
try {
console.log('FormData obsah:');
for (let pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
const response = await fetch('/translate', {
method: 'POST',
body: formData
});
console.log("translate checked");
const data = await response.json();
clearInterval(progressInterval);
if (data.success) {
progressBar.style.width = '100%';
progressBar.setAttribute('aria-valuenow', '100');
progressText.textContent = "Hotovo!";
setTimeout(() => {
result.innerHTML = `<div style="display:none;" class="alert alert-success">Překlad byl úspěšně dokončen!</div>`;
const ids = ["zpracovani1", "zpracovani2", "zpracovani3", "zpracovani4", "submitBtn"];
ids.forEach(id => {
const element = document.getElementById(id);
if (element) {
element.style.display = "none";
}
});
// Zobrazení video přehrávače a nastavení zdrojů
const videoResult = document.getElementById('videoResult');
const outputVideo = document.getElementById('outputVideo');
const downloadFiles = document.getElementById('downloadFiles');
if (data.video) {
// Použít cesty přímo z backendu
const videoUrl = data.video;
const originalVideoUrl = data.original_video;
// Uložit cesty k videím
currentVideoState.translatedVideo = videoUrl;
currentVideoState.originalVideo = originalVideoUrl;
// Nastavit video přehrávač
videoResult.style.display = 'block';
outputVideo.src = videoUrl;
outputVideo.load();
// Přidat odkazy na stažení
let downloadLinks = '<ul class="list-unstyled" style="display:none;">';
let counter = 0; // Čítač pro unikátní ID
data.files.forEach(file => {
const fileName = file.split('/').pop();
downloadLinks += `<li><a href="${file}" id="stahnuti${counter}" download class="btn btn-link">${fileName}</a></li>`;
counter++;
});
downloadLinks += '</ul>';
downloadLinks += '<button id="downloadButton3"><svg width="50px" height="50px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C22 4.92893 22 7.28595 22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12ZM12 6.25C12.4142 6.25 12.75 6.58579 12.75 7V12.1893L14.4697 10.4697C14.7626 10.1768 15.2374 10.1768 15.5303 10.4697C15.8232 10.7626 15.8232 11.2374 15.5303 11.5303L12.5303 14.5303C12.3897 14.671 12.1989 14.75 12 14.75C11.8011 14.75 11.6103 14.671 11.4697 14.5303L8.46967 11.5303C8.17678 11.2374 8.17678 10.7626 8.46967 10.4697C8.76256 10.1768 9.23744 10.1768 9.53033 10.4697L11.25 12.1893V7C11.25 6.58579 11.5858 6.25 12 6.25ZM8 16.25C7.58579 16.25 7.25 16.5858 7.25 17C7.25 17.4142 7.58579 17.75 8 17.75H16C16.4142 17.75 16.75 17.4142 16.75 17C16.75 16.5858 16.4142 16.25 16 16.25H8Z" fill="#1C274C" style=" fill: gainsboro;"></path> </svg></button><button id="refreshbrt"><svg width="50px" height="50px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M3.46447 3.46447C2 4.92893 2 7.28595 2 12C2 16.714 2 19.0711 3.46447 20.5355C4.92893 22 7.28595 22 12 22C16.714 22 19.0711 22 20.5355 20.5355C22 19.0711 22 16.714 22 12C22 7.28595 22 4.92893 20.5355 3.46447C19.0711 2 16.714 2 12 2C7.28595 2 4.92893 2 3.46447 3.46447ZM5.46058 11.0833C5.83333 7.79988 8.62406 5.25 12.0096 5.25C13.9916 5.25 15.7702 6.12471 16.9775 7.50653C17.25 7.81846 17.2181 8.29226 16.9061 8.56479C16.5942 8.83733 16.1204 8.80539 15.8479 8.49347C14.9136 7.42409 13.541 6.75 12.0096 6.75C9.45215 6.75 7.33642 8.63219 6.97332 11.0833H7.33654C7.63998 11.0833 7.91353 11.2662 8.02955 11.5466C8.14558 11.8269 8.08122 12.1496 7.86651 12.364L6.69825 13.5307C6.40544 13.8231 5.93113 13.8231 5.63832 13.5307L4.47005 12.364C4.25534 12.1496 4.19099 11.8269 4.30701 11.5466C4.42304 11.2662 4.69658 11.0833 5.00002 11.0833H5.46058ZM17.3018 10.4693C17.5947 10.1769 18.069 10.1769 18.3618 10.4693L19.53 11.636C19.7448 11.8504 19.8091 12.1731 19.6931 12.4534C19.5771 12.7338 19.3035 12.9167 19.0001 12.9167H18.5395C18.1668 16.2001 15.376 18.75 11.9905 18.75C10.0085 18.75 8.22995 17.8753 7.02263 16.4935C6.7501 16.1815 6.78203 15.7077 7.09396 15.4352C7.40589 15.1627 7.87968 15.1946 8.15222 15.5065C9.08654 16.5759 10.4591 17.25 11.9905 17.25C14.548 17.25 16.6637 15.3678 17.0268 12.9167H16.6636C16.3601 12.9167 16.0866 12.7338 15.9705 12.4534C15.8545 12.1731 15.9189 11.8504 16.1336 11.636L17.3018 10.4693Z" fill="#1C274C" style=" fill: gainsboro; "></path> </svg></button>';
downloadFiles.innerHTML = downloadLinks;
document.getElementById("refreshbrt").addEventListener("click", function () {
location.reload(); // Provede obnovu stránky
});
document.getElementById("downloadButton3").addEventListener("click", function () {
// Dynamické sestavení pole z atributů href odkazů
const files = [];
for (let i = 0; i <= 2; i++) {
const link = document.getElementById(`stahnuti${i}`);
if (link) {
files.push(link.href); // Přidáme URL z atributu href
}
}
// Pro každý soubor vytvoříme odkaz a automaticky jej stáhneme
files.forEach(file => {
const a = document.createElement("a");
a.href = file;
a.download = ""; // Pokud chceš specifický název, zadej např. "file1.pdf"
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
});
}
progress.classList.add('hidden');
}, 1000);
} else {
result.innerHTML = `<div class="alert alert-danger">Error: ${data.error}</div>`;
progress.classList.add('hidden');
}
} catch (error) {
result.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`;
progress.classList.add('hidden');
} finally {
submitBtn.disabled = false;
}
}
});
// Update all range input values
document.getElementById('segmentDuration').addEventListener('input', function () {
document.getElementById('segmentDurationValue').textContent = this.value;
});
document.getElementById('batchSize').addEventListener('input', function () {
document.getElementById('batchSizeValue').textContent = this.value;
});
// Voice Imitation Settings Handlers
document.getElementById('enableVoiceImitation').addEventListener('change', function () {
const options = document.getElementById('voiceImitationOptions');
options.style.display = this.checked ? 'block' : 'none';
});
// Update range input displays
document.getElementById('transposeValue').addEventListener('input', function () {
document.getElementById('transposeValueDisplay').textContent = this.value;
});
document.getElementById('indexRate').addEventListener('input', function () {
document.getElementById('indexRateDisplay').textContent = this.value;
});
document.getElementById('protectValue').addEventListener('input', function () {
document.getElementById('protectValueDisplay').textContent = this.value;
});
// Load voice models based on method
document.getElementById('voiceImitationMethod').addEventListener('change', async function () {
const voiceModelSelect = document.getElementById('voiceModel');
voiceModelSelect.innerHTML = '<option value="">Loading models...</option>';
try {
const response = await fetch(`/get_voice_models?method=${this.value}`);
const data = await response.json();
if (data.success) {
voiceModelSelect.innerHTML = '<option value="">Select a voice model...</option>' +
data.models.map(model => `<option value="${model}">${model}</option>`).join('');
} else {
voiceModelSelect.innerHTML = '<option value="">Error loading models</option>';
}
} catch (error) {
voiceModelSelect.innerHTML = '<option value="">Error loading models</option>';
}
});
// Handle get subtitles button
document.getElementById('getSubtitlesBtn').addEventListener('click', async function () {
const progress = document.getElementById('progress');
const result = document.getElementById('result');
const submitBtn = document.getElementById('submitBtn');
const subtitlesEditor = document.getElementById('subtitlesEditor');
progress.classList.remove('hidden');
result.innerHTML = '';
this.disabled = true;
submitBtn.disabled = true;
try {
const formData = new FormData(document.getElementById('translateForm'));
const response = await fetch('/get_subtitles', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
result.innerHTML = `<div style="display:none;" class="alert alert-success">Subtitles generated successfully! You can now edit them below.</div>`;
if (data.subtitles) {
subtitlesEditor.value = data.subtitles;
document.getElementById('editSubtitlesCollapse').classList.add('show');
subtitlesEditor.focus();
}
} else {
result.innerHTML = `<div class="alert alert-danger">Error: ${data.error}</div>`;
}
} catch (error) {
result.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`;
} finally {
progress.classList.add('hidden');
this.disabled = false;
submitBtn.disabled = false;
}
});
// Funkce pro zobrazení toast notifikace
function showToast(message, isError = false) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.style.display = 'block';
if (isError) {
toast.classList.add('error');
} else {
toast.classList.remove('error');
}
setTimeout(() => {
toast.style.display = 'none';
}, 3000);
}
// Handler pro PURGE tlačítko
document.getElementById('purgeButton').addEventListener('click', async function () {
if (!confirm('Opravdu chcete vymazat obsah složek outputs a audio?')) {
return;
}
try {
const response = await fetch('/purge', {
method: 'POST'
});
const data = await response.json();
if (data.success) {
showToast(data.message);
} else {
showToast(data.error, true);
}
} catch (error) {
showToast('Došlo k chybě při mazání složek', true);
}
});
let currentVideoState = {
translatedVideo: '',
originalVideo: '',
currentTime: 0,
isPlaying: false
};
// Funkce pro přepínání mezi videi
function switchVideo(type) {
const video = document.getElementById('outputVideo');
const translatedBtn = document.getElementById('translatedBtn');
const originalBtn = document.getElementById('originalBtn');
// Uložit aktuální stav přehrávání
currentVideoState.currentTime = video.currentTime;
currentVideoState.isPlaying = !video.paused;
// Přepnout video zdroj
if (type === 'translated') {
video.src = currentVideoState.translatedVideo;
// Přidání tříd pro translatedBtn
translatedBtn.classList.add(
'active',
'inline-flex',
'items-center',
'justify-center',
'rounded-md',
'px-3',
'py-1',
'text-sm',
'font-medium',
'bg-card',
'text-foreground',
'shadow',
'transition-colors'
);
// Odebrání tříd z originalBtn
originalBtn.classList.remove(
'active',
'inline-flex',
'items-center',
'justify-center',
'rounded-md',
'px-3',
'py-1',
'text-sm',
'font-medium',
'bg-card',
'text-foreground',
'shadow',
'transition-colors'
);
} else {
video.src = currentVideoState.originalVideo;
// Přidání tříd pro originalBtn
originalBtn.classList.add(
'active',
'inline-flex',
'items-center',
'justify-center',
'rounded-md',
'px-3',
'py-1',
'text-sm',
'font-medium',
'bg-card',
'text-foreground',
'shadow',
'transition-colors'
);
// Odebrání tříd z translatedBtn
translatedBtn.classList.remove(
'active',
'inline-flex',
'items-center',
'justify-center',
'rounded-md',
'px-3',
'py-1',
'text-sm',
'font-medium',
'bg-card',
'text-foreground',
'shadow',
'transition-colors'
);
}
// Obnovit stav přehrávání
video.addEventListener('loadedmetadata', function () {
video.currentTime = currentVideoState.currentTime;
if (currentVideoState.isPlaying) {
video.play();
}
}, { once: true });
}
</script>
<div id="progressBarContainer" class="progress-container">
<div class="progress">
<div id="progressBar" class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0"
aria-valuemin="0" aria-valuemax="100">0%</div>
</div>
</div>
<script>
// Funkce pro zobrazení progress baru
function showProgressBar() {
const progressBarContainer = document.getElementById('progressBarContainer');
if (progressBarContainer) {
progressBarContainer.style.display = 'block';
const progressBar = document.getElementById('progressBar');
if (progressBar) {
progressBar.style.width = '0%';
progressBar.textContent = '0%';
}
}
}
// Funkce pro skrytí progress baru
function hideProgressBar() {
const progressBarContainer = document.getElementById('progressBarContainer');
if (progressBarContainer) {
progressBarContainer.style.display = 'none';
}
}
// Funkce pro aktualizaci progress baru
function updateProgressBar(progress) {
const progressBar = document.getElementById('progressBar');
if (progressBar) {
progressBar.style.width = progress + '%';
progressBar.textContent = progress + '%';
progressBar.setAttribute('aria-valuenow', progress);
}
}
// Event listener pro tlačítko "Vytvořit dabing"
/*document.getElementById('createDubbingBtn').addEventListener('click', function () {
showProgressBar();
// Simulace průběhu (později nahradit skutečným průběhem)
let progress = 0;
const interval = setInterval(() => {
progress += 1;
updateProgressBar(progress);
if (progress >= 100) {
clearInterval(interval);
setTimeout(hideProgressBar, 1000); // Skrýt progress bar po dokončení
}
}, 50);
});*/
</script>
<script>
// Funkce pro zobrazení progress baru
function showProgressBar() {
const progressBarContainer = document.getElementById('progressBarContainer');
if (progressBarContainer) {
progressBarContainer.style.display = 'block';
const progressBar = document.getElementById('progressBar');
if (progressBar) {
progressBar.style.width = '0%';
progressBar.textContent = '0%';
}
}
}
// Funkce pro skrytí progress baru
function hideProgressBar() {
const progressBarContainer = document.getElementById('progressBarContainer');
if (progressBarContainer) {
progressBarContainer.style.display = 'none';
}
}
// Funkce pro aktualizaci progress baru
function updateProgressBar(progress) {
const progressBar = document.getElementById('progressBar');
if (progressBar) {
progressBar.style.width = progress + '%';
progressBar.textContent = progress + '%';
progressBar.setAttribute('aria-valuenow', progress);
}
}
// Přidání progress baru pro zpracování dabingu
document.addEventListener('DOMContentLoaded', function () {
const form = document.querySelector('form');
if (form) {
form.addEventListener('submit', function (e) {
if (!e.defaultPrevented) {
showProgressBar();
// Začneme na 0%
updateProgressBar(0);
// Simulace průběhu (později nahradit skutečným průběhem)
let progress = 0;
const interval = setInterval(() => {
progress += 1;
if (progress <= 100) {
updateProgressBar(progress);
}
if (progress >= 100) {
clearInterval(interval);
setTimeout(hideProgressBar, 1000);
}
}, 50);
}
});
}
});
</script>
<script>
// Connect to SSE endpoint for logs
const eventSource = new EventSource('/logs');
const logMessages = document.getElementById('logMessages');
eventSource.onmessage = function (event) {
const data = JSON.parse(event.data);
// Skip keepalive messages
if (data.message === 'keepalive') return;
// Create log message element
const logElement = document.createElement('div');
// Handle YouTube status messages
if (data.message.includes('*YOUTUBE-download*')) {
const youtubeStatus = document.getElementById('youtubeStatusPRG');
if (youtubeStatus) {
youtubeStatus.textContent = data.message.replace('*YOUTUBE-download*', '').trim();
youtubeStatus.style.display = 'block';
}
} else if (data.message.includes('*YOUTUBE-finish*')) {
const youtubeStatus = document.getElementById('youtubeStatusPRG');
if (youtubeStatus) {
youtubeStatus.textContent = '';
youtubeStatus.style.display = 'none';
}
}
// Create log message element
logElement.className = `log-message ${data.level.toLowerCase()}`;
logElement.textContent = data.message;
// Add to container and scroll to bottom
logMessages.appendChild(logElement);
logMessages.scrollTop = logMessages.scrollHeight;
// Přidání console.log pro zprávy obsahující specifické značky
if (data.message.includes('*YOUTUBE')) {
console.log('%c[YouTube]%c ' + data.message.replace(/\*YOUTUBE[^*]*\*/, ''),
'color: red; font-weight: bold', 'color: inherit');
}
if (data.message.includes('*YOUTUBE-zahajeni*')) {
console.log('%c[YouTube Start]%c ' + data.message.replace(/\*YOUTUBE-zahajeni\*/, ''),
'color: #4CAF50; font-weight: bold', 'color: inherit');
}
if (data.message.includes('*YOUTUBE-download*')) {
console.log('%c[YouTube Download]%c ' + data.message.replace(/\*YOUTUBE-download\*/, ''),
'color: #2196F3; font-weight: bold', 'color: inherit');
}
if (data.message.includes('*YOUTUBE-finish*')) {
console.log('%c[YouTube Finish]%c ' + data.message.replace(/\*YOUTUBE-finish\*/, ''),
'color: #9C27B0; font-weight: bold', 'color: inherit');
}
// Keep only last 50 messages
while (logMessages.children.length > 50) {
logMessages.removeChild(logMessages.firstChild);
}
};
// Add some basic styles for status indicators
const statusStyle = document.createElement('style');
statusStyle.textContent = `
#statusContainer {
background: none;
color: white;
font-size: 0.875rem;
}
.status-indicator {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
}
.status-enabled {
background-color: #10b981;
}
.status-disabled {
background-color: #ef4444;
}
`;
document.head.appendChild(statusStyle);
// Get all statuses when page loads
fetch('/get_status')
.then(response => response.json())
.then(data => {
// Update status elements
const xttsStatus = document.getElementById('xttsStatus');
const piperStatus = document.getElementById('piperStatus');
const ngrokStatus = document.getElementById('ngrokStatus');
xttsStatus.innerHTML = `
<span class="status-indicator ${data.xtts_enabled ? 'status-enabled' : 'status-disabled'}"></span>
XTTS: ${data.xtts_enabled ? 'online' : 'offline'}
`;
piperStatus.innerHTML = `
<span class="status-indicator ${data.piper_enabled ? 'status-enabled' : 'status-disabled'}"></span>
Piper: ${data.piper_enabled ? 'online' : 'offline'}
`;
ngrokStatus.innerHTML = `
<span class="status-indicator ${data.ngrok_server_online ? 'status-enabled' : 'status-disabled'}"></span>
YT server: ${data.ngrok_server_online ? 'online' : 'offline'}
`;
// Also log to console and log container as before
console.log('XTTS:', data.xtts_enabled ? 'enabled' : 'disabled');
console.log('Piper:', data.piper_enabled ? 'enabled' : 'disabled');
console.log('Ngrok server:', data.ngrok_server_online ? 'online' : 'offline');
const messages = [
`XTTS is ${data.xtts_enabled ? 'enabled' : 'disabled'}`,
`Piper is ${data.piper_enabled ? 'enabled' : 'disabled'}`,
`Ngrok server is ${data.ngrok_server_online ? 'online' : 'offline'}`
];
messages.forEach(message => {
const logElement = document.createElement('div');
logElement.className = 'log-message info';
logElement.textContent = message;
logMessages.appendChild(logElement);
});
})
.catch(error => {
console.error('Error fetching status:', error);
const logElement = document.createElement('div');
logElement.className = 'log-message error';
logElement.textContent = 'Error fetching server status';
logMessages.appendChild(logElement);
});
</script>
<script>
function srtToJson(srtText) {
const subtitles = [];
const blocks = srtText.trim().split('\n\n');
blocks.forEach((block) => {
const lines = block.split('\n');
if (lines.length >= 3) {
const index = parseInt(lines[0]);
const timeMatch = lines[1].match(/(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})/);
if (timeMatch) {
const startTime = timeMatch[1];
const endTime = timeMatch[2];
const text = lines.slice(2).join('\n');
// Extrahujeme mluvčího z textu (pokud existuje)
let speaker = 1; // Výchozí mluvčí jako číslo
let finalText = text;
// Pokud text začíná formátem [Číslo mluvčího]: Text
const speakerMatch = text.match(/^\[(\d+)\]:\s*(.*)/);
if (speakerMatch) {
speaker = parseInt(speakerMatch[1]);
finalText = speakerMatch[2];
}
subtitles.push({
//id: index,
start: startTime,
//end: endTime,
text: finalText,
speaker: speaker
});
}
}
});
return subtitles;
}
window.addEventListener('message', function(event) {
if (event.data.type === 'subtitlesChanged') {
//alert('Obsah titulků se změnil:\n' + event.data.data);
const subtitlesEditor = document.getElementById('subtitlesEditor');
//subtitlesEditor.value = event.data.data;
const jsonSubtitles = srtToJson(event.data.data);
console.log('Titulky v JSON formátu:', jsonSubtitles);
subtitlesEditor.value = JSON.stringify(jsonSubtitles, null, 2);
//subtitlesEditor.value = jsonSubtitles;
}
});
document.getElementById('submitBtn2').addEventListener('click', function() {
// Skryjeme tlačítko
this.style.display = 'none';
//document.querySelector('.editortit').style.display = 'none';
const subtitlesEditor = document.getElementById('subtitlesEditor');
// Nebo pokud chcete animaci fade-out
this.style.opacity = '0';
this.style.transition = 'opacity 0.5s';
// Po 500ms úplně skryjeme element
setTimeout(() => {
this.style.display = 'none';
}, 500);
});
</script>
</body>
</html>