"""Download a pinned release pair. Native Windows; never changes app files.""" import argparse import hashlib import json import os from pathlib import Path from huggingface_hub import hf_hub_download,snapshot_download ASR_REPO='humoiww/PhonoCommand-ASR' def read(path):return json.loads(Path(path).read_text(encoding='utf-8')) def sha(path): h=hashlib.sha256() with Path(path).open('rb') as f: for block in iter(lambda:f.read(8*1024*1024),b''):h.update(block) return h.hexdigest() def verify(directory,files): for name,info in files.items(): relative=Path(name) if relative.is_absolute() or '..' in relative.parts:raise ValueError('Unsafe manifest path') path=Path(directory)/relative if path.stat().st_size!=info['size'] or sha(path)!=info['sha256']: raise ValueError('Checksum mismatch: '+name) def sync(output,release_lock=None,with_lid=False): lock=read(release_lock or hf_hub_download(ASR_REPO,'release_set.json',revision='main')) output=Path(output).resolve();output.mkdir(parents=True,exist_ok=True) release_dir=output/'releases'/lock['release_id'];release_dir.mkdir(parents=True,exist_ok=True) paths={} for component,item in lock['components'].items(): if len(item['revision'])!=40:raise ValueError('Expected immutable component commit') directory=release_dir/component/item['revision'] snapshot_download(item['repo_id'],revision=item['revision'],local_dir=directory, allow_patterns=item['files']) manifest_path=directory/'MANIFEST.json' if sha(manifest_path)!=item['manifest_sha256']:raise ValueError('Release manifest mismatch') verify(directory,read(manifest_path)['files']) paths[component]=str(directory) upstream=read(Path(paths['asr'])/'upstream_lock.json') for key,item in upstream.items(): if key=='sensevoice' and not with_lid:continue directory=output/'upstream'/key/item['revision'] snapshot_download(item['repo_id'],revision=item['revision'],local_dir=directory, allow_patterns=list(item['files'])) verify(directory,item['files']);paths[key]=str(directory) state={'release_id':lock['release_id'],'lock':lock,'paths':paths, 'includes_sensevoice':with_lid,'app_reload_required':True} (release_dir/'release_set.json').write_text(json.dumps(lock,indent=2)+'\n',encoding='utf-8') target=output/'active_release.json';temporary=output/'active_release.json.tmp' temporary.write_text(json.dumps(state,indent=2)+'\n',encoding='utf-8') os.replace(temporary,target) print(target) return state if __name__=='__main__': p=argparse.ArgumentParser();p.add_argument('--output',type=Path,required=True) p.add_argument('--release-lock',type=Path);p.add_argument('--with-lid',action='store_true') args=p.parse_args();sync(args.output,args.release_lock,args.with_lid)