58 lines
2.6 KiB
Python
58 lines
2.6 KiB
Python
"""把人工演示录制转换成新手机的初始档案;不把任何像素坐标写进配置。"""
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import hashlib
|
||
import shutil
|
||
import struct
|
||
from datetime import datetime, timezone
|
||
from pathlib import Path
|
||
|
||
from .profiles import save
|
||
from .paths import ROOT, WORK
|
||
|
||
|
||
def png_size(path: Path) -> tuple[int, int]:
|
||
with path.open("rb") as handle:
|
||
header = handle.read(24)
|
||
if header[:8] != b"\x89PNG\r\n\x1a\n" or header[12:16] != b"IHDR":
|
||
raise ValueError(f"不是有效 PNG:{path}")
|
||
return struct.unpack(">II", header[16:24])
|
||
|
||
|
||
def import_recording(recording: Path, device: dict[str, str], label: str = "") -> dict:
|
||
recording = recording.resolve()
|
||
session_path = recording / "session.json"
|
||
timeline_path = recording / "timeline.json"
|
||
if not session_path.exists() or not timeline_path.exists():
|
||
raise ValueError("录制目录必须包含 session.json 和 timeline.json")
|
||
session = json.loads(session_path.read_text(encoding="utf-8"))
|
||
recorded_device = session.get("device", {})
|
||
if recorded_device.get("serial") != device.get("serial"):
|
||
raise ValueError("录制设备与当前导入设备不一致")
|
||
timeline = json.loads(timeline_path.read_text(encoding="utf-8"))
|
||
destination = WORK / "samples" / hashlib.sha256(device["serial"].encode()).hexdigest()[:10] / datetime.now().strftime("%Y%m%d-%H%M%S")
|
||
destination.mkdir(parents=True, exist_ok=False)
|
||
samples: dict[str, dict] = {}
|
||
for entry in timeline.get("snapshots", []):
|
||
hint = str(entry.get("page_hint") or "other")
|
||
folder = (recording / str(entry.get("folder") or "")).resolve()
|
||
if recording not in folder.parents:
|
||
continue
|
||
screenshot = folder / "screen.png"
|
||
if hint in samples or not screenshot.exists():
|
||
continue
|
||
width, height = png_size(screenshot)
|
||
copied = destination / f"{hint}.png"
|
||
shutil.copy2(screenshot, copied)
|
||
samples[hint] = {"snapshot": str(copied.relative_to(ROOT)), "sha256": hashlib.sha256(copied.read_bytes()).hexdigest(), "screen": {"width": width, "height": height}, "captured_at": entry.get("timestamp", "")}
|
||
profile = {
|
||
"schema_version": 1, "label": label or device.get("model") or device["serial"],
|
||
"status": "draft", "device": device,
|
||
"sampling": {"source": "android_manual_recording", "recorded_at": session.get("started_at", ""), "samples": samples},
|
||
"gestures": {"scroll_up": {"from": [0.5, 0.82], "to": [0.5, 0.25], "duration_ms": 350}},
|
||
"created_at": datetime.now(timezone.utc).isoformat(),
|
||
}
|
||
save(profile)
|
||
return profile
|