mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-10-17 15:00:47 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09a4b20ac6 | ||
|
|
9bb4e9cd54 |
No files matched your search
@@ -106,28 +106,52 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Checking the ComfyUI Registry node..."
|
||||
# The registry publishes the node as JSON, including the dependency the
|
||||
# published version declares. Read that directly instead of proxying
|
||||
# through the sync workflow's run status: the node repo runs
|
||||
# sync-library.yml on a daily schedule too, so a green nightly run says
|
||||
# nothing about whether THIS release was ever synced.
|
||||
node=$(curl -fsSL "https://api.comfy.org/nodes/remove-ai-watermarks")
|
||||
echo "$node" | VERSION="$VERSION" python3 -c "
|
||||
# Read the published versions directly. Do NOT proxy through the sync
|
||||
# workflow's run status: the node repo runs that workflow on a daily
|
||||
# schedule too, so a green nightly says nothing about THIS release.
|
||||
#
|
||||
# And read the versions list, not `latest_version` on the node object.
|
||||
# The registry reviews a new upload before activating it, and
|
||||
# `latest_version` reports only the active one -- so for the whole
|
||||
# review window a correctly published version looks identical to one
|
||||
# that was never uploaded. Those are opposite situations: one is a
|
||||
# queue nobody here controls, the other is a broken release.
|
||||
versions=$(curl -fsSL "https://api.comfy.org/nodes/remove-ai-watermarks/versions")
|
||||
status=$(printf '%s' "$versions" | VERSION="$VERSION" python3 -c "
|
||||
import json, os, sys
|
||||
version = os.environ['VERSION']
|
||||
latest = json.load(sys.stdin).get('latest_version') or {}
|
||||
deps = latest.get('dependencies') or []
|
||||
floor = f'>={version}'
|
||||
if latest.get('status') != 'NodeVersionStatusActive':
|
||||
sys.exit(f\"::error::ComfyUI node {latest.get('version')} is {latest.get('status')}, not active\")
|
||||
if not any(floor in d for d in deps):
|
||||
sys.exit(
|
||||
f'::error::ComfyUI node {latest.get(\"version\")} does not require '
|
||||
f'{version}; it declares {deps}'
|
||||
)
|
||||
print(f' ComfyUI: node {latest[\"version\"]} requires {version}')
|
||||
"
|
||||
echo "- ComfyUI: registry node requires $VERSION" >> "$GITHUB_STEP_SUMMARY"
|
||||
matches = [
|
||||
v for v in json.load(sys.stdin)
|
||||
if any(floor in d for d in (v.get('dependencies') or []))
|
||||
]
|
||||
if not matches:
|
||||
sys.exit(f'::error::No ComfyUI node version requires {version}; the sync never published one')
|
||||
# Sort rather than trusting the response order: the endpoint happens to
|
||||
# return newest-first today and nothing documents that it must.
|
||||
newest = max(matches, key=lambda v: v.get('createdAt') or '')
|
||||
print(f\"{newest.get('version')} {newest.get('status')}\")
|
||||
")
|
||||
node_version=${status%% *}
|
||||
node_status=${status##* }
|
||||
echo " ComfyUI: node $node_version requires $VERSION (status $node_status)"
|
||||
case "$node_status" in
|
||||
NodeVersionStatusActive)
|
||||
echo "- ComfyUI: registry node $node_version requires $VERSION and is live" >> "$GITHUB_STEP_SUMMARY"
|
||||
;;
|
||||
NodeVersionStatusPending)
|
||||
# Published and waiting on the registry's own review. Say exactly
|
||||
# that in the summary rather than reporting it as live, and do not
|
||||
# fail: an external moderation queue going red on every release
|
||||
# trains everyone to ignore this workflow.
|
||||
echo "::notice::ComfyUI node $node_version is published and awaiting registry review."
|
||||
echo "- ComfyUI: registry node $node_version requires $VERSION, awaiting registry activation" >> "$GITHUB_STEP_SUMMARY"
|
||||
;;
|
||||
*)
|
||||
echo "::error::ComfyUI node $node_version is $node_status"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# -- HF Space -------------------------------------------------------------------
|
||||
- name: Verify the HF Space serves this release
|
||||
|
||||
@@ -77,9 +77,11 @@ uv tool install --force "remove-ai-watermarks[qwen-zimage]"
|
||||
Both remaining profiles run a Z-Image face stage on the DiffSynth runtime, so
|
||||
both need this extra. The third profile, `chroma-zimage`, uses diffusers'
|
||||
ChromaImg2ImgPipeline instead of DiffSynth for its global stage but inherits
|
||||
the same face stage, so it needs this extra too. It includes the `diffusion`
|
||||
dependencies; `diffusion` on its own covers the torch and diffusers imports
|
||||
but not the face stage, so it is not enough to run a removal.
|
||||
the same face stage, so it needs this extra too. The extra also installs
|
||||
SentencePiece for Chroma1-HD's binary `spiece.model`; without that backend,
|
||||
Transformers cannot construct the Chroma tokenizer. It includes the `diffusion`
|
||||
dependencies; `diffusion` on its own covers the torch and diffusers imports but
|
||||
not the face stage or Chroma tokenizer, so it is not enough to run a removal.
|
||||
|
||||
**An NVIDIA GPU is required.** `qwen-zimage`, `sdxl-zimage` and `chroma-zimage`
|
||||
are CUDA-only, and
|
||||
@@ -117,7 +119,7 @@ application actually uses:
|
||||
| `diffusion` | Torch and Diffusers runtime; video SynthID regeneration | `pixels`, Torch, Diffusers | Yes |
|
||||
| `migan` | MI-GAN ONNX fill backend | `visible`, ONNX Runtime | Model download, no Torch |
|
||||
| `lama` | big-LaMa ONNX fill backend | `visible`, ONNX Runtime | Model download, no Torch |
|
||||
| `qwen-zimage` | Invisible image-watermark removal, all CUDA-only profiles | `diffusion`, DiffSynth | Yes |
|
||||
| `qwen-zimage` | Invisible image-watermark removal, all CUDA-only profiles | `diffusion`, DiffSynth, SentencePiece | Yes |
|
||||
| `text-restoration` | Opt-in verified profile-VAE glyph restoration | `qwen-zimage`, `lama` | Yes |
|
||||
| `text-draft` | Draft OCR proposals for operator verification | PaddleOCR, PaddlePaddle | Model download, no Torch |
|
||||
| `all` | Every production feature available on the active Python | All compatible rows above | Yes |
|
||||
|
||||
@@ -137,12 +137,20 @@ After publication, the `verify-release.yml` workflow runs automatically once
|
||||
`distribute.yml` completes and checks every surface below. It can also be
|
||||
dispatched manually with a specific version. Every check fails the job; none of
|
||||
them warns and continues, because a surface nobody can confirm is not a verified
|
||||
surface. The ComfyUI check reads the published node from the registry's JSON API
|
||||
(`https://api.comfy.org/nodes/remove-ai-watermarks`) and asserts the active
|
||||
version declares this library release. It deliberately does NOT proxy through
|
||||
the sync workflow's run status: the node repository also runs that workflow on a
|
||||
daily schedule, so a green nightly run says nothing about whether this release
|
||||
was synced. The full checklist for completeness:
|
||||
surface, with one deliberate exception noted below. The ComfyUI check reads the
|
||||
registry's JSON API and asserts that some published node version declares this
|
||||
library release. It deliberately does NOT proxy through the sync workflow's run
|
||||
status: the node repository also runs that workflow on a daily schedule, so a
|
||||
green nightly run says nothing about whether this release was synced.
|
||||
|
||||
Read `/nodes/remove-ai-watermarks/versions`, not `latest_version` on the node
|
||||
object. The registry reviews an upload before activating it, and
|
||||
`latest_version` reports only the active version, so during that review window a
|
||||
correctly published version is indistinguishable from one that was never
|
||||
uploaded. A new version therefore sits in `NodeVersionStatusPending` for a while
|
||||
after a release; the check reports that state in the run summary and passes,
|
||||
because it is the registry's queue rather than an incomplete release. A missing
|
||||
version, or any other status, fails. The full checklist for completeness:
|
||||
|
||||
- both wheel and source distribution exist on PyPI;
|
||||
- the package version matches the tag;
|
||||
|
||||
+4
-1
@@ -4,7 +4,7 @@
|
||||
# on it, including the ComfyUI node package. The console script below carries
|
||||
# the same weight, since users have it on PATH.
|
||||
name = "remove-ai-watermarks"
|
||||
version = "0.37.0"
|
||||
version = "0.37.1"
|
||||
description = "AI watermark remover for visible, invisible, and provenance marks in images and video"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11,<3.15"
|
||||
@@ -118,6 +118,9 @@ diffusion = [
|
||||
qwen-zimage = [
|
||||
"remove-ai-watermarks[diffusion]",
|
||||
"diffsynth>=2.0.17,<3",
|
||||
# Chroma1-HD ships a binary spiece.model. Without the matching backend,
|
||||
# Transformers falls through to tiktoken, which cannot parse that file.
|
||||
"sentencepiece>=0.2.2,<0.3",
|
||||
"torchvision>=0.20.0",
|
||||
]
|
||||
# Opt-in verified-text reconstruction over qwen-zimage. LaMa removes the changed
|
||||
|
||||
@@ -33,7 +33,7 @@ _os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error")
|
||||
_warnings.filterwarnings("ignore", message=r".*ImageProcessorFast.*")
|
||||
|
||||
|
||||
__version__ = "0.37.0"
|
||||
__version__ = "0.37.1"
|
||||
|
||||
__all__ = [
|
||||
"BatchSummary",
|
||||
|
||||
@@ -81,6 +81,10 @@ def test_classify_extra_owns_the_photo_heads() -> None:
|
||||
assert {"torch", "transformers", "huggingface-hub", "tokenizers"} <= names
|
||||
|
||||
|
||||
def test_qwen_zimage_extra_owns_every_profile_tokenizer_backend() -> None:
|
||||
assert "sentencepiece" in _requirement_names("qwen-zimage")
|
||||
|
||||
|
||||
def test_production_all_does_not_include_development_tools():
|
||||
assert {
|
||||
"pyright",
|
||||
|
||||
@@ -3311,7 +3311,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "remove-ai-watermarks"
|
||||
version = "0.37.0"
|
||||
version = "0.37.1"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "c2pa-python" },
|
||||
@@ -3336,6 +3336,7 @@ all = [
|
||||
{ name = "pillow-heif" },
|
||||
{ name = "pywavelets" },
|
||||
{ name = "safetensors" },
|
||||
{ name = "sentencepiece" },
|
||||
{ name = "tokenizers" },
|
||||
{ name = "torch" },
|
||||
{ name = "torchvision" },
|
||||
@@ -3412,6 +3413,7 @@ qwen-zimage = [
|
||||
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
|
||||
{ name = "opencv-python-headless" },
|
||||
{ name = "safetensors" },
|
||||
{ name = "sentencepiece" },
|
||||
{ name = "tokenizers" },
|
||||
{ name = "torch" },
|
||||
{ name = "torchvision" },
|
||||
@@ -3432,6 +3434,7 @@ text-restoration = [
|
||||
{ name = "onnxruntime" },
|
||||
{ name = "opencv-python-headless" },
|
||||
{ name = "safetensors" },
|
||||
{ name = "sentencepiece" },
|
||||
{ name = "tokenizers" },
|
||||
{ name = "torch" },
|
||||
{ name = "torchvision" },
|
||||
@@ -3500,6 +3503,7 @@ requires-dist = [
|
||||
{ name = "remove-ai-watermarks", extras = ["visible"], marker = "extra == 'video'" },
|
||||
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.4.0" },
|
||||
{ name = "safetensors", marker = "extra == 'diffusion'" },
|
||||
{ name = "sentencepiece", marker = "extra == 'qwen-zimage'", specifier = ">=0.2.2,<0.3" },
|
||||
{ name = "tokenizers", marker = "extra == 'classify'", specifier = ">=0.22,<0.23" },
|
||||
{ name = "tokenizers", marker = "extra == 'diffusion'", specifier = ">=0.22,<0.23" },
|
||||
{ name = "torch", marker = "extra == 'classify'", specifier = ">=2.0.0" },
|
||||
|
||||
Reference in new issue
Block a user