fix: resolve 18 QA-discovered bugs across tools, previews, and the AI pipeline (#242)

Exhaustive QA sweep of all 157 tools. Fixes: CSP blob media, csv-excel ExcelJS interop, ocr-pdf segfault, chart-maker upload, non-PDF doc preview, RAW decode, merge-tool multi-file path, html-to-image chromium, ogv/wma/amr/ac3 preview fallbacks, meme/gif/stabilize codecs, nav+home a11y. Plus orphan-format and test-debt cleanup, the AI bundle build script, and a reusable Playwright QA harness under tests/qa/.
This commit is contained in:
SnapOtter
2026-06-15 22:26:24 +08:00
committed by GitHub
parent 25babfae15
commit d8cf979d4b
83 changed files with 16014 additions and 112 deletions
+11 -4
View File
@@ -200,7 +200,7 @@ RUN for i in 1 2 3; do apt-get -o Acquire::Retries=3 update && break || sleep $(
tini \
imagemagick \
libjxl-tools \
libraw-dev \
libraw-dev libraw-bin \
libopenexr-dev \
potrace \
ghostscript \
@@ -309,9 +309,16 @@ RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store/v3 \
npm pkg delete scripts.prepare && \
pnpm install --frozen-lockfile --prod
# Install Playwright Chromium for HTML-to-Image tool
RUN npx playwright install chromium --with-deps && \
rm -rf /tmp/* /root/.cache/ms-playwright/.links
# Install Playwright Chromium for HTML-to-Image tool.
# PLAYWRIGHT_BROWSERS_PATH puts browsers in a shared location so the
# non-root snapotter user can find and execute them at runtime.
# Use the workspace's pinned Playwright (not `npx playwright`, which fetches a
# NEWER version and installs a chromium build the runtime playwright cannot
# resolve) so the installed browser matches chromium.executablePath().
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
RUN pnpm --filter @snapotter/api exec playwright install chromium --with-deps && \
chmod -R a+rX /opt/playwright-browsers && \
rm -rf /tmp/*
# Copy source code for API (tsx runs TS directly - no build step needed)
COPY apps/api/src ./apps/api/src
+27 -3
View File
@@ -174,6 +174,12 @@ bundle = manifest["bundles"][os.environ["BUNDLE_ID"]]
models = bundle.get("models", [])
models_dir = os.environ["MODELS_DIR"]
# Point rembg's model home at the staging dir so downloaded ONNX models
# end up inside the tarball (default U2NET_HOME is outside MODELS_DIR).
rembg_home = os.path.join(models_dir, "rembg")
os.makedirs(rembg_home, exist_ok=True)
os.environ["U2NET_HOME"] = rembg_home
if not models:
print(" No models to download")
sys.exit(0)
@@ -221,9 +227,27 @@ for model in models:
args = model["args"]
session_name = args[0]
print(f" [{model_id}] rembg session: {session_name}", flush=True)
from rembg.sessions import new_session
new_session(session_name)
print(f" [{model_id}] Done")
# birefnet-matting and birefnet-hr-matting are custom sessions
# registered at runtime in remove_bg.py (not in rembg's built-in
# session registry). new_session() cannot resolve them, so
# download their ONNX files directly using the same URLs and
# filenames the custom session classes use.
CUSTOM_BIREFNET = {
"birefnet-matting": "https://github.com/ZhengPeng7/BiRefNet/releases/download/v1/BiRefNet-matting-epoch_100.onnx",
"birefnet-hr-matting": "https://github.com/ZhengPeng7/BiRefNet/releases/download/v1/BiRefNet_HR-matting-epoch_135.onnx",
}
if session_name in CUSTOM_BIREFNET:
dest = os.path.join(models_dir, "rembg", f"{session_name}.onnx")
os.makedirs(os.path.dirname(dest), exist_ok=True)
print(f" [{model_id}] Custom BiRefNet -> rembg/{session_name}.onnx", flush=True)
urllib.request.urlretrieve(CUSTOM_BIREFNET[session_name], dest)
size = os.path.getsize(dest)
print(f" [{model_id}] Done ({size:,} bytes)")
else:
from rembg.sessions import new_session
new_session(session_name)
print(f" [{model_id}] Done")
else:
print(f" WARNING: Unknown download method for {model_id}", file=sys.stderr)