from __future__ import annotations import requests from xhs_bridge.config import load_config from xhs_bridge.cookies_loader import image_request_headers, normalize_image_url from xhs_bridge.extension_cookies import get_cookie_header_from_extension _TIMEOUT = 30 def _fetch_once(url: str, cookie_header: str | None) -> bytes: headers = image_request_headers(cookie_header=cookie_header) resp = requests.get(url, headers=headers, timeout=_TIMEOUT) resp.raise_for_status() content_type = (resp.headers.get("Content-Type") or "").lower() data = resp.content if len(data) < 128: raise ValueError("图片数据过短") if "text/html" in content_type or "application/json" in content_type: raise ValueError("CDN 返回了非图片内容") return data def fetch_image_bytes(url: str) -> bytes: """下载小红书 CDN 图片;失败时从 Chrome 扩展读取 Cookie 重试。""" url = normalize_image_url(url) if not url: raise ValueError("图片地址为空") cfg = load_config() bridge_url = cfg.skills.bridge_ws_url last_error: Exception | None = None try: return _fetch_once(url, None) except Exception as exc: last_error = exc try: cookie = get_cookie_header_from_extension(bridge_url, force_refresh=True) if not cookie: raise ValueError("扩展未返回 Cookie,请确认 Chrome 已登录小红书") return _fetch_once(url, cookie) except Exception as exc: last_error = exc raise ValueError(f"图片加载失败:{last_error}") from last_error