feat(notion-writer): materialize local images via upload walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 08:18:45 +09:00
parent 9fbd719048
commit 337f2ad6e1
2 changed files with 100 additions and 0 deletions

View File

@@ -83,3 +83,54 @@ def upload(path: Path) -> str:
first_line = result.stdout.strip().splitlines()[0]
upload_id = first_line.split("\t")[0].strip()
return upload_id
_REMOTE_PREFIXES = ("http://", "https://")
def _resolve_local(url: str, base_dir: Path) -> Optional[Path]:
"""Return the resolved path for a local media url, or None if remote."""
if url.startswith(_REMOTE_PREFIXES):
return None
p = Path(url)
return p if p.is_absolute() else Path(base_dir) / p
def materialize_local_media(
blocks: List[Dict[str, Any]],
base_dir: Path,
upload_fn: Callable[[Path], str] = upload,
) -> List[Dict[str, Any]]:
"""Walk blocks (and nested children); upload local image files and
rewrite them to file_upload shape. Remote images are left as-is."""
for block in blocks:
if block.get("type") == "image":
img = block["image"]
if img.get("type") == "external":
url = img.get("external", {}).get("url", "")
resolved = _resolve_local(url, base_dir)
if resolved is not None:
if not resolved.is_file():
raise NtnUploadError(
f"image references missing file: {url}",
path=str(resolved),
)
upload_id = upload_fn(resolved)
caption = img.get("caption")
new_img: Dict[str, Any] = {
"type": "file_upload",
"file_upload": {"id": upload_id},
}
if caption:
new_img["caption"] = caption
block["image"] = new_img
# Recurse into any nested children list.
btype = block.get("type")
nested = block.get(btype, {}) if isinstance(block.get(btype), dict) else {}
if isinstance(nested.get("children"), list):
materialize_local_media(nested["children"], base_dir, upload_fn)
if btype == "column_list":
for col in nested.get("children", []):
col_children = col.get("column", {}).get("children", [])
materialize_local_media(col_children, base_dir, upload_fn)
return blocks