diff --git a/web-ui/html/workshop-app.html b/web-ui/html/workshop-app.html index 85963de..1590fb9 100644 --- a/web-ui/html/workshop-app.html +++ b/web-ui/html/workshop-app.html @@ -148,9 +148,12 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis .rb-equip-dot.off{background:#ef4444;} .rb-equip-refresh{margin-left:auto;background:none;border:none;cursor:pointer;font-size:12px;color:var(--muted);} .rb-equip-refresh:hover{color:#f59e0b;} -.rb-equip-frame-wrap{width:100%;aspect-ratio:16/9;background:#000;display:flex;align-items:center;justify-content:center;overflow:hidden;} +.rb-equip-frame-wrap{width:100%;aspect-ratio:16/9;background:#000;display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative;} .rb-equip-frame-wrap img{width:100%;height:100%;object-fit:contain;} .rb-equip-frame-wrap .rb-equip-placeholder{color:#666;font-size:11px;} +.rb-equip-live-btn{position:absolute;right:8px;bottom:8px;background:rgba(0,0,0,.55);color:#fff;border:1px solid rgba(255,255,255,.3);border-radius:6px;padding:4px 10px;font-size:11px;cursor:pointer;z-index:2;} +.rb-equip-live-btn:hover{background:rgba(245,158,11,.85);border-color:transparent;} +.rb-equip-live-btn.on{background:rgba(239,68,68,.8);border-color:transparent;} .rb-equip-status{padding:8px 12px;font-size:11px;color:var(--muted);line-height:1.7;} .rb-equip-status b{color:var(--text);} @@ -241,7 +244,12 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis ๐Ÿ–จ๏ธ K2 ์ฝค๋ณด -
๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...
+
+ + + ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘... + +
-
@@ -578,6 +586,7 @@ function startEquipTimers(){ function stopEquipTimers(){ clearInterval(equipTimer); equipTimer=null; + stopK2Live(); // ์žฅ๋น„ ํƒญ์„ ๋ฒ—์–ด๋‚˜๋ฉด ๋ผ์ด๋ธŒ WebRTC ์—ฐ๊ฒฐ๋„ ๋‹ซ๋Š”๋‹ค(์•ˆ ๋ณด๋Š” ๋™์•ˆ ์ž์› ์•ˆ ์“ฐ๊ธฐ) } function refreshMillWidget(){ @@ -653,6 +662,81 @@ function grabK2Frame(){ }); } +// โ”€โ”€ K2 ๋ผ์ด๋ธŒ ๋ทฐ(2026-09-24) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +// โ–ถ ๋ผ์ด๋ธŒ ๋ฒ„ํŠผ์œผ๋กœ ์นด๋“œ ์•ˆ์—์„œ ์ƒ์‹œ WebRTC ์ŠคํŠธ๋ฆฌ๋ฐ(k2-app.html์˜ ๊ฒ€์ฆ๋œ ์—ฐ๊ฒฐ ๋กœ์ง +// ์ด์‹). ๋ผ์ด๋ธŒ ์ค‘์—” 20์ดˆ ์Šค๋ƒ…์ƒท ๊ทธ๋žฉ์„ ๋ฉˆ์ถ”๊ณ (refreshK2Widget ์ฐธ์กฐ), ์žฅ๋น„ ํƒญ์„ +// ๋ฒ—์–ด๋‚˜๋ฉด stopEquipTimers๊ฐ€ ์—ฐ๊ฒฐ์„ ๋‹ซ๋Š”๋‹ค โ€” ์•ˆ ๋ณด๋Š” ๋™์•ˆ์—” ์ž์›์„ ์•ˆ ์“ด๋‹ค. +let k2LivePC=null; +function k2Note(msg){ + const ph=document.getElementById('k2-placeholder'); + const img=document.getElementById('k2-frame'); + const video=document.getElementById('k2-live'); + video.style.display='none'; + img.style.display='none'; + ph.style.display='block'; + ph.textContent=msg; +} +function toggleK2Live(){ + if(k2LivePC)stopK2Live();else startK2Live(); +} +function startK2Live(){ + if(k2LivePC)return; + const video=document.getElementById('k2-live'); + const btn=document.getElementById('k2-live-btn'); + btn.classList.add('on');btn.textContent='โน ๋ผ์ด๋ธŒ ์ข…๋ฃŒ'; + const ph=document.getElementById('k2-placeholder'); + ph.style.display='none'; + // STUN์„ ๋นผ๋ฉด(host candidate๋งŒ) ์ด ํ”„๋ฆฐํ„ฐ์˜ ICE ์‘๋‹ต์ด ์‹ค์ œ๋กœ๋Š” ์•ˆ ์˜ด(DTLS failed, + // bytesReceived 0) โ€” k2-app.html์—์„œ 2026-09-18 ํ™•์ธ๋œ ํ€ดํฌ๋ผ ๊ทธ๋Œ€๋กœ ์œ ์ง€. + const pc=new RTCPeerConnection({iceServers:[{urls:'stun:stun.l.google.com:19302'}]}); + k2LivePC=pc; + pc.ontrack=(ev)=>{ + if(k2LivePC!==pc)return; + video.srcObject=ev.streams[0]; + video.play().catch(()=>{}); + ph.style.display='none'; + }; + pc.oniceconnectionstatechange=()=>{ + if(k2LivePC!==pc)return; + const st=pc.iceConnectionState; + if(st==='failed'){ + stopK2Live(); + k2Note('๋ผ์ด๋ธŒ ์—ฐ๊ฒฐ ์‹คํŒจ โ€” โ–ถ ๋ผ์ด๋ธŒ๋ฅผ ๋‹ค์‹œ ๋ˆŒ๋Ÿฌ์ฃผ์„ธ์š”'); + }else if(st==='disconnected'){ + k2Note('๋ผ์ด๋ธŒ ์‹ ํ˜ธ ๋Š๊น€(์ผ์‹œ์ ) โ€” ๋ณต๊ตฌ๋˜๋ฉด ๊ทธ๋ ค์ง‘๋‹ˆ๋‹ค'); + } + }; + // ์˜คํผ๋Š” ICE gathering ์™„์ „์ข…๋ฃŒ(null candidate) ๋˜๋Š” 1.5์ดˆ ์ค‘ ๋น ๋ฅธ ์ชฝ์— ๋ณด๋‚ธ๋‹ค โ€” + // ์™„์ „์ข…๋ฃŒ๊ฐ€ ์ด ๋„คํŠธ์›Œํฌ์—์„œ 20์ดˆ+ ๊ฑธ๋ฆฌ๋Š” k2-app.html์˜ ์‹ค์ธก ํ€ดํฌ ๊ทธ๋Œ€๋กœ. + let offerSent=false; + const sendOfferOnce=()=>{ + if(offerSent||k2LivePC!==pc)return; + offerSent=true; + fetch('/api/k2/webrtc-signal',{method:'POST',headers:{'Content-Type':'text/plain',...authH()},body:btoa(JSON.stringify({type:'offer',sdp:pc.localDescription.sdp}))}) + .then(r=>r.text()).then(txt=>{ + const res=JSON.parse(atob(txt)); + if(res.type==='answer'&&k2LivePC===pc)pc.setRemoteDescription(new RTCSessionDescription(res)); + }).catch(()=>{if(k2LivePC===pc)stopK2Live();}); + }; + pc.onicecandidate=(ev)=>{if(ev.candidate===null)sendOfferOnce();}; + pc.addTransceiver('video',{direction:'sendrecv'}); + pc.createOffer().then(d=>pc.setLocalDescription(d)).then(()=>{setTimeout(sendOfferOnce,1500);}).catch(()=>{if(k2LivePC===pc)stopK2Live();}); +} +function stopK2Live(){ + if(!k2LivePC)return; + try{k2LivePC.close();}catch{} + k2LivePC=null; + const video=document.getElementById('k2-live'); + try{video.pause();video.srcObject=null;}catch{} + video.style.display='none'; + const btn=document.getElementById('k2-live-btn'); + btn.classList.remove('on');btn.textContent='โ–ถ ๋ผ์ด๋ธŒ'; + // ์Šค๋ƒ…์ƒท์œผ๋กœ ์ฆ‰์‹œ ๋ณต๊ท€ โ€” ๋‹จ, ์žฅ๋น„ ํƒญ์„ ๋ฒ—์–ด๋‚˜๋ฉฐ stopEquipTimers๊ฐ€ ๋ถ€๋ฅธ ๊ฒฝ์šฐ์—” + // ์•ˆ ๊ทธ๋ž˜๋„ ๋œ๋‹ค(์•ˆ ๋ณด๋Š” ํƒญ์— ํ”„๋ ˆ์ž„ ๊ทธ๋žฉ์„ ํŠธ๋ฆฌ๊ฑฐํ•˜๋Š” ์…ˆ). + const pane=document.getElementById('pane-equip'); + if(pane&&pane.classList.contains('active'))refreshK2Widget(); +} + function fmtK2Duration(sec){ const h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60); if(h>0)return h+'์‹œ๊ฐ„ '+m+'๋ถ„'; @@ -663,10 +747,14 @@ const K2_STATE_LABEL={standby:'๋Œ€๊ธฐ์ค‘',printing:'์ถœ๋ ฅ์ค‘',paused:'์ผ์‹œ์ • async function refreshK2Widget(){ const img=document.getElementById('k2-frame'); const placeholder=document.getElementById('k2-placeholder'); - grabK2Frame().then(dataUrl=>{ - if(dataUrl){img.src=dataUrl;img.style.display='block';placeholder.style.display='none';} - else if(img.style.display==='none'){placeholder.textContent='์นด๋ฉ”๋ผ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค';} - }); + // ๋ผ์ด๋ธŒ ์—ฐ๊ฒฐ์ด ์‚ด์•„์žˆ์œผ๋ฉด ํ”„๋ ˆ์ž„ ๊ทธ๋žฉ(WebRTC ํ’€ํ˜‘์ƒ)์„ ๊ฑด๋„ˆ๋›ด๋‹ค โ€” ๋น„๋””์˜ค๊ฐ€ ์ด๋ฏธ + // ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ๋ฅด๊ณ  ์žˆ๊ณ , ์งง์€ ์—ฐ๊ฒฐ์„ ๋˜ ์—ด๋ฉด ๋‘˜์ด ๊ฐ„์„ญํ•œ๋‹ค. ์ƒํƒœ ์กฐํšŒ๋Š” ๊ณ„์†ํ•œ๋‹ค. + if(!k2LivePC){ + grabK2Frame().then(dataUrl=>{ + if(dataUrl){img.src=dataUrl;img.style.display='block';placeholder.style.display='none';} + else if(img.style.display==='none'){placeholder.textContent='์นด๋ฉ”๋ผ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค';} + }); + } try{ const r=await fetch('/api/k2/status',{headers:authH()}); const d=await r.json(); @@ -684,6 +772,7 @@ async function refreshK2Widget(){ if(d.state==='printing'){ html+='
์ง„ํ–‰๋ฅ : '+d.progress+'% ยท ๊ฒฝ๊ณผ: '+fmtK2Duration(d.printDurationSec)+''; } + if(k2LivePC)html+=' ยท โ— ์‹ค์‹œ๊ฐ„'; statusEl.innerHTML=html; }catch{ document.getElementById('k2-status-text').textContent='์ƒํƒœ ์กฐํšŒ ์‹คํŒจ';