Spaces:
Sleeping
Sleeping
| // تعامل با سرور FastAPI | |
| async function startUpload() { | |
| console.log("Upload started..."); // دیباگ | |
| const f = document.getElementById('fileIn').files[0]; | |
| if(!f) return; | |
| // اطمینان از اینکه دیتابیس آماده است | |
| if (!db) { | |
| try { | |
| console.log("Waiting for DB..."); | |
| await initDB(); | |
| } catch(e) { | |
| alert("خطا در بارگذاری دیتابیس. لطفا صفحه را رفرش کنید."); | |
| return; | |
| } | |
| } | |
| document.getElementById('homeScreen').style.display = 'none'; | |
| document.getElementById('loader').style.display = 'flex'; | |
| document.getElementById('queueStatusMsg').innerText = "در حال آپلود و پردازش اولیه..."; | |
| const txCheck = db.transaction("projects", "readwrite"); | |
| const storeCheck = txCheck.objectStore("projects"); | |
| const reqAll = storeCheck.getAll(); | |
| reqAll.onsuccess = async (e) => { | |
| let projects = e.target.result; | |
| projects.sort((a, b) => a.id - b.id); | |
| if (projects.length >= 4) { | |
| const toDelete = projects[0]; | |
| storeCheck.delete(toDelete.id); | |
| } | |
| const defaultName = `پروژه ${projects.length >= 4 ? 4 : projects.length + 1}`; | |
| const fd = new FormData(); fd.append('file', f); | |
| try { | |
| const r = await fetch('/api/upload', {method:'POST', body:fd}); | |
| if (!r.ok) throw new Error("Server Error: " + r.status); | |
| const d = await r.json(); | |
| state.id = d.file_id; state.w = d.width; state.h = d.height; | |
| state.st = { f: 'vazir', fz: 65, col: '#A020F0', bg: '#000000', type: 'solid', y: 150, x: 0, name: 'karaoke_static' }; | |
| let rawSegs = d.segments.map(s => ({...s, isHidden: false})); | |
| rawSegs.sort((a, b) => a.start - b.start); | |
| for(let i = 0; i < rawSegs.length - 1; i++) { | |
| const curr = rawSegs[i]; const next = rawSegs[i+1]; | |
| if (curr.end > next.start) { curr.end = next.start; | |
| if (curr.words) { | |
| curr.words = curr.words.filter(w => w.start < curr.end); | |
| if(curr.words.length > 0) { | |
| let lastW = curr.words[curr.words.length - 1]; | |
| if(lastW.end > curr.end) lastW.end = curr.end; | |
| } | |
| } | |
| } | |
| } | |
| state.segs = rawSegs; | |
| const durSec = rawSegs.length > 0 ? rawSegs[rawSegs.length-1].end : 0; | |
| let mm = Math.floor(durSec / 60); | |
| let ss = Math.floor(durSec % 60); | |
| const durStr = `${mm < 10 ? '0'+mm : mm}:${ss < 10 ? '0'+ss : ss}`; | |
| const txSave = db.transaction("projects", "readwrite"); | |
| const newProject = { | |
| id: Date.now(), name: defaultName, dateStr: getPersianDate(), | |
| lastModified: Date.now(), videoBlob: f, state: JSON.parse(JSON.stringify(state)), | |
| duration: durStr, thumbnail: null | |
| }; | |
| txSave.objectStore("projects").add(newProject); | |
| txSave.oncomplete = () => { openProject(newProject.id); }; | |
| } catch(e) { | |
| console.error(e); | |
| alert("خطا در آپلود یا پردازش: " + e); loadHome(); | |
| } finally { | |
| document.getElementById('loader').style.display='none'; | |
| document.getElementById('fileIn').value = ''; | |
| document.getElementById('queueStatusMsg').innerText = ""; | |
| } | |
| }; | |
| } | |
| async function generateAIStyle() { | |
| const desc = document.getElementById('magicPrompt').value; | |
| if(!desc) return; | |
| const btn = document.querySelector('.btn-magic-action'); | |
| const originalText = btn.innerHTML; | |
| btn.innerHTML = '<div class="spinner" style="width:20px;height:20px;border-width:3px;"></div> در حال طراحی...'; | |
| btn.disabled = true; | |
| try { | |
| const r = await fetch('/api/generate-style', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({description: desc}) | |
| }); | |
| const styleData = await r.json(); | |
| state.st.col = styleData.primaryColor; | |
| state.st.bg = styleData.outlineColor; | |
| state.st.f = styleData.font; | |
| state.st.fz = styleData.fontSize; | |
| document.getElementById('col').value = state.st.col; | |
| document.getElementById('bgCol').value = state.st.bg; | |
| document.getElementById('fz').value = state.st.fz; | |
| activateCustomStyle(); | |
| setMode(styleData.backType); | |
| upd(); | |
| toolsContainer.classList.remove('open'); | |
| alert('طراحی اعمال شد!'); | |
| } catch(e) { alert('خطا در هوش مصنوعی'); } finally { btn.innerHTML = originalText; btn.disabled = false; } | |
| } | |
| async function render() { | |
| v.pause(); togglePlayIcon(false); | |
| document.getElementById('loader').style.display='flex'; | |
| const statusMsg = document.getElementById('queueStatusMsg'); | |
| statusMsg.innerText = "در حال ارسال به صف..."; | |
| const activeSegments = state.segs.filter(s => !s.isHidden); | |
| const pl = { file_id: state.id, segments: activeSegments, video_width: state.w, video_height: state.h, style: { font: state.st.f, fontSize: state.st.fz, primaryColor: state.st.col, outlineColor: state.st.bg, backType: state.st.type, marginV: state.st.y, x: state.st.x, name: state.st.name } }; | |
| try { | |
| let r = await fetch('/api/enqueue-render', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(pl)}); | |
| let d = await r.json(); | |
| if (d.error_code && d.error_code === 'VIDEO_NOT_FOUND') { | |
| statusMsg.innerText = "ویدیو در سرور یافت نشد، در حال بارگذاری مجدد..."; | |
| const videoBlob = await getVideoBlobFromDB(currentProjectId); | |
| const fd = new FormData(); fd.append('file', videoBlob); fd.append('file_id', state.id); | |
| const reuploadResponse = await fetch('/api/reupload', { method: 'POST', body: fd }); | |
| if (!reuploadResponse.ok) throw new Error('خطا در بارگذاری مجدد ویدیو.'); | |
| statusMsg.innerText = "بارگذاری مجدد موفق بود، ارسال دوباره به صف..."; | |
| r = await fetch('/api/enqueue-render', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(pl)}); | |
| d = await r.json(); | |
| } | |
| if (d.job_id) { pollStatus(d.job_id); } | |
| else { throw new Error(d.error || "خطا لطفاً این پروژه را حذف و ویدیو را مجدداً آپلود کنید."); } | |
| } catch(e) { | |
| alert('خطا: ' + e.message); document.getElementById('loader').style.display='none'; | |
| } | |
| } | |
| function pollStatus(jobId) { | |
| const statusMsg = document.getElementById('queueStatusMsg'); | |
| const interval = setInterval(async () => { | |
| try { | |
| const r = await fetch(`/api/job-status/${jobId}`); | |
| const d = await r.json(); | |
| if (d.status === 'queued') statusMsg.innerHTML = `شما نفر <span style="color:#00e676; font-size:1.3em;">${d.queue_position}</span> در صف ساخت هستید...`; | |
| else if (d.status === 'processing') statusMsg.innerText = "نوبت شماست! در حال ساخت ویدیو..."; | |
| else if (d.status === 'completed') { clearInterval(interval); showFinalResult(d.url); } | |
| else if (d.status === 'failed') { clearInterval(interval); alert("خطا در ساخت ویدیو: " + d.error); document.getElementById('loader').style.display='none'; } | |
| } catch (e) { console.error("خطا در چک کردن وضعیت", e); } | |
| }, 2500); | |
| } | |
| function showFinalResult(url) { | |
| document.getElementById('loader').style.display='none'; | |
| const resVid = document.getElementById('resultVideo'); | |
| resVid.src = url + "?t=" + new Date().getTime(); | |
| resVid.load(); | |
| const fullUrl = new URL(url, window.location.origin).href; | |
| const dlBtn = document.getElementById('downloadBtn'); | |
| dlBtn.href = fullUrl; | |
| dlBtn.onclick = function(e) { | |
| e.preventDefault(); | |
| window.parent.postMessage({ type: 'DOWNLOAD_REQUEST', url: fullUrl }, '*'); | |
| }; | |
| document.getElementById('resultScreen').style.display='flex'; | |
| resVid.play(); | |
| } | |
| function closeResult() { document.getElementById('resultScreen').style.display='none'; const rv = document.getElementById('resultVideo'); rv.pause(); rv.src = ""; } |