diff --git a/load_image_from_path.py b/load_image_from_path.py index b3d271a..e791c8d 100644 --- a/load_image_from_path.py +++ b/load_image_from_path.py @@ -3,9 +3,11 @@ import io import os import json import shutil +import ssl from pathlib import Path from typing import Iterable -from urllib.request import urlopen, Request +from urllib.request import urlopen, Request, build_opener, HTTPSHandler +from urllib.error import URLError from PIL import (Image, ImageOps, ImageSequence, ImageFile, UnidentifiedImageError, ) import numpy as np @@ -22,12 +24,57 @@ def _is_url(s) -> bool: def _fetch_url_bytes(url: str, timeout: float = 30.0) -> bytes: - """Download the bytes of a URL. Raises on any network/HTTP problem.""" + """Download the bytes of a URL. Raises on any network/HTTP problem. + + Embedded Python distributions (StabilityMatrix, ComfyUI portable, ...) often + ship with a missing or outdated CA bundle, causing CERTIFICATE_VERIFY_FAILED + errors on perfectly valid sites. Strategy: + 1. Try a normal verified request. + 2. On SSL verification failure, retry with the `certifi` CA bundle if the + package is available (verification still active, just better roots). + 3. As a last resort, retry without certificate verification, warning once. + """ req = Request(url, headers={"User-Agent": "ComfyUI-noEmbryo/1.0"}) - with urlopen(req, timeout=timeout) as resp: + try: + with urlopen(req, timeout=timeout) as resp: + return resp.read() + except URLError as e: + # urlopen wraps the raw SSL error inside URLError as its `.reason`. + if not isinstance(getattr(e, "reason", e), ssl.SSLCertVerificationError): + raise # a genuine network error — don't mask it + # fall through to the recovery strategies below + + # Strategy 2: use certifi's CA bundle if it's installed. + try: + import certifi + ctx = ssl.create_default_context(cafile=certifi.where()) + opener = build_opener(HTTPSHandler(context=ctx)) + with opener.open(req, timeout=timeout) as resp: + return resp.read() + except ImportError: + pass + except Exception as e: + reason = getattr(e, "reason", e) + if not isinstance(reason, ssl.SSLCertVerificationError): + raise # genuine network error between retries — re-raise + pass # certifi roots didn't help either — fall through + + # Strategy 3 (last resort): skip certificate validation entirely. + global _ssl_warning_shown + if not _ssl_warning_shown: + print("[noEmbryo] SSL certificate verification failed — retrying URL " + "downloads without certificate validation. Consider installing/" + "updating the 'certifi' package for secure downloads.") + _ssl_warning_shown = True + ctx = ssl._create_unverified_context() + opener = build_opener(HTTPSHandler(context=ctx)) + with opener.open(req, timeout=timeout) as resp: return resp.read() +_ssl_warning_shown = False + + def _pillow(fn, arg): prev_value = None try: diff --git a/nodes.py b/nodes.py index 5893ab9..05ba14a 100644 --- a/nodes.py +++ b/nodes.py @@ -7,14 +7,14 @@ from .load_image_from_path import LoadImageFromPathEnhanced from .stitcher import H3MotionContextClipStitcher, H3ContextLatentConverter MANIFEST = {"name": "noEmbryo Nodes", - "version": (1, 6, 4), + "version": (1, 6, 5), "author": "noEmbryo", "project": "https://github.com/noembryo/ComfyUI-noEmbryo", "description": "Nodes for ComfyUI", "license": "MIT", } __author__ = "noEmbryo" -__version__ = "1.6.4" +__version__ = "1.6.5" LISTS_PATH = join(dirname(realpath(__file__)), "TermLists") diff --git a/pyproject.toml b/pyproject.toml index 0b5e27c..4b4d263 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ limit the output's size in megapixels. \n * "PromptTermList (1-6)" nodes to help with the creation of Prompts. \n * "Auto Save Workflow" can save the current workflow to a .json file automatically. \n """ -version = "1.6.4" +version = "1.6.5" license = {text = "MIT License"} [project.urls]