diff --git a/slaeforms/app.py b/slaeforms/app.py index 034256e..0aca2aa 100644 --- a/slaeforms/app.py +++ b/slaeforms/app.py @@ -11,7 +11,7 @@ import uuid from sqlalchemy.exc import SQLAlchemyError from sqlalchemy import select, join from sqlalchemy.orm import DeclarativeBase -from flask_wtf.csrf import CSRFProtect +#from flask_wtf.csrf import CSRFProtect import os import csv from zipfile import ZipFile diff --git a/slaeforms/static/videoscript.js b/slaeforms/static/videoscript.js index 25dd1d0..9cb8681 100644 --- a/slaeforms/static/videoscript.js +++ b/slaeforms/static/videoscript.js @@ -1,64 +1,72 @@ const buttonCamera = document.getElementById('buttonCamera'); const buttonRecord = document.getElementById('buttonRecord'); const buttonDelete = document.getElementById('buttonDelete'); -const videoDisplay = document.getElementById('videoDisplay'); const buttonCameraIcon = document.getElementById('buttonCameraIcon'); const buttonRecordIcon = document.getElementById('buttonRecordIcon'); const buttonDeleteIcon = document.getElementById('buttonDeleteIcon'); -const videoContainer = document.getElementById('videoContainer'); -const videoContainerCss = document.querySelector(".video-container ") //might be unnecessary -var mediaRecorder = null; -var stream = null; -let recordedVideoBlob = null; -let isRecording = false; -let videoAccess = false; -let videoHeigt = 720; + +const videoDisplay = document.getElementById('videoDisplay'); +const videoContainer = document.getElementById('videoContainer'); // div that contains the videodisplay +const videoContainerCss = document.querySelector('.video-container') //might be unnecessary + +var mediaRecorder = null; // interface of the Media Stream Recording API to record the video stream +var stream = null; // webcam video input stream +let recordedVideoBlob = null; // recorded videodata +let isRecording = false; // false to display record button, true to display stop button +let videoAccess = false; // true if user has given permission to use the webcam + +// default video dimensions +let videoHeigt = 720; let videoWidth = 1280; -// Handle form submission +// handle form submission +//the Video is beeing send together with the form data document.getElementById("question_form").addEventListener("submit", function (event) { event.preventDefault(); // Prevent the default form submission console.log("submit button pressed"); - // Create a FormData object + // create a FormData object const formData = new FormData(event.target); console.log("form data: ",formData); - // Append the recorded video Blob to the FormData object + // append the recorded video blob to the FormData object if (recordedVideoBlob) { console.log("video is available: ", recordedVideoBlob); formData.append("recordedVideo", recordedVideoBlob, "recordedVideo.webm"); } - console.log("Data to be submitted: ",formData); - // Use fetch to send the form data and video to the backend console.log("try to send the form and video"); + + // send the form data and video to the backend fetch("/send", { method: "POST", body: formData }).then(response => { + // code to handle proper redirection after submit console.log('Response:', response); - // Check if response is a redirect (HTTP 3xx status) + // check if response is a redirect if (response.redirected) { console.log("Redirecting to:", response.url); window.location.href = response.url; // Redirect to the new page } else { - console.log("Non-redirect response received."); - // Handle other responses if needed + console.log("No redirect response received."); } }) .catch(error => { console.error('Error:', error); - // Handle errors if fetch fails }); }); +// enable/disable video recording feature async function cameraButton() { if (!videoAccess) { console.log("cameraButton case videoAccess = false"); + try { + // ask the user for permission to use the webcam + // if given, assign it to "stream" stream = await navigator.mediaDevices.getUserMedia({ video: true, }); @@ -67,39 +75,53 @@ async function cameraButton() { return; } console.log("stream is active"); + videoAccess = true; + // set the dimensions of the webcam stream to the video display videoHeigt = stream.getVideoTracks()[0].getSettings().height videoWidth = stream.getVideoTracks()[0].getSettings().width + // calculate the aspec ratio let aspectratio = (videoHeigt / videoWidth) *100 - console.log("videoHeigt: ",videoHeigt); - console.log("videoWidth: ",videoWidth); - console.log("aspect ratio: ",aspectratio); - console.log("device: ",stream.getVideoTracks()[0].getSettings().deviceId); + //console.log("videoHeigt: ",videoHeigt); + //console.log("videoWidth: ",videoWidth); + //console.log("aspect ratio: ",aspectratio); + //console.log("device: ",stream.getVideoTracks()[0].getSettings().deviceId); + + // adjust CSS to make the video fit properly + // this makes sure that there is no buggy "jumping" when the video display source changes videoContainer.style.setProperty("padding-bottom", "min("+videoHeigt+"px,"+aspectratio+"%)"); + // adjust the max width for horizontal and vertical video input if (videoHeigt > videoWidth){ //hochkant video videoContainer.style.setProperty("max-width", "min(80%,576"); - }else{ //Normal, horizontal + }else{ //normal, horizontal videoContainer.style.setProperty("max-width", "100%"); } + // show the webcam input stream on the video display videoDisplay.srcObject = stream; + // change the icon to indicate the disable option buttonCameraIcon.src = ICON_PATHS.cameraofficon; buttonCameraIcon.alt = "Camera-off Icon"; + + // display the elements for the video recording buttonRecord.style.display = 'inline-block'; buttonDelete.style.display = 'inline-block'; videoDisplay.style.display = 'block'; videoContainer.style.display = 'block'; videoDisplay.classList.add("videomirror"); + // initialize MediaRecorder, give it the webcam stream as input to record mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm", // could use different video format // videoBitsPerSecond: 5000000, // Standard bitrate for video is 2,5 mbps }); + // when data is available at the mediaRecorder (this means the recording has ended, since I have not specified time intervalls) + // save the data in "recordedVideoBlob" mediaRecorder.addEventListener("dataavailable", (event) => { console.log("Data available Event triggered"); if (event.data.size > 0) { @@ -110,28 +132,37 @@ async function cameraButton() { } }); - mediaRecorder.addEventListener("stop", () => { + // when recording is stopped, display the recorded video + // TODO possibly replace this and just add it to the dataavailable event + // TODO this would ensure proper order in a concurrent scenario (the stop code here can only be executed after the dataavailable code happened) + // TODO part of it could also go to the record button function + mediaRecorder.addEventListener("stop", () => { if (recordedVideoBlob) { - videoDisplay.srcObject = null; - videoDisplay.src = URL.createObjectURL(recordedVideoBlob); - videoDisplay.controls = true; - videoDisplay.pause(); + videoDisplay.srcObject = null; //remove the stream as input + videoDisplay.src = URL.createObjectURL(recordedVideoBlob); // create a url for the recorded video and set it as input + videoDisplay.controls = true; // display video controls + videoDisplay.pause(); // pause, so that it doesn't immediately play, TODO maybe disable autoplay? - buttonRecordIcon.src = ICON_PATHS.recordicon; + // TODO maybe move this to the record button function + buttonRecordIcon.src = ICON_PATHS.recordicon; //change the icon of the record button back buttonRecordIcon.alt = 'Record Icon'; isRecording = false; + console.log('Recording stopped'); console.log("Src path:", videoDisplay.src); } }); } else { + // -> the user disabled the videorecording feature + console.log("cameraButton case videoAccess = true"); - stream.getTracks().forEach(track => track.stop()); // Stop all tracks to release the camera + stream.getTracks().forEach(track => track.stop()); // stop all tracks to release the camera stream = null; videoAccess = false; + // change the icon and hide all the elements again buttonCameraIcon.src = ICON_PATHS.cameraicon; buttonCameraIcon.alt = "Camera Icon"; buttonRecord.style.display = 'none'; @@ -142,41 +173,57 @@ async function cameraButton() { console.log("camera button function ends"); } + function recordButton() { console.log("recordButton pressed"); if (!isRecording) { + // a new recording starts + console.log("recordButton pressed case isRecording = false"); - deleteButton(); - videoDisplay.classList.add("videomirror"); - buttonDelete.setAttribute("disabled", ""); - buttonDeleteIcon.classList.add("buttondisable"); - videoDisplay.srcObject = stream; - videoDisplay.src = null; - videoDisplay.controls = false; - mediaRecorder.start(); + + deleteButton(); //delete previous video + + videoDisplay.classList.add("videomirror"); // mirror the video while recording, for a more natural experience + buttonDelete.setAttribute("disabled", ""); // can't delete during recording + buttonDeleteIcon.classList.add("buttondisable"); // delete button icon indicates that it is disabled + + videoDisplay.src = null; // get rid of the previous video on the display + videoDisplay.srcObject = stream; // set webcam stream as input for the video display + videoDisplay.controls = false; // hide the video controls durin recording, because they are confusing + mediaRecorder.start(); // start recording + console.log("video bitrate = ",mediaRecorder.videoBitsPerSecond); + // change icon to indicate "stop" buttonRecordIcon.src = ICON_PATHS.stopicon; buttonRecordIcon.alt = 'Stop Icon'; isRecording = true; + console.log('Recording started'); } else { + // recording got stopped + console.log("recordButton pressed case isRecording = true"); - mediaRecorder.stop(); - videoDisplay.classList.remove("videomirror"); + + mediaRecorder.stop(); // stop recording + videoDisplay.classList.remove("videomirror"); // don't mirror the videodisplay anymore console.log("recording stops"); + + // enable the deletebutton again buttonDelete.removeAttribute("disabled"); buttonDeleteIcon.classList.remove("buttondisable"); } } function deleteButton() { - // TODO delete data + // delete the recorded video videoDisplay.controls = false; - videoDisplay.srcObject = stream; + videoDisplay.srcObject = stream; // set webcam stream as source for the video display again recordedVideoBlob = null - videoDisplay.classList.add("videomirror"); + videoDisplay.classList.add("videomirror"); // mirror the video display during live feed again + + // disable the deletebutton again buttonDelete.setAttribute("disabled", ""); buttonDeleteIcon.classList.add("buttondisable"); }