import sys, json, hashlib, wave
from pathlib import Path
root=Path(sys.argv[1]).resolve(); rows=json.loads(Path(sys.argv[2]).read_text())
entries=[]
for row in rows:
    p=(root/row['path']).resolve()
    if not p.is_relative_to(root): raise ValueError('path outside source root')
    with wave.open(str(p),'rb') as w:
        rate=w.getframerate(); width=w.getsampwidth(); channels=w.getnchannels(); frames=w.getnframes()
        blocks=[]; start=0
        while start<frames:
            pcm=w.readframes(min(rate,frames-start)); count=len(pcm)//(width*channels)
            blocks.append({'startFrame':start,'endFrame':start+count,'startSeconds':start/rate,'endSeconds':(start+count)/rate,'pcmSha256':hashlib.sha256(pcm).hexdigest()})
            start+=count
    entries.append({**row,'byteSha256':hashlib.sha256(p.read_bytes()).hexdigest(),'sampleRate':rate,'sampleWidthBytes':width,'channels':channels,'frames':frames,'durationSeconds':frames/rate,'blocks':blocks})
manifest={'identity':'author_project_handoff_contract_not_official_Adobe_CLI','trustBoundary':'Sender approves complete original first; SHA does not authenticate Adobe provenance or pre-existing missing content.','files':entries}
Path(sys.argv[3]).write_text(json.dumps(manifest,ensure_ascii=False,indent=2)+'\n')
print(json.dumps({'frozenFiles':len(entries),'manifest':sys.argv[3],'identity':manifest['identity']},indent=2))
