NHLOCAL's picture
הודעת פרסום אלף בוט
033dc1e
document.addEventListener('DOMContentLoaded', () => {
// --- Beta Banner Logic ---
const betaBanner = document.getElementById('beta-banner');
const closeBetaBannerBtn = document.getElementById('close-beta-banner');
if (betaBanner && closeBetaBannerBtn) {
if (localStorage.getItem('betaBannerDismissed') !== 'true') {
betaBanner.style.display = 'block';
}
closeBetaBannerBtn.addEventListener('click', () => {
betaBanner.style.display = 'none';
localStorage.setItem('betaBannerDismissed', 'true');
});
}
// --- End Beta Banner Logic ---
const form = document.getElementById('transcribe-form');
const apiKeyInput = document.getElementById('api-key-input');
const audioFileInput = document.getElementById('audio-file-input');
const audioDropZone = document.getElementById('audio-drop-zone');
const audioFileNameEl = document.getElementById('audio-file-name');
const modelSelect = document.getElementById('model-select');
const modelCustomInput = document.getElementById('model-custom-input');
const formatToggleButtons = document.querySelectorAll('.format-toggle-btn');
const submitButton = document.getElementById('submit-button');
const statusContainer = document.getElementById('status-container');
const statusMessage = document.getElementById('status-message');
const progressBar = document.getElementById('progress-bar');
const resultsSection = document.getElementById('results-section');
const downloadSrtButton = document.getElementById('download-srt-button');
const downloadTxtButton = document.getElementById('download-txt-button');
const copyButton = document.getElementById('copy-button');
const resultsOutput = document.getElementById('results-output');
const resultsViewToggle = document.getElementById('results-view-toggle');
const viewSrtBtn = document.getElementById('view-srt-btn');
const viewTxtBtn = document.getElementById('view-txt-btn');
const checkScrollbar = () => {
if (resultsOutput.scrollHeight > resultsOutput.clientHeight) {
copyButton.classList.add('has-scrollbar');
} else {
copyButton.classList.remove('has-scrollbar');
}
};
const scrollObserver = new ResizeObserver(checkScrollbar);
scrollObserver.observe(resultsOutput);
let audioFile = null;
let srtContent = '';
let textContent = '';
let currentMode = 'plain_text';
function checkInputs() {
submitButton.disabled = !(apiKeyInput.value.trim() && audioFile);
}
function updateStatus(message, percent, isError = false) {
statusContainer.style.display = 'block';
statusMessage.textContent = message;
progressBar.style.width = `${percent}%`;
progressBar.classList.toggle('error', isError);
statusMessage.className = isError ? 'error' : 'loading';
}
function setFormEnabled(enabled) {
submitButton.disabled = !enabled;
audioFileInput.disabled = !enabled;
if (enabled) {
audioDropZone.classList.remove('disabled');
checkInputs();
} else {
audioDropZone.classList.add('disabled');
}
}
function updateResultsView(mode) {
resultsViewToggle.style.display = 'flex';
if (mode === 'srt') {
downloadSrtButton.classList.add('btn-primary');
downloadSrtButton.classList.remove('btn-secondary');
downloadTxtButton.classList.add('btn-secondary');
downloadTxtButton.classList.remove('btn-primary');
} else { // plain_text
downloadTxtButton.classList.add('btn-primary');
downloadTxtButton.classList.remove('btn-secondary');
downloadSrtButton.classList.add('btn-secondary');
downloadSrtButton.classList.remove('btn-primary');
}
}
function resetUI() {
setFormEnabled(true);
statusContainer.style.display = 'none';
resultsSection.style.display = 'none';
resultsViewToggle.style.display = 'none';
updateStatus("", 0);
}
function loadApiKey() {
const savedKey = localStorage.getItem('geminiApiKey');
if (savedKey) { apiKeyInput.value = savedKey; checkInputs(); }
}
function saveApiKey() {
localStorage.setItem('geminiApiKey', apiKeyInput.value);
}
loadApiKey();
apiKeyInput.addEventListener('input', () => { saveApiKey(); checkInputs(); });
audioFileInput.addEventListener('change', (e) => {
audioFile = e.target.files[0];
audioFileNameEl.textContent = audioFile ? `קובץ: ${audioFile.name}` : '';
checkInputs();
});
modelSelect.addEventListener('change', () => {
modelCustomInput.style.display = (modelSelect.value === 'custom') ? 'block' : 'none';
});
formatToggleButtons.forEach(button => {
button.addEventListener('click', () => {
formatToggleButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
currentMode = button.dataset.format;
});
});
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
audioDropZone.addEventListener(eventName, e => { e.preventDefault(); e.stopPropagation(); });
});
audioDropZone.addEventListener('dragover', () => { if (!audioFileInput.disabled) audioDropZone.classList.add('drag-over') });
audioDropZone.addEventListener('dragleave', () => audioDropZone.classList.remove('drag-over'));
audioDropZone.addEventListener('drop', e => {
if (audioFileInput.disabled) return;
audioDropZone.classList.remove('drag-over');
const droppedFile = e.dataTransfer.files[0];
if (droppedFile && (droppedFile.type.startsWith('audio/') || droppedFile.type.startsWith('video/'))) {
audioFile = droppedFile;
audioFileInput.files = e.dataTransfer.files;
audioFileNameEl.textContent = `קובץ: ${audioFile.name}`;
} else {
audioFile = null;
audioFileNameEl.textContent = 'יש לבחור קובץ שמע או וידאו';
}
checkInputs();
});
copyButton.addEventListener('click', () => {
if (!resultsOutput.value) return;
navigator.clipboard.writeText(resultsOutput.value).then(() => {
const originalText = copyButton.innerHTML;
copyButton.innerHTML = `<span class="material-symbols-outlined">check</span>`;
setTimeout(() => {
copyButton.innerHTML = originalText;
}, 2000);
}).catch(err => {
console.error('Failed to copy text: ', err);
alert('ההעתקה נכשלה');
});
});
function downloadContent(content, extension) {
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const originalFileName = audioFile ? audioFile.name.split('.').slice(0, -1).join('.') : 'download';
const a = document.createElement('a');
a.href = url;
a.download = `${originalFileName}_transcript.${extension}`;
document.body.appendChild(a); a.click(); document.body.removeChild(a);
URL.revokeObjectURL(url);
}
downloadSrtButton.addEventListener('click', async () => {
if (currentMode === 'srt' || srtContent) {
if (!srtContent) { alert('אין תוכן להורדה.'); return; }
downloadContent(srtContent, 'srt');
} else {
if (!textContent) { alert('אין תוכן תמלול להמרה.'); return; }
try {
const response = await fetch('/convert-to-srt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text_data: textContent })
});
if (!response.ok) throw new Error((await response.json()).detail || 'שגיאה בהמרת הקובץ');
const data = await response.json();
if (data.srt) {
downloadContent(data.srt, 'srt');
} else {
throw new Error('השרת לא החזיר תוכן SRT תקני.');
}
} catch (error) {
alert(`אירעה שגיאה בהמרת הקובץ: ${error.message}`);
}
}
});
downloadTxtButton.addEventListener('click', async () => {
if (currentMode === 'plain_text' || textContent) {
if (!textContent) { alert('אין תוכן להורדה.'); return; }
downloadContent(textContent.replace(/<!-- DURATION_MS:\d+ -->\s*$/, '').trim(), 'txt');
} else {
if (!srtContent) { alert('אין תוכן תמלול להמרה.'); return; }
try {
const response = await fetch('/convert-to-text', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ srt_data: srtContent })
});
if (!response.ok) throw new Error((await response.json()).detail || 'שגיאה בהמרת הקובץ');
const data = await response.json();
downloadContent(data.text, 'txt');
} catch (error) { alert(`אירעה שגיאה בהמרת הקובץ: ${error.message}`); }
}
});
viewSrtBtn.addEventListener('click', async () => {
if (viewSrtBtn.classList.contains('active')) return;
viewSrtBtn.classList.add('active'); viewTxtBtn.classList.remove('active');
if (srtContent) {
resultsOutput.value = srtContent;
checkScrollbar();
return;
}
if (!textContent) {
resultsOutput.value = '';
checkScrollbar();
return;
}
resultsOutput.value = 'ממיר לתצוגת כתוביות...';
checkScrollbar();
try {
const response = await fetch('/convert-to-srt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text_data: textContent })
});
if (!response.ok) throw new Error((await response.json()).detail || 'שגיאה בהמרת הקובץ');
const data = await response.json();
srtContent = data.srt;
resultsOutput.value = srtContent;
} catch (error) {
resultsOutput.value = `אירעה שגיאה בהמרת התצוגה: ${error.message}`;
}
checkScrollbar();
});
viewTxtBtn.addEventListener('click', async () => {
if (viewTxtBtn.classList.contains('active')) return;
viewTxtBtn.classList.add('active'); viewSrtBtn.classList.remove('active');
if (textContent) {
resultsOutput.value = textContent.replace(/<!-- DURATION_MS:\d+ -->\s*$/, '').trim();
checkScrollbar();
return;
}
if (!srtContent) {
resultsOutput.value = '';
checkScrollbar();
return;
}
resultsOutput.value = 'ממיר לתצוגת טקסט...';
checkScrollbar();
try {
const response = await fetch('/convert-to-text', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ srt_data: srtContent })
});
if (!response.ok) throw new Error((await response.json()).detail || 'שגיאה בהמרת הקובץ');
const data = await response.json();
textContent = data.text;
resultsOutput.value = textContent;
} catch (error) {
resultsOutput.value = `אירעה שגיאה בהמרת התצוגה: ${error.message}`;
}
checkScrollbar();
});
const userPromptInput = document.getElementById('user-prompt-input');
function autoGrowTextarea() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
}
userPromptInput.addEventListener('input', autoGrowTextarea, false);
form.addEventListener('submit', async (e) => {
e.preventDefault();
if (submitButton.disabled) return;
resultsSection.style.display = 'none';
setFormEnabled(false);
updateStatus('מתחיל את התהליך...', 0);
let modelName = modelSelect.value === 'custom' ? modelCustomInput.value.trim() : modelSelect.value;
if (modelSelect.value === 'custom' && !modelName) {
updateStatus('יש להזין שם מודל מותאם אישית', 100, true);
setFormEnabled(true);
return;
}
const formData = new FormData();
formData.append('api_key', apiKeyInput.value);
formData.append('model_name', modelName);
formData.append('output_format', currentMode);
formData.append('user_prompt', document.getElementById('user-prompt-input').value);
formData.append('audio_file', audioFile);
try {
const response = await fetch('/transcribe-stream', { method: 'POST', body: formData });
if (!response.ok) throw new Error((await response.json()).detail || `שגיאת שרת: ${response.status}`);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const events = buffer.split('\n\n');
buffer = events.pop();
for (const eventStr of events) {
if (!eventStr.trim()) continue;
const event = JSON.parse(eventStr);
if (event.type === 'progress') {
updateStatus(event.message, event.percent);
} else if (event.type === 'result') {
updateStatus(event.message, event.percent);
resultsSection.style.display = 'block';
statusContainer.style.display = 'none';
if (currentMode === 'srt') {
srtContent = event.data;
textContent = '';
resultsOutput.value = srtContent;
updateResultsView('srt');
viewSrtBtn.classList.add('active');
viewTxtBtn.classList.remove('active');
} else {
textContent = event.data;
srtContent = '';
const displayContent = textContent.replace(/<!-- DURATION_MS:\d+ -->\s*$/, '').trim();
resultsOutput.value = displayContent;
updateResultsView('plain_text');
viewTxtBtn.classList.add('active');
viewSrtBtn.classList.remove('active');
}
checkScrollbar();
} else if (event.type === 'error') {
throw new Error(event.message);
}
}
}
setFormEnabled(true);
} catch (error) {
console.error('Transcription error:', error);
updateStatus(`אירעה שגיאה: ${error.message}`, 100, true);
setFormEnabled(true);
}
});
});