smodusermc/offline-games / _tools /repair_large_batch_audio.py
smodusermc's picture
download
raw
5.14 kB
"""Restore missing OGG files from a secondary mirror after control-file SHA matching."""
import os,sys,json,hashlib,tempfile,concurrent.futures,threading,importlib.util,shutil
from pathlib import Path
import requests
from huggingface_hub import HfApi
from playwright.sync_api import sync_playwright
H=Path('/home/user/game-archive');R=H/'reports';B='smodusermc/offline-games';api=HfApi(token=os.environ['HF_TOKEN']);m=json.loads((R/'game-543.json').read_text());records={x['path']:x for x in m['files']};state=json.loads((R/'large-batch-validation.json').read_text());row=next(x for x in state['results'] if x['id']==543)
base='https://raw.githubusercontent.com/3kh0/3kh0-lite/main/projects/there-is-no-game/media/'
controls=[]
for name in ['drag.ogg','dropbounce.ogg','vinyl.ogg']:
f=next(x for x in m['files'] if x['path'].endswith('/'+name));r=requests.get(base+name,timeout=20);r.raise_for_status();sha=hashlib.sha256(r.content).hexdigest();assert sha==f['sha256'];controls.append({'name':name,'sha256':sha,'matches_stored_capture':True})
missing=[x for x in row['declared_unresolved'] if x.get('path','').endswith('.ogg')];restored=[]
with tempfile.TemporaryDirectory(prefix='large-batch-audio-repair-') as temp:
def fetch(f):
url=base+f['path'].split('/')[-1];r=requests.get(url,timeout=20);r.raise_for_status();assert r.content.startswith(b'OggS');p=Path(temp)/f['path'].split('/')[-1];p.write_bytes(r.content)
return p,{'path':f['path'],'url':url,'bytes':len(r.content),'sha256':hashlib.sha256(r.content).hexdigest(),'content_type':'audio/ogg','provenance':{'type':'secondary public mirror; not claimed to be the author upstream','control_files':'Three existing OGG files matched stored SHA-256 exactly','missing_original_byte_equivalence':'not established','original_url':f['url']}}
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as ex:items=list(ex.map(fetch,missing))
api.batch_bucket_files(B,add=[(p,f['path']) for p,f in items])
for p,f in items:p.unlink()
api.download_bucket_files(B,[(f['path'],p) for p,f in items],raise_on_missing_files=True)
for p,f in items:
assert hashlib.sha256(p.read_bytes()).hexdigest()==f['sha256'];records[f['path']]=f;restored.append(f)
m['files']=list(records.values());(R/'game-543.json').write_text(json.dumps(m,indent=2));api.batch_bucket_files(B,add=[(R/'game-543.json','_reports/games/0543.json')])
# Decode the repaired bytes again in Chromium, with browser network restricted to localhost.
sys.argv=[sys.argv[0]];spec=importlib.util.spec_from_file_location('v',H/'tools/verify_games.py');v=importlib.util.module_from_spec(spec);spec.loader.exec_module(v);shutil.rmtree(v.STAGE,ignore_errors=True);v.STAGE.mkdir()
server=None
try:
for p,f in items:
dest=v.STAGE/f['path'];dest.parent.mkdir(parents=True,exist_ok=True);shutil.copyfile(p,dest)
(v.STAGE/'site/__repair__.html').write_text('<!doctype html><title>Repair validation</title>')
S=v.State({'files':restored});S.repair=False;server=v.ThreadingHTTPServer(('127.0.0.1',0),v.Handler);server.state=S;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']);c=b.new_context(service_workers='block');origin=f'http://127.0.0.1:{server.server_port}';c.route('**/*',lambda r:r.continue_() if r.request.url.startswith(origin+'/') else r.abort());page=c.new_page();page.goto(origin+'/__repair__.html')
decoded=page.evaluate('''async(paths)=>{const c=new AudioContext();const a=[];for(const path of paths){const r=await fetch(path);const b=await c.decodeAudioData(await r.arrayBuffer());a.push({path,kind:'audio',decoded:true,frames:b.length,duration:b.duration});}await c.close();return a;}''',['/'+f['path'].removeprefix('site/') for f in restored]);b.close()
finally:
if server:server.shutdown();server.server_close()
shutil.rmtree(v.STAGE,ignore_errors=True)
previous=json.loads((R/'large-batch-audio-repair.json').read_text()) if (R/'large-batch-audio-repair.json').exists() else {}
report={'game_id':543,'controls':controls,'restored':previous.get('restored',[])+restored,'decoded':previous.get('decoded',[])+decoded,'scope':'Missing OGG clips restored from the matching-control mirror; original-source M4A alternatives remain missing.'};(R/'large-batch-audio-repair.json').write_text(json.dumps(report,indent=2))
row['resolved_declared_files']=row.get('resolved_declared_files',[])+missing;row['declared_unresolved']=[x for x in row['declared_unresolved'] if not x.get('path','').endswith('.ogg')];row['files']=len(records);row['sha_comparisons']+=len(restored);row['media'].extend(decoded);row['supplemental_repair_report']='_verification/large-batch-audio-repair.json';row['independent_repairs'].extend(restored)
(R/'large-batch-validation.json').write_text(json.dumps(state,indent=2));api.batch_bucket_files(B,add=[(R/'large-batch-audio-repair.json','_verification/large-batch-audio-repair.json'),(R/'large-batch-validation.json','_verification/large-batch-validation.json')]);print('Control matches',len(controls),'restored/hash-verified/decoded',len(restored))

Xet Storage Details

Size:
5.14 kB
·
Xet hash:
ebdaa0cdf3a6bd32b561f33d920b6c68b1efc3509e258d4d4f90412250923c00

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.