// منطق اصلی ادیتور (نمایش ویدیو، سینک متن، ابزارها) function openProject(projectId) { currentProjectId = projectId; const tx = db.transaction("projects", "readonly"); const store = tx.objectStore("projects"); const req = store.get(projectId); req.onsuccess = (e) => { const p = e.target.result; if (!p) { alert('پروژه یافت نشد'); loadHome(); return; } state = p.state; document.getElementById('currentProjectTitle').innerText = p.name; const videoURL = URL.createObjectURL(p.videoBlob); v.src = videoURL; document.getElementById('col').value = state.st.col; document.getElementById('bgCol').value = state.st.bg; document.getElementById('fz').value = state.st.fz; document.getElementById('pos').value = state.st.y; document.querySelectorAll('.font-btn').forEach(btn => btn.classList.remove('ticked')); if(state.st.f === 'vazir') document.querySelectorAll('.font-btn')[1].classList.add('ticked'); else if(state.st.f === 'lalezar') document.querySelectorAll('.font-btn')[0].classList.add('ticked'); document.querySelectorAll('.style-card').forEach(c => c.classList.remove('selected')); if(state.st.name === 'karaoke_static') { const staticCard = Array.from(document.querySelectorAll('.style-card')).find(c => c.textContent.includes("هوشمند (ثابت)")); if(staticCard) staticCard.classList.add('selected'); document.getElementById('staticColorPicker').value = state.st.col; } document.getElementById('homeScreen').style.display = 'none'; document.getElementById('editorScreen').style.display = 'flex'; renderSegList(); fit(); upd(); v.onloadeddata = () => { fit(); if(!p.thumbnail && v.duration > 1) { v.currentTime = 1.0; let captured = false; v.addEventListener('seeked', function cap() { if(captured) return; captured = true; saveProjectToDB(); v.currentTime = 0; }, {once:true}); } else if (!p.thumbnail) { saveProjectToDB(); } }; }; } function goHome() { v.pause(); saveProjectToDB(); loadHome(); } function fit() { if(!state.w) return; const ws = document.getElementById('workspace'); const availableHeight = window.innerHeight * 0.6; const scale = Math.min((ws.clientWidth - 40) / state.w, availableHeight / state.h); const c = document.getElementById('videoContainer'); c.style.width = state.w + 'px'; c.style.height = state.h + 'px'; document.getElementById('scaler').style.transform = `scale(${scale})`; ws.style.height = (state.h * scale + 40) + 'px'; } function activateCustomStyle() { if(['karaoke_static', 'auto_director', 'plain_white', 'white_outline'].includes(state.st.name)) { state.st.name = 'classic'; document.querySelectorAll('.style-card').forEach(c => c.classList.remove('selected')); const classicCard = document.querySelector('.custom-style-container .style-card:first-child'); if(classicCard) classicCard.classList.add('selected'); } } function renderSegList() { saveProjectToDB(); const timeline = document.getElementById('timelineScroll'); const spacers = timeline.querySelectorAll('.spacer'); timeline.innerHTML = ''; timeline.appendChild(spacers[0]); // Start spacer state.segs.forEach((seg, sIdx) => { if (!seg.words || seg.words.length === 0) { const wordsArr = seg.text.trim().split(/\s+/).filter(w => w.length > 0); const duration = seg.end - seg.start; const timePerWord = duration / Math.max(1, wordsArr.length); let wStart = seg.start; seg.words = wordsArr.map((wStr, i) => { let wEnd = wStart + timePerWord; if (i === wordsArr.length - 1) wEnd = seg.end; let obj = { word: wStr, start: parseFloat(wStart.toFixed(2)), end: parseFloat(wEnd.toFixed(2)) }; wStart = wEnd; return obj; }); } seg.words.forEach((w, wIdx) => { const el = document.createElement('div'); el.className = 'word-chip'; el.innerText = w.word; const uid = `${sIdx}-${wIdx}`; el.id = `w-${uid}`; if (activeWordId === uid) el.classList.add('active'); el.onclick = (e) => { e.stopPropagation(); highlightWord(sIdx, wIdx, true); }; timeline.appendChild(el); }); if (sIdx < state.segs.length - 1) { const nl = document.createElement('div'); nl.className = 'newline-indicator'; nl.innerHTML = ''; timeline.appendChild(nl); } }); timeline.appendChild(spacers[1]); // End spacer updateSplitButton(); } function highlightWord(sIdx, wIdx, showToolbar) { activeWordId = `${sIdx}-${wIdx}`; v.pause(); togglePlayIcon(false); const seg = state.segs[sIdx]; const word = seg.words[wIdx]; v.currentTime = word.start; manualOverride = true; updateOverlayContent(v.currentTime); document.querySelectorAll('.word-chip').forEach(c => c.classList.remove('active')); const el = document.getElementById(`w-${activeWordId}`); if(el) { el.classList.add('active'); el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' }); } if(showToolbar) document.getElementById('toolbar').classList.add('show'); updateSplitButton(); } function playPreviewWord() { if (!activeWordId) return; const [sIdx, wIdx] = activeWordId.split('-').map(Number); const word = state.segs[sIdx].words[wIdx]; if(previewInterval) clearInterval(previewInterval); v.pause(); v.currentTime = word.start; const icon = document.getElementById('btnPreviewPlay').querySelector('i'); icon.className = "fa-solid fa-pause"; v.play(); previewInterval = setInterval(() => { if(v.currentTime >= word.end) { v.pause(); clearInterval(previewInterval); icon.className = "fa-solid fa-play"; if (v.currentTime >= word.end) v.currentTime = word.end - 0.01; } }, 20); } function saveText() { if (!activeWordId) return; const [sIdx, wIdx] = activeWordId.split('-').map(Number); const val = document.getElementById('textInput').value.trim(); if (val) { state.segs[sIdx].words[wIdx].word = val; state.segs[sIdx].text = state.segs[sIdx].words.map(w => w.word).join(' '); renderSegList(); highlightWord(sIdx, wIdx, true); } closeAllSheets(); } function adjustTime(type, am) { if(type === 'start') { let v = parseFloat((tempStartTime + am).toFixed(2)); if(v < 0) v = 0; if(v >= tempEndTime) v = parseFloat((tempEndTime - 0.1).toFixed(2)); tempStartTime = v; document.getElementById('valStart').innerText = fmt(tempStartTime); } else { let v = parseFloat((tempEndTime + am).toFixed(2)); if(v <= tempStartTime) v = parseFloat((tempStartTime + 0.1).toFixed(2)); tempEndTime = v; document.getElementById('valEnd').innerText = fmt(tempEndTime); } } function confirmTimeChanges() { if (!activeWordId) return; const [sIdx, wIdx] = activeWordId.split('-').map(Number); const seg = state.segs[sIdx]; seg.words[wIdx].start = tempStartTime; seg.words[wIdx].end = tempEndTime; if (wIdx === 0 && tempStartTime < seg.start) seg.start = tempStartTime; if (wIdx === seg.words.length - 1 && tempEndTime > seg.end) seg.end = tempEndTime; closeAllSheets(); renderSegList(); highlightWord(sIdx, wIdx, true); } function cancelTimeChanges() { closeAllSheets(); } function confirmDelete() { if (!activeWordId) return; const [sIdx, wIdx] = activeWordId.split('-').map(Number); const seg = state.segs[sIdx]; seg.words.splice(wIdx, 1); if (seg.words.length === 0) { state.segs.splice(sIdx, 1); activeWordId = null; document.getElementById('toolbar').classList.remove('show'); } else { seg.text = seg.words.map(w => w.word).join(' '); activeWordId = null; document.getElementById('toolbar').classList.remove('show'); } closeAllSheets(); renderSegList(); } function toggleSplit() { if (!activeWordId) return; const [sIdx, wIdx] = activeWordId.split('-').map(Number); if (wIdx === 0) { if (sIdx > 0) { const prevSeg = state.segs[sIdx - 1]; const currSeg = state.segs[sIdx]; prevSeg.words = prevSeg.words.concat(currSeg.words); prevSeg.end = currSeg.end; prevSeg.text = prevSeg.words.map(w => w.word).join(' '); state.segs.splice(sIdx, 1); const newWIdx = prevSeg.words.length - currSeg.words.length; renderSegList(); highlightWord(sIdx - 1, newWIdx, true); } } else { const seg = state.segs[sIdx]; const wordsFirstHalf = seg.words.slice(0, wIdx); const wordsSecondHalf = seg.words.slice(wIdx); seg.words = wordsFirstHalf; seg.text = seg.words.map(w => w.word).join(' '); seg.end = wordsFirstHalf[wordsFirstHalf.length-1].end; const newSeg = { text: wordsSecondHalf.map(w => w.word).join(' '), start: wordsSecondHalf[0].start, end: wordsSecondHalf[wordsSecondHalf.length-1].end, words: wordsSecondHalf, isHidden: false }; state.segs.splice(sIdx + 1, 0, newSeg); renderSegList(); highlightWord(sIdx + 1, 0, true); } } function updateSplitButton() { const btn = document.getElementById('btnSplit'); if(!activeWordId) { btn.classList.remove('active-state'); return; } const [sIdx, wIdx] = activeWordId.split('-').map(Number); if (wIdx === 0 && sIdx > 0) { btn.classList.add('active-state'); btn.style.transform = "rotate(180deg)"; } else { btn.classList.remove('active-state'); btn.style.transform = ""; } } function togglePlay() { if(v.paused) { v.play(); togglePlayIcon(true); } else { v.pause(); togglePlayIcon(false); } } function togglePlayIcon(isPlaying) { const overlay = document.getElementById('playOverlay'); overlay.className = isPlaying ? 'playing' : ''; } function updateOverlayContent(currentTime) { const idx = state.segs.findIndex(s => currentTime >= s.start && currentTime <= s.end); if(idx !== -1) { const seg = state.segs[idx]; if(seg.isHidden) { tEl.style.opacity = 0; } else { tEl.style.opacity = 1; if(state.st.name === 'auto_director' && seg.words) { let html = ""; seg.words.forEach((w, i) => { let isActive = (currentTime >= w.start && currentTime <= w.end); let boxColor = (i % 2 === 0) ? '#00D7FF' : '#FF0080'; if(isActive) html += `${w.word} `; else html += `${w.word} `; }); tEl.innerHTML = html; } else if(state.st.name === 'karaoke_static' && seg.words) { let html = ""; seg.words.forEach(w => { let isActive = (currentTime >= w.start && currentTime <= w.end); let cls = isActive ? "word-active" : ""; let styleAttr = ""; if(isActive) { let boxColor = state.st.col; styleAttr = `style="background-color: ${boxColor} !important; color: #fff !important; box-shadow: 0 2px 8px ${boxColor};"`; } html += `${w.word} `; }); tEl.innerHTML = html; } else if (state.st.name === 'progressive_write' && seg.words) { let html = ""; seg.words.forEach(w => { if(currentTime >= w.start) html += `${w.word} `; else html += `${w.word} `; }); tEl.innerHTML = html.trim(); } else { tEl.innerText = seg.text; } } } else { tEl.style.opacity = 0; } } function upd() { saveProjectToDB(); state.st.fz = parseInt(document.getElementById('fz').value); state.st.y = parseInt(document.getElementById('pos').value); if (state.st.name !== 'karaoke_static') state.st.col = document.getElementById('col').value; state.st.bg = document.getElementById('bgCol').value; let font = 'Vazirmatn'; if(state.st.f === 'lalezar') font = 'Lalezar'; if(state.st.f === 'bangers') font = 'Impact'; if(state.st.f === 'roboto') font = 'Arial'; tEl.style.fontFamily = font; tEl.style.fontSize = state.st.fz + 'px'; tEl.style.bottom = state.st.y + 'px'; tEl.style.textAlign = 'center'; tEl.style.left = '50%'; tEl.style.transform = `translateX(calc(-50% + ${state.st.x}px))`; tEl.style.paintOrder = 'normal'; tEl.style.webkitPaintOrder = 'normal'; tEl.style.borderRadius = '0px'; if(state.st.name === 'karaoke_static' || state.st.name === 'auto_director') { tEl.style.backgroundColor = 'transparent'; tEl.style.color = '#FFFFFF'; tEl.style.webkitTextStroke = '0px'; tEl.style.textShadow = 'none'; } else if (state.st.name === 'plain_white') { tEl.style.color = '#FFFFFF'; tEl.style.backgroundColor = 'transparent'; tEl.style.webkitTextStroke = '0px'; tEl.style.textShadow = 'none'; } else if (state.st.name === 'white_outline') { tEl.style.color = '#FFFFFF'; tEl.style.backgroundColor = 'transparent'; const s = Math.max(3, state.st.fz / 4.5); tEl.style.webkitTextStroke = `${s}px #000000`; tEl.style.paintOrder = 'stroke fill'; tEl.style.webkitPaintOrder = 'stroke fill'; tEl.style.textShadow = 'none'; } else { tEl.style.color = state.st.col; if(state.st.type === 'solid') { tEl.style.backgroundColor = state.st.bg; tEl.style.webkitTextStroke = '0px'; tEl.style.textShadow = 'none'; tEl.style.borderRadius = '12px'; } else if (state.st.type === 'transparent') { let c = state.st.bg.replace('#', ''); let r = parseInt(c.substring(0, 2), 16); let g = parseInt(c.substring(2, 4), 16); let b = parseInt(c.substring(4, 6), 16); tEl.style.backgroundColor = `rgba(${r},${g},${b},0.6)`; tEl.style.webkitTextStroke = '0px'; tEl.style.textShadow = 'none'; tEl.style.borderRadius = '12px'; } else if (state.st.type === 'outline') { tEl.style.backgroundColor = 'transparent'; tEl.style.color = state.st.col; const s = Math.max(3, state.st.fz / 4.5); tEl.style.webkitTextStroke = `${s}px ${state.st.bg}`; tEl.style.paintOrder = 'stroke fill'; tEl.style.webkitPaintOrder = 'stroke fill'; tEl.style.textShadow = 'none'; } else { tEl.style.backgroundColor = 'transparent'; tEl.style.webkitTextStroke = '0px'; tEl.style.textShadow = 'none'; } } } function openStaticColorPicker(event) { event.stopPropagation(); document.getElementById('staticColorPicker').click(); } function updateStaticColor(val) { document.documentElement.style.setProperty('--static-color', val); state.st.col = val; document.getElementById('col').value = val; upd(); } function setStylePreset(name, el, skipModeSet = false) { state.st.name = name; document.querySelectorAll('.style-card').forEach(c => c.classList.remove('selected')); if(el) el.classList.add('selected'); if(name === 'karaoke_static' && !skipModeSet) { const defaultPurple = '#A020F0'; state.st.col = defaultPurple; document.documentElement.style.setProperty('--static-color', defaultPurple); document.getElementById('staticColorPicker').value = defaultPurple; document.getElementById('col').value = defaultPurple; } if(name === 'classic' && !skipModeSet) { setMode('solid'); } else if (name === 'progressive_write') { setMode('none'); } else if (name === 'plain_white' || name === 'white_outline') { state.st.col = '#FFFFFF'; state.st.bg = '#000000'; document.getElementById('col').value = '#FFFFFF'; document.getElementById('bgCol').value = '#000000'; setMode('outline'); } upd(); } function handleClassicDoubleClick(element) { event.stopPropagation(); activateCustomStyle(); setStylePreset('classic', element, true); setMode('none'); } function setFont(f, el) { document.querySelectorAll('.font-btn').forEach(btn => btn.classList.remove('ticked')); el.classList.add('ticked'); state.st.f = f; upd(); } function setMode(m) { state.st.type = m; syncModeButtons(); upd(); }