Buckets:
| """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.