smodusermc/offline-games / _tools /reconcile_manifests.py
smodusermc's picture
download
raw
2.64 kB
"""Reconcile existing entry-page drift without replacing the bucket's stored game files."""
import os,json,hashlib,tempfile
from pathlib import Path
from huggingface_hub import HfApi
R=Path('/home/user/game-archive/reports');B='smodusermc/offline-games';api=HfApi(token=os.environ['HF_TOKEN'])
remote={f.path:f for f in api.list_bucket_tree(B,recursive=True) if hasattr(f,'size')}
updates=[];audits=[];uploads=[]
with tempfile.TemporaryDirectory(prefix='game-manifest-audit-') as tmp:
for mp in sorted(R.glob('game-*.json')):
ident=int(mp.stem.split('-')[1]);m=json.loads(mp.read_text());changed=False;missing=[];size_mismatch=[]
for f in m['files']:
current=remote.get(f['path'])
if current is None:missing.append(f['path']);continue
if current.size!=f['bytes']:
if f['path'].endswith('.html') and current.size<2*1024**2:
local=Path(tmp)/'entry.html';api.download_bucket_files(B,[(f['path'],local)],raise_on_missing_files=True)
with local.open('rb') as stream:sha=hashlib.file_digest(stream,'sha256').hexdigest()
old={'bytes':f['bytes'],'sha256':f.get('sha256')}
updates.append({'id':ident,'path':f['path'],'previous_manifest':old,'current_bytes':current.size,'current_sha256':sha,'action':'Updated stale manifest metadata to current stored entry. Did not alter entry bytes. Existing bucket edits preserved.'})
f['prior_capture_metadata']=old;f['bytes']=current.size;f['sha256']=sha;f['note']='Entry already differed from original capture before this verification pass; current stored bytes preserved.';changed=True
else:size_mismatch.append({'path':f['path'],'recorded':f['bytes'],'stored':current.size})
if changed:
mp.write_text(json.dumps(m,indent=2));uploads.append((mp,f'_reports/games/{ident:04d}.json'))
audits.append({'id':ident,'name':m['name'],'recorded_files':len(m['files']),'missing_objects':missing,'size_mismatches':size_mismatch,'check':'Current bucket presence and size; not a full SHA256 check or gameplay verification.'})
if len(uploads)>=20:api.batch_bucket_files(B,add=uploads);uploads=[]
if uploads:api.batch_bucket_files(B,add=uploads)
(R/'manifest-reconciliation.json').write_text(json.dumps(updates,indent=2));(R/'storage-audit.json').write_text(json.dumps(audits,indent=2))
api.batch_bucket_files(B,add=[(R/'manifest-reconciliation.json','_verification/manifest-reconciliation.json'),(R/'storage-audit.json','_verification/storage-audit.json')])
print('Audited games',len(audits),'metadata reconciliations',len(updates),'missing objects',sum(len(a['missing_objects']) for a in audits),'size mismatches',sum(len(a['size_mismatches']) for a in audits))

Xet Storage Details

Size:
2.64 kB
·
Xet hash:
ceba7ebb14501f573323ca073b54cef46e804cafd14b5ff32655e9c1dd49d9b2

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