Spaces:
Running
Running
faradayfuture Claude Opus 5.5 commited on
Commit ·
8d83ceb
1
Parent(s): fae2f7e
teleopSample viewers: WeChat-safe autoplay (top-frame WeixinJSBridge, no readyState deadlock, tap retry)
Browse filesSame fix as the egoSample viewers: in-app WebViews never reach readyState>=2
before play(), and WeixinJSBridge only exists in the top frame. Covers the
task episode pages, epN viewers, lib/viewer.js (faberU viewers) and clip.html.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- teleopSample/clip.html +45 -0
- teleopSample/drink_ep0.html +44 -10
- teleopSample/drink_ep1.html +44 -10
- teleopSample/ep0/viewer.html +43 -18
- teleopSample/ep1/viewer.html +43 -18
- teleopSample/ep2/viewer.html +43 -18
- teleopSample/ep3/viewer.html +43 -18
- teleopSample/ep4/viewer.html +43 -18
- teleopSample/ep5/viewer.html +43 -18
- teleopSample/faberU/t2_water_bottle_into_steel_tray/20260707052527/viewer.html +1 -1
- teleopSample/faberU/t2_water_bottle_into_steel_tray/20260707052609/viewer.html +1 -1
- teleopSample/faberU/t3_juice_bottle_onto_plate/20260724050737/viewer.html +1 -1
- teleopSample/faberU/t3_juice_bottle_onto_plate/20260724050827/viewer.html +1 -1
- teleopSample/faberU/t4_juice_bottle_into_black_tray/20260728075709/viewer.html +1 -1
- teleopSample/faberU/t4_juice_bottle_into_black_tray/20260728080339/viewer.html +1 -1
- teleopSample/faberU/t6_pick_up_green_tea_bottle/20260730045050/viewer.html +1 -1
- teleopSample/faberU/t6_pick_up_green_tea_bottle/20260730045152/viewer.html +1 -1
- teleopSample/lib/viewer.js +43 -9
- teleopSample/mem_ep0.html +44 -10
- teleopSample/mem_ep1.html +44 -10
- teleopSample/sort_ep0.html +44 -10
- teleopSample/sort_ep1.html +44 -10
- teleopSample/wipe_ep0.html +44 -10
- teleopSample/wipe_ep1.html +44 -10
teleopSample/clip.html
CHANGED
|
@@ -26,6 +26,51 @@ document.getElementById('dl').href = v;
|
|
| 26 |
document.getElementById('dl').setAttribute('download', (t || 'clip').replace(/[^\w.-]+/g, '_') + '.mp4');
|
| 27 |
document.getElementById('name').textContent = t;
|
| 28 |
document.title = t || 'clip';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
document.getElementById('back').onclick = e => {
|
| 30 |
e.preventDefault();
|
| 31 |
if (back) location.href = back;
|
|
|
|
| 26 |
document.getElementById('dl').setAttribute('download', (t || 'clip').replace(/[^\w.-]+/g, '_') + '.mp4');
|
| 27 |
document.getElementById('name').textContent = t;
|
| 28 |
document.title = t || 'clip';
|
| 29 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 30 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 31 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 32 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 33 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 34 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 35 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 36 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 37 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 38 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 39 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 40 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 41 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 42 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 43 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 44 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 45 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 46 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 47 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 48 |
+
function go(force){
|
| 49 |
+
if(started||isPlaying())return;
|
| 50 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 51 |
+
if(wxWait&&!force)return;
|
| 52 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 53 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 54 |
+
vs.forEach(function(v){v.muted=true;});
|
| 55 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 56 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 57 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 58 |
+
}
|
| 59 |
+
function arm(){
|
| 60 |
+
if(armed)return; armed=true;
|
| 61 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 62 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 63 |
+
armed=false;go(true);}
|
| 64 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 65 |
+
wxCall(function(){go(true);});
|
| 66 |
+
}
|
| 67 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 68 |
+
window.addEventListener('load',function(){go();});
|
| 69 |
+
iv=setInterval(go,400);
|
| 70 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 71 |
+
}
|
| 72 |
+
{ const vv = document.getElementById('v');
|
| 73 |
+
wxAuto(() => [vv], () => !vv.paused, () => vv.play(), () => vv.pause()); }
|
| 74 |
document.getElementById('back').onclick = e => {
|
| 75 |
e.preventDefault();
|
| 76 |
if (back) location.href = back;
|
teleopSample/drink_ep0.html
CHANGED
|
@@ -226,17 +226,51 @@ CAMS.forEach(c=>{ const a=document.createElement('a'); a.className='file-chip';
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
-
(
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
-
vids.forEach(v
|
| 238 |
-
window.addEventListener('load',
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 241 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 242 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
|
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 230 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 231 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 232 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 233 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 234 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 235 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 236 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 237 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 238 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 239 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 240 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 241 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 242 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 243 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 244 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 245 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 246 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 247 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 248 |
+
function go(force){
|
| 249 |
+
if(started||isPlaying())return;
|
| 250 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 251 |
+
if(wxWait&&!force)return;
|
| 252 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 253 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 254 |
+
vs.forEach(function(v){v.muted=true;});
|
| 255 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 256 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 257 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 258 |
+
}
|
| 259 |
+
function arm(){
|
| 260 |
+
if(armed)return; armed=true;
|
| 261 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 262 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 263 |
+
armed=false;go(true);}
|
| 264 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 265 |
+
wxCall(function(){go(true);});
|
| 266 |
}
|
| 267 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 268 |
+
window.addEventListener('load',function(){go();});
|
| 269 |
+
iv=setInterval(go,400);
|
| 270 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 271 |
+
}
|
| 272 |
+
(function(){
|
| 273 |
+
wxAuto(()=>vids, ()=>playing, ()=>{ setPlaying(true); return master.play(); }, ()=>setPlaying(false));
|
| 274 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 275 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 276 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
teleopSample/drink_ep1.html
CHANGED
|
@@ -226,17 +226,51 @@ CAMS.forEach(c=>{ const a=document.createElement('a'); a.className='file-chip';
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
-
(
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
-
vids.forEach(v
|
| 238 |
-
window.addEventListener('load',
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 241 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 242 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
|
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 230 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 231 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 232 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 233 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 234 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 235 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 236 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 237 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 238 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 239 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 240 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 241 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 242 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 243 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 244 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 245 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 246 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 247 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 248 |
+
function go(force){
|
| 249 |
+
if(started||isPlaying())return;
|
| 250 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 251 |
+
if(wxWait&&!force)return;
|
| 252 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 253 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 254 |
+
vs.forEach(function(v){v.muted=true;});
|
| 255 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 256 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 257 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 258 |
+
}
|
| 259 |
+
function arm(){
|
| 260 |
+
if(armed)return; armed=true;
|
| 261 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 262 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 263 |
+
armed=false;go(true);}
|
| 264 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 265 |
+
wxCall(function(){go(true);});
|
| 266 |
}
|
| 267 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 268 |
+
window.addEventListener('load',function(){go();});
|
| 269 |
+
iv=setInterval(go,400);
|
| 270 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 271 |
+
}
|
| 272 |
+
(function(){
|
| 273 |
+
wxAuto(()=>vids, ()=>playing, ()=>{ setPlaying(true); return master.play(); }, ()=>setPlaying(false));
|
| 274 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 275 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 276 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
teleopSample/ep0/viewer.html
CHANGED
|
@@ -380,28 +380,53 @@ window.addEventListener('load',update);
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
-
(
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 391 |
vs.forEach(function(v){v.muted=true;});
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e';
|
| 396 |
-
});
|
| 397 |
}
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 404 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
|
| 406 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 407 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
|
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 384 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 385 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 386 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 387 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 388 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 389 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 390 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 391 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 392 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 393 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 394 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 395 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 396 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 397 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 398 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 399 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 400 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 401 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 402 |
+
function go(force){
|
| 403 |
+
if(started||isPlaying())return;
|
| 404 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 405 |
+
if(wxWait&&!force)return;
|
| 406 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 407 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 408 |
vs.forEach(function(v){v.muted=true;});
|
| 409 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 410 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 411 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
|
|
|
|
|
|
| 412 |
}
|
| 413 |
+
function arm(){
|
| 414 |
+
if(armed)return; armed=true;
|
| 415 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 416 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 417 |
+
armed=false;go(true);}
|
| 418 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 419 |
+
wxCall(function(){go(true);});
|
| 420 |
+
}
|
| 421 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 422 |
+
window.addEventListener('load',function(){go();});
|
| 423 |
+
iv=setInterval(go,400);
|
| 424 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 425 |
+
}
|
| 426 |
+
wxAuto(function(){try{return videos;}catch(e){return [];}}, function(){return playing;},
|
| 427 |
+
function(){ return Promise.all(videos.map(function(v){return Promise.resolve(v.play());})).then(function(){
|
| 428 |
+
playing=true; playBtn.textContent='\u275a\u275a'; requestAnimationFrame(loop); }); },
|
| 429 |
+
function(){ videos.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e'; });
|
| 430 |
|
| 431 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 432 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
teleopSample/ep1/viewer.html
CHANGED
|
@@ -380,28 +380,53 @@ window.addEventListener('load',update);
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
-
(
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 391 |
vs.forEach(function(v){v.muted=true;});
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e';
|
| 396 |
-
});
|
| 397 |
}
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 404 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
|
| 406 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 407 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
|
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 384 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 385 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 386 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 387 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 388 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 389 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 390 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 391 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 392 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 393 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 394 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 395 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 396 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 397 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 398 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 399 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 400 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 401 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 402 |
+
function go(force){
|
| 403 |
+
if(started||isPlaying())return;
|
| 404 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 405 |
+
if(wxWait&&!force)return;
|
| 406 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 407 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 408 |
vs.forEach(function(v){v.muted=true;});
|
| 409 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 410 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 411 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
|
|
|
|
|
|
| 412 |
}
|
| 413 |
+
function arm(){
|
| 414 |
+
if(armed)return; armed=true;
|
| 415 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 416 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 417 |
+
armed=false;go(true);}
|
| 418 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 419 |
+
wxCall(function(){go(true);});
|
| 420 |
+
}
|
| 421 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 422 |
+
window.addEventListener('load',function(){go();});
|
| 423 |
+
iv=setInterval(go,400);
|
| 424 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 425 |
+
}
|
| 426 |
+
wxAuto(function(){try{return videos;}catch(e){return [];}}, function(){return playing;},
|
| 427 |
+
function(){ return Promise.all(videos.map(function(v){return Promise.resolve(v.play());})).then(function(){
|
| 428 |
+
playing=true; playBtn.textContent='\u275a\u275a'; requestAnimationFrame(loop); }); },
|
| 429 |
+
function(){ videos.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e'; });
|
| 430 |
|
| 431 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 432 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
teleopSample/ep2/viewer.html
CHANGED
|
@@ -380,28 +380,53 @@ window.addEventListener('load',update);
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
-
(
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 391 |
vs.forEach(function(v){v.muted=true;});
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e';
|
| 396 |
-
});
|
| 397 |
}
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 404 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
|
| 406 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 407 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
|
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 384 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 385 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 386 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 387 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 388 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 389 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 390 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 391 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 392 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 393 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 394 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 395 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 396 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 397 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 398 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 399 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 400 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 401 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 402 |
+
function go(force){
|
| 403 |
+
if(started||isPlaying())return;
|
| 404 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 405 |
+
if(wxWait&&!force)return;
|
| 406 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 407 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 408 |
vs.forEach(function(v){v.muted=true;});
|
| 409 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 410 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 411 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
|
|
|
|
|
|
| 412 |
}
|
| 413 |
+
function arm(){
|
| 414 |
+
if(armed)return; armed=true;
|
| 415 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 416 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 417 |
+
armed=false;go(true);}
|
| 418 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 419 |
+
wxCall(function(){go(true);});
|
| 420 |
+
}
|
| 421 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 422 |
+
window.addEventListener('load',function(){go();});
|
| 423 |
+
iv=setInterval(go,400);
|
| 424 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 425 |
+
}
|
| 426 |
+
wxAuto(function(){try{return videos;}catch(e){return [];}}, function(){return playing;},
|
| 427 |
+
function(){ return Promise.all(videos.map(function(v){return Promise.resolve(v.play());})).then(function(){
|
| 428 |
+
playing=true; playBtn.textContent='\u275a\u275a'; requestAnimationFrame(loop); }); },
|
| 429 |
+
function(){ videos.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e'; });
|
| 430 |
|
| 431 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 432 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
teleopSample/ep3/viewer.html
CHANGED
|
@@ -380,28 +380,53 @@ window.addEventListener('load',update);
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
-
(
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 391 |
vs.forEach(function(v){v.muted=true;});
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e';
|
| 396 |
-
});
|
| 397 |
}
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 404 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
|
| 406 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 407 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
|
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 384 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 385 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 386 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 387 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 388 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 389 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 390 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 391 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 392 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 393 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 394 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 395 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 396 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 397 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 398 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 399 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 400 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 401 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 402 |
+
function go(force){
|
| 403 |
+
if(started||isPlaying())return;
|
| 404 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 405 |
+
if(wxWait&&!force)return;
|
| 406 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 407 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 408 |
vs.forEach(function(v){v.muted=true;});
|
| 409 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 410 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 411 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
|
|
|
|
|
|
| 412 |
}
|
| 413 |
+
function arm(){
|
| 414 |
+
if(armed)return; armed=true;
|
| 415 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 416 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 417 |
+
armed=false;go(true);}
|
| 418 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 419 |
+
wxCall(function(){go(true);});
|
| 420 |
+
}
|
| 421 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 422 |
+
window.addEventListener('load',function(){go();});
|
| 423 |
+
iv=setInterval(go,400);
|
| 424 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 425 |
+
}
|
| 426 |
+
wxAuto(function(){try{return videos;}catch(e){return [];}}, function(){return playing;},
|
| 427 |
+
function(){ return Promise.all(videos.map(function(v){return Promise.resolve(v.play());})).then(function(){
|
| 428 |
+
playing=true; playBtn.textContent='\u275a\u275a'; requestAnimationFrame(loop); }); },
|
| 429 |
+
function(){ videos.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e'; });
|
| 430 |
|
| 431 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 432 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
teleopSample/ep4/viewer.html
CHANGED
|
@@ -380,28 +380,53 @@ window.addEventListener('load',update);
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
-
(
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 391 |
vs.forEach(function(v){v.muted=true;});
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e';
|
| 396 |
-
});
|
| 397 |
}
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 404 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
|
| 406 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 407 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
|
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 384 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 385 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 386 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 387 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 388 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 389 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 390 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 391 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 392 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 393 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 394 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 395 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 396 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 397 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 398 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 399 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 400 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 401 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 402 |
+
function go(force){
|
| 403 |
+
if(started||isPlaying())return;
|
| 404 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 405 |
+
if(wxWait&&!force)return;
|
| 406 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 407 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 408 |
vs.forEach(function(v){v.muted=true;});
|
| 409 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 410 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 411 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
|
|
|
|
|
|
| 412 |
}
|
| 413 |
+
function arm(){
|
| 414 |
+
if(armed)return; armed=true;
|
| 415 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 416 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 417 |
+
armed=false;go(true);}
|
| 418 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 419 |
+
wxCall(function(){go(true);});
|
| 420 |
+
}
|
| 421 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 422 |
+
window.addEventListener('load',function(){go();});
|
| 423 |
+
iv=setInterval(go,400);
|
| 424 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 425 |
+
}
|
| 426 |
+
wxAuto(function(){try{return videos;}catch(e){return [];}}, function(){return playing;},
|
| 427 |
+
function(){ return Promise.all(videos.map(function(v){return Promise.resolve(v.play());})).then(function(){
|
| 428 |
+
playing=true; playBtn.textContent='\u275a\u275a'; requestAnimationFrame(loop); }); },
|
| 429 |
+
function(){ videos.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e'; });
|
| 430 |
|
| 431 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 432 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
teleopSample/ep5/viewer.html
CHANGED
|
@@ -380,28 +380,53 @@ window.addEventListener('load',update);
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
-
(
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 391 |
vs.forEach(function(v){v.muted=true;});
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e';
|
| 396 |
-
});
|
| 397 |
}
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 404 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
|
| 406 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 407 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
|
|
|
| 380 |
button. Videos are muted, which is what browser autoplay policies require;
|
| 381 |
if a browser still refuses, we roll back to the paused state so the manual
|
| 382 |
button keeps working exactly as before. */
|
| 383 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 384 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 385 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 386 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 387 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 388 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 389 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 390 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 391 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 392 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 393 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 394 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 395 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 396 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 397 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 398 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 399 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 400 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 401 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 402 |
+
function go(force){
|
| 403 |
+
if(started||isPlaying())return;
|
| 404 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 405 |
+
if(wxWait&&!force)return;
|
| 406 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 407 |
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 408 |
vs.forEach(function(v){v.muted=true;});
|
| 409 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 410 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 411 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
|
|
|
|
|
|
| 412 |
}
|
| 413 |
+
function arm(){
|
| 414 |
+
if(armed)return; armed=true;
|
| 415 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 416 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 417 |
+
armed=false;go(true);}
|
| 418 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 419 |
+
wxCall(function(){go(true);});
|
| 420 |
+
}
|
| 421 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 422 |
+
window.addEventListener('load',function(){go();});
|
| 423 |
+
iv=setInterval(go,400);
|
| 424 |
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 425 |
+
}
|
| 426 |
+
wxAuto(function(){try{return videos;}catch(e){return [];}}, function(){return playing;},
|
| 427 |
+
function(){ return Promise.all(videos.map(function(v){return Promise.resolve(v.play());})).then(function(){
|
| 428 |
+
playing=true; playBtn.textContent='\u275a\u275a'; requestAnimationFrame(loop); }); },
|
| 429 |
+
function(){ videos.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.textContent='\u25b6\ufe0e'; });
|
| 430 |
|
| 431 |
/* demo loop: when the clip ends, wrap every video back to the start and keep playing.
|
| 432 |
ended is the primary trigger; the interval is a fallback for decoders that never fire it. */
|
teleopSample/faberU/t2_water_bottle_into_steel_tray/20260707052527/viewer.html
CHANGED
|
@@ -72,5 +72,5 @@
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"341.1 KB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"559.3 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"1.3 MB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"475.8 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.3 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"479.9 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the clear plastic water bottle from the retail checkout counter and set it upright in the white plastic tray.","object":"clear plastic water bottle (red label with QR code)","target":"white plastic tray","scene":"retail store checkout counter, shelves of household goods behind"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
-
<script src="../../../lib/viewer.js"></script>
|
| 76 |
</body></html>
|
|
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"341.1 KB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"559.3 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"1.3 MB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"475.8 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.3 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"479.9 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the clear plastic water bottle from the retail checkout counter and set it upright in the white plastic tray.","object":"clear plastic water bottle (red label with QR code)","target":"white plastic tray","scene":"retail store checkout counter, shelves of household goods behind"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
+
<script src="../../../lib/viewer.js?v=wx2"></script>
|
| 76 |
</body></html>
|
teleopSample/faberU/t2_water_bottle_into_steel_tray/20260707052609/viewer.html
CHANGED
|
@@ -72,5 +72,5 @@
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"359.3 KB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"557.4 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"1.3 MB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"443.5 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.6 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"459.0 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the clear plastic water bottle from the retail checkout counter and set it upright in the white plastic tray.","object":"clear plastic water bottle (red label with QR code)","target":"white plastic tray","scene":"retail store checkout counter, shelves of household goods behind"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
-
<script src="../../../lib/viewer.js"></script>
|
| 76 |
</body></html>
|
|
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"359.3 KB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"557.4 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"1.3 MB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"443.5 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.6 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"459.0 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the clear plastic water bottle from the retail checkout counter and set it upright in the white plastic tray.","object":"clear plastic water bottle (red label with QR code)","target":"white plastic tray","scene":"retail store checkout counter, shelves of household goods behind"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
+
<script src="../../../lib/viewer.js?v=wx2"></script>
|
| 76 |
</body></html>
|
teleopSample/faberU/t3_juice_bottle_onto_plate/20260724050737/viewer.html
CHANGED
|
@@ -72,5 +72,5 @@
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.5 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"509.3 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"274.3 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"483.9 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.0 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"449.9 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the CapriSun juice bottle from the yellow table and stand it upright on the blue-and-white patterned plate.","object":"CapriSun juice bottle (green/blue label, orange cap)","target":"blue-and-white patterned plate","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
-
<script src="../../../lib/viewer.js"></script>
|
| 76 |
</body></html>
|
|
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.5 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"509.3 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"274.3 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"483.9 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.0 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"449.9 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the CapriSun juice bottle from the yellow table and stand it upright on the blue-and-white patterned plate.","object":"CapriSun juice bottle (green/blue label, orange cap)","target":"blue-and-white patterned plate","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
+
<script src="../../../lib/viewer.js?v=wx2"></script>
|
| 76 |
</body></html>
|
teleopSample/faberU/t3_juice_bottle_onto_plate/20260724050827/viewer.html
CHANGED
|
@@ -72,5 +72,5 @@
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.5 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"501.9 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"315.9 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"489.9 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.1 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"481.5 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the CapriSun juice bottle from the yellow table and stand it upright on the blue-and-white patterned plate.","object":"CapriSun juice bottle (green/blue label, orange cap)","target":"blue-and-white patterned plate","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
-
<script src="../../../lib/viewer.js"></script>
|
| 76 |
</body></html>
|
|
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.5 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"501.9 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"315.9 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"489.9 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.1 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"481.5 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the CapriSun juice bottle from the yellow table and stand it upright on the blue-and-white patterned plate.","object":"CapriSun juice bottle (green/blue label, orange cap)","target":"blue-and-white patterned plate","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
+
<script src="../../../lib/viewer.js?v=wx2"></script>
|
| 76 |
</body></html>
|
teleopSample/faberU/t4_juice_bottle_into_black_tray/20260728075709/viewer.html
CHANGED
|
@@ -72,5 +72,5 @@
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"829.7 KB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"274.1 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"201.2 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"266.2 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"1.3 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"255.3 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the green juice bottle from the yellow table and place it into the black tray.","object":"green juice bottle (yellow cap)","target":"black tray","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
-
<script src="../../../lib/viewer.js"></script>
|
| 76 |
</body></html>
|
|
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"829.7 KB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"274.1 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"201.2 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"266.2 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"1.3 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"255.3 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the green juice bottle from the yellow table and place it into the black tray.","object":"green juice bottle (yellow cap)","target":"black tray","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
+
<script src="../../../lib/viewer.js?v=wx2"></script>
|
| 76 |
</body></html>
|
teleopSample/faberU/t4_juice_bottle_into_black_tray/20260728080339/viewer.html
CHANGED
|
@@ -72,5 +72,5 @@
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.0 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"288.0 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"227.8 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"296.9 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"1.7 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"280.4 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the green juice bottle from the yellow table and place it into the black tray.","object":"green juice bottle (yellow cap)","target":"black tray","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
-
<script src="../../../lib/viewer.js"></script>
|
| 76 |
</body></html>
|
|
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.0 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"288.0 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"227.8 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"296.9 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"1.7 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"280.4 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the green juice bottle from the yellow table and place it into the black tray.","object":"green juice bottle (yellow cap)","target":"black tray","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
+
<script src="../../../lib/viewer.js?v=wx2"></script>
|
| 76 |
</body></html>
|
teleopSample/faberU/t6_pick_up_green_tea_bottle/20260730045050/viewer.html
CHANGED
|
@@ -72,5 +72,5 @@
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.0 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"348.4 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"170.4 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"290.2 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"1.9 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"275.5 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the green-tea bottle from the yellow table and lift it clear of the surface; the episode ends with the bottle still held.","object":"green-tea bottle (transparent, green label)","target":"- (pick-and-hold task)","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
-
<script src="../../../lib/viewer.js"></script>
|
| 76 |
</body></html>
|
|
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.0 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"348.4 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"170.4 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"290.2 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"1.9 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"275.5 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the green-tea bottle from the yellow table and lift it clear of the surface; the episode ends with the bottle still held.","object":"green-tea bottle (transparent, green label)","target":"- (pick-and-hold task)","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
+
<script src="../../../lib/viewer.js?v=wx2"></script>
|
| 76 |
</body></html>
|
teleopSample/faberU/t6_pick_up_green_tea_bottle/20260730045152/viewer.html
CHANGED
|
@@ -72,5 +72,5 @@
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.4 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"569.8 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"245.0 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"518.8 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.4 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"473.6 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the green-tea bottle from the yellow table and lift it clear of the surface; the episode ends with the bottle still held.","object":"green-tea bottle (transparent, green label)","target":"- (pick-and-hold task)","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
-
<script src="../../../lib/viewer.js"></script>
|
| 76 |
</body></html>
|
|
|
|
| 72 |
<script id="cams" type="application/json">[{"key":"hand_left_color","label":"wrist \u00b7 left","src":"video/hand_left_color.mp4","size":"1.4 MB"},{"key":"head_color","label":"head \u00b7 color","src":"video/head_color.mp4","size":"569.8 KB"},{"key":"hand_right_color","label":"wrist \u00b7 right","src":"video/hand_right_color.mp4","size":"245.0 KB"},{"key":"head_stereo_left","label":"head \u00b7 stereo L","src":"video/head_stereo_left.mp4","size":"518.8 KB"},{"key":"head_depth","label":"head \u00b7 depth","src":"video/head_depth.mp4","size":"2.4 MB"},{"key":"head_stereo_right","label":"head \u00b7 stereo R","src":"video/head_stereo_right.mp4","size":"473.6 KB"}]</script>
|
| 73 |
<script id="task" type="application/json">{"instruction":"Pick up the green-tea bottle from the yellow table and lift it clear of the surface; the episode ends with the bottle still held.","object":"green-tea bottle (transparent, green label)","target":"- (pick-and-hold task)","scene":"lab bay behind acoustic screens, yellow table"}</script>
|
| 74 |
<script src="../../../lib/steps-ui.js"></script>
|
| 75 |
+
<script src="../../../lib/viewer.js?v=wx2"></script>
|
| 76 |
</body></html>
|
teleopSample/lib/viewer.js
CHANGED
|
@@ -54,16 +54,50 @@
|
|
| 54 |
else videos.forEach(v => v.pause());
|
| 55 |
}
|
| 56 |
playBtn.onclick = () => setPlaying(!playing);
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
}
|
| 64 |
-
|
| 65 |
-
window.addEventListener('load', tryStart);
|
| 66 |
-
setTimeout(() => clearInterval(iv), 90000);
|
| 67 |
setInterval(() => {
|
| 68 |
if (playing && master.currentTime >= DUR - 0.08) {
|
| 69 |
seekTo(0); videos.forEach(v => v.play().catch(() => {}));
|
|
|
|
| 54 |
else videos.forEach(v => v.pause());
|
| 55 |
}
|
| 56 |
playBtn.onclick = () => setPlaying(!playing);
|
| 57 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 58 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 59 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 60 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 61 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 62 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 63 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 64 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 65 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 66 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 67 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 68 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 69 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 70 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 71 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 72 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 73 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 74 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 75 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 76 |
+
function go(force){
|
| 77 |
+
if(started||isPlaying())return;
|
| 78 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 79 |
+
if(wxWait&&!force)return;
|
| 80 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 81 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 82 |
+
vs.forEach(function(v){v.muted=true;});
|
| 83 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 84 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 85 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 86 |
+
}
|
| 87 |
+
function arm(){
|
| 88 |
+
if(armed)return; armed=true;
|
| 89 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 90 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 91 |
+
armed=false;go(true);}
|
| 92 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 93 |
+
wxCall(function(){go(true);});
|
| 94 |
+
}
|
| 95 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 96 |
+
window.addEventListener('load',function(){go();});
|
| 97 |
+
iv=setInterval(go,400);
|
| 98 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 99 |
}
|
| 100 |
+
wxAuto(() => videos, () => playing, () => { setPlaying(true); return master.play(); }, () => setPlaying(false));
|
|
|
|
|
|
|
| 101 |
setInterval(() => {
|
| 102 |
if (playing && master.currentTime >= DUR - 0.08) {
|
| 103 |
seekTo(0); videos.forEach(v => v.play().catch(() => {}));
|
teleopSample/mem_ep0.html
CHANGED
|
@@ -226,17 +226,51 @@ CAMS.forEach(c=>{ const a=document.createElement('a'); a.className='file-chip';
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
-
(
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
-
vids.forEach(v
|
| 238 |
-
window.addEventListener('load',
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 241 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 242 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
|
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 230 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 231 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 232 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 233 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 234 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 235 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 236 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 237 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 238 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 239 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 240 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 241 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 242 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 243 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 244 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 245 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 246 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 247 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 248 |
+
function go(force){
|
| 249 |
+
if(started||isPlaying())return;
|
| 250 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 251 |
+
if(wxWait&&!force)return;
|
| 252 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 253 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 254 |
+
vs.forEach(function(v){v.muted=true;});
|
| 255 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 256 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 257 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 258 |
+
}
|
| 259 |
+
function arm(){
|
| 260 |
+
if(armed)return; armed=true;
|
| 261 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 262 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 263 |
+
armed=false;go(true);}
|
| 264 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 265 |
+
wxCall(function(){go(true);});
|
| 266 |
}
|
| 267 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 268 |
+
window.addEventListener('load',function(){go();});
|
| 269 |
+
iv=setInterval(go,400);
|
| 270 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 271 |
+
}
|
| 272 |
+
(function(){
|
| 273 |
+
wxAuto(()=>vids, ()=>playing, ()=>{ setPlaying(true); return master.play(); }, ()=>setPlaying(false));
|
| 274 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 275 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 276 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
teleopSample/mem_ep1.html
CHANGED
|
@@ -226,17 +226,51 @@ CAMS.forEach(c=>{ const a=document.createElement('a'); a.className='file-chip';
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
-
(
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
-
vids.forEach(v
|
| 238 |
-
window.addEventListener('load',
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 241 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 242 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
|
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 230 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 231 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 232 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 233 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 234 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 235 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 236 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 237 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 238 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 239 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 240 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 241 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 242 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 243 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 244 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 245 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 246 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 247 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 248 |
+
function go(force){
|
| 249 |
+
if(started||isPlaying())return;
|
| 250 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 251 |
+
if(wxWait&&!force)return;
|
| 252 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 253 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 254 |
+
vs.forEach(function(v){v.muted=true;});
|
| 255 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 256 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 257 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 258 |
+
}
|
| 259 |
+
function arm(){
|
| 260 |
+
if(armed)return; armed=true;
|
| 261 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 262 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 263 |
+
armed=false;go(true);}
|
| 264 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 265 |
+
wxCall(function(){go(true);});
|
| 266 |
}
|
| 267 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 268 |
+
window.addEventListener('load',function(){go();});
|
| 269 |
+
iv=setInterval(go,400);
|
| 270 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 271 |
+
}
|
| 272 |
+
(function(){
|
| 273 |
+
wxAuto(()=>vids, ()=>playing, ()=>{ setPlaying(true); return master.play(); }, ()=>setPlaying(false));
|
| 274 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 275 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 276 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
teleopSample/sort_ep0.html
CHANGED
|
@@ -226,17 +226,51 @@ CAMS.forEach(c=>{ const a=document.createElement('a'); a.className='file-chip';
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
-
(
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
-
vids.forEach(v
|
| 238 |
-
window.addEventListener('load',
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 241 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 242 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
|
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 230 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 231 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 232 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 233 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 234 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 235 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 236 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 237 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 238 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 239 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 240 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 241 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 242 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 243 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 244 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 245 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 246 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 247 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 248 |
+
function go(force){
|
| 249 |
+
if(started||isPlaying())return;
|
| 250 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 251 |
+
if(wxWait&&!force)return;
|
| 252 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 253 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 254 |
+
vs.forEach(function(v){v.muted=true;});
|
| 255 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 256 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 257 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 258 |
+
}
|
| 259 |
+
function arm(){
|
| 260 |
+
if(armed)return; armed=true;
|
| 261 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 262 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 263 |
+
armed=false;go(true);}
|
| 264 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 265 |
+
wxCall(function(){go(true);});
|
| 266 |
}
|
| 267 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 268 |
+
window.addEventListener('load',function(){go();});
|
| 269 |
+
iv=setInterval(go,400);
|
| 270 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 271 |
+
}
|
| 272 |
+
(function(){
|
| 273 |
+
wxAuto(()=>vids, ()=>playing, ()=>{ setPlaying(true); return master.play(); }, ()=>setPlaying(false));
|
| 274 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 275 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 276 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
teleopSample/sort_ep1.html
CHANGED
|
@@ -226,17 +226,51 @@ CAMS.forEach(c=>{ const a=document.createElement('a'); a.className='file-chip';
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
-
(
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
-
vids.forEach(v
|
| 238 |
-
window.addEventListener('load',
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 241 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 242 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
|
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 230 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 231 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 232 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 233 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 234 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 235 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 236 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 237 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 238 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 239 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 240 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 241 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 242 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 243 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 244 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 245 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 246 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 247 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 248 |
+
function go(force){
|
| 249 |
+
if(started||isPlaying())return;
|
| 250 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 251 |
+
if(wxWait&&!force)return;
|
| 252 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 253 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 254 |
+
vs.forEach(function(v){v.muted=true;});
|
| 255 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 256 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 257 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 258 |
+
}
|
| 259 |
+
function arm(){
|
| 260 |
+
if(armed)return; armed=true;
|
| 261 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 262 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 263 |
+
armed=false;go(true);}
|
| 264 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 265 |
+
wxCall(function(){go(true);});
|
| 266 |
}
|
| 267 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 268 |
+
window.addEventListener('load',function(){go();});
|
| 269 |
+
iv=setInterval(go,400);
|
| 270 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 271 |
+
}
|
| 272 |
+
(function(){
|
| 273 |
+
wxAuto(()=>vids, ()=>playing, ()=>{ setPlaying(true); return master.play(); }, ()=>setPlaying(false));
|
| 274 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 275 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 276 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
teleopSample/wipe_ep0.html
CHANGED
|
@@ -226,17 +226,51 @@ CAMS.forEach(c=>{ const a=document.createElement('a'); a.className='file-chip';
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
-
(
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
-
vids.forEach(v
|
| 238 |
-
window.addEventListener('load',
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 241 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 242 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
|
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 230 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 231 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 232 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 233 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 234 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 235 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 236 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 237 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 238 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 239 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 240 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 241 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 242 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 243 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 244 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 245 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 246 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 247 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 248 |
+
function go(force){
|
| 249 |
+
if(started||isPlaying())return;
|
| 250 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 251 |
+
if(wxWait&&!force)return;
|
| 252 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 253 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 254 |
+
vs.forEach(function(v){v.muted=true;});
|
| 255 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 256 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 257 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 258 |
+
}
|
| 259 |
+
function arm(){
|
| 260 |
+
if(armed)return; armed=true;
|
| 261 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 262 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 263 |
+
armed=false;go(true);}
|
| 264 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 265 |
+
wxCall(function(){go(true);});
|
| 266 |
}
|
| 267 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 268 |
+
window.addEventListener('load',function(){go();});
|
| 269 |
+
iv=setInterval(go,400);
|
| 270 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 271 |
+
}
|
| 272 |
+
(function(){
|
| 273 |
+
wxAuto(()=>vids, ()=>playing, ()=>{ setPlaying(true); return master.play(); }, ()=>setPlaying(false));
|
| 274 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 275 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 276 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
teleopSample/wipe_ep1.html
CHANGED
|
@@ -226,17 +226,51 @@ CAMS.forEach(c=>{ const a=document.createElement('a'); a.className='file-chip';
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
-
(
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
-
vids.forEach(v
|
| 238 |
-
window.addEventListener('load',
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 241 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 242 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|
|
|
|
| 226 |
const a=document.createElement('a'); a.className='file-chip'; a.href=href; a.innerHTML=lbl; fm.appendChild(a); });
|
| 227 |
|
| 228 |
// ---- autoplay (muted) + loop
|
| 229 |
+
/* autoplay that also works in in-app WebViews (WeChat iOS/Android X5): they ignore
|
| 230 |
+
preload, so readyState>=2 / canplay never arrive before play() — don't wait forever.
|
| 231 |
+
WeChat injects WeixinJSBridge into the top frame only (this page usually runs in
|
| 232 |
+
index.html's iframe) and play() from its callback needs no tap; if play() is still
|
| 233 |
+
refused, roll back to paused and retry on the first tap. ?wxdebug on the top URL
|
| 234 |
+
shows a log. start() must return the play() promise, stop() resets the UI. */
|
| 235 |
+
function wxAuto(vids,isPlaying,start,stop){
|
| 236 |
+
var inWx=/MicroMessenger/i.test(navigator.userAgent), t0=Date.now(), started=false, armed=false, wxTried=false, wxWait=false, iv=null, dbgEl=null;
|
| 237 |
+
function frames(){var ws=[];try{ws.push(window.top);}catch(e){}try{ws.push(window.parent);}catch(e){}ws.push(window);return ws;}
|
| 238 |
+
function dbg(m){try{if(!/wxdebug/.test(window.top.location.search+window.location.search))return;}catch(e){return;}
|
| 239 |
+
if(!dbgEl){dbgEl=document.createElement('pre');dbgEl.style.cssText='position:fixed;left:0;bottom:0;z-index:99999;max-height:40vh;overflow:auto;margin:0;padding:4px 6px;font:10px/1.3 monospace;background:rgba(0,0,0,.8);color:#7f7;pointer-events:none;white-space:pre-wrap';document.body.appendChild(dbgEl);}
|
| 240 |
+
dbgEl.textContent+=(performance.now()/1000).toFixed(2)+' '+m+'\n';}
|
| 241 |
+
function bridge(){var ws=frames();for(var i=0;i<ws.length;i++){try{if(ws[i].WeixinJSBridge&&ws[i].WeixinJSBridge.invoke)return ws[i].WeixinJSBridge;}catch(e){}}return null;}
|
| 242 |
+
function wxCall(cb){var b=bridge();dbg('bridge '+(b?'yes':'no')+' top='+(window.top===window));
|
| 243 |
+
if(b){b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});return;}
|
| 244 |
+
function on(){var b=bridge();dbg('bridge ready '+(b?'yes':'no'));if(b)b.invoke('getNetworkType',{},function(){dbg('bridge cb');cb();});}
|
| 245 |
+
frames().forEach(function(w){try{w.document.addEventListener('WeixinJSBridgeReady',on,{once:true});}catch(e){}});}
|
| 246 |
+
function ready(vs){return vs.every(function(v){return v.readyState>=2;})||
|
| 247 |
+
(Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}));}
|
| 248 |
+
function go(force){
|
| 249 |
+
if(started||isPlaying())return;
|
| 250 |
+
var vs=vids(); if(!vs.length||(!force&&!ready(vs)))return;
|
| 251 |
+
if(wxWait&&!force)return;
|
| 252 |
+
if(inWx&&!wxTried){wxTried=wxWait=true;wxCall(function(){wxWait=false;go(true);});setTimeout(function(){wxWait=false;go(true);},1500);return;}
|
| 253 |
+
started=true; if(iv){clearInterval(iv);iv=null;}
|
| 254 |
+
vs.forEach(function(v){v.muted=true;});
|
| 255 |
+
dbg('play() rs='+vs.map(function(v){return v.readyState;}).join(','));
|
| 256 |
+
Promise.resolve(start()).then(function(){dbg('play ok');}).catch(function(err){
|
| 257 |
+
dbg('play rejected '+(err&&err.name)); try{stop();}catch(e){} started=false; arm(); });
|
| 258 |
+
}
|
| 259 |
+
function arm(){
|
| 260 |
+
if(armed)return; armed=true;
|
| 261 |
+
function once(ev){if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
|
| 262 |
+
document.removeEventListener('touchend',once,true);document.removeEventListener('click',once,true);
|
| 263 |
+
armed=false;go(true);}
|
| 264 |
+
document.addEventListener('touchend',once,true);document.addEventListener('click',once,true);
|
| 265 |
+
wxCall(function(){go(true);});
|
| 266 |
}
|
| 267 |
+
vids().forEach(function(v){v.addEventListener('canplay',function(){go();});v.addEventListener('loadeddata',function(){go();});});
|
| 268 |
+
window.addEventListener('load',function(){go();});
|
| 269 |
+
iv=setInterval(go,400);
|
| 270 |
+
setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
|
| 271 |
+
}
|
| 272 |
+
(function(){
|
| 273 |
+
wxAuto(()=>vids, ()=>playing, ()=>{ setPlaying(true); return master.play(); }, ()=>setPlaying(false));
|
| 274 |
setInterval(()=>{ if(playing&&master&&master.currentTime>=DUR-0.08){
|
| 275 |
try{master.currentTime=0;}catch(e){} syncSlaves(0,true);
|
| 276 |
if(playing) vids.forEach(v=>v.play().catch(()=>{})); }},200);
|