let streaming; var current_stream; var current_camera_is; var supports_facing_mode; var camera_options; var width = document.getElementById("video").parentNode.parentElement.clientWidth; var height = width / (4 / 3); var socket = io(); socket.on('connect', function() { socket.emit('ping', {data: 'I\'m connected!'}); console.log("Sent a ping to the server :)"); }); socket.on('pong', () => { console.log('Received pong back ! '); }); socket.on('new_image', () => { console.log("Received new image event"); const img = document.getElementById('snapshot'); img.src = '/image?' + new Date().getTime(); // Bust cache }); async function startup(){ video = document.getElementById('video'); canvas = document.getElementById('canvas'); photo = document.getElementById('photo'); switch_cameras = document.getElementById('flip') printButton = document.getElementById('print_button'); console.log("Checking for client webcam capabilities"); // We have a camera_options dictionnary in return, or false if the device does not support webcams. let client_webcam_capabilities = await check_webcam_capabilies(); if ( client_webcam_capabilities != false ){ get_webcam(client_webcam_capabilities); setup_events(); clear_canvas(); } else { console.log("Checking for server webcam capabilities"); let server_webcam_capabilities = await check_server_camera(); if (server_webcam_capabilities === true ){ console.log("The server has a camera, using it."); console.log("TODO"); } else { no_webcam_error(); console.log("Seems like it's impossible to get a webcam from the client nor the server."); } } } async function check_webcam_capabilies(){ console.log("Checking for a the capabilities of the client's media devices"); // We try to start with the front facing camera, // if we have no support, we switch back to a normal camera. try { // We first check if the navigator has a media device if (!navigator.mediaDevices?.enumerateDevices) { console.log("enumerateDevices() not supported."); return false; } else { // List cameras and microphones. console.log("The device has the following media devices : ") await navigator.mediaDevices .enumerateDevices() .then((devices) => { devices.forEach((device) => { console.log(`${device.kind}: ${device.label} id = ${device.deviceId}`); }); }) .catch((err) => { console.error(`${err.name}: ${err.message}`); return false; }); } } catch (e) { console.log("The device does not seem to support webcams " + e); return false; } console.log("Checking for the supported constraints of the media devices "); try { const supports = navigator.mediaDevices.getSupportedConstraints(); if (!supports['facingMode']) { throw new Error("This browser does not support facingMode!"); } else { console.log("The device supports facing mode, selecting the front camera by default"); supports_facing_mode = true; camera_options = { video: { facingMode: 'user', // Or 'environment' if we want a camera facing away }, audio: false }; return camera_options; } } catch (e) { console.log("Resetting to default camera : " + e); supports_facing_mode = false; camera_options = { video: true, audio: false }; return camera_options } } async function get_webcam(options){ stop_video_streams(); try { navigator.mediaDevices.getUserMedia(options) .then(function(stream) { // on success, stream it in video tag // the video tag is hidden, as is the canvas. console.log("Got the webcam stream"); printButton.removeAttribute("disabled",""); current_stream = stream; video.srcObject = stream; video.setAttribute('autoplay', ''); video.setAttribute('muted', ''); video.setAttribute('playsinline', '') video.play(); return true; }) .catch(function(err) { console.log("Didn't manage to get a camera :" + err); return false; }); } catch (err) { console.log("Didn't manage to get a camera :" + err); return false; } } async function check_server_camera(){ console.log("Checking for a camera on the server"); await socket.emit('get_camera_status', (response) => { if (response){ console.log("Server has a camera !"); return true; } else { console.log("The server doesn't seem to have a camera"); return false; } }); } function setup_events(){ // When the video is ready, we start streaming it to the canvas. // The canvas is hidden, but it still exists in the browser. video.addEventListener('canplay', function(ev) { if (!streaming) { height = video.videoHeight / (video.videoWidth / width); if (isNaN(height)) { height = width / (4 / 3); } video.setAttribute('width', width); video.setAttribute('height', height); canvas.setAttribute('width', width); canvas.setAttribute('height', height); photo.setAttribute('width', width); photo.setAttribute('height', height); streaming = true; printButton.removeAttribute("disabled",""); switch_cameras.removeAttribute("disabled",""); } }, false); switch_cameras.addEventListener('click', function(ev) { flip_cameras(); }, false ); printButton.addEventListener('click', async function(ev) { ev.preventDefault(); try { await countDownToPicture(); // wait for countdown to finish var data = await take_picture(); // take_picture can be async print_picture(data); } catch (e) { alert("Failed to print a picture because : " + e); } }, false); } function clear_canvas() { var context = canvas.getContext('2d'); context.fillStyle = "#AAA"; context.fillRect(0, 0, canvas.width, canvas.height); var data = canvas.toDataURL('image/png'); photo.setAttribute('src', data); } function dataURLtoFile(dataurl, filename) { if(dataurl === "") { console.log("We didn't receive data"); } var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while(n--){ u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], filename, {type:mime}); } async function take_picture() { const context = canvas.getContext('2d'); let data = null; if (width && height) { canvas.width = width; canvas.height = height; context.drawImage(video, 0, 0, width, height); data = canvas.toDataURL('image/png'); photo.setAttribute('src', data); } else { clear_canvas(); } return data; } function print_picture(data){ var url = "/api/print/img" var picture = dataURLtoFile(data); const formData = new FormData(); let currentDate = new Date(); let time = currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds(); formData.set("img", picture, "picture.png"); formData.set("signature", "Webcam") fetch(url, { method: 'POST', // or 'PUT' body: formData, // headers:{ // 'Content-Type': 'multipart/form-data' // } }).then(function(response) { console.log('Reponse:', response); if(response.status != 200 ){ alert("The picture could not be printed be : " + response.statusText) } } , true) .catch(error => console.error('Error:', error), false); } function flip_cameras(){ console.log("Current facing mode : " + camera_options.video.facingMode) if ( supports_facing_mode ) { console.log("Support facing mode, trying to switch !"); if (camera_options.video.facingMode == 'user'){ camera_options = { audio: false, video: { facingMode: "environment" },}; } else { camera_options = { audio: false, video: { facingMode: "user" },}; } } console.log("Switching to " + camera_options.video.facingMode ); get_webcam(camera_options); } function stop_video_streams(){ console.log("Stopping existing video streams."); // Stop the tracks try { if (current_stream) { const tracks = current_stream.getTracks(); tracks.forEach(track => track.stop()); console.log("Stopped playing current videostreams."); return true; } else { console.log("No streams currently playing."); } } catch (e) { console.log("Could not stop playing current streams." + e); } } function no_webcam_error(){ console.log("Seems like they is no webcam available.") // We disable the print button is it cannot be clicked. printButton.setAttribute("disabled",""); // We create an alert message. const frame_div = document.getElementById("image_dither"); frame_div.removeAttribute('class'); const alert_div = document.createElement("div"); alert_div.setAttribute("class", "alert alert-warning"); alert_div.setAttribute("role", "alert"); var alert_message = document.createTextNode("We where unable to get a Webcam device, this page will not work."); alert_div.appendChild(alert_message); frame_div.appendChild(alert_div); throw new Error("Unable to get a video device, stopping the photobooth."); } async function countDownToPicture(){ return new Promise((resolve) => { console.log("Starting countdown"); // We create full-page overlay that displays a number to countdown until the picture is taken. const countdown_div = document.getElementById("countdown"); countdown_div.style.display = "block"; // Set the different styling attributes of the element countdown_div.setAttribute("class", "fullscreen-countdown"); // The loop must run for 3 seconds interval = 1000; var loops = 3; var x = setInterval(() => { console.log(loops); // Update the content of the div document.getElementById("countdown-number").innerHTML = loops; if (loops == 0) { countdown_div.style.display = "none"; // The timer has finished clearInterval(x); resolve(); } loops--; }, interval); }); } window.addEventListener('load', startup, false);