2024-05-17 17:16:08 +00:00
import { app } from "../../scripts/app.js" ;
import { api } from "../../scripts/api.js" ;
const WEBCAM _READY = Symbol ( ) ;
app . registerExtension ( {
name : "Comfy.WebcamCapture" ,
getCustomWidgets ( app ) {
return {
WEBCAM ( node , inputName ) {
let res ;
node [ WEBCAM _READY ] = new Promise ( ( resolve ) => ( res = resolve ) ) ;
const container = document . createElement ( "div" ) ;
container . style . background = "rgba(0,0,0,0.25)" ;
container . style . textAlign = "center" ;
const video = document . createElement ( "video" ) ;
video . style . height = video . style . width = "100%" ;
const loadVideo = async ( ) => {
try {
const stream = await navigator . mediaDevices . getUserMedia ( { video : true , audio : false } ) ;
container . replaceChildren ( video ) ;
setTimeout ( ( ) => res ( video ) , 500 ) ; // Fallback as loadedmetadata doesnt fire sometimes?
video . addEventListener ( "loadedmetadata" , ( ) => res ( video ) , false ) ;
video . srcObject = stream ;
video . play ( ) ;
} catch ( error ) {
const label = document . createElement ( "div" ) ;
label . style . color = "red" ;
label . style . overflow = "auto" ;
label . style . maxHeight = "100%" ;
label . style . whiteSpace = "pre-wrap" ;
2024-05-17 18:02:09 +00:00
if ( window . isSecureContext ) {
label . textContent = "Unable to load webcam, please ensure access is granted:\n" + error . message ;
} else {
label . textContent = "Unable to load webcam. A secure context is required, if you are not accessing ComfyUI on localhost (127.0.0.1) you will have to enable TLS (https)\n\n" + error . message ;
}
2024-05-17 17:16:08 +00:00
container . replaceChildren ( label ) ;
}
} ;
loadVideo ( ) ;
return { widget : node . addDOMWidget ( inputName , "WEBCAM" , container ) } ;
} ,
} ;
} ,
nodeCreated ( node ) {
if ( ( node . type , node . constructor . comfyClass !== "WebcamCapture" ) ) return ;
let video ;
const camera = node . widgets . find ( ( w ) => w . name === "image" ) ;
const w = node . widgets . find ( ( w ) => w . name === "width" ) ;
const h = node . widgets . find ( ( w ) => w . name === "height" ) ;
const captureOnQueue = node . widgets . find ( ( w ) => w . name === "capture_on_queue" ) ;
const canvas = document . createElement ( "canvas" ) ;
const capture = ( ) => {
canvas . width = w . value ;
canvas . height = h . value ;
const ctx = canvas . getContext ( "2d" ) ;
ctx . drawImage ( video , 0 , 0 , w . value , h . value ) ;
const data = canvas . toDataURL ( "image/png" ) ;
const img = new Image ( ) ;
img . onload = ( ) => {
node . imgs = [ img ] ;
app . graph . setDirtyCanvas ( true ) ;
requestAnimationFrame ( ( ) => {
node . setSizeForImage ? . ( ) ;
} ) ;
} ;
img . src = data ;
} ;
const btn = node . addWidget ( "button" , "waiting for camera..." , "capture" , capture ) ;
btn . disabled = true ;
btn . serializeValue = ( ) => undefined ;
camera . serializeValue = async ( ) => {
if ( captureOnQueue . value ) {
capture ( ) ;
} else if ( ! node . imgs ? . length ) {
const err = ` No webcam image captured ` ;
alert ( err ) ;
throw new Error ( err ) ;
}
// Upload image to temp storage
const blob = await new Promise ( ( r ) => canvas . toBlob ( r ) ) ;
const name = ` ${ + new Date ( ) } .png ` ;
const file = new File ( [ blob ] , name ) ;
const body = new FormData ( ) ;
body . append ( "image" , file ) ;
body . append ( "subfolder" , "webcam" ) ;
body . append ( "type" , "temp" ) ;
const resp = await api . fetchApi ( "/upload/image" , {
method : "POST" ,
body ,
} ) ;
if ( resp . status !== 200 ) {
const err = ` Error uploading camera image: ${ resp . status } - ${ resp . statusText } ` ;
alert ( err ) ;
throw new Error ( err ) ;
}
return ` webcam/ ${ name } [temp] ` ;
} ;
node [ WEBCAM _READY ] . then ( ( v ) => {
video = v ;
// If width isnt specified then use video output resolution
if ( ! w . value ) {
w . value = video . videoWidth || 640 ;
h . value = video . videoHeight || 480 ;
}
btn . disabled = false ;
btn . label = "capture" ;
} ) ;
} ,
} ) ;