import { getNativePreviewImage } from "./shared/media.js"; const VIDEO_EXTS = /* @__PURE__ */ new Set(["mp4", "mov", "webm", "mkv", "avi", "m4v", "flv", "wmv", "mpg", "mpeg", "ogv"]); const VIDEO_MIME_BY_EXT = { avi: "video/x-msvideo", flv: "video/x-flv", m4v: "video/mp4", mkv: "video/x-matroska", mov: "video/quicktime", mp4: "video/mp4", mpg: "video/mpeg", mpeg: "video/mpeg", ogv: "video/ogg", webm: "video/webm", wmv: "video/x-ms-wmv" }; function parseAnnotated(raw) { if (!raw) return { filename: null, subfolder: "", type: "input" }; let s = String(raw); let type = "input"; const mType = s.match(/\s*\[(input|output|temp)\]\s*$/i); if (mType) type = mType[1].toLowerCase(); s = s.replace(/\s*\[(input|output|temp)\]\s*$/i, ""); s = s.replace(/\\/g, "/"); const abs = /^[a-zA-Z]:\//.test(s) || s.startsWith("//"); if (abs) { const parts = s.split("/"); return { filename: parts[parts.length - 1], subfolder: "", type: "input" }; } const idx = s.lastIndexOf("/"); if (idx >= 0) return { filename: s.slice(idx + 1), subfolder: s.slice(0, idx), type }; return { filename: s, subfolder: "", type }; } function fileExtLower(value) { const match = String(value ?? "").toLowerCase().match(/\.([a-z0-9]+)(\s*\[[^\]]+\]\s*)?$/i); return match ? match[1] : ""; } function canBrowserPlayVideoExt(ext) { const mime = VIDEO_MIME_BY_EXT[ext]; if (!mime || typeof document === "undefined") return false; const probe = document.createElement("video"); const support = probe.canPlayType(mime); return support === "probably" || support === "maybe"; } function makeViewUrl(api, rawFilename) { const { filename, subfolder, type } = parseAnnotated(rawFilename); if (!filename) return null; const qs = new URLSearchParams({ filename, type, subfolder }); const ext = fileExtLower(filename); if (VIDEO_EXTS.has(ext) && !canBrowserPlayVideoExt(ext)) { qs.set("deadline", "realtime"); return api.apiURL(`/imageops/viewmedia?${qs.toString()}`); } return api.apiURL(`/view?${qs.toString()}`); } function fitWithinMaxSize(width, height, maxSize) { const safeWidth = Math.max(1, Math.round(width || 1)); const safeHeight = Math.max(1, Math.round(height || 1)); const safeMax = Math.max(1, Math.round(maxSize || 1)); const scale = Math.min(1, safeMax / Math.max(safeWidth, safeHeight)); return { width: Math.max(1, Math.round(safeWidth * scale)), height: Math.max(1, Math.round(safeHeight * scale)) }; } function ensureCanvasSize(canvas, width, height) { const targetWidth = Math.max(1, Math.round(width || 1)); const targetHeight = Math.max(1, Math.round(height || 1)); const next = canvas ?? document.createElement("canvas"); if (next.width !== targetWidth) next.width = targetWidth; if (next.height !== targetHeight) next.height = targetHeight; return next; } async function waitForVideoReady(videoEl, timeoutMs = 4e3) { if (videoEl.readyState >= 2) return; await new Promise((resolve) => { let done = false; let timeoutId = 0; const finish = () => { if (done) return; done = true; videoEl.removeEventListener("loadeddata", finish); videoEl.removeEventListener("canplay", finish); videoEl.removeEventListener("error", finish); if (timeoutId) window.clearTimeout(timeoutId); resolve(); }; timeoutId = window.setTimeout(finish, timeoutMs); videoEl.addEventListener("loadeddata", finish, { once: true }); videoEl.addEventListener("canplay", finish, { once: true }); videoEl.addEventListener("error", finish, { once: true }); }); } function renderImageSourceToCanvas(node, source, width, height, slot) { node.__imageops_media ?? (node.__imageops_media = {}); const st = node.__imageops_media; const canvas = ensureCanvasSize(st[slot], width, height); const ctx = canvas.getContext("2d"); if (!ctx) return canvas; ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(source, 0, 0, canvas.width, canvas.height); st[slot] = canvas; return canvas; } function resolveNodeIntrinsicMediaSize(node, fallbackCanvas) { const nativeImage = getNativePreviewImage(node); if (nativeImage && nativeImage.naturalWidth > 0 && nativeImage.naturalHeight > 0) { return { width: Math.max(1, nativeImage.naturalWidth), height: Math.max(1, nativeImage.naturalHeight) }; } const previewMedia = Array.isArray(node.imgs) && node.imgs.length > 0 ? node.imgs[Math.max(0, Math.min(node.imgs.length - 1, typeof node.imageIndex === "number" ? node.imageIndex : node.imgs.length - 1))] ?? null : null; if (previewMedia instanceof HTMLImageElement && previewMedia.naturalWidth > 0 && previewMedia.naturalHeight > 0) { return { width: Math.max(1, previewMedia.naturalWidth), height: Math.max(1, previewMedia.naturalHeight) }; } if (previewMedia instanceof HTMLVideoElement && previewMedia.videoWidth > 0 && previewMedia.videoHeight > 0) { return { width: Math.max(1, previewMedia.videoWidth), height: Math.max(1, previewMedia.videoHeight) }; } if (previewMedia instanceof HTMLCanvasElement && previewMedia.width > 0 && previewMedia.height > 0) { return { width: Math.max(1, previewMedia.width), height: Math.max(1, previewMedia.height) }; } for (const widget of node.widgets ?? []) { const element = widget?.element; if (!element) continue; const media = element instanceof HTMLImageElement || element instanceof HTMLVideoElement || element instanceof HTMLCanvasElement ? element : element.querySelector?.("img,video,canvas") ?? null; if (media instanceof HTMLImageElement && media.naturalWidth > 0 && media.naturalHeight > 0) { return { width: Math.max(1, media.naturalWidth), height: Math.max(1, media.naturalHeight) }; } if (media instanceof HTMLVideoElement && media.videoWidth > 0 && media.videoHeight > 0) { return { width: Math.max(1, media.videoWidth), height: Math.max(1, media.videoHeight) }; } if (media instanceof HTMLCanvasElement && media.width > 0 && media.height > 0) { return { width: Math.max(1, media.width), height: Math.max(1, media.height) }; } } return { width: Math.max(1, fallbackCanvas?.width || 1), height: Math.max(1, fallbackCanvas?.height || 1) }; } async function ensureImageElement(node, url) { node.__imageops_media ?? (node.__imageops_media = {}); const st = node.__imageops_media; if (st.lastImageURL === url && st.imageEl) return st.imageEl; const img = new Image(); img.src = url; try { await img.decode(); } catch (e) { console.warn("[ImageOps] image decode failed:", url, e); return null; } st.lastImageURL = url; st.imageEl = img; if (st.lastBitmapURL !== url) { st.lastBitmap?.close(); st.lastBitmapURL = void 0; st.lastBitmap = void 0; } return img; } async function ensureBitmap(node, url) { node.__imageops_media ?? (node.__imageops_media = {}); const st = node.__imageops_media; if (st.lastBitmapURL === url && st.lastBitmap) return st.lastBitmap; const img = await ensureImageElement(node, url); if (!img) return null; const bmp = await createImageBitmap(img); if (st.lastBitmap && st.lastBitmapURL !== url) { st.lastBitmap.close(); } st.lastBitmapURL = url; st.lastBitmap = bmp; return bmp; } async function ensureVideoFrameCanvas(node, url, size, tick = 0) { node.__imageops_media ?? (node.__imageops_media = {}); const st = node.__imageops_media; if (!st.videoEl || st.lastVideoURL !== url) { if (st.videoEl) { try { st.videoEl.pause(); } catch { } try { st.videoEl.removeAttribute("src"); st.videoEl.load(); } catch { } } const v2 = document.createElement("video"); v2.preload = "auto"; v2.src = url; v2.muted = true; v2.loop = true; v2.playsInline = true; v2.autoplay = true; st.videoEl = v2; st.lastVideoURL = url; st.lastVideoFrameKey = null; try { v2.load(); } catch { } await waitForVideoReady(v2); if (st.videoEl === v2) { try { await v2.play(); } catch (e) { console.warn("[ImageOps] video play failed:", e); } } } const v = st.videoEl; void tick; if (v.readyState < 2) { await waitForVideoReady(v, 1500); if (v.readyState < 2) { if (st.videoCanvas) return st.videoCanvas; const placeholder = ensureCanvasSize(st.videoCanvas, Math.max(1, size), Math.max(1, size)); st.videoCanvas = placeholder; return placeholder; } } const { width, height } = fitWithinMaxSize(v.videoWidth || size, v.videoHeight || size, size); let c = st.videoCanvas; if (!c) { c = ensureCanvasSize(void 0, width, height); } else if (c.width !== width || c.height !== height) { const next = document.createElement("canvas"); next.width = Math.max(1, Math.round(width)); next.height = Math.max(1, Math.round(height)); try { next.getContext("2d")?.drawImage(c, 0, 0, next.width, next.height); } catch { } c = next; } st.videoCanvas = c; const ctx = c.getContext("2d"); if (!ctx) return c; if (v.paused && !st.videoPlayInFlight) { st.videoPlayInFlight = true; v.play().catch(() => { }).finally(() => { st.videoPlayInFlight = false; }); } const frameKey = `${v.currentTime.toFixed(4)}|${width}x${height}`; if (st.lastVideoFrameKey === frameKey) return c; st.lastVideoFrameKey = frameKey; ctx.drawImage(v, 0, 0, width, height); return c; } function disposeMediaState(node) { const st = node.__imageops_media; if (!st) return; try { if (st.videoEl) { try { st.videoEl.pause(); } catch { } try { st.videoEl.removeAttribute("src"); st.videoEl.load(); } catch { } } } catch { } try { st.lastBitmap?.close(); } catch { } st.videoEl = void 0; st.lastVideoURL = void 0; st.lastBitmap = void 0; st.lastBitmapURL = void 0; st.imageEl = void 0; st.lastImageURL = void 0; st.imageCanvas = void 0; st.animatedImageCanvas = void 0; st.videoCanvas = void 0; st.nativeCanvas = void 0; st.staticRenderCache?.clear(); st.staticRenderCache = void 0; st.lastVideoFrameKey = null; } export { disposeMediaState, ensureBitmap, ensureImageElement, ensureVideoFrameCanvas, fitWithinMaxSize, makeViewUrl, parseAnnotated, renderImageSourceToCanvas, resolveNodeIntrinsicMediaSize };