Files
littleprynter/src/static/js/webcam.js
2026-05-22 11:01:06 +02:00

349 lines
10 KiB
JavaScript

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);