Files
MajoorWaldi-ComfyUI-Majoor-…/ui/components/VideoControls.ts
T
Ewald ALOEBOETOE 9ed8759fb1 Use the Studio player in every floating viewer mode and restore light dismiss
- Floating viewer compare modes (A/B, side by side, grid) now build their
  primary video/audio with the Studio player, like the simple mode. The
  legacy video controls, their media bridge, styles and tests are removed;
  only the sidebar's preview controls remain.
- The Studio viewer closes again when the empty area around the picture is
  clicked (quick click only, so panning never dismisses it).
2026-11-04 13:12:33 +00:00

138 lines
3.9 KiB
TypeScript

import { noop, safeCall, safeAddListener } from "../utils/safeCall.js";
import { t } from "../app/i18n.js";
function mountPreviewControls(video: any, hostEl: any) {
const unsubs: any[] = [];
try {
// Preview thumbnails should loop continuously (muted).
video.controls = false;
video.loop = true;
video.muted = true;
video.playsInline = true;
video.autoplay = true;
} catch (e: any) {
console.debug?.(e);
}
const controls = document.createElement("div");
controls.className = "mjr-video-controls mjr-video-controls--preview";
try {
controls.setAttribute("role", "group");
controls.setAttribute("aria-label", t("video.previewControls", "Video preview controls"));
} catch (e: any) {
console.debug?.(e);
}
const btn = document.createElement("button");
btn.type = "button";
btn.className = "mjr-video-preview-btn";
btn.title = t("video.playPause", "Play/Pause");
try {
btn.setAttribute("aria-label", t("video.playPause", "Play/Pause"));
} catch (e: any) {
console.debug?.(e);
}
const icon = document.createElement("span");
icon.className = "pi pi-play";
try {
icon.setAttribute("aria-hidden", "true");
} catch (e: any) {
console.debug?.(e);
}
btn.appendChild(icon);
controls.appendChild(btn);
const setIcon = () => {
try {
const paused = Boolean(video?.paused);
icon.className = `pi ${paused ? "pi-play" : "pi-pause"}`;
} catch (e: any) {
console.debug?.(e);
}
};
const tryPlay = () => {
try {
const p = video.play?.();
if (p && typeof p.catch === "function") p.catch(() => {});
} catch (e: any) {
console.debug?.(e);
}
};
const toggle = (e: any) => {
try {
e?.stopPropagation?.();
} catch (e: any) {
console.debug?.(e);
}
try {
if (video.paused) {
tryPlay();
} else {
video.pause?.();
}
} catch (e: any) {
console.debug?.(e);
}
setIcon();
};
try {
hostEl.appendChild(controls);
} catch (e: any) {
console.debug?.(e);
}
// Best-effort autoplay.
try {
tryPlay();
} catch (e: any) {
console.debug?.(e);
}
unsubs.push(safeAddListener(video, "loadedmetadata", () => tryPlay(), { passive: true }));
unsubs.push(safeAddListener(video, "canplay", () => tryPlay(), { passive: true }));
unsubs.push(safeAddListener(btn, "click", toggle));
unsubs.push(safeAddListener(video, "play", setIcon, { passive: true }));
unsubs.push(safeAddListener(video, "pause", setIcon, { passive: true }));
unsubs.push(safeAddListener(video, "ended", () => tryPlay(), { passive: true }));
try {
setIcon();
} catch (e: any) {
console.debug?.(e);
}
return {
controlsEl: controls,
destroy: () => {
try {
for (const u of unsubs) safeCall(() => u?.());
} catch (e: any) {
console.debug?.(e);
}
try {
controls.remove?.();
} catch (e: any) {
console.debug?.(e);
}
},
};
}
/**
* Minimal play/pause overlay with continuous muted looping, used by the asset
* sidebar preview. Must never throw.
*/
export function mountVideoControls(video: any, opts: Record<string, any> = {}) {
try {
const hostEl = opts?.hostEl || video?.parentElement;
if (!video || !hostEl) return { controlsEl: null, destroy: noop };
return mountPreviewControls(video, hostEl);
} catch (e: any) {
console.debug?.(e);
return { controlsEl: null, destroy: noop };
}
}