From 1d40830066a1bb5d98a67e6f85c9121b24b9c32b Mon Sep 17 00:00:00 2001 From: nono Date: Wed, 4 May 2022 21:19:22 +0200 Subject: [PATCH 01/11] Added webcam, using partial templates --- src/templates/banner.html | 21 +++++ src/templates/base.html | 57 ++++++++++++ src/templates/footer.html | 5 + src/templates/header.html | 10 ++ src/templates/index.html | 47 +--------- src/templates/webcam.html | 186 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 282 insertions(+), 44 deletions(-) create mode 100644 src/templates/banner.html create mode 100644 src/templates/base.html create mode 100644 src/templates/webcam.html diff --git a/src/templates/banner.html b/src/templates/banner.html new file mode 100644 index 0000000..17b82a5 --- /dev/null +++ b/src/templates/banner.html @@ -0,0 +1,21 @@ + +
+ LittlePrynter icon

Little Prynter

+
+ +
+ {% with messages = get_flashed_messages(category_filter=('error')) %} + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + + {% with messages = get_flashed_messages(category_filter=('info')) %} + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} diff --git a/src/templates/base.html b/src/templates/base.html new file mode 100644 index 0000000..a6cf7a8 --- /dev/null +++ b/src/templates/base.html @@ -0,0 +1,57 @@ + + + + + LittlePrynter + + + + + + +
+ LittlePrynter icon

Little Prynter

+
+ + +
+ + +
+ {% with messages = get_flashed_messages(category_filter=('error')) %} + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + + {% with messages = get_flashed_messages(category_filter=('info')) %} + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + + + {% block content %} + + + {% endblock %} +
+ +
+ + diff --git a/src/templates/footer.html b/src/templates/footer.html index e69de29..6b3e682 100644 --- a/src/templates/footer.html +++ b/src/templates/footer.html @@ -0,0 +1,5 @@ +
+ +
+ + diff --git a/src/templates/header.html b/src/templates/header.html index e69de29..f669595 100644 --- a/src/templates/header.html +++ b/src/templates/header.html @@ -0,0 +1,10 @@ + + + + + LittlePrynter + + + + + diff --git a/src/templates/index.html b/src/templates/index.html index e5aead0..2f171f9 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -1,40 +1,7 @@ - - - - - LittlePrynter - - - - - - -
- LittlePrynter icon

Little Prynter

-
- -
- {% with messages = get_flashed_messages(category_filter=('error')) %} - {% if messages %} - {% for message in messages %} - - {% endfor %} - {% endif %} - {% endwith %} +{% extends 'base.html' %} - {% with messages = get_flashed_messages(category_filter=('info')) %} - {% if messages %} - {% for message in messages %} - - {% endfor %} - {% endif %} - {% endwith %} +{% block content %} -

Print a short message

@@ -60,12 +27,4 @@
- -
-
- -
- - + {% endblock %} diff --git a/src/templates/webcam.html b/src/templates/webcam.html new file mode 100644 index 0000000..4aed202 --- /dev/null +++ b/src/templates/webcam.html @@ -0,0 +1,186 @@ +{% extends 'base.html' %} + +{% block content %} + +
+

Photomaton

+
+ + + +
+ + + +
+
+ +
+ + + + + + +
+ + + + + +
+ +{% endblock %} From 0e6fd73ee8b6eda6a4db1e452ca4098c5db40d9b Mon Sep 17 00:00:00 2001 From: nono Date: Wed, 4 May 2022 21:22:01 +0200 Subject: [PATCH 02/11] Added webcam, changed rate limits, added template reload --- src/main.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index 9599dd9..fbffff9 100644 --- a/src/main.py +++ b/src/main.py @@ -51,6 +51,7 @@ app.secret_key = configuration_file["secrets"]["flask_secret_key"] app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['ALLOWED_EXTENSIONS'] = ALLOWED_EXTENSIONS app.config['MAX_CONTENT_LENGTH'] = 3 * 1000 * 1000 # Maximum 3Mb for a file upload +app.config['TEMPLATES_AUTO_RELOAD'] = True # Printer connection # Uses the class defined in the printer.py file @@ -64,7 +65,7 @@ web = Web(app, printer) limiter = Limiter( app, key_func=get_remote_address, - default_limits=["200 per day", "50 per hour"] + default_limits=["1500 per day", "500 per hour"] ) @app.route('/') @@ -73,6 +74,12 @@ def index(): app.logger.debug("Loading index") return render_template('index.html') +@app.route('/webcam') +@limiter.limit("1/second", override_defaults=False) +def webcam(): + app.logger.debug("Loading webcam interface") + return render_template('webcam.html') + # API routes # The api has the following methods # api/print/{sms,img,letter,qr,barcode} From 27e8518be3c31b004ade8ef868d7c8d229dfb8ab Mon Sep 17 00:00:00 2001 From: nono Date: Wed, 4 May 2022 21:22:22 +0200 Subject: [PATCH 03/11] Update CSS --- src/static/css/style.css | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/static/css/style.css b/src/static/css/style.css index 3ba4234..05ea9b8 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -1,11 +1,7 @@ -body { +/* body { margin-left: 20%; margin-right: 20%; -} - -button { - margin-top: 3em; -} +} */ /*// Small devices (landscape phones, 576px and up)*/ @media (min-width: 576px) { @@ -14,8 +10,8 @@ button { /*// Medium devices (tablets, 768px and up)*/ @media (min-width: 768px) { - margin-left: 0%; - margin-right: 0%; + margin-left: 0; + margin-right: 0; } /*// Large devices (desktops, 992px and up)*/ From 214d3502c0392bf7842648ce06edb19f788c2107 Mon Sep 17 00:00:00 2001 From: n07070 Date: Mon, 9 May 2022 00:51:52 +0200 Subject: [PATCH 04/11] Update requirements.txt --- requirements.txt | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/requirements.txt b/requirements.txt index 631e4d2..e207789 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,31 @@ -Adafruit-Thermal>=1.1.0 -Click>=7.0 -Flask>=1.0.2 -Flask-Limiter>=1.0.1 -itsdangerous>=0.24 -Jinja2>=2.10 -limits>=1.3 -MarkupSafe>=1.0 -Pillow>=5.3.0 -pyserial>=3.4 -six>=1.11.0 -Unidecode>=1.0.22 -Werkzeug>=0.14.1 +Adafruit-Thermal==1.1.0 +appdirs==1.4.4 +argcomplete==2.0.0 +click==8.1.3 +commonmark==0.9.1 +Deprecated==1.2.13 +escpos==1.9 +Flask==2.1.2 +Flask-Limiter==2.4.5.1 +future==0.18.2 +itsdangerous==2.1.2 +Jinja2==3.1.2 +limits==2.6.1 +MarkupSafe==2.1.1 +packaging==21.3 +Pillow==9.1.0 +Pygments==2.12.0 +pyparsing==3.0.8 +pyserial==3.5 +python-barcode==0.13.1 +pyusb==1.2.1 +PyYAML==6.0 +qrcode==7.3.1 +rich==12.4.1 +six==1.16.0 +toml==0.10.2 +typing_extensions==4.2.0 +Unidecode==1.3.4 +viivakoodi==0.8.0 +Werkzeug==2.1.2 +wrapt==1.14.1 From 07e444c3b46dd38320618218abcd6e094c79eaba Mon Sep 17 00:00:00 2001 From: n07070 Date: Mon, 9 May 2022 01:49:35 +0200 Subject: [PATCH 05/11] Mise en route plus simple --- run.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/run.sh b/run.sh index bc38cda..8b5a274 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,7 @@ +virtualenv . + +pip install -r requirements.txt + export FLASK_APP=src/main.py export FLASK_ENV=development flask run --host 192.168.0.42 --debugger --eager-loading From cd663087c867ed9c2af79ad1a8c7accba0c1e14d Mon Sep 17 00:00:00 2001 From: n07070 Date: Mon, 9 May 2022 01:49:57 +0200 Subject: [PATCH 06/11] Fixed printing images from AJAX calls --- src/main.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main.py b/src/main.py index fbffff9..57ee70c 100644 --- a/src/main.py +++ b/src/main.py @@ -122,17 +122,24 @@ def api_print_image(): sign = request.form["signature"] except Exception as e: flash(str(e),'error') - redirect(url_for('index')) + app.logger.error(str(e) + " - Whoops, no forms submitted or missing signature.") + return redirect(url_for('index')) if request.method == 'POST': # check if the post request has the file part - - if 'img' not in request.files: - flash('No file found. Did you use the good form ?', 'error') - return redirect(url_for("index")) - else: - file = request.files['img'] - + try: + if 'img' not in request.files: + flash('No file found. Did you use the good form ?', 'error') + app.logger.error("No file found. Did you use the good form ?") + return redirect(url_for("index")) + else: + file = request.files['img'] + except Exception as e: + if sign is not None and photo is not None: + pass + else: + flash(str(e), 'error') + app.logger.error("Couldn't get an image nor signature : " + str(e)) # If the user does not select a file, the browser submits an # empty file without a filename. From 5d99e78dea9ec9389072e90509f68e5ef0c652cf Mon Sep 17 00:00:00 2001 From: n07070 Date: Mon, 9 May 2022 01:50:18 +0200 Subject: [PATCH 07/11] Webcam buttons added, preview added --- src/templates/base.html | 6 +- src/templates/index.html | 46 ++++++------ src/templates/webcam.html | 154 +++++++++----------------------------- 3 files changed, 62 insertions(+), 144 deletions(-) diff --git a/src/templates/base.html b/src/templates/base.html index a6cf7a8..237fa2a 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -51,7 +51,9 @@ {% endblock %}
- - + + diff --git a/src/templates/index.html b/src/templates/index.html index 2f171f9..1fbd86b 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -2,29 +2,27 @@ {% block content %} -
-

Print a short message

-
+
+

Print a short message

+
+
+
+
+ +
+
+
-
-
-
+
- -
-
-
- -
- -
-

Print an image

-
-
-
-
- -
-
- - {% endblock %} +
+

Print an image

+
+
+
+
+ +
+
+
+ {% endblock %} diff --git a/src/templates/webcam.html b/src/templates/webcam.html index 4aed202..097133d 100644 --- a/src/templates/webcam.html +++ b/src/templates/webcam.html @@ -2,13 +2,40 @@ {% block content %} + +

Photomaton

-
+
+ + +
@@ -17,21 +44,11 @@
- - - - + + + + +
@@ -40,9 +57,9 @@ display: none; } - video { + /* video { display: none; - } + } */ .image_dither { /* width: 300px; @@ -80,106 +97,7 @@ - +
From 5c35a8586c0629d228f3459251d0411be45d17c6 Mon Sep 17 00:00:00 2001 From: n07070 Date: Mon, 9 May 2022 01:50:39 +0200 Subject: [PATCH 08/11] Move JS to another file, added printing from webcam --- src/static/js/webcam.js | 160 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/static/js/webcam.js diff --git a/src/static/js/webcam.js b/src/static/js/webcam.js new file mode 100644 index 0000000..9fe9a9a --- /dev/null +++ b/src/static/js/webcam.js @@ -0,0 +1,160 @@ +let streaming; +var width = document.getElementById("video").parentNode.parentElement.clientWidth; +var height = width / (4 / 3); + +function startup(){ + video = document.getElementById('video'); + canvas = document.getElementById('canvas'); + photo = document.getElementById('photo'); + switch_cameras = document.getElementById('flip') + printButton = document.getElementById('print_button'); + + if ( check_webcam() ){ + setup_events(); + clear_canvas(); + } else { + console.log("Seems like it's impossible to get a webcam."); + } +} + +function check_webcam(){ + try { + // access video stream from webcam + navigator.mediaDevices.getUserMedia({ + video: true, + audio: false + }) + // on success, stream it in video tag + // the video tag is hidden, as is the canvas. + .then(function(stream) { + switch_cameras.removeAttribute("disabled",""); + + video.srcObject = stream; + video.play(); + }) + .catch(function(err) { + console.log("An error occurred: " + err); + + }); + } catch (err) { + console.log("An error occurred: " + err); + 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); + console.log("Should be a new div somewhere"); + throw new Error("Unable to get a video device, stopping the photobooth."); + } finally { + + + } + return true; +} + +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",""); + + } + }, false); + + switch_cameras.addEventListener('click', function(ev) { + alert("Just turn your phone."); + }, false ); + + printButton.addEventListener('click', function(ev){ + data = take_picture(); + print_picture(data) + ev.preventDefault(); + }, 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) { + 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}); +} + +function take_picture(){ + var context = canvas.getContext('2d'); + if (width && height) { + canvas.width = width; + canvas.height = height; + context.drawImage(video, 0, 0, width, height); + + var 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", "Printed via the webcam @ " + time) + + fetch(url, { + method: 'POST', // or 'PUT' + body: formData, + // headers:{ + // 'Content-Type': 'multipart/form-data' + // } + }).then(response => console.log('Success:', response), true) + .catch(error => console.error('Error:', error), false); + +} + +window.addEventListener('load', startup, false); From 10080e36f11eb799a8ee6eaf70eec6024b080498 Mon Sep 17 00:00:00 2001 From: n07070 Date: Mon, 9 May 2022 21:41:19 +0200 Subject: [PATCH 09/11] Updated requirements file to be compatible with futur versions. --- requirements.txt | 62 ++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/requirements.txt b/requirements.txt index e207789..becc2ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,31 +1,31 @@ -Adafruit-Thermal==1.1.0 -appdirs==1.4.4 -argcomplete==2.0.0 -click==8.1.3 -commonmark==0.9.1 -Deprecated==1.2.13 -escpos==1.9 -Flask==2.1.2 -Flask-Limiter==2.4.5.1 -future==0.18.2 -itsdangerous==2.1.2 -Jinja2==3.1.2 -limits==2.6.1 -MarkupSafe==2.1.1 -packaging==21.3 -Pillow==9.1.0 -Pygments==2.12.0 -pyparsing==3.0.8 -pyserial==3.5 -python-barcode==0.13.1 -pyusb==1.2.1 -PyYAML==6.0 -qrcode==7.3.1 -rich==12.4.1 -six==1.16.0 -toml==0.10.2 -typing_extensions==4.2.0 -Unidecode==1.3.4 -viivakoodi==0.8.0 -Werkzeug==2.1.2 -wrapt==1.14.1 +Adafruit-Thermal~=1.1.0 +appdirs~=1.4.4 +argcomplete~=2.0.0 +click~=8.1.3 +commonmark~=0.9.1 +Deprecated~=1.2.13 +escpos~=1.9 +Flask~=2.1.2 +Flask-Limiter~=2.4.5.1 +future~=0.18.2 +itsdangerous~=2.1.2 +Jinja2~=3.1.2 +limits~=2.6.1 +MarkupSafe~=2.1.1 +packaging~=21.3 +Pillow~=9.1.0 +Pygments~=2.12.0 +pyparsing~=3.0.8 +pyserial~=3.5 +python-barcode~=0.13.1 +pyusb~=1.2.1 +PyYAML~=6.0 +qrcode~=7.3.1 +rich~=12.4.1 +six~=1.16.0 +toml~=0.10.2 +typing_extensions~=4.2.0 +Unidecode~=1.3.4 +viivakoodi~=0.8.0 +Werkzeug~=2.1.2 +wrapt~=1.14.1 From 3aa0bc7f1b517a0062a8d9e9034340a3af03639b Mon Sep 17 00:00:00 2001 From: n07070 Date: Mon, 9 May 2022 23:59:36 +0200 Subject: [PATCH 10/11] Fixed buttons placement --- src/templates/webcam.html | 84 +++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/templates/webcam.html b/src/templates/webcam.html index 097133d..dda04ee 100644 --- a/src/templates/webcam.html +++ b/src/templates/webcam.html @@ -2,56 +2,56 @@ {% block content %} -