Spaces:
Running
Running
Download assets/scene-assets.js from SimWorld-AI/Code4Scene: direct link, hf CLI and curl.
- Browser
- Download file 4.51 kB
-
https://huggingface.co/spaces/SimWorld-AI/Code4Scene/resolve/main/assets/scene-assets.js
- Command line
-
hf download hf://spaces/SimWorld-AI/Code4Scene/assets/scene-assets.js
-
curl -L -o scene-assets.js https://huggingface.co/spaces/SimWorld-AI/Code4Scene/resolve/main/assets/scene-assets.js
4.51 kB
| // A bounded, reference-counted queue. A shared scene has one job regardless of how many cards display it. | |
| export class AssetQueue { | |
| constructor({load,dispose,maxConcurrent=2,maxIdle=1,maxBytes=128*1024*1024,onChange=()=>{}}){Object.assign(this,{load,dispose,maxConcurrent,maxIdle,maxBytes,onChange});this.entries=new Map();this.running=0;this.peakRunning=0;} | |
| retain(key,listener,priority=()=>0){ | |
| let e=this.entries.get(key); | |
| if(!e||e.state==='error'||e.state==='cancelled'){ | |
| e={key,users:new Set(),state:'queued',asset:null,controller:new AbortController(),last:Date.now(),bytes:0};this.entries.set(key,e); | |
| } | |
| const user={listener,priority};e.users.add(user);e.last=Date.now();queueMicrotask(()=>{if(e.users.has(user))listener(e);this.pump();});this.changed(); | |
| let released=false; | |
| return {entry:e,release:()=>{if(released)return;released=true;e.users.delete(user);e.last=Date.now();if(!e.users.size&&e.asset)e.asset.materials?.pause();if(!e.users.size&&!e.asset){e.state='cancelled';e.controller.abort();if(this.entries.get(key)===e)this.entries.delete(key);}this.evict();this.pump();this.changed();}}; | |
| } | |
| notify(e,state,detail={}){if(e.controller.signal.aborted)return;Object.assign(e,detail,{state});for(const u of e.users)u.listener(e);this.changed();} | |
| pump(){ | |
| while(this.running<this.maxConcurrent){const queued=[...this.entries.values()].filter(e=>e.state==='queued'&&e.users.size).sort((a,b)=>Math.min(...[...a.users].map(u=>u.priority()))-Math.min(...[...b.users].map(u=>u.priority())));if(!queued.length)break;const e=queued[0];e.state='loading';this.running++;this.peakRunning=Math.max(this.peakRunning,this.running);this.run(e);} | |
| } | |
| async run(e){ | |
| try{ | |
| let asset; | |
| for(let attempt=0;attempt<2;attempt++){ | |
| try{asset=await this.load(e.key,e.controller.signal,(state,detail)=>this.notify(e,state,detail));break;} | |
| catch(error){if(e.controller.signal.aborted)throw error;if(attempt===1)throw error;this.notify(e,'retrying');await new Promise(r=>setTimeout(r,800));} | |
| } | |
| if(e.controller.signal.aborted||!e.users.size){this.dispose(asset);return;} | |
| e.asset=asset;e.bytes=asset.bytes||0;this.notify(e,'ready'); | |
| }catch(error){if(!e.controller.signal.aborted)this.notify(e,'error',{error});} | |
| finally{this.running--;this.evict();this.pump();this.changed();} | |
| } | |
| evict(){ | |
| const idle=[...this.entries.values()].filter(e=>!e.users.size&&e.asset).sort((a,b)=>a.last-b.last);let total=[...this.entries.values()].reduce((n,e)=>n+e.bytes,0); | |
| while(idle.length&&(idle.length>this.maxIdle||total>this.maxBytes)){const e=idle.shift();total-=e.bytes;this.dispose(e.asset);e.asset=null;if(this.entries.get(e.key)===e)this.entries.delete(e.key);} | |
| } | |
| changed(){this.onChange({running:this.running,peak:this.peakRunning,queued:[...this.entries.values()].filter(e=>e.state==='queued').length,cached:[...this.entries.values()].filter(e=>e.asset).length,bytes:[...this.entries.values()].reduce((n,e)=>n+e.bytes,0)});} | |
| } | |
| // A stalled body must not leave a loading card forever. Idle and total limits cover headers and streamed bytes. | |
| export async function fetchSceneBytes(url,signal,progress,{idleMs=20000,totalMs=90000,fetcher=fetch}={}){ | |
| const abort=new AbortController();let idle,total,reason=''; | |
| const stop=()=>abort.abort();signal.addEventListener('abort',stop,{once:true});if(signal.aborted)stop(); | |
| const reset=()=>{clearTimeout(idle);idle=setTimeout(()=>{reason='The scene download stopped responding.';abort.abort();},idleMs);}; | |
| reset();total=setTimeout(()=>{reason='The scene download took too long.';abort.abort();},totalMs); | |
| try{ | |
| const response=await fetcher(url,{signal:abort.signal});if(!response.ok)throw new Error('Scene request failed (HTTP '+response.status+').'); | |
| const size=Number(response.headers.get('content-length'))||0,reader=response.body.getReader(),chunks=[];let received=0,last=0; | |
| for(;;){const {done,value}=await reader.read();if(done)break;reset();chunks.push(value);received+=value.length;if(Date.now()-last>120){progress({received,total:size});last=Date.now();}} | |
| if(!received||size&&received!==size)throw new Error('The scene download was incomplete.'); | |
| progress({received,total:size});const bytes=new Uint8Array(received);let at=0;for(const c of chunks){bytes.set(c,at);at+=c.length;}return bytes; | |
| }catch(error){if(reason)throw new Error(reason);throw error;} | |
| finally{clearTimeout(idle);clearTimeout(total);signal.removeEventListener('abort',stop);} | |
| } | |