Lots of ui work

This commit is contained in:
Jan 2024-04-16 19:05:12 +02:00
parent 2e0426f3e2
commit 05d0200275
18 changed files with 551 additions and 4 deletions

View File

@ -1,7 +1,8 @@
import sys
import json
import random
from flask import Flask, redirect, url_for, request, session, make_response
import base64
from flask import Flask, redirect, url_for, request, session, make_response, jsonify
from flask import render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase
@ -13,6 +14,9 @@ import uuid
random_order = True
# activate environment: cd C:\Users\Jan\Google Drive\Master Stuff\Code\SLAEForms Testing\.venv\Scripts\
# then this: activate
#create the app
app = Flask(__name__)
# configure the database, give it a path (it will be in the instances folder)
@ -58,12 +62,21 @@ with app.app_context():
@app.route("/video", methods=["GET", "POST"])
def sendpage():
def videopage():
return render_template(
"layout2.html"
#"videorecorder3.html"
"myvideotemplate.html"
)
@app.route("/send_video", methods=["POST"])
def send_video():
data_url = request.json['dataUrl']
data = data_url.split(',')[1]
with open('video.webm', 'wb') as f:
f.write(base64.b64decode(data))
return jsonify({'message': 'Video saved successfully'})
@app.route("/send", methods=["POST"])
@ -83,6 +96,8 @@ def sendpage():
return redirect("/form")
except:
return "There was a problem while adding the response to the Database"
@app.route("/form", methods=["GET", "POST"]) # /<username> should not even be needed right?
def formpage():

View File

@ -1,3 +1,54 @@
https://jamesalvarez.co.uk/blog/how-to-make-responsive-likert-scales-in-css-(like-qualtrics)/
Licenses:
Open Iconic Icon Set (https://github.com/iconic/open-iconic)
The MIT License (MIT)
Copyright (c) 2014 Waybury
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Bootstrap Icons https://github.com/twbs/icons
The MIT License (MIT)
Copyright (c) 2019-2024 The Bootstrap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -8,6 +8,22 @@ body {
font-size: 16px;
}
.videocontrols {
width: 100px; /* Set a specific width for the buttons */
height: 70px; /* Set a specific height for the buttons */
background-color: #cae4ff;
border: none;
border: none;
color: white;
margin: 0 10px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
display: inline-flex; /* Display button contents as a flexbox */
justify-content: center; /* Center contents horizontally */
align-items: center; /* Center contents vertically */
}
.columncontainer {
display: flex;
}

View File

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="de">
<head>
<meta charset="utf-8" />

View File

@ -0,0 +1,139 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css')}}" />
<!-- styles.css {{ url_for('static', filename='styles.css')}}-->
<title>Testform</title>
<style>
/* Add any styling here */
.videocontrols {
border: none;
color: white;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.videocontrols img {
max-width: 60%;
max-height: 60%;
width: auto;
/* Make the image fill its container */
height: auto;
/* Make the image fill its container */
display: block;
/* Remove any extra space around the image */
}
</style>
</head>
<body>
<h2>Gib Feedback als Video</h2>
<div class="centertext">
<button type="button" class="videocontrols" id="buttonCamera">
<img id="buttonCameraIcon" src="{{ url_for('static', filename='icons/camera-icon.png')}}" alt="Camera Icon"
onclick="cameraButton()">
</button>
</div>
<div class="centertext">
<button type="button" class="videocontrols" id="buttonRecord" style="display:none">
<img id="buttonRecordIcon" src="{{ url_for('static', filename='icons/record-icon.png')}}" alt="Camera Icon"
onclick="recordButton()">
</button>
<button type="button" class="videocontrols" id="buttonAccept" style="display:none">
<img id="buttonAcceptIcon" src="{{ url_for('static', filename='icons/check-icon.png')}}" alt="Accept Icon"
onclick="acceptButton()">
</button>
<button type="button" class="videocontrols" id="buttonCancel" style="display:none">
<img id="buttonCancelIcon" src="{{ url_for('static', filename='icons/x-icon.png')}}" alt="Cancel Icon"
onclick="cancelButton()">
</button>
<button type="button" class="videocontrols" id="buttonDelete" style="display:none">
<img id="buttonDeleteIcon" src="{{ url_for('static', filename='icons/trash-icon.png')}}" alt="Delete Icon"
onclick="deleteButton()">
</button>
<div>
<video autoplay muted playsinline width="1280" height="720" id="videoDisplay"></video>
</div>
</div>
<script>
const buttonCamera = document.getElementById('buttonCamera');
const buttonRecord = document.getElementById('buttonRecord');
const buttonAccept = document.getElementById('buttonAccept');
const buttonCancel = document.getElementById('buttonCancel');
const buttonDelete = document.getElementById('buttonRecord');
const videoDisplay = document.getElementById('videoDisplay');
const buttonCameraIcon = document.getElementById('buttonCameraIcon');
const buttonRecordIcon = document.getElementById('buttonRecordIcon');
var mediaRecorder = null;
var stream = null;
let recordedVideoBlob = null;
let isRecording = false;
let videoAccess = false;
async function cameraButton() {
if (!videoAccess) { //TODO what happens if you dont get the device
console.log("videoAccess = false");
stream = await navigator.mediaDevices.getUserMedia({
video: true,
});
console.log("stream is active");
videoAccess = true
videoDisplay.srcObject = stream
buttonCameraIcon.src = "{{ url_for('static', filename='icons/camera-off-icon.png') }}"; //todo, not sure if this works
buttonCameraIcon.alt = "Camera-off Icon";
buttonRecord.style.display = 'inline-block';
} else {
console.log("videoAccess = true");
stream = null;
videoAccess = false
buttonCameraIcon.src = "{{ url_for('static', filename='icons/camera-icon.png') }}"; //todo, not sure if this works
buttonCameraIcon.alt = "Camera Icon";
buttonRecord.style.display = 'none';
}
console.log("camera button function ends");
}
function recordButton() {
if (!isRecording) {
mediaRecorder = new MediaRecorder(stream, {
mimeType: "video/webm", //could use other video format: https://www.iana.org/assignments/media-types/media-types.xhtml#video
// I probably want to change the bitrate: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder
});
isRecording = true;
} else {
}
}
if (videoAccess)
buttonRecording.addEventListener("click", function () {
if (isRecording) {
recordingIcon.src = 'record-icon.png';
recordingIcon.alt = 'record icon';
console.log('Action started');
} else {
recordingIcon.src = 'stop-icon.png';
recordingIcon.alt = 'Stop Icon';
console.log('Action stopped');
}
isRecording = !isRecording;
})
</script>
</body>
</html>

View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webcam Recorder</title>
</head>
<body>
<video id="video" width="640" height="480" autoplay></video>
<button id="startRecord">Start Recording</button>
<button id="stopRecord">Stop Recording</button>
<button id="save">Save</button>
<canvas id="canvas" style="display: none;"></canvas>
<script>
let video = document.getElementById('video');
let startRecordBtn = document.getElementById('startRecord');
let stopRecordBtn = document.getElementById('stopRecord');
let saveBtn = document.getElementById('save');
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let stream;
navigator.mediaDevices.getUserMedia({ video: true })
.then(function (mediaStream) {
video.srcObject = mediaStream;
stream = mediaStream;
})
.catch(function (err) {
console.error('Could not access the webcam: ', err);
});
startRecordBtn.onclick = function () {
console.log('Recording testprint')
stream.getVideoTracks()[0].requestRecord({ mimeType: 'video/webm' })
.then(() => console.log('Recording started'))
.catch(err => console.error('Failed to start recording: ', err));
};
stopRecordBtn.onclick = function () {
stream.getVideoTracks()[0].stopRecord()
.then(blob => {
console.log('Recording stopped');
let videoURL = URL.createObjectURL(blob);
video.src = videoURL;
})
.catch(err => console.error('Failed to stop recording: ', err));
};
saveBtn.onclick = function () {
let data = video.toDataURL('image/png');
fetch('/send_video', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ image: data })
})
.then(response => response.json())
.then(data => console.log('Video saved successfully:', data))
.catch(error => console.error('Error saving video:', error));
};
</script>
</body>
</html>

View File

@ -0,0 +1,84 @@
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webcam Recorder</title>
</head>
<body>
<video id="video" width="640" height="480" autoplay></video>
<button id="startRecord">Start Recording</button>
<button id="stopRecord">Stop Recording</button>
<button id="save">Save</button>
<canvas id="canvas" style="display: none;"></canvas>
<div id="messageArea"></div>
<script>
let video = document.getElementById('video');
let startRecordBtn = document.getElementById('startRecord');
let stopRecordBtn = document.getElementById('stopRecord');
let saveBtn = document.getElementById('save');
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let stream;
let messageArea = document.getElementById('messageArea');
showMessage('Start', 'success')
navigator.mediaDevices.getUserMedia({ video: true })
.then(function (mediaStream) {
video.srcObject = mediaStream;
stream = mediaStream;
})
.catch(function (err) {
showMessage('Could not access the webcam: ' + err, 'error');
});
startRecordBtn.onclick = function () {
showMessage('Recording started', 'success')
stream.getVideoTracks()[0].requestRecord({ mimeType: 'video/webm' })
.then(() => showMessage('Recording started', 'success'))
.catch(err => showMessage('Failed to start recording: ' + err, 'error'));
};
stopRecordBtn.onclick = function () {
stream.getVideoTracks()[0].stopRecord()
.then(blob => {
showMessage('Recording stopped', 'success');
let videoURL = URL.createObjectURL(blob);
video.src = videoURL;
})
.catch(err => showMessage('Failed to stop recording: ' + err, 'error'));
};
saveBtn.onclick = function () {
let data = canvas.toDataURL('image/png');
fetch('/send_video', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ image: data })
})
.then(response => response.json())
.then(data => showMessage('Video saved successfully', 'success'))
.catch(error => showMessage('Error saving video: ' + error, 'error'));
};
function showMessage(message, type) {
let messageDiv = document.createElement('div');
messageDiv.textContent = message;
messageDiv.classList.add(type);
messageArea.appendChild(messageDiv);
}
</script>
<style>
.success {
color: green;
}
.error {
color: red;
}
</style>
</body>
</html>

View File

@ -0,0 +1,177 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>
Videorecorder test
</title>
</head>
<body>
<h1>Videorecordertest</h1>
<div>
<button type="button" id="buttonStart">Start</button>
<button type="button" id="buttonStop" disabled>Stop</button>
<button type="button" id="buttonUploadBlob" disabled>
Upload (Blob)
</button>
<button type="button" id="buttonUploadDataURL" disabled>
Upload (DataURL)
</button>
<div id="messageArea"></div>
</div>
<div>
<video autoplay muted playsinline width="1280" height="720" id="videoLive"></video>
</div>
<div>
<video controls playsinline width="1280" height="720" id="videoRecorded"></video>
</div>
<script>
function showMessage(message, type) {
let messageDiv = document.createElement('div');
messageDiv.textContent = message;
messageDiv.classList.add(type);
messageArea.appendChild(messageDiv);
}
async function main() {
const buttonStart = document.querySelector("#buttonStart");
const buttonStop = document.querySelector("#buttonStop");
const buttonUploadBlob = document.querySelector("#buttonUploadBlob");
const buttonUploadDataURL = document.querySelector(
"#buttonUploadDataURL"
);
const videoLive = document.querySelector("#videoLive");
const videoRecorded = document.querySelector("#videoRecorded");
let recordedVideoBlob = null;
let messageArea = document.getElementById('messageArea');
const search = new URLSearchParams({
extname: ".webm",
}).toString();
// this string is literally jsut "extname=.webm", WHY?
showMessage('const search:', 'success')
showMessage(search, 'success')
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
});
videoLive.srcObject = stream;
const mediaRecorder = new MediaRecorder(stream, {
mimeType: "video/webm", //could use other video format: https://www.iana.org/assignments/media-types/media-types.xhtml#video
// I probably want to influence the bitrate: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder
});
buttonStart.addEventListener("click", () => {
mediaRecorder.start();
buttonStart.setAttribute("disabled", "");
buttonStop.removeAttribute("disabled");
});
buttonStop.addEventListener("click", () => {
mediaRecorder.stop();
buttonStart.removeAttribute("disabled");
buttonStop.setAttribute("disabled", "");
buttonUploadBlob.removeAttribute("disabled");
buttonUploadDataURL.removeAttribute("disabled");
});
mediaRecorder.addEventListener("dataavailable", (event) => {
videoRecorded.src = URL.createObjectURL(event.data);
recordedVideoBlob = event.data;
});
buttonUploadBlob.addEventListener("click", async () => {
try {
const search = new URLSearchParams({
extname: ".webm",
}).toString();
// this string is literally jsut "extname=.webm", WHY?
const url = "/api/upload/blob?" + search;
console.log(recordedVideoBlob);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/octet-stream",
},
body: recordedVideoBlob,
});
if (response.status !== 201) {
console.warn(response.status);
console.warn(await response.text());
}
} catch (err) {
console.error(err);
}
});
buttonUploadDataURL.addEventListener("click", async () => {
try {
const url = "/send_video";
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
body: JSON.stringify({
extname: ".webm",
dataUrl: await convertBlob(recordedVideoBlob),
}),
});
if (response.status !== 201) {
console.warn(response.status);
console.warn(await response.text());
}
} catch (err) {
console.error(err);
}
});
}
async function convertBlob(blob) {
return await new Promise((resolve, reject) => {
const fileReader = new FileReader();
const subscribe = () => {
fileReader.addEventListener("abort", onAbort);
fileReader.addEventListener("error", onError);
fileReader.addEventListener("load", onLoad);
};
const unsubscribe = () => {
fileReader.removeEventListener("abort", onAbort);
fileReader.removeEventListener("error", onError);
fileReader.removeEventListener("load", onLoad);
};
const onAbort = () => {
unsubscribe();
reject(new Error("abort"));
};
const onError = (event) => {
unsubscribe();
reject(event.target.error);
};
const onLoad = (event) => {
unsubscribe();
resolve(event.target.result);
};
subscribe();
fileReader.readAsDataURL(blob);
});
}
main();
</script>
</body>
</html>

BIN
slaeforms/video.webm Normal file

Binary file not shown.