Spaces:
Sleeping
Sleeping
| const $ = (id)=>document.getElementById(id); | |
| async function api(path, opts){ | |
| const res = await fetch(path, opts); | |
| if(!res.ok){ | |
| const t = await res.text(); | |
| throw new Error(`${res.status} ${res.statusText}: ${t}`); | |
| } | |
| return res; | |
| } | |
| async function refresh(){ | |
| const res = await api('/api/templates'); | |
| const items = await res.json(); | |
| const list = $('list'); | |
| list.innerHTML = ''; | |
| for(const t of items){ | |
| const li = document.createElement('li'); | |
| const name = document.createElement('span'); | |
| name.textContent = `${t.id} — ${t.name ?? ''}`; | |
| const loadBtn = document.createElement('button'); | |
| loadBtn.textContent = 'Load'; | |
| loadBtn.onclick = ()=>loadTemplate(t.id); | |
| li.appendChild(name); | |
| li.appendChild(loadBtn); | |
| list.appendChild(li); | |
| } | |
| } | |
| async function loadTemplate(id){ | |
| const res = await api(`/api/templates/${encodeURIComponent(id)}`); | |
| const tpl = await res.json(); | |
| $('tplId').value = tpl.id; | |
| $('json').value = JSON.stringify(tpl, null, 2); | |
| } | |
| function newTemplate(){ | |
| const id = `tpl_${Math.random().toString(16).slice(2,8)}`; | |
| $('tplId').value = id; | |
| $('json').value = JSON.stringify({ | |
| id, | |
| name: "My Template", | |
| width: 1280, | |
| height: 720, | |
| fps: 30, | |
| duration_sec: 5, | |
| bg_color: "#111827", | |
| text: { value: "{name}", x: 80, y: 600, fontsize: 48 } | |
| }, null, 2); | |
| } | |
| async function saveTemplate(){ | |
| const id = $('tplId').value.trim(); | |
| if(!id) return alert('Template id required'); | |
| let body; | |
| try{ body = JSON.parse($('json').value); } | |
| catch(e){ return alert('Invalid JSON'); } | |
| body.id = id; | |
| await api(`/api/templates/${encodeURIComponent(id)}`, { | |
| method:'POST', | |
| headers:{'Content-Type':'application/json'}, | |
| body: JSON.stringify(body) | |
| }); | |
| await refresh(); | |
| } | |
| async function render(){ | |
| const tplId = $('tplId').value.trim(); | |
| if(!tplId) return alert('Load or create a template first'); | |
| const name = $('name').value || 'Friend'; | |
| $('renderStatus').textContent = 'Queued…'; | |
| const res = await api('/api/render', { | |
| method:'POST', | |
| headers:{'Content-Type':'application/json'}, | |
| body: JSON.stringify({ template_id: tplId, variables: { name } }) | |
| }); | |
| const job = await res.json(); | |
| poll(job.job_id); | |
| } | |
| async function poll(jobId){ | |
| while(true){ | |
| const res = await api(`/api/jobs/${encodeURIComponent(jobId)}`); | |
| const job = await res.json(); | |
| $('renderStatus').textContent = `${job.status}${job.detail?` — ${job.detail}`:''}`; | |
| if(job.status === 'done'){ | |
| const url = `/api/jobs/${encodeURIComponent(jobId)}/download`; | |
| $('player').src = url; | |
| break; | |
| } | |
| if(job.status === 'error') break; | |
| await new Promise(r=>setTimeout(r, 1200)); | |
| } | |
| } | |
| $('refresh').onclick = refresh; | |
| $('new').onclick = ()=>{ newTemplate(); }; | |
| $('save').onclick = saveTemplate; | |
| $('render').onclick = render; | |
| refresh().catch(e=>$('renderStatus').textContent = e.message); | |