diff --git a/ExtrasNode/README.md b/ExtrasNode/README.md index c5962a3..8ee4a33 100644 --- a/ExtrasNode/README.md +++ b/ExtrasNode/README.md @@ -20,4 +20,8 @@ - Speech & Recognition speech +- Preview image, video and sound select list combo + +![Preview image, video and audio select list combo](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/raw/master/ExtrasNode/extras_node_preview_img_vid_audio_combo.gif) + **Note** (Speech & Recognition speech): If your browser Firefox: go to `about:config`, find `media.webspeech.recognition.enable`, `media.webspeech.recognition.force_enable` set values ​​to `true` diff --git a/ExtrasNode/extras_node_preview_img_vid_audio_combo.gif b/ExtrasNode/extras_node_preview_img_vid_audio_combo.gif new file mode 100644 index 0000000..ccf8e3d Binary files /dev/null and b/ExtrasNode/extras_node_preview_img_vid_audio_combo.gif differ diff --git a/ExtrasNode/js/extras_node.js b/ExtrasNode/js/extras_node.js index 01a8390..e25a694 100644 --- a/ExtrasNode/js/extras_node.js +++ b/ExtrasNode/js/extras_node.js @@ -39,6 +39,11 @@ const SpeechAndRecognationSpeechLS = localStorage.getItem( ); // end - Recognation & speech +// Preview image, video and audio select list combo +const PreviewImageVideoComboLS = localStorage.getItem( + `Comfy.Settings.${idExt}.PreviewImageVideoCombo` +); + // Settings set values from LS or default let PreviewImageSize = PreviewImageSizeLS ? JSON.parse(PreviewImageSizeLS) @@ -52,14 +57,212 @@ let PreviewImageSize = PreviewImageSizeLS // Speech & Recognition widget settings SpeechAndRecognationSpeech = SpeechAndRecognationSpeechLS ? JSON.parse(SpeechAndRecognationSpeechLS) + : true, + // Preview image, video and audio select list combo + PreviewImageVideoCombo = PreviewImageVideoComboLS + ? JSON.parse(PreviewImageVideoComboLS) : true; +// Preview image, video and audio content loading function +const SUPPORTS_FORMAT = { + image: ["jpg", "jpeg", "bmp", "png", "gif", "tiff", "avif"], + video: ["mp4", "webm"], + audio: ["ogg", "wav", "mp3"], +}; + +const loadingContent = (src) => { + return new Promise((res, rej) => { + let ext = src.slice(src.lastIndexOf(".") + 1).toLowerCase(); + ext = /\w+/.exec(ext)[0]; + + if (SUPPORTS_FORMAT.image.includes(ext)) { + const img = new Image(); + img.crossOrigin = ""; + img.onload = (e) => res({ raw: img, type: "image", src }); + img.onerror = (err) => rej(err); + img.src = src; + } else if (SUPPORTS_FORMAT.video.includes(ext)) { + const video = document.querySelector(".preview_vid"); + video.onerror = (err) => rej(err); + + video.addEventListener("canplay", (e) => { + res({ raw: video, type: "video", src }); + }); + video.src = src; + } else if (SUPPORTS_FORMAT.audio.includes(ext)) { + const audio = document.querySelector(".preview_audio"); + audio.onerror = (err) => rej(err); + + audio.addEventListener("canplaythrough", (e) => { + res({ raw: audio, type: "audio", src }); + }); + audio.src = src; + } + }); +}; + +function drawVid(raw, canvas, ctx, type) { + ctx.drawImage(raw, 0, 0, canvas.width, canvas.height); + + canvas.requanim = requestAnimationFrame( + drawVid.bind(this, raw, canvas, ctx, type) + ); +} +// + // Register Extension app.registerExtension({ name: idExt, + init() { addStylesheet("css/extrasnode/extras_node_styles.css", import.meta.url); + // Preview image, video and audio select list combo + const addItem = LiteGraph.ContextMenu.prototype.addItem; + LiteGraph.ContextMenu.prototype.addItem = function () { + const element = addItem?.apply(this, arguments); + + if (!PreviewImageVideoCombo) return; + + const [name, value, options] = arguments; + if (value instanceof Object) return; + + if (!this.root?.preview_content_combo) { + const preview_content_combo = $el( + "div.preview_content_combo", + { + style: { + position: "absolute", + maxWidth: "160px", + right: "calc(-1% - 160px)", + }, + parent: this.root, + }, + [ + $el("canvas.preview_img", { + style: { width: "100%" }, + }), + $el("video.preview_vid", { + style: { opacity: 0, width: 0, height: 0 }, + crossOrigin: "anonymous", + autoplay: false, + muted: false, + preload: "auto", + }), + $el("audio.preview_audio", { + style: { opacity: 0, position: "absolute", left: 0 }, + crossOrigin: "anonymous", + autoplay: false, + muted: false, + preload: "auto", + controls: true, + }), + ] + ); + + this.root.preview_content_combo = preview_content_combo; + } + + LiteGraph.pointerListenerAdd(element, "enter", (e) => { + if (element?.dataset?.value) { + const body_rect = document.body.getBoundingClientRect(); + const root_rect = this.root.getBoundingClientRect(); + const { x, y } = element.getBoundingClientRect(); + + const scale = app.graph.extra.ds.scale; + + const canvas = this.root.preview_content_combo.children[0]; + const ctx = canvas.getContext("2d"); + + loadingContent( + api.apiURL( + `/view?filename=${encodeURIComponent( + element.dataset.value + )}&type=input` + ) + ).then(({ raw, type, src }) => { + this.root.preview_content_combo.style.maxWidth = + type === "audio" ? "300px" : "160px"; + + const previewWidth = parseInt( + this.root.preview_content_combo.style.maxWidth + ); + + if (scale >= 1) { + this.root.preview_content_combo.style.top = `${ + (y - root_rect.top) / scale + }px`; + } else { + this.root.preview_content_combo.style.top = `${ + y - root_rect.top + }px`; + } + + if ( + body_rect.width && + root_rect.right + previewWidth > body_rect.width + ) { + this.root.preview_content_combo.style.left = `${ + -previewWidth - 10 + }px`; + this.root.preview_content_combo.style.right = "auto"; + } else { + this.root.preview_content_combo.style.left = "auto"; + this.root.preview_content_combo.style.right = `calc(-1% - ${this.root.preview_content_combo.style.maxWidth})`; + } + + ctx.clearRect(0, 0, canvas.width, canvas.height); + const video = document.querySelector(".preview_vid"); + const audio = document.querySelector(".preview_audio"); + + if (type === "image") { + canvas.width = raw.naturalWidth; + canvas.height = raw.naturalHeight; + if (!video.paused) { + video.pause(); + video.currentTime = 0; + } + cancelAnimationFrame(canvas.requanim); + } else if (type === "video") { + canvas.width = raw.videoWidth; + canvas.height = raw.videoHeight; + video.play(); + } else if (type === "audio") { + Object.assign(audio.style, { + opacity: 1, + }); + audio.play(); + } + + if (type === "video" || type === "image") { + Object.assign(audio.style, { + opacity: 0, + }); + !audio.paused && audio.pause(); + ctx.drawImage(raw, 0, 0, canvas.width, canvas.height); + } + + if (type === "video") + canvas.requanim = requestAnimationFrame( + drawVid.bind(this, raw, canvas, ctx, type) + ); + }); + } + }); + + LiteGraph.pointerListenerAdd(element, "leave", (e) => { + if (element?.dataset?.value) { + const canvas = this.root.preview_content_combo.children[0]; + const video = document.querySelector(".preview_vid"); + if (!video.paused) { + video.pause(); + video.currentTime = 0; + } + cancelAnimationFrame(canvas.requanim); + } + }); + }; + // PreviewImage settings ui app.ui.settings.addSetting({ id: `${idExt}.PreviewImage`, @@ -242,6 +445,17 @@ app.registerExtension({ ]); }, }); + + // Preview image, video and audio settings ui + app.ui.settings.addSetting({ + id: `${idExt}.PreviewImageVideoCombo`, + name: "🔸 Preview images and videos in the list", + defaultValue: true, + type: "boolean", + onChange(value) { + PreviewImageVideoCombo = value; + }, + }); }, async beforeRegisterNodeDef(nodeType, nodeData, app) { // --- Preview Text Node diff --git a/PoseNode/js/pose_node.js b/PoseNode/js/pose_node.js index 4b612fb..f89371c 100644 --- a/PoseNode/js/pose_node.js +++ b/PoseNode/js/pose_node.js @@ -2,7 +2,7 @@ * Title: Set Poses in ComflyUI from ControlNet * Author: AlekPet * Description: I rewrote the main.js file as a class, from fkunn1326's openpose-editor (https://github.com/fkunn1326/openpose-editor/blob/master/javascript/main.js) - * Version: 2024.07.21 + * Version: 2024.10.13 * Github: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet */ @@ -492,7 +492,7 @@ class OpenPose { let formData = new FormData(); formData.append("image", blob, fileName); formData.append("overwrite", "true"); - formData.append("type", "temp"); + formData.append("type", "input"); uploadFile(formData); }, "image/png"); // - end diff --git a/pyproject.toml b/pyproject.toml index e08dc3f..dedd2dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_custom_nodes_alekpet" description = "Nodes: PoseNode, PainterNode, TranslateTextNode, TranslateCLIPTextEncodeNode, DeepTranslatorTextNode, DeepTranslatorCLIPTextEncodeNode, ArgosTranslateTextNode, ArgosTranslateCLIPTextEncodeNode, PreviewTextNode, HexToHueNode, ColorsCorrectNode, IDENode." -version = "1.0.29.1" +version = "1.0.29.3" license = { file = "LICENSE" } [project.urls]