Finalize video package boundary and product wording

This commit is contained in:
Victor Kuznetsov
2026-07-31 10:52:44 -07:00
parent 6315b58628
commit 5767c1d7b3
9 changed files with 55 additions and 16 deletions
+2
View File
@@ -4,6 +4,8 @@ __pycache__/
*.egg-info/
dist/
build/
tmp/
.sc/
# Environment secrets
.env
+1 -1
View File
@@ -38,7 +38,7 @@ Per-command exit-code semantics (the no-signal / GPU-missing skip branches), tes
- **CI** (`.github/workflows/test.yml`): runs on push to `main` + every PR. A `lint` job (ubuntu: `ruff check` + `ruff format --check`) plus a `test` matrix (ubuntu/macos/windows x py3.10/3.12) that does `uv sync --frozen --extra dev` then `pytest`. The matrix installs only core + dev (no `gpu` extra), so the GPU/model-running tests skip there and it exercises the metadata/identify/visible/cv2-eraser surface on all three OSes. Keep `uv.lock` valid (don't break `--frozen`) when editing `pyproject.toml`.
- Dependency PR checks run the GitHub merge ref against current `main`, not the contributor branch in isolation. If `main` moves after the dependency branch was opened, merge current `main` locally and rerun the full gate; a new linter can expose stale directives in code that landed later.
- **Release flow + distribution channels** (PyPI publish via `publish.yml`/`uv publish`, the automated Homebrew-tap + HF-Space bumps in `distribute.yml`, conda-forge, ComfyUI Registry, the sdist `data/` exclusion, hatchling pin history): see `docs/release-and-distribution.md` before cutting a release.
- **Release flow + distribution channels** (PyPI publish via `publish.yml`/`uv publish`, the automated Homebrew-tap + HF-Space bumps in `distribute.yml`, conda-forge, ComfyUI Registry, the sdist public allowlist, hatchling pin history): see `docs/release-and-distribution.md` before cutting a release.
- `bash maintain.sh` — uv-outdated, uv-secure, ruff check/fix, ruff format, pyright (scoped `src/`, see the OOM note below), pytest -n auto. The helper tools live in the `dev` extra (`pytest-xdist`, plus `uv-outdated`/`uv-secure` marker-gated to py3.12+ so the py3.10 resolution stays solvable) — a bare env without `--extra dev` does not have them.
- **Strict pyright is clean across `src/` (0 errors).** The cv2/torch/diffusers boundary files (`gemini_engine`, `region_eraser`, `doubao_engine`, `humanizer`, `invisible_engine`, `noai/watermark_remover`) carry a documented per-file `# pyright:` relax pragma that turns off only the unknown-type / untyped-third-party rules — those libs ship no usable types, so strict typing there fights the ecosystem. Pure-logic files stay fully strict; `typings/piexif/__init__.pyi` is a local stub so `metadata.py`/`extractor.py` resolve piexif. Public ndarray-returning signatures on the relaxed engines are still annotated `NDArray[Any]` so strict consumers (`cli.py`) stay clean. When touching a relaxed file, prefer fixing real issues over widening the pragma; keep the pragma scoped to genuinely-untyped boundaries. The `uv-secure` CVE-resolution history (idna/aiohttp bumps, retired basicsr, the dismissed torch `GHSA-rrmf-rvhw-rf47`) lives in `docs/release-and-distribution.md` — read it before re-triaging a dependency alert.
- **Full-project `uv run pyright` (no path) OOMs/crashes node on this ML-heavy repo** (emits a `libnode` stack frame, no summary) — a known environment limit, not a code error. Gate with `uv run --extra dev --extra gpu pyright src/` (completes, authoritative) or scope to changed files; also run `uv run ruff check` and `uv run pytest` directly.
+3 -3
View File
@@ -212,10 +212,10 @@ WebM, Matroska, MP3, WAV, FLAC, OGG, Opus, and AAC container metadata is strippe
through ffmpeg with stream copying. The operation fails if ffmpeg is absent or
cannot parse the input.
### Video pixel removal is provider-specific and still experimental
### Video pixel removal is provider-specific
The experimental `video metadata` command and high level video API inspect and
strip supported AI provenance metadata without transcoding streams.
The `video metadata` command and high level video API inspect and strip
supported AI provenance metadata without transcoding streams.
`video visible` and `remove_video_visible` additionally support the moving
Sora 2 mascot and wordmark, the current Veo four-point diamond, the legacy
+8 -4
View File
@@ -65,10 +65,14 @@ current line according to the environment.
The wheel includes the package under `src/`.
The source distribution explicitly excludes `/data` through
`[tool.hatch.build.targets.sdist]` in `pyproject.toml`. Keep that exclusion:
calibration captures and test corpora do not belong in the published package
archive.
The source distribution uses an explicit allowlist for `/src`, `/LICENSE`,
`/README.md`, and `/pyproject.toml` through
`[tool.hatch.build.targets.sdist]` in `pyproject.toml`. It also defensively
excludes `/data`, `/tmp`, and `/.sc`. Keep both controls: calibration captures,
test corpora, generated research outputs, and local session state do not belong
in the published package archive. The matching local-root entries in
`.gitignore` prevent accidental commits, but are not a substitute for the build
boundary because hatchling may include untracked files.
## Build backend
+3 -3
View File
@@ -242,9 +242,9 @@ was negative on both carriers, and a weaker candidate was negative on one. On
ordinary Gemini to ignore and reinterpret the detector result. Those follow-ups
were mistakenly treated as a stricter oracle; they were not detector reruns.
The implementation remains exposed as `video invisible` and
`remove_video_invisible`. Its default is a calibrated operating point, while
each important result still requires a fresh control-positive,
candidate-negative pair.
`remove_video_invisible`. Its oracle-certified default is the product operating
point. A fresh control-positive, candidate-negative pair remains an optional
per-file audit after provider changes or for unusually important files.
The 2026-07-31 full-clip check added a public eight-second Veo carrier. The
source and the complete `0.10` product output were both detected, proving the
+1 -1
View File
@@ -59,7 +59,7 @@ about:
Detect and remove registered visible AI-provenance marks and strip
AI-provenance metadata (C2PA, EXIF, IPTC, and PNG text chunks) from images
and supported video containers. The core package covers the identify,
metadata, visible, erase, and experimental video command surface. Optional
metadata, visible, erase, and product-oriented video command surface. Optional
pip extras add SynthID diffusion removal and additional invisible-watermark
detectors.
license: Apache-2.0
+14 -3
View File
@@ -191,9 +191,20 @@ build-backend = "hatchling.build"
packages = ["src/remove_ai_watermarks"]
[tool.hatch.build.targets.sdist]
# Keep the source distribution small: ship the package and metadata, not
# calibration or evaluation data under data/. The wheel ships only src/.
exclude = ["/data"]
# Keep the source distribution small and public-safe: ship tracked source and
# metadata, not corpora or local research/session artifacts. The wheel ships
# only src/.
include = [
"/src",
"/LICENSE",
"/README.md",
"/pyproject.toml",
]
exclude = [
"/data",
"/tmp",
"/.sc",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
+1 -1
View File
@@ -1,4 +1,4 @@
"""Shared configuration for experimental video SynthID regeneration."""
"""Shared configuration for oracle-certified video SynthID removal."""
DEFAULT_VIDEO_SYNTHID_VAE = "stabilityai/sd-vae-ft-mse"
DEFAULT_VIDEO_SYNTHID_NOISE_STD = 0.15
+22
View File
@@ -0,0 +1,22 @@
"""Distribution-boundary regression tests."""
from __future__ import annotations
import re
from pathlib import Path
def _array_values(section: str, key: str) -> set[str]:
match = re.search(rf"(?ms)^{key}\s*=\s*\[(.*?)^\]", section)
assert match is not None
return set(re.findall(r'"([^"]+)"', match.group(1)))
def test_sdist_has_explicit_public_boundary() -> None:
"""Hatchling must publish only package source and required metadata."""
root = Path(__file__).resolve().parents[1]
config = (root / "pyproject.toml").read_text(encoding="utf-8")
sdist_config = config.split("[tool.hatch.build.targets.sdist]", maxsplit=1)[1].split("\n[", maxsplit=1)[0]
assert _array_values(sdist_config, "include") == {"/src", "/LICENSE", "/README.md", "/pyproject.toml"}
assert {"/data", "/tmp", "/.sc"} <= _array_values(sdist_config, "exclude")