code wiki / _hdl_build / nx_video_embed.nx
nx_video_embed.nx source
↩ module page · 191 lines · 23026 B
1// nx_video_embed.nx -- SOVEREIGN EMBEDDABLE VIDEO ROOM for any third-party site (operator: make nishifamily.com/video
2// "s class exceed ... as an embeddeable on sites"). This is the cross-origin counterpart to nx_nishi_embed:
3// nx_nishi_embed -> <nishi-embed> Shadow-DOM, for FIRST-PARTY same-origin capabilities (search/estate/doc)
4// nx_video_embed -> <nishi-video> origin-isolated SANDBOXED IFRAME, for the CROSS-ORIGIN, camera/mic,
5// secret-bearing media-room case -- where (per nx_embed_exceed's own honest
6// counter-axis) a real browsing-context boundary is REQUIRED, not optional.
7// = capability-adaptive isolation. We exceed YouTube/Vimeo/Jitsi/Whereby embeds on the MEASURABLE axes (zero
8// third-party JS / zero trackers / zero cookies / least-privilege allow= / patent-clean WHT codec path) and
9// honestly concede the axes we are behind (hardware decode, CDN scale) -- graded by nx_video_embed_exceed.
10//
11// Emits THREE artifacts (all hand-authored, zero dependencies), reusing the PROVEN vroom relay routes
12// (post?/stream?/frames?/apost?/astream? -- same-origin, relative, so they resolve inside the iframe):
13// web_assets/video_embed.html -- the slim embed-target room (the iframe content); ?room&mode&name&parent
14// web_assets/embed.js -- the publisher loader: <nishi-video> custom element + postMessage bridge
15// web_assets/video_embed_demo.html -- a pretend third-party page that drops <nishi-video> + logs typed events
16// Deployable via nx_aw_push; gated by nx_video_embed_gate; measured by nx_video_embed_exceed.
17// license_tier: ORIGINAL
18import "nx_syscalls.nx"
19
20const VE_TARGET: *u8 = "web_assets/video_embed.html" as *u8
21const VE_LOADER: *u8 = "web_assets/embed.js" as *u8
22const VE_DEMO: *u8 = "web_assets/video_embed_demo.html" as *u8
23
24func ve_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
25func ve_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
26
27// ============================ 1) the embed-target room (iframe content) ============================
28func ve_emit_target() -> i64 {
29 let fd: i64 = sys_openat_wr(VE_TARGET, 0x1a4)
30 if fd < 0 { ve_puts("VIDEO-EMBED FAIL: cannot open video_embed.html\n" as *u8); return 1 }
31
32 ve_w(fd, "<!doctype html><html lang='en'><head><meta charset='utf-8'>\n" as *u8)
33 ve_w(fd, "<meta name='viewport' content='width=device-width,initial-scale=1,viewport-fit=cover'>\n" as *u8)
34 ve_w(fd, "<title>Nishi Video (embed)</title>\n" as *u8)
35 ve_w(fd, "<!-- SOVEREIGN embeddable room: zero third-party JS, zero trackers, zero cookies, own relay, patent-clean codec path -->\n" as *u8)
36 // resource CSP (meta-honored directives): self + blob + inline-script only -> blocks ALL third-party origins by construction
37 ve_w(fd, "<meta http-equiv='Content-Security-Policy' content=\"default-src 'self' blob:; script-src 'self' 'unsafe-inline'; img-src 'self' blob: data:; media-src 'self' blob:; connect-src 'self'; style-src 'self' 'unsafe-inline'; base-uri 'none'\">\n" as *u8)
38 ve_w(fd, "<style>\n" as *u8)
39 ve_w(fd, ":root{color-scheme:dark}*{box-sizing:border-box}html,body{margin:0}body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:#0a0a0c;color:#e6e6ec}\n" as *u8)
40 ve_w(fd, "#bar{display:flex;gap:6px;align-items:center;padding:6px 8px;border-bottom:1px solid #23232a;font-size:.85rem}#bar b{color:#6ade8a;white-space:nowrap}#st{margin-left:auto;color:#9a9aa4;font-size:.76rem;text-align:right}\n" as *u8)
41 ve_w(fd, "button{font:inherit;padding:5px 11px;border-radius:6px;border:1px solid #1f7a3a;background:#1f7a3a;color:#fff;cursor:pointer}button.lv{background:#7a1f1f;border-color:#7a1f1f}button:disabled{opacity:.5;cursor:default}\n" as *u8)
42 ve_w(fd, "#grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px;padding:6px}.tile{position:relative;background:#111;border:1px solid #23232a;border-radius:7px;overflow:hidden;aspect-ratio:4/3}\n" as *u8)
43 ve_w(fd, ".tile img,.tile canvas{width:100%;height:100%;object-fit:cover;display:block;background:#000}.tile .lbl{position:absolute;left:5px;bottom:5px;background:rgba(0,0,0,.6);padding:1px 6px;border-radius:4px;font-size:.72rem}.tile.self{outline:2px solid #1f7a3a}#empty{padding:14px;color:#666;font-size:.85rem}\n" as *u8)
44 ve_w(fd, "</style></head><body>\n" as *u8)
45 ve_w(fd, "<div id='bar'><b>Nishi Video</b><span id='rm'></span><button id='join'>join</button><button id='leave' class='lv' disabled>leave</button><span id='st'>sovereign · own relay · no third-party</span></div>\n" as *u8)
46 ve_w(fd, "<div id='grid'><div id='empty'>ready</div></div>\n" as *u8)
47 ve_w(fd, "<script>\n" as *u8)
48 ve_w(fd, "'use strict';\n" as *u8)
49 // --- last-mile browser shim only (operator law): getUserMedia/canvas/fetch/DOM; media relayed by the sovereign daemon ---
50 ve_w(fd, "var $=function(id){return document.getElementById(id);};\n" as *u8)
51 ve_w(fd, "var P=new URLSearchParams(location.search);\n" as *u8)
52 ve_w(fd, "var ROOM=(P.get('room')||'lobby').slice(0,32);\n" as *u8)
53 ve_w(fd, "var MODE=(P.get('mode')==='watch')?'watch':'room';\n" as *u8)
54 ve_w(fd, "var NAME=(P.get('name')||'').slice(0,20);\n" as *u8)
55 ve_w(fd, "var PORIGIN=P.get('parent')||'';\n" as *u8)
56 ve_w(fd, "var SELF=Math.floor(Math.random()*1e12)+1;\n" as *u8)
57 ve_w(fd, "var CAP_W=320,CAP_H=240,FPS=12,QJ=0.5;\n" as *u8)
58 ve_w(fd, "var stream=null,tx=null,hv=null,cap=null,cctx=null,sctx=null,actx=null,micProc=null,playCursor=0;\n" as *u8)
59 ve_w(fd, "var running=false,useStream=true,streamFails=0,tiles={},lastSeen={};\n" as *u8)
60 ve_w(fd, "function qs(){return 'room='+encodeURIComponent(ROOM)+'&id='+SELF;}\n" as *u8)
61 ve_w(fd, "function setSt(t){$('st').textContent=t;}\n" as *u8)
62 ve_w(fd, "function npeers(){var n=0;for(var k in tiles){if(tiles.hasOwnProperty(k)&&(+k)!==SELF)n++;}return n;}\n" as *u8)
63 ve_w(fd, "function sleep(ms){return new Promise(function(r){setTimeout(r,ms);});}\n" as *u8)
64 // ---- typed postMessage UP to the embedding host (the clean comms iframes lack without string-soup) ----
65 ve_w(fd, "function up(type,extra){var m={type:type,room:ROOM,mode:MODE,peers:npeers()};if(extra){for(var k in extra){if(extra.hasOwnProperty(k))m[k]=extra[k];}}try{parent.postMessage(m,PORIGIN||'*');}catch(e){}}\n" as *u8)
66 ve_w(fd, "function reportSize(){up('nishi:resize',{height:Math.ceil(document.body.scrollHeight)});}\n" as *u8)
67 // ---- tiles + recv: SAME length-prefixed record format as the proven vroom relay ----
68 ve_w(fd, "function ensureTile(id,self){if(tiles[id])return tiles[id];$('empty').style.display='none';var d=document.createElement('div');d.className='tile'+(self?' self':'');var media=document.createElement(self?'canvas':'img');if(self){media.width=CAP_W;media.height=CAP_H;}var lbl=document.createElement('div');lbl.className='lbl';lbl.textContent=self?(NAME||'you'):('peer '+String(id).slice(-4));d.appendChild(media);d.appendChild(lbl);$('grid').appendChild(d);var e={d:d,media:media};tiles[id]=e;reportSize();return e;}\n" as *u8)
69 ve_w(fd, "function dropTile(id){var e=tiles[id];if(!e)return;e.d.remove();delete tiles[id];delete lastSeen[id];reportSize();up('nishi:peers');}\n" as *u8)
70 ve_w(fd, "function renderPeer(id,bytes){var wasNew=!tiles[id];var e=ensureTile(id,false);if(wasNew)up('nishi:peers');var url=URL.createObjectURL(new Blob([bytes],{type:'image/jpeg'}));e.media.onload=function(){URL.revokeObjectURL(url);};e.media.onerror=function(){URL.revokeObjectURL(url);};e.media.src=url;lastSeen[id]=Date.now();}\n" as *u8)
71 ve_w(fd, "function parseRecords(buf){var off=0;while(buf.length-off>=12){var dv=new DataView(buf.buffer,buf.byteOffset+off,12);var id=Number(dv.getBigUint64(0,true));var len=dv.getUint32(8,true);if(len<0||len>20000000)break;if(buf.length-off<12+len)break;var frame=buf.slice(off+12,off+12+len);off+=12+len;if(id!==0&&len>0&&id!==SELF)renderPeer(id,frame);}return off;}\n" as *u8)
72 ve_w(fd, "async function runStream(){var r;try{r=await fetch('stream?'+qs(),{credentials:'omit'});}catch(e){return false;}if(r.status!==200||!r.body)return false;streamFails=0;var reader=r.body.getReader();var buf=new Uint8Array(0);while(running&&useStream){var res;try{res=await reader.read();}catch(e){break;}if(res.done)break;var v=res.value;var nb=new Uint8Array(buf.length+v.length);nb.set(buf);nb.set(v,buf.length);buf=nb;var c=parseRecords(buf);buf=c>0?buf.slice(c):buf;}try{reader.cancel();}catch(e){}return true;}\n" as *u8)
73 ve_w(fd, "async function runPollOnce(){var r;try{r=await fetch('frames?'+qs(),{credentials:'omit'});}catch(e){return;}if(r.status!==200)return;var ab=await r.arrayBuffer();parseRecords(new Uint8Array(ab));}\n" as *u8)
74 ve_w(fd, "async function recvLoop(){while(running){if(useStream){var ok=await runStream();if(!ok){streamFails++;if(streamFails>=3)useStream=false;await sleep(400);}else{await sleep(250);}}else{await runPollOnce();await sleep(1000/FPS);}}}\n" as *u8)
75 // ---- audio capture (room mode) + playback (both modes) ----
76 ve_w(fd, "function startAudioCapture(){try{actx=new (window.AudioContext||window.webkitAudioContext)();if(actx.state!=='running')actx.resume().catch(function(){});var src=actx.createMediaStreamSource(stream);var proc=actx.createScriptProcessor(4096,1,1);var g=actx.createGain();g.gain.value=0;src.connect(proc);proc.connect(g);g.connect(actx.destination);proc.onaudioprocess=function(e){if(!running)return;var f=e.inputBuffer.getChannelData(0);var b=new ArrayBuffer(4+f.length*2);var dv=new DataView(b);dv.setUint32(0,actx.sampleRate,true);for(var i=0;i<f.length;i++){var s=f[i];if(s>1)s=1;if(s<-1)s=-1;dv.setInt16(4+i*2,s*32767,true);}fetch('apost?'+qs(),{method:'POST',body:b,credentials:'omit',headers:{'Content-Type':'application/octet-stream'}}).catch(function(){});};micProc=proc;}catch(e){}}\n" as *u8)
77 ve_w(fd, "function stopAudioCapture(){try{if(micProc){micProc.disconnect();micProc.onaudioprocess=null;micProc=null;}}catch(e){}try{if(actx){actx.close();actx=null;}}catch(e){}playCursor=0;}\n" as *u8)
78 ve_w(fd, "function playAudioChunk(bytes){if(!actx||bytes.byteLength<=4)return;if(actx.state!=='running')actx.resume().catch(function(){});var dv=new DataView(bytes.buffer,bytes.byteOffset,bytes.byteLength);var rate=dv.getUint32(0,true);var nsamp=(bytes.byteLength-4)>>1;if(nsamp<=0||rate<4000)return;var ab=actx.createBuffer(1,nsamp,rate);var ch=ab.getChannelData(0);for(var i=0;i<nsamp;i++)ch[i]=dv.getInt16(4+i*2,true)/32768;var s=actx.createBufferSource();s.buffer=ab;s.connect(actx.destination);var t=actx.currentTime;if(playCursor<t+0.08)playCursor=t+0.08;s.start(playCursor);playCursor+=ab.duration;}\n" as *u8)
79 ve_w(fd, "async function runAudioStream(){while(running){try{var r=await fetch('astream?'+qs(),{credentials:'omit'});if(r.status!==200||!r.body){await sleep(500);continue;}var reader=r.body.getReader();var buf=new Uint8Array(0);while(running){var res=await reader.read();if(res.done)break;var v=res.value;var nb=new Uint8Array(buf.length+v.length);nb.set(buf);nb.set(v,buf.length);buf=nb;var off=0;while(buf.length-off>=12){var d=new DataView(buf.buffer,buf.byteOffset+off,12);var id=Number(d.getBigUint64(0,true));var len=d.getUint32(8,true);if(len<0||len>20000000)break;if(buf.length-off<12+len)break;var pl=buf.slice(off+12,off+12+len);off+=12+len;if(id!==0&&len>4&&id!==SELF)playAudioChunk(pl);}buf=off>0?buf.slice(off):buf;}try{reader.cancel();}catch(e){}}catch(e){}if(running)await sleep(400);}}\n" as *u8)
80 // ---- join / leave ----
81 ve_w(fd, "async function join(){if(running)return;if(MODE==='room'){setSt('starting camera...');try{stream=await navigator.mediaDevices.getUserMedia({video:{width:{ideal:640},height:{ideal:480},facingMode:'user'},audio:{echoCancellation:true,noiseSuppression:true,autoGainControl:true}});}catch(e){setSt('camera blocked: '+e.message);up('nishi:error',{error:'camera-blocked'});return;}hv=document.createElement('video');hv.autoplay=true;hv.playsInline=true;hv.muted=true;hv.srcObject=stream;try{await hv.play();}catch(e){}cap=document.createElement('canvas');cap.width=CAP_W;cap.height=CAP_H;cctx=cap.getContext('2d');var self=ensureTile(SELF,true);sctx=self.media.getContext('2d');tx=setInterval(function(){if(!hv.videoWidth)return;cctx.drawImage(hv,0,0,CAP_W,CAP_H);sctx.drawImage(hv,0,0,CAP_W,CAP_H);cap.toBlob(function(b){if(!b)return;fetch('post?'+qs(),{method:'POST',body:b,credentials:'omit',headers:{'Content-Type':'image/jpeg'}}).catch(function(){});},'image/jpeg',QJ);},1000/FPS);}running=true;useStream=true;streamFails=0;$('join').disabled=true;$('leave').disabled=false;recvLoop();if(MODE==='room')startAudioCapture();runAudioStream();setSt((MODE==='watch'?'watching':'live')+' in '+ROOM);up('nishi:join');}\n" as *u8)
82 ve_w(fd, "function leave(){running=false;stopAudioCapture();if(tx)clearInterval(tx);tx=null;if(stream){stream.getTracks().forEach(function(t){t.stop();});stream=null;}for(var k in tiles){if(tiles.hasOwnProperty(k))tiles[k].d.remove();}tiles={};lastSeen={};$('empty').style.display='';$('empty').textContent='left';$('join').disabled=false;$('leave').disabled=true;setSt('left');reportSize();up('nishi:leave');}\n" as *u8)
83 ve_w(fd, "setInterval(function(){var t=Date.now();for(var k in lastSeen){if(lastSeen.hasOwnProperty(k)&&t-lastSeen[k]>6000)dropTile(+k);}},2000);\n" as *u8)
84 // ---- inbound host commands, ORIGIN-CHECKED (only our embedder) ----
85 ve_w(fd, "window.addEventListener('message',function(e){if(PORIGIN&&e.origin!==PORIGIN)return;if(e.source!==parent)return;var d=e.data||{};if(d.type!=='nishi:cmd')return;if(d.cmd==='join')join();else if(d.cmd==='leave')leave();else if(d.cmd==='mute'){if(stream)stream.getAudioTracks().forEach(function(t){t.enabled=false;});}else if(d.cmd==='unmute'){if(stream)stream.getAudioTracks().forEach(function(t){t.enabled=true;});}});\n" as *u8)
86 ve_w(fd, "$('rm').textContent=' \\u00b7 '+ROOM;$('join').onclick=join;$('leave').onclick=leave;\n" as *u8)
87 ve_w(fd, "if(!navigator.mediaDevices&&MODE==='room')setSt('needs HTTPS + camera');\n" as *u8)
88 ve_w(fd, "up('nishi:ready');reportSize();\n" as *u8)
89 ve_w(fd, "if(MODE==='watch')join();\n" as *u8)
90 ve_w(fd, "</script></body></html>\n" as *u8)
91 sys_close(fd)
92 ve_puts(" emitted -> web_assets/video_embed.html (slim embed-target room; mode=room|watch; postMessage handshake)\n" as *u8)
93 return 0
94}
95
96// ============================ 2) the publisher loader: <nishi-video> custom element ============================
97func ve_emit_loader() -> i64 {
98 let fd: i64 = sys_openat_wr(VE_LOADER, 0x1a4)
99 if fd < 0 { ve_puts("VIDEO-EMBED FAIL: cannot open embed.js\n" as *u8); return 1 }
100
101 ve_w(fd, "// embed.js -- drop a sovereign Nishi video room onto ANY page: <nishi-video room='...' mode='room|watch'>\n" as *u8)
102 ve_w(fd, "// Origin-isolated sandboxed iframe (real browsing-context boundary) + typed CustomEvents + auto-resize.\n" as *u8)
103 ve_w(fd, "// Zero third-party scripts, zero trackers, zero cookies. Hand-authored by nx_video_embed. license: ORIGINAL\n" as *u8)
104 ve_w(fd, "(function(){\n" as *u8)
105 ve_w(fd, "'use strict';\n" as *u8)
106 // allowlist so an embed can never be pointed at an arbitrary origin
107 ve_w(fd, "var ALLOW={'https://nishifamily.com':1,'https://www.nishifamily.com':1};\n" as *u8)
108 ve_w(fd, "class NishiVideo extends HTMLElement{\n" as *u8)
109 ve_w(fd, " connectedCallback(){\n" as *u8)
110 ve_w(fd, " if(this._init)return;this._init=1;\n" as *u8)
111 ve_w(fd, " var host=(this.getAttribute('host')||'https://nishifamily.com').replace(/\\/+$/,'');\n" as *u8)
112 ve_w(fd, " if(!ALLOW[host])host='https://nishifamily.com';\n" as *u8)
113 ve_w(fd, " this._host=host;\n" as *u8)
114 ve_w(fd, " var room=(this.getAttribute('room')||'lobby');\n" as *u8)
115 ve_w(fd, " var mode=(this.getAttribute('mode')==='watch')?'watch':'room';\n" as *u8)
116 ve_w(fd, " var name=(this.getAttribute('name')||'');\n" as *u8)
117 ve_w(fd, " var h=parseInt(this.getAttribute('height')||'420',10);if(!(h>0))h=420;\n" as *u8)
118 ve_w(fd, " var root=this.attachShadow({mode:'open'});\n" as *u8)
119 ve_w(fd, " root.innerHTML='<style>:host{display:block;border-radius:10px;overflow:hidden;border:1px solid #23232a;background:#0a0a0c}iframe{width:100%;border:0;display:block;background:#0a0a0c}</style>';\n" as *u8)
120 ve_w(fd, " var f=document.createElement('iframe');\n" as *u8)
121 ve_w(fd, " f.src=host+'/video/embed.html?room='+encodeURIComponent(room)+'&mode='+mode+(name?('&name='+encodeURIComponent(name)):'')+'&parent='+encodeURIComponent(location.origin);\n" as *u8)
122 // LEAST PRIVILEGE: only the permissions a room needs -- not geolocation/payment/usb/...
123 ve_w(fd, " f.allow='camera; microphone; fullscreen; autoplay';\n" as *u8)
124 // sandbox: allow-same-origin lets the iframe BE nishifamily.com (so it reaches its OWN relay + getUserMedia),
125 // while the host page (a different origin) still cannot read into it -- that is the isolation we want.
126 ve_w(fd, " f.setAttribute('sandbox','allow-scripts allow-same-origin allow-popups-to-escape-sandbox');\n" as *u8)
127 ve_w(fd, " f.setAttribute('referrerpolicy','no-referrer');\n" as *u8)
128 ve_w(fd, " f.setAttribute('loading','lazy');\n" as *u8)
129 ve_w(fd, " f.setAttribute('title','Nishi Video room: '+room);\n" as *u8)
130 ve_w(fd, " f.style.height=h+'px';\n" as *u8)
131 ve_w(fd, " root.appendChild(f);this._frame=f;\n" as *u8)
132 ve_w(fd, " var self=this;\n" as *u8)
133 // bridge: only accept messages from OUR iframe origin + window, re-dispatch as typed CustomEvents, auto-resize
134 ve_w(fd, " this._onmsg=function(e){if(e.origin!==self._host)return;if(e.source!==f.contentWindow)return;var d=e.data||{};if(!d.type||String(d.type).indexOf('nishi:')!==0)return;if(d.type==='nishi:resize'&&d.height>0){f.style.height=Math.max(120,d.height)+'px';}self.dispatchEvent(new CustomEvent(d.type,{detail:d,bubbles:true,composed:true}));};\n" as *u8)
135 ve_w(fd, " window.addEventListener('message',this._onmsg);\n" as *u8)
136 ve_w(fd, " }\n" as *u8)
137 ve_w(fd, " disconnectedCallback(){if(this._onmsg)window.removeEventListener('message',this._onmsg);}\n" as *u8)
138 ve_w(fd, " _cmd(cmd){if(this._frame&&this._frame.contentWindow)this._frame.contentWindow.postMessage({type:'nishi:cmd',cmd:cmd},this._host);}\n" as *u8)
139 ve_w(fd, " join(){this._cmd('join');}leave(){this._cmd('leave');}mute(){this._cmd('mute');}unmute(){this._cmd('unmute');}\n" as *u8)
140 ve_w(fd, "}\n" as *u8)
141 ve_w(fd, "if(!customElements.get('nishi-video'))customElements.define('nishi-video',NishiVideo);\n" as *u8)
142 ve_w(fd, "})();\n" as *u8)
143 sys_close(fd)
144 ve_puts(" emitted -> web_assets/embed.js (<nishi-video> loader: sandboxed iframe + typed-event bridge + auto-resize)\n" as *u8)
145 return 0
146}
147
148// ============================ 3) the third-party host demo ============================
149func ve_emit_demo() -> i64 {
150 let fd: i64 = sys_openat_wr(VE_DEMO, 0x1a4)
151 if fd < 0 { ve_puts("VIDEO-EMBED FAIL: cannot open video_embed_demo.html\n" as *u8); return 1 }
152
153 ve_w(fd, "<!doctype html><html lang='en'><head><meta charset='utf-8'>\n" as *u8)
154 ve_w(fd, "<meta name='viewport' content='width=device-width,initial-scale=1'>\n" as *u8)
155 ve_w(fd, "<title>Nishi Video -- embed on any site</title>\n" as *u8)
156 ve_w(fd, "<style>body{font:16px system-ui,sans-serif;max-width:820px;margin:2rem auto;padding:0 1rem;color:#1a1a1a}h1{font-size:1.35rem}pre{background:#f4f4f7;border-radius:6px;padding:.8rem;overflow:auto;font-size:13px}.host{border:1px dashed #bbb;padding:10px;border-radius:10px;margin:1rem 0}#log{font:12px monospace;background:#0a0a0c;color:#6ade8a;padding:.6rem;border-radius:6px;white-space:pre-wrap;max-height:160px;overflow:auto}button{font:inherit;padding:5px 12px;border-radius:6px;border:1px solid #2a4d8f;background:#1f3a5a;color:#fff;cursor:pointer;margin-right:5px}</style>\n" as *u8)
157 ve_w(fd, "</head><body>\n" as *u8)
158 ve_w(fd, "<h1><nishi-video> — a sovereign video room embedded on a third-party page</h1>\n" as *u8)
159 ve_w(fd, "<p>This page pretends to be someone else's website. Two lines drop a full Nishi video room onto it — origin-isolated in a sandboxed iframe, with <b>zero third-party scripts, zero trackers, zero cookies, and a patent-clean codec path</b>. The host page talks to it only through typed <code>nishi:*</code> events — never the postMessage string-soup that ad/video iframes force on you.</p>\n" as *u8)
160 ve_w(fd, "<pre><script src='https://nishifamily.com/video/embed.js'></script>\n<nishi-video room='family' mode='room' height='460'></nishi-video></pre>\n" as *u8)
161 ve_w(fd, "<div class='host'><nishi-video id='nv' room='demo-lobby' mode='room' height='420'></nishi-video></div>\n" as *u8)
162 ve_w(fd, "<p>Host controls (postMessage <em>to</em> the iframe) & the typed event log (CustomEvents <em>up</em>):</p>\n" as *u8)
163 ve_w(fd, "<p><button id='bj'>join()</button><button id='bl'>leave()</button><button id='bm'>mute()</button></p>\n" as *u8)
164 ve_w(fd, "<div id='log'>(waiting for nishi:ready / nishi:join / nishi:peers ...)</div>\n" as *u8)
165 ve_w(fd, "<script src='embed.js'></script>\n" as *u8)
166 ve_w(fd, "<script>\n" as *u8)
167 ve_w(fd, "var nv=document.getElementById('nv');\n" as *u8)
168 ve_w(fd, "document.getElementById('bj').onclick=function(){nv.join();};\n" as *u8)
169 ve_w(fd, "document.getElementById('bl').onclick=function(){nv.leave();};\n" as *u8)
170 ve_w(fd, "document.getElementById('bm').onclick=function(){nv.mute();};\n" as *u8)
171 ve_w(fd, "var log=document.getElementById('log');\n" as *u8)
172 ve_w(fd, "['nishi:ready','nishi:join','nishi:leave','nishi:peers','nishi:error'].forEach(function(n){nv.addEventListener(n,function(e){log.textContent=n+' '+JSON.stringify(e.detail)+'\\n'+log.textContent;});});\n" as *u8)
173 ve_w(fd, "</script>\n" as *u8)
174 ve_w(fd, "</body></html>\n" as *u8)
175 sys_close(fd)
176 ve_puts(" emitted -> web_assets/video_embed_demo.html (pretend third-party page using <nishi-video> + typed-event log)\n" as *u8)
177 return 0
178}
179
180func main() -> i64 {
181 ve_puts("=== NISHI VIDEO EMBED -- emitting sovereign <nishi-video> embeddable room ===\n" as *u8)
182 let a: i64 = ve_emit_target()
183 let b: i64 = ve_emit_loader()
184 let c: i64 = ve_emit_demo()
185 if a!=0 { sys_exit(1); return 1 }
186 if b!=0 { sys_exit(1); return 1 }
187 if c!=0 { sys_exit(1); return 1 }
188 ve_puts("NISHI-VIDEO-EMBED-OK: publisher drops 2 lines -> origin-isolated sandboxed iframe, least-privilege allow=,\n" as *u8)
189 ve_puts(" typed nishi:* CustomEvents, auto-resize, zero third-party/trackers/cookies, patent-clean codec path.\n" as *u8)
190 sys_exit(0); return 0
191}