|
|
<!DOCTYPE html> |
|
|
<html lang="pt-BR"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Conversor Avançado de Texto para SRT com Terminação em Pontos</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<style> |
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; transform: translateY(10px); } |
|
|
to { opacity: 1; transform: translateY(0); } |
|
|
} |
|
|
@keyframes slideIn { |
|
|
from { transform: translateX(100%); } |
|
|
to { transform: translateX(0); } |
|
|
} |
|
|
@keyframes pulse { |
|
|
0%, 100% { opacity: 1; } |
|
|
50% { opacity: 0.5; } |
|
|
} |
|
|
@keyframes rotate { |
|
|
from { transform: rotate(0deg); } |
|
|
to { transform: rotate(360deg); } |
|
|
} |
|
|
.fade-in { |
|
|
animation: fadeIn 0.5s ease-out forwards; |
|
|
} |
|
|
.slide-in { |
|
|
animation: slideIn 0.3s ease-out forwards; |
|
|
} |
|
|
.pulse { |
|
|
animation: pulse 2s infinite; |
|
|
} |
|
|
.rotate { |
|
|
animation: rotate 2s linear infinite; |
|
|
} |
|
|
.text-area-container { |
|
|
position: relative; |
|
|
} |
|
|
.char-count { |
|
|
position: absolute; |
|
|
right: 10px; |
|
|
bottom: 10px; |
|
|
background: rgba(255, 255, 255, 0.8); |
|
|
padding: 2px 8px; |
|
|
border-radius: 10px; |
|
|
font-size: 0.8rem; |
|
|
} |
|
|
.dark .char-count { |
|
|
background: rgba(31, 41, 55, 0.8); |
|
|
color: white; |
|
|
} |
|
|
.progress-bar { |
|
|
height: 4px; |
|
|
background: #e2e8f0; |
|
|
border-radius: 2px; |
|
|
overflow: hidden; |
|
|
} |
|
|
.dark .progress-bar { |
|
|
background: #374151; |
|
|
} |
|
|
.progress-fill { |
|
|
height: 100%; |
|
|
background: #3b82f6; |
|
|
transition: width 0.3s ease; |
|
|
} |
|
|
.srt-block { |
|
|
border-left: 4px solid #3b82f6; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
.dark .srt-block { |
|
|
background-color: #1f2937; |
|
|
border-left-color: #60a5fa; |
|
|
} |
|
|
.srt-block:hover { |
|
|
transform: translateX(5px); |
|
|
background-color: #f8fafc; |
|
|
} |
|
|
.dark .srt-block:hover { |
|
|
background-color: #374151; |
|
|
} |
|
|
.copy-btn { |
|
|
opacity: 0; |
|
|
transition: opacity 0.3s ease; |
|
|
} |
|
|
.srt-block:hover .copy-btn { |
|
|
opacity: 1; |
|
|
} |
|
|
.tooltip { |
|
|
position: relative; |
|
|
} |
|
|
.tooltip-text { |
|
|
visibility: hidden; |
|
|
width: 120px; |
|
|
background-color: #333; |
|
|
color: #fff; |
|
|
text-align: center; |
|
|
border-radius: 6px; |
|
|
padding: 5px; |
|
|
position: absolute; |
|
|
z-index: 1; |
|
|
bottom: 125%; |
|
|
left: 50%; |
|
|
margin-left: -60px; |
|
|
opacity: 0; |
|
|
transition: opacity 0.3s; |
|
|
} |
|
|
.dark .tooltip-text { |
|
|
background-color: #111827; |
|
|
} |
|
|
.tooltip:hover .tooltip-text { |
|
|
visibility: visible; |
|
|
opacity: 1; |
|
|
} |
|
|
.tab-content { |
|
|
display: none; |
|
|
} |
|
|
.tab-content.active { |
|
|
display: block; |
|
|
animation: fadeIn 0.5s ease-out; |
|
|
} |
|
|
.image-block { |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
.image-block:hover { |
|
|
transform: scale(1.02); |
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); |
|
|
} |
|
|
.dark .image-block:hover { |
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2); |
|
|
} |
|
|
.clock { |
|
|
font-family: 'Courier New', monospace; |
|
|
letter-spacing: 2px; |
|
|
} |
|
|
.dark { |
|
|
background-color: #111827; |
|
|
color: #f3f4f6; |
|
|
} |
|
|
.dark .bg-white { |
|
|
background-color: #1f2937; |
|
|
} |
|
|
.dark .text-gray-800 { |
|
|
color: #f3f4f6; |
|
|
} |
|
|
.dark .text-gray-600 { |
|
|
color: #d1d5db; |
|
|
} |
|
|
.dark .border-gray-300 { |
|
|
border-color: #4b5563; |
|
|
} |
|
|
.dark .bg-gray-200 { |
|
|
background-color: #374151; |
|
|
color: #f3f4f6; |
|
|
} |
|
|
.dark .bg-gray-50 { |
|
|
background-color: #1f2937; |
|
|
} |
|
|
.dark .border-gray-200 { |
|
|
border-color: #374151; |
|
|
} |
|
|
.dark .shadow-md { |
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2); |
|
|
} |
|
|
.dark .shadow-inner { |
|
|
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.3); |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-50 min-h-screen flex flex-col dark:bg-gray-900 transition-colors duration-300"> |
|
|
<div class="container mx-auto px-4 py-8 flex-grow"> |
|
|
<div class="max-w-5xl mx-auto"> |
|
|
|
|
|
<header class="text-center mb-8 fade-in relative"> |
|
|
<div class="absolute top-0 right-0 flex items-center space-x-4"> |
|
|
<div class="clock bg-gray-200 dark:bg-gray-700 px-3 py-1 rounded-md text-sm font-mono"> |
|
|
<span id="hours">00</span>:<span id="minutes">00</span>:<span id="seconds">00</span> |
|
|
</div> |
|
|
<button id="darkModeToggle" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200"> |
|
|
<i class="fas fa-moon dark:hidden"></i> |
|
|
<i class="fas fa-sun hidden dark:inline"></i> |
|
|
</button> |
|
|
</div> |
|
|
<h1 class="text-3xl md:text-4xl font-bold text-gray-800 dark:text-white mb-2"> |
|
|
<i class="fas fa-closed-captioning text-blue-500 mr-2"></i> |
|
|
Conversor de Texto para SRT com Terminação em Pontos |
|
|
</h1> |
|
|
<p class="text-gray-600 dark:text-gray-300">Transforme texto em legendas SRT respeitando a pontuação</p> |
|
|
</header> |
|
|
|
|
|
|
|
|
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md overflow-hidden mb-8"> |
|
|
<div class="p-6"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h2 class="text-xl font-semibold text-gray-800 dark:text-white"> |
|
|
<i class="fas fa-edit text-blue-500 mr-2"></i> |
|
|
Insira seu texto |
|
|
</h2> |
|
|
<div class="flex space-x-2"> |
|
|
<button id="sampleBtn" type="button" class="px-3 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200 rounded-md text-sm hover:bg-gray-300 dark:hover:bg-gray-600 transition"> |
|
|
<i class="fas fa-lightbulb mr-1"></i> Exemplo |
|
|
</button> |
|
|
<button id="clearInputBtn" type="button" class="px-3 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200 rounded-md text-sm hover:bg-gray-300 dark:hover:bg-gray-600 transition"> |
|
|
<i class="fas fa-trash-alt mr-1"></i> Limpar |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="text-area-container mb-2"> |
|
|
<textarea id="textoInput" class="w-full h-64 p-4 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none dark:bg-gray-700 dark:text-white" placeholder="Cole ou digite o texto que deseja converter para formato SRT..."></textarea> |
|
|
<div class="char-count dark:bg-gray-700 dark:text-gray-300"> |
|
|
<span id="charCount">0</span> caracteres | |
|
|
<span id="wordCount">0</span> palavras |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="progress-bar mb-4"> |
|
|
<div id="progressFill" class="progress-fill" style="width: 0%"></div> |
|
|
</div> |
|
|
|
|
|
<div class="flex flex-wrap justify-between items-center"> |
|
|
<div class="mb-4 md:mb-0"> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<div class="flex items-center"> |
|
|
<label for="charsPerBlock" class="mr-2 text-sm text-gray-600 dark:text-gray-300">Caracteres/bloco:</label> |
|
|
<input type="number" id="charsPerBlock" value="490" min="50" max="1000" class="w-20 p-1 border border-gray-300 dark:border-gray-600 rounded text-sm dark:bg-gray-700 dark:text-white"> |
|
|
</div> |
|
|
<div class="flex items-center"> |
|
|
<label for="blockDuration" class="mr-2 text-sm text-gray-600 dark:text-gray-300">Duração (s):</label> |
|
|
<input type="number" id="blockDuration" value="50" min="1" max="120" class="w-16 p-1 border border-gray-300 dark:border-gray-600 rounded text-sm dark:bg-gray-700 dark:text-white"> |
|
|
</div> |
|
|
<div class="flex items-center"> |
|
|
<input type="checkbox" id="endOnPeriod" checked class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"> |
|
|
<label for="endOnPeriod" class="ml-2 text-sm text-gray-600 dark:text-gray-300">Terminar em pontos</label> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<button id="convertBtn" type="button" class="px-6 py-3 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition flex items-center"> |
|
|
<i class="fas fa-exchange-alt mr-2"></i> Converter para SRT |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="resultsSection" class="hidden"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h2 class="text-xl font-semibold text-gray-800 dark:text-white"> |
|
|
<i class="fas fa-file-alt text-green-500 mr-2"></i> |
|
|
Legendas SRT Geradas |
|
|
</h2> |
|
|
<div class="flex space-x-2"> |
|
|
<button id="downloadBtn" class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition flex items-center"> |
|
|
<i class="fas fa-download mr-2"></i> Baixar SRT |
|
|
</button> |
|
|
<button id="copyAllBtn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition flex items-center"> |
|
|
<i class="fas fa-copy mr-2"></i> Copiar Tudo |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg p-4 mb-4"> |
|
|
<div class="flex justify-between items-center mb-3"> |
|
|
<div class="text-sm text-gray-600 dark:text-gray-300"> |
|
|
<span id="blockCount">0</span> blocos gerados | |
|
|
<span id="totalDuration">00:00:00</span> de duração total | |
|
|
<span id="totalWords">0</span> palavras |
|
|
</div> |
|
|
<div class="flex items-center"> |
|
|
<label for="fontSize" class="mr-2 text-sm text-gray-600 dark:text-gray-300">Tamanho:</label> |
|
|
<select id="fontSize" class="p-1 border border-gray-300 dark:border-gray-600 rounded text-sm bg-white dark:bg-gray-700 dark:text-white"> |
|
|
<option value="text-sm">Pequeno</option> |
|
|
<option value="text-base" selected>Normal</option> |
|
|
<option value="text-lg">Grande</option> |
|
|
</select> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="resultado" class="space-y-4"></div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<footer class="bg-white dark:bg-gray-800 py-4 shadow-inner"> |
|
|
<div class="container mx-auto px-4 text-center text-gray-600 dark:text-gray-300 text-sm"> |
|
|
<p>© 2023 Conversor SRT Avançado | Desenvolvido por <a href="#" class="text-blue-600 dark:text-blue-400 hover:underline">Renatim Dark</a></p> |
|
|
<div class="flex justify-center space-x-4 mt-2"> |
|
|
<a href="#" class="hover:text-blue-600 dark:hover:text-blue-400"><i class="fab fa-github"></i></a> |
|
|
<a href="#" class="hover:text-blue-600 dark:hover:text-blue-400"><i class="fab fa-twitter"></i></a> |
|
|
<a href="#" class="hover:text-blue-600 dark:hover:text-blue-400"><i class="fab fa-linkedin"></i></a> |
|
|
</div> |
|
|
</div> |
|
|
</footer> |
|
|
|
|
|
<script> |
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
|
|
const textoInput = document.getElementById('textoInput'); |
|
|
const convertBtn = document.getElementById('convertBtn'); |
|
|
const resultado = document.getElementById('resultado'); |
|
|
const downloadBtn = document.getElementById('downloadBtn'); |
|
|
const clearInputBtn = document.getElementById('clearInputBtn'); |
|
|
const sampleBtn = document.getElementById('sampleBtn'); |
|
|
const copyAllBtn = document.getElementById('copyAllBtn'); |
|
|
const resultsSection = document.getElementById('resultsSection'); |
|
|
const charCount = document.getElementById('charCount'); |
|
|
const wordCount = document.getElementById('wordCount'); |
|
|
const blockCount = document.getElementById('blockCount'); |
|
|
const totalDuration = document.getElementById('totalDuration'); |
|
|
const totalWords = document.getElementById('totalWords'); |
|
|
const progressFill = document.getElementById('progressFill'); |
|
|
const charsPerBlock = document.getElementById('charsPerBlock'); |
|
|
const blockDuration = document.getElementById('blockDuration'); |
|
|
const fontSize = document.getElementById('fontSize'); |
|
|
const darkModeToggle = document.getElementById('darkModeToggle'); |
|
|
const endOnPeriod = document.getElementById('endOnPeriod'); |
|
|
const hoursElement = document.getElementById('hours'); |
|
|
const minutesElement = document.getElementById('minutes'); |
|
|
const secondsElement = document.getElementById('seconds'); |
|
|
|
|
|
|
|
|
const sampleText = `Este é um exemplo de texto que será convertido para o formato SRT. O formato SRT é usado para legendas em vídeos e cada bloco contém: |
|
|
|
|
|
1. Um número sequencial |
|
|
2. O tempo de início e fim no formato HH:MM:SS,mmm |
|
|
3. O texto da legenda |
|
|
4. Uma linha em branco para separar os blocos |
|
|
|
|
|
Você pode editar este texto ou apagá-lo para inserir o seu próprio conteúdo. O conversor irá dividir automaticamente o texto em blocos de acordo com as configurações de caracteres por bloco e duração. Cada bloco será terminado no ponto final mais próximo do limite de caracteres, garantindo que as frases não sejam cortadas no meio.`; |
|
|
|
|
|
|
|
|
updateCounts(); |
|
|
updateClock(); |
|
|
setInterval(updateClock, 1000); |
|
|
checkDarkModePreference(); |
|
|
|
|
|
|
|
|
textoInput.addEventListener('input', updateCounts); |
|
|
sampleBtn.addEventListener('click', insertSampleText); |
|
|
clearInputBtn.addEventListener('click', clearInput); |
|
|
convertBtn.addEventListener('click', convertToSRT); |
|
|
downloadBtn.addEventListener('click', downloadSRT); |
|
|
copyAllBtn.addEventListener('click', copyAllSRT); |
|
|
fontSize.addEventListener('change', changeFontSize); |
|
|
darkModeToggle.addEventListener('click', toggleDarkMode); |
|
|
|
|
|
|
|
|
function updateClock() { |
|
|
const now = new Date(); |
|
|
hoursElement.textContent = now.getHours().toString().padStart(2, '0'); |
|
|
minutesElement.textContent = now.getMinutes().toString().padStart(2, '0'); |
|
|
secondsElement.textContent = now.getSeconds().toString().padStart(2, '0'); |
|
|
} |
|
|
|
|
|
function checkDarkModePreference() { |
|
|
if (localStorage.getItem('darkMode') === 'enabled' || |
|
|
(!localStorage.getItem('darkMode') && window.matchMedia('(prefers-color-scheme: dark)').matches)) { |
|
|
document.body.classList.add('dark'); |
|
|
} |
|
|
} |
|
|
|
|
|
function toggleDarkMode() { |
|
|
document.body.classList.toggle('dark'); |
|
|
|
|
|
if (document.body.classList.contains('dark')) { |
|
|
localStorage.setItem('darkMode', 'enabled'); |
|
|
} else { |
|
|
localStorage.setItem('darkMode', 'disabled'); |
|
|
} |
|
|
} |
|
|
|
|
|
function countWords(text) { |
|
|
if (!text.trim()) return 0; |
|
|
return text.trim().split(/\s+/).length; |
|
|
} |
|
|
|
|
|
function updateCounts() { |
|
|
const text = textoInput.value; |
|
|
const charCountValue = text.length; |
|
|
const wordCountValue = countWords(text); |
|
|
|
|
|
charCount.textContent = charCountValue.toLocaleString(); |
|
|
wordCount.textContent = wordCountValue.toLocaleString(); |
|
|
|
|
|
|
|
|
const maxChars = 1000000; |
|
|
const percentage = Math.min((charCountValue / maxChars) * 100, 100); |
|
|
progressFill.style.width = `${percentage}%`; |
|
|
|
|
|
|
|
|
if (percentage > 90) { |
|
|
progressFill.style.backgroundColor = '#ef4444'; |
|
|
} else if (percentage > 70) { |
|
|
progressFill.style.backgroundColor = '#f59e0b'; |
|
|
} else { |
|
|
progressFill.style.backgroundColor = '#3b82f6'; |
|
|
} |
|
|
} |
|
|
|
|
|
function insertSampleText() { |
|
|
textoInput.value = sampleText; |
|
|
updateCounts(); |
|
|
animateButton(sampleBtn); |
|
|
} |
|
|
|
|
|
function clearInput() { |
|
|
textoInput.value = ''; |
|
|
updateCounts(); |
|
|
resultsSection.classList.add('hidden'); |
|
|
animateButton(clearInputBtn); |
|
|
} |
|
|
|
|
|
function formatTime(seconds) { |
|
|
const date = new Date(seconds * 1000); |
|
|
const hours = date.getUTCHours().toString().padStart(2, '0'); |
|
|
const minutes = date.getUTCMinutes().toString().padStart(2, '0'); |
|
|
const secs = date.getUTCSeconds().toString().padStart(2, '0'); |
|
|
const ms = date.getUTCMilliseconds().toString().padStart(3, '0').substring(0, 3); |
|
|
return `${hours}:${minutes}:${secs},${ms}`; |
|
|
} |
|
|
|
|
|
function formatBlockSRT(counter, start, text) { |
|
|
return `${counter}\n${formatTime(start)} --> ${formatTime(start + parseInt(blockDuration.value))}\n${text.trim()}\n\n`; |
|
|
} |
|
|
|
|
|
function convertToSRT() { |
|
|
const text = textoInput.value.trim(); |
|
|
if (!text) { |
|
|
showAlert('Por favor, insira algum texto para converter.', 'error'); |
|
|
return; |
|
|
} |
|
|
|
|
|
animateButton(convertBtn); |
|
|
|
|
|
|
|
|
convertBtn.disabled = true; |
|
|
convertBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Processando...'; |
|
|
|
|
|
setTimeout(() => { |
|
|
try { |
|
|
const srtBlocks = []; |
|
|
let counter = 1; |
|
|
let startTime = 0; |
|
|
const intervalBetweenBlocks = 5; |
|
|
const maxChars = parseInt(charsPerBlock.value) || 490; |
|
|
const duration = parseInt(blockDuration.value) || 50; |
|
|
const shouldEndOnPeriod = endOnPeriod.checked; |
|
|
|
|
|
if (shouldEndOnPeriod) { |
|
|
|
|
|
const sentences = splitIntoSentences(text); |
|
|
|
|
|
|
|
|
let currentBlock = ''; |
|
|
let currentBlockLength = 0; |
|
|
|
|
|
for (const sentence of sentences) { |
|
|
if (currentBlockLength + sentence.length <= maxChars) { |
|
|
currentBlock += sentence + ' '; |
|
|
currentBlockLength += sentence.length + 1; |
|
|
} else { |
|
|
if (currentBlock.trim()) { |
|
|
srtBlocks.push({ |
|
|
counter, |
|
|
startTime, |
|
|
text: currentBlock.trim(), |
|
|
html: formatBlockSRT(counter, startTime, currentBlock.trim()) |
|
|
}); |
|
|
counter++; |
|
|
startTime += duration + intervalBetweenBlocks; |
|
|
} |
|
|
|
|
|
currentBlock = sentence + ' '; |
|
|
currentBlockLength = sentence.length + 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (currentBlock.trim()) { |
|
|
srtBlocks.push({ |
|
|
counter, |
|
|
startTime, |
|
|
text: currentBlock.trim(), |
|
|
html: formatBlockSRT(counter, startTime, currentBlock.trim()) |
|
|
}); |
|
|
} |
|
|
} else { |
|
|
|
|
|
const words = text.split(/\s+/); |
|
|
let block = ''; |
|
|
let blockLength = 0; |
|
|
|
|
|
for (const word of words) { |
|
|
if (blockLength + word.length <= maxChars) { |
|
|
block += word + ' '; |
|
|
blockLength += word.length + 1; |
|
|
} else { |
|
|
if (block.trim()) { |
|
|
srtBlocks.push({ |
|
|
counter, |
|
|
startTime, |
|
|
text: block.trim(), |
|
|
html: formatBlockSRT(counter, startTime, block.trim()) |
|
|
}); |
|
|
counter++; |
|
|
startTime += duration + intervalBetweenBlocks; |
|
|
} |
|
|
|
|
|
block = word + ' '; |
|
|
blockLength = word.length + 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (block.trim()) { |
|
|
srtBlocks.push({ |
|
|
counter, |
|
|
startTime, |
|
|
text: block.trim(), |
|
|
html: formatBlockSRT(counter, startTime, block.trim()) |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
displaySRTResults(srtBlocks, countWords(text)); |
|
|
} catch (error) { |
|
|
console.error('Error during conversion:', error); |
|
|
showAlert('Ocorreu um erro durante a conversão.', 'error'); |
|
|
} finally { |
|
|
convertBtn.disabled = false; |
|
|
convertBtn.innerHTML = '<i class="fas fa-exchange-alt mr-2"></i> Converter para SRT'; |
|
|
} |
|
|
}, 500); |
|
|
} |
|
|
|
|
|
function splitIntoSentences(text) { |
|
|
|
|
|
const sentenceRegex = /[^.!?]*[.!?](?:\s|$)/g; |
|
|
const sentences = []; |
|
|
let match; |
|
|
|
|
|
while ((match = sentenceRegex.exec(text)) !== null) { |
|
|
sentences.push(match[0].trim()); |
|
|
} |
|
|
|
|
|
|
|
|
const remainingText = text.substring(sentenceRegex.lastIndex).trim(); |
|
|
if (remainingText) { |
|
|
sentences.push(remainingText); |
|
|
} |
|
|
|
|
|
return sentences; |
|
|
} |
|
|
|
|
|
function displaySRTResults(blocks, totalWordCount) { |
|
|
resultado.innerHTML = ''; |
|
|
blockCount.textContent = blocks.length; |
|
|
totalWords.textContent = totalWordCount.toLocaleString(); |
|
|
|
|
|
|
|
|
const lastBlock = blocks[blocks.length - 1]; |
|
|
const totalSecs = lastBlock.startTime + parseInt(blockDuration.value); |
|
|
totalDuration.textContent = formatTime(totalSecs).split(',')[0]; |
|
|
|
|
|
blocks.forEach(block => { |
|
|
const blockElement = document.createElement('div'); |
|
|
blockElement.className = `srt-block bg-white dark:bg-gray-700 p-4 rounded-lg shadow-sm ${fontSize.value}`; |
|
|
blockElement.innerHTML = ` |
|
|
<div class="flex justify-between items-start mb-2"> |
|
|
<span class="font-mono text-sm text-gray-500 dark:text-gray-400">Bloco ${block.counter} | ${formatTime(block.startTime)} → ${formatTime(block.startTime + parseInt(blockDuration.value))} | ${countWords(block.text)} palavras</span> |
|
|
<button class="copy-btn px-2 py-1 bg-gray-100 dark:bg-gray-600 text-gray-600 dark:text-gray-300 rounded text-sm hover:bg-gray-200 dark:hover:bg-gray-500 tooltip" data-text="${escapeHtml(block.text)}"> |
|
|
<i class="fas fa-copy"></i> |
|
|
<span class="tooltip-text">Copiar bloco</span> |
|
|
</button> |
|
|
</div> |
|
|
<div class="whitespace-pre-wrap dark:text-gray-300">${block.text}</div> |
|
|
`; |
|
|
resultado.appendChild(blockElement); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.copy-btn').forEach(btn => { |
|
|
btn.addEventListener('click', function() { |
|
|
const text = this.getAttribute('data-text'); |
|
|
copyToClipboard(text); |
|
|
showAlert('Bloco copiado!', 'success'); |
|
|
animateButton(this); |
|
|
}); |
|
|
}); |
|
|
|
|
|
resultsSection.classList.remove('hidden'); |
|
|
window.scrollTo({ |
|
|
top: resultsSection.offsetTop - 20, |
|
|
behavior: 'smooth' |
|
|
}); |
|
|
} |
|
|
|
|
|
function downloadSRT() { |
|
|
const allBlocks = Array.from(document.querySelectorAll('.srt-block')); |
|
|
let srtContent = ''; |
|
|
|
|
|
allBlocks.forEach((block, index) => { |
|
|
const timeInfo = block.querySelector('span').textContent; |
|
|
const text = block.querySelector('div:last-child').textContent; |
|
|
srtContent += `${index + 1}\n${timeInfo.split(' | ')[1].replace(' → ', ' --> ')}\n${text}\n\n`; |
|
|
}); |
|
|
|
|
|
const blob = new Blob([srtContent], { type: 'text/plain' }); |
|
|
const url = URL.createObjectURL(blob); |
|
|
const a = document.createElement('a'); |
|
|
a.href = url; |
|
|
a.download = 'legendas.srt'; |
|
|
document.body.appendChild(a); |
|
|
a.click(); |
|
|
document.body.removeChild(a); |
|
|
URL.revokeObjectURL(url); |
|
|
|
|
|
showAlert('Arquivo SRT baixado!', 'success'); |
|
|
animateButton(downloadBtn); |
|
|
} |
|
|
|
|
|
function copyAllSRT() { |
|
|
const allBlocks = Array.from(document.querySelectorAll('.srt-block')); |
|
|
let srtContent = ''; |
|
|
|
|
|
allBlocks.forEach((block, index) => { |
|
|
const timeInfo = block.querySelector('span').textContent; |
|
|
const text = block.querySelector('div:last-child').textContent; |
|
|
srtContent += `${index + 1}\n${timeInfo.split(' | ')[1].replace(' → ', ' --> ')}\n${text}\n\n`; |
|
|
}); |
|
|
|
|
|
copyToClipboard(srtContent); |
|
|
showAlert('Todo o conteúdo SRT copiado!', 'success'); |
|
|
animateButton(copyAllBtn); |
|
|
} |
|
|
|
|
|
function changeFontSize() { |
|
|
const blocks = document.querySelectorAll('.srt-block'); |
|
|
blocks.forEach(block => { |
|
|
block.classList.remove('text-sm', 'text-base', 'text-lg'); |
|
|
block.classList.add(fontSize.value); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function escapeHtml(unsafe) { |
|
|
return unsafe |
|
|
.replace(/&/g, "&") |
|
|
.replace(/</g, "<") |
|
|
.replace(/>/g, ">") |
|
|
.replace(/"/g, """) |
|
|
.replace(/'/g, "'"); |
|
|
} |
|
|
|
|
|
function copyToClipboard(text) { |
|
|
const textarea = document.createElement('textarea'); |
|
|
textarea.value = text; |
|
|
document.body.appendChild(textarea); |
|
|
textarea.select(); |
|
|
document.execCommand('copy'); |
|
|
document.body.removeChild(textarea); |
|
|
} |
|
|
|
|
|
function showAlert(message, type) { |
|
|
const alert = document.createElement('div'); |
|
|
alert.className = `fixed top-4 right-4 px-4 py-2 rounded-md shadow-lg text-white ${ |
|
|
type === 'error' ? 'bg-red-500' : 'bg-green-500' |
|
|
} animate-bounce`; |
|
|
alert.textContent = message; |
|
|
document.body.appendChild(alert); |
|
|
|
|
|
setTimeout(() => { |
|
|
alert.style.opacity = '0'; |
|
|
setTimeout(() => { |
|
|
document.body.removeChild(alert); |
|
|
}, 300); |
|
|
}, 3000); |
|
|
} |
|
|
|
|
|
function animateButton(button) { |
|
|
button.classList.add('animate-pulse'); |
|
|
setTimeout(() => { |
|
|
button.classList.remove('animate-pulse'); |
|
|
}, 300); |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=hiojo/srt" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |