#!/usr/bin/env python3
from pathlib import Path
from zipfile import ZipFile
import xml.etree.ElementTree as ET
import shutil, re, json, hashlib, os, subprocess
NS={'a':'http://schemas.openxmlformats.org/drawingml/2006/main','p':'http://schemas.openxmlformats.org/presentationml/2006/main','r':'http://schemas.openxmlformats.org/officeDocument/2006/relationships','rel':'http://schemas.openxmlformats.org/package/2006/relationships'}
def natural_key(p): return [int(t) if t.isdigit() else t for t in re.split(r'(\d+)',str(p))]
def safe_name(s): return re.sub(r'[\\/:*?"<>|\s]+','_',s).strip('_')[:80] or 'ppt'
def parse_rels(z, rel_path):
    rels={}
    if rel_path not in z.namelist(): return rels
    root=ET.fromstring(z.read(rel_path))
    for rel in root.findall('rel:Relationship',NS): rels[rel.attrib.get('Id')]=(rel.attrib.get('Target'),rel.attrib.get('Type',''))
    return rels
def target_to_zip_path(sp,target):
    if target.startswith('/'): return target.lstrip('/')
    parts=[]
    for part in (str(Path(sp).parent)+'/'+target).split('/'):
        if part=='..':
            if parts: parts.pop()
        elif part!='.': parts.append(part)
    return '/'.join(parts)
def iter_slides(z): return sorted([n for n in z.namelist() if re.match(r'ppt/slides/slide\d+\.xml$',n)],key=natural_key)
def texts(root): return [t.text.strip() for t in root.findall('.//a:t',NS) if t.text and t.text.strip()]
def dim_with_sips(path):
    try:
        out=subprocess.run(['/usr/bin/sips','-g','pixelWidth','-g','pixelHeight',str(path)],capture_output=True,text=True,timeout=5).stdout
        w=re.search(r'pixelWidth:\s*(\d+)',out); h=re.search(r'pixelHeight:\s*(\d+)',out)
        return (int(w.group(1)) if w else 0, int(h.group(1)) if h else 0)
    except Exception: return (0,0)
def main():
    project=Path('/Users/bot1/Volumes/root_for_ai/AI工作区/文博IP_PPT_公司文创开发能力展示_20260604_2014')
    sources=sorted((project/'source').rglob('*.pptx'), key=lambda p:str(p))
    outroot=project/'work'/'incoming_extract_20260605'
    outroot.mkdir(parents=True,exist_ok=True)
    deck_summaries=[]
    for ppt in sources:
        name=safe_name(ppt.stem); out=outroot/name; media_out=out/'media'; out.mkdir(parents=True,exist_ok=True); media_out.mkdir(parents=True,exist_ok=True)
        deck={'file':str(ppt),'slides':[]}
        with ZipFile(ppt) as z:
            for idx,sp in enumerate(iter_slides(z),1):
                root=ET.fromstring(z.read(sp)); txt=texts(root)
                rels=parse_rels(z,'ppt/slides/_rels/'+Path(sp).name+'.rels')
                rids=[]
                for blip in root.findall('.//a:blip',NS):
                    rid=blip.attrib.get('{%s}embed'%NS['r']) or blip.attrib.get('{%s}link'%NS['r'])
                    if rid and rid not in rids: rids.append(rid)
                imgs=[]
                for n,rid in enumerate(rids,1):
                    if rid not in rels: continue
                    target,typ=rels[rid]
                    if 'image' not in typ: continue
                    zp=target_to_zip_path(sp,target)
                    if zp not in z.namelist(): continue
                    data=z.read(zp); ext=Path(zp).suffix or '.bin'; h=hashlib.sha1(data).hexdigest()[:10]
                    fp=media_out/f'slide{idx:03d}_{n:02d}_{h}{ext}'
                    if not fp.exists(): fp.write_bytes(data)
                    w,hh=dim_with_sips(fp)
                    imgs.append({'file':str(fp.relative_to(project)),'w':w,'h':hh,'zip_path':zp})
                deck['slides'].append({'slide':idx,'texts':txt,'images':imgs})
        (out/'manifest.json').write_text(json.dumps(deck,ensure_ascii=False,indent=2),encoding='utf-8')
        md=[f'# {name}', f'file: {ppt}', '']
        for s in deck['slides']:
            md.append(f"## Slide {s['slide']} | images={len(s['images'])}")
            if s['texts']: md += [f'- {x}' for x in s['texts']]
            else: md.append('- [no text]')
            md.append('')
        (out/'slide_text.md').write_text('\n'.join(md),encoding='utf-8')
        deck_summaries.append({'name':name,'slides':len(deck['slides']),'images':sum(len(s['images']) for s in deck['slides']),'out':str(out.relative_to(project))})
    (outroot/'summary.json').write_text(json.dumps(deck_summaries,ensure_ascii=False,indent=2),encoding='utf-8')
    print(json.dumps(deck_summaries,ensure_ascii=False,indent=2))
if __name__=='__main__': main()
