Buckets:
| """Browser-decode all enumerated audio from bucket copies, with no external browser network.""" | |
| import os,sys,json,shutil,threading,importlib.util,requests,hashlib | |
| from pathlib import Path | |
| from playwright.sync_api import sync_playwright | |
| H=Path('/home/user/game-archive');R=H/'reports';sys.argv=[sys.argv[0]] | |
| spec=importlib.util.spec_from_file_location('av',H/'tools/verify_games.py');v=importlib.util.module_from_spec(spec);spec.loader.exec_module(v) | |
| reports=[];audits=json.loads((R/'lazy-audio-manifests.json').read_text()) | |
| for ident in [6,101,55]: | |
| m=json.loads((R/f'game-{ident}.json').read_text());records={x['path']:x for x in m['files']} | |
| if ident==6:paths=[x['path'] for x in json.loads((R/'thirty-dollar-audio-audit.json').read_text())['files']]+[x['path'] for x in json.loads((R/'upstream-audio-restorations.json').read_text()) if x['game_id']==6] | |
| else: | |
| audit=next(x for x in audits if x['game_id']==ident);paths=[x['path'] for x in audit['verified']+audit['failures']] | |
| paths=list(dict.fromkeys(paths));files=[records[k] for k in paths if k in records];report={'game_id':ident,'expected':len(paths),'scope':'Decode tests from bucket files in a local fixture, not all in-game audio events.','missing_bucket_manifest_paths':[p for p in paths if p not in records]} | |
| shutil.rmtree(v.STAGE,ignore_errors=True);v.STAGE.mkdir();server=None | |
| try: | |
| v.api.download_bucket_files(v.BUCKET,[(x['path'],v.STAGE/x['path']) for x in files],raise_on_missing_files=True) | |
| (v.STAGE/'site/__audio_test__.html').write_text('<!doctype html><meta charset="utf-8"><title>Audio file audit</title>Audio decoding fixture') | |
| state=v.State({'files':files});state.repair=False;server=v.ThreadingHTTPServer(('127.0.0.1',0),v.Handler);server.state=state;server.daemon_threads=False;threading.Thread(target=server.serve_forever,daemon=True).start() | |
| with sync_playwright() as p: | |
| b=p.chromium.launch(headless=True,args=['--no-sandbox','--disable-dev-shm-usage']);c=b.new_context(service_workers='block') | |
| c.route('**/*',lambda route:route.continue_() if route.request.url.startswith(f'http://127.0.0.1:{server.server_port}/') else route.abort()) | |
| page=c.new_page();page.goto(f'http://127.0.0.1:{server.server_port}/__audio_test__.html') | |
| report['files']=page.evaluate('''async(paths)=>{const ctx=new AudioContext();const result=[];for(const path of paths){try{const r=await fetch(path);if(!r.ok)throw Error('HTTP '+r.status);const bytes=await r.arrayBuffer();const a=await ctx.decodeAudioData(bytes);let peak=0;const d=a.getChannelData(0);for(let i=0;i<d.length;i+=Math.max(1,Math.floor(d.length/4096)))peak=Math.max(peak,Math.abs(d[i]));result.push({path,decoded:true,duration:a.duration,frames:a.length,channels:a.numberOfChannels,sampled_peak:peak});}catch(e){result.push({path,decoded:false,error:String(e)});}}await ctx.close();return result;}''',['/'+x.removeprefix('site/') for x in paths if x in records]);b.close() | |
| report['decoded']=sum(x['decoded'] for x in report['files']);report['all_expected_decode']=report['decoded']==len(paths) | |
| if ident==55: | |
| key='site/games/basket-random/media/intro-sound.webm';f=v.STAGE/key | |
| with requests.get('https://garbsoftball.com/games/basket-random/media/intro-sound.webm',timeout=20) as response: | |
| response.raise_for_status();sourcehash=hashlib.sha256(response.content).hexdigest() | |
| storedhash=hashlib.sha256(f.read_bytes()).hexdigest() | |
| report['size_discrepancy']={'path':key,'declared_bytes':38492,'stored_bytes':f.stat().st_size,'matches_current_source_sha256':sourcehash==storedhash,'browser_decodes':next(x['decoded'] for x in report['files'] if x['path'].endswith('intro-sound.webm')),'interpretation':'Manifest byte count differs, but the stored audio matches current source bytes and decodes successfully. No replacement made.'} | |
| reports.append(report);print('Audio decode',ident,report['decoded'],'/',len(paths),flush=True) | |
| finally: | |
| if server:server.shutdown();server.server_close() | |
| shutil.rmtree(v.STAGE,ignore_errors=True) | |
| (R/'audio-manifest-browser-tests.json').write_text(json.dumps(reports,indent=2));v.api.batch_bucket_files(v.BUCKET,add=[(R/'audio-manifest-browser-tests.json','_verification/audio-manifest-browser-tests.json')]) | |
Xet Storage Details
- Size:
- 4.2 kB
- Xet hash:
- 9cdb4f273b7c0fe75a3967bb40f88fc06f546900aa2a478d0eb3b18e4516d349
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.