Compare commits

..

5 Commits

Author SHA1 Message Date
7ca6878d0d Added printing images 2022-03-17 10:39:22 +01:00
980d12613c Removed old images 2022-03-17 10:39:05 +01:00
f43a66275b Changed favicon 2022-03-17 10:38:52 +01:00
3393290df2 Updated config for the printing of images 2022-03-17 10:38:30 +01:00
cd12dbc792 Added gitignore entry 2022-03-17 10:38:05 +01:00
19 changed files with 159 additions and 36 deletions

1
.gitignore vendored
View File

@ -62,6 +62,7 @@ db.sqlite3
# Flask stuff: # Flask stuff:
instance/ instance/
.webassets-cache .webassets-cache
src/static/uploads
# Scrapy stuff: # Scrapy stuff:
.scrapy .scrapy

View File

@ -4,6 +4,7 @@
[printer] [printer]
vendor_id = 0x04b8 vendor_id = 0x04b8
device_id = 0x0e28 device_id = 0x0e28
upload_folder = "src/static/uploads"
# Users = Password # Users = Password
[users] [users]

View File

@ -18,30 +18,39 @@ import time # To sleep
import os # For VARS from the shell. import os # For VARS from the shell.
# Variables # Variables
app = Flask(__name__)
# Load the configuration file # Load the configuration file
try: try:
app.logger.debug("Loading config file...")
configuration_file = toml.load("configuration/config.toml") configuration_file = toml.load("configuration/config.toml")
except TypeError : except TypeError :
print("Unable to load the config file: invalid type or is a list containing invalid types") app.logger.error("Unable to load the config file: invalid type or is a list containing invalid types")
exit(-1) exit(-1)
except toml.TomlDecodeError: except toml.TomlDecodeError:
print("An error occured while decoding the file") app.logger.error("An error occured while decoding the file")
exit(-1) exit(-1)
except Exception as e: except Exception as e:
print("Error while loading file : " + str(e)) app.logger.error("Error while loading file : " + str(e))
exit(-1)
app.logger.debug("Config file loaded !")
# Define the USB connections here. # Define the USB connections here.
vendor_id = configuration_file["printer"]["vendor_id"] vendor_id = configuration_file["printer"]["vendor_id"]
device_id = configuration_file["printer"]["device_id"] device_id = configuration_file["printer"]["device_id"]
UPLOAD_FOLDER = str(configuration_file["printer"]["upload_folder"])
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
# Output the config file # Output the config file
if os.getenv('LIPY_DEBUG') == True: if os.getenv('LIPY_DEBUG') == True:
pprint.pprint(configuration_file) pprint.pprint(configuration_file)
# We define the app module used by Flask # We define the app module used by Flask
app = Flask(__name__)
app.secret_key = configuration_file["secrets"]["flask_secret_key"] 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
# Printer connection # Printer connection
# Uses the class defined in the printer.py file # Uses the class defined in the printer.py file
@ -61,6 +70,7 @@ limiter = Limiter(
@app.route('/') @app.route('/')
@limiter.limit("1/second", override_defaults=False) @limiter.limit("1/second", override_defaults=False)
def index(): def index():
app.logger.debug("Loading index")
return render_template('index.html') return render_template('index.html')
# API routes # API routes
@ -74,17 +84,19 @@ def index():
@app.route('/api/print') @app.route('/api/print')
@limiter.limit("1/second", override_defaults=False) @limiter.limit("1/second", override_defaults=False)
def api_index(): def api_index():
app.logger.debug("Loading API")
return render_template("api.html") return render_template("api.html")
@app.route('/api/print/sms', methods=['POST']) @app.route('/api/print/sms', methods=['POST'])
@limiter.limit("2/minute", override_defaults=False) @limiter.limit("2/minute", override_defaults=False)
def api_print_sms(): def api_print_sms():
app.logger.debug("Printing an sms")
try: try:
txt = request.form["txt"] txt = request.form["txt"]
sign = request.form["signature"] sign = request.form["signature"]
except Exception as e: except Exception as e:
flash(e) flash(e,'error')
redirect(url_for('index')) redirect(url_for('index'))
try: try:
@ -94,30 +106,64 @@ def api_print_sms():
return redirect(url_for('index')) return redirect(url_for('index'))
@app.route('/api/print/image', methods=['POST']) @app.route('/api/print/img', methods=['POST'])
@limiter.limit("2/minute", override_defaults=False) @limiter.limit("2/minute", override_defaults=False)
def api_print_image(): def api_print_image():
flash(web.print_image()) app.logger.debug("Printing an image")
return "print image"
try:
sign = request.form["signature"]
except Exception as e:
flash(str(e),'error')
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']
# If the user does not select a file, the browser submits an
# empty file without a filename.
if file.filename == '':
app.logger.error("Submitted file has no filename !")
flash('No file submitted, please select a file','error')
return redirect(url_for("index"))
try:
app.logger.debug("Sending the image to the printer.")
web.print_image(file, sign)
except Exception as e:
pass
else:
flash('Cannot access to page with this method.','error')
app.logger.debug('Bad access type to this API.')
return redirect(url_for("index"))
@app.route('/login') @app.route('/login')
@limiter.limit("1/second", override_defaults=False) @limiter.limit("1/second", override_defaults=False)
def login_page(): def login_page():
# web.login(username,password) # web.login(username,password)
return render_template("index.html") return redirect(url_for("index"))
@app.route('/logout') @app.route('/logout')
@limiter.limit("1/second", override_defaults=False) @limiter.limit("1/second", override_defaults=False)
def logout_page(): def logout_page():
# web.logout(username, password) # web.logout(username, password)
return render_template("index.html") return redirect(url_for("index"))
@app.errorhandler(429) @app.errorhandler(429)
def ratelimit_handler(e): def ratelimit_handler(e):
flash("Rate limit reached, please slow down :) ( Currently at "+ e.description + ")", 'error') flash("Rate limit reached, please slow down :) ( Currently at "+ e.description + ")", 'error')
return render_template('index.html') return redirect(url_for("index"))
@app.route("/ping") @app.route("/ping")
@limiter.exempt @limiter.exempt
def ping(): def ping():
return "PONG" flash("🏓 Pong !",'info')
return redirect(url_for("index"))

View File

@ -3,6 +3,7 @@ from flask import flash
from escpos.printer import Usb, USBNotFoundError from escpos.printer import Usb, USBNotFoundError
from time import sleep, gmtime, strftime from time import sleep, gmtime, strftime
import os.path import os.path
from PIL import Image
class Printer(object): class Printer(object):
@ -134,19 +135,44 @@ class Printer(object):
flash("Message printed : " + clean_msg ,category='info') flash("Message printed : " + clean_msg ,category='info')
return True return True
def print_img(self, path): def print_img(self, path, sign):
clean_signature = str(sign)
if os.path.isfile(str(path)): if not os.path.isfile(str(path)):
pass
else:
self.app.logger.warning("File does not exist : " + str(path)) self.app.logger.warning("File does not exist : " + str(path))
flash('The file path for this image :' + str(path) + " wasn't found. Please try again.", 'error')
return False
else:
self.app.logger.debug("Printing file from " + str(path))
try:
self.app.logger.debug("Resizing the image")
with Image.open(path) as im:
basewidth = 575
img = Image.open(path)
wpercent = (basewidth/float(img.size[0]))
hsize = int((float(img.size[1])*float(wpercent)))
img = img.resize((basewidth,hsize), Image.ANTIALIAS)
if img.height > 1000:
flash("Image is too long, sorry ! Keep it below 500×1000 pixels.",'error')
return False
img.save(path)
except Exception as e:
flash(str(e))
self.app.logger.error(str(e))
try: try:
self.printer.open(self.usb_args) self.printer.open(self.usb_args)
self.printer.textln("> " + clean_signature + " @ " + strftime("%Y-%m-%d %H:%M:%S", gmtime()))
self.printer.image(path) self.printer.image(path)
self.printer.cut()
self.printer.close() self.printer.close()
self.app.logger.debug("Printed an image : " + str(path))
return True
except Exception as e: except Exception as e:
self.printer.close() self.printer.close()
return e flash(str(e),'error')
else: return False
self.app.logging.debug("Printed an image : " + str(path))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

View File

@ -9,15 +9,15 @@
<link rel="stylesheet" href="{{ url_for('static',filename='css/style.css') }}"> <link rel="stylesheet" href="{{ url_for('static',filename='css/style.css') }}">
</head> </head>
<body class="container"> <body class="container">
<div class="col-md-6 offset-md-3"> <div class="col-md-7 offset-md-2">
<img class="rounded" width="100px" src="{{ url_for('static', filename='images/little-printer.png') }}" alt="LittlePrynter icon"><h1 class="card-title font-weight-bold">Little Prynter</h1> <a href="/"><img class="rounded" width="100px" src="{{ url_for('static', filename='images/little-printer.png') }}" alt="LittlePrynter icon"></a><h1 class="card-title font-weight-bold">Little Prynter</h1>
<hr> <hr>
<div class="alert alert-dark" role="alert">LittlePrynter is under heavy developpement, you may encounter bugs ! If so, try again. Thanks !</div> <div class="alert alert-dark" role="alert">LittlePrynter is under heavy developpement, you may encounter bugs ! If so, try again. Thanks !</div>
<br> <br>
{% with messages = get_flashed_messages(category_filter=('error')) %} {% with messages = get_flashed_messages(category_filter=('error')) %}
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<div class="alert alert-danger" role="alert">{{ message }}</div> <div class="alert alert-danger" role="alert">⚠️ {{ message }}</div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endwith %} {% endwith %}
@ -25,7 +25,7 @@
{% with messages = get_flashed_messages(category_filter=('info')) %} {% with messages = get_flashed_messages(category_filter=('info')) %}
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<div class="alert alert-info" role="alert">{{ message }}</div> <div class="alert alert-info" role="alert"> {{ message }}</div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endwith %} {% endwith %}
@ -40,20 +40,30 @@
<div class="card-body"> <div class="card-body">
<form class="form-group" action="/api/print/sms" method="post"> <form class="form-group" action="/api/print/sms" method="post">
<input class="form-control" type="text" name="txt" placeholder="200 chars or less " maxlength="200"><br> <input class="form-control" type="text" name="txt" placeholder="200 chars or less " maxlength="200" required><br>
<input class="form-control" type="text" name="signature" placeholder="Signature or pseudo" maxlength="200"><br> <input class="form-control" type="text" name="signature" placeholder="Signature or pseudo" maxlength="200" required><br>
<input class="btn btn-primary float-right" type="submit" value="Imprimer" name="imprimer"> <input class="btn btn-primary float-right" type="submit" value="Imprimer" name="imprimer">
</form> </form>
</div> </div>
<div id="pbar" style="display:none">
<img src="{{ url_for('static',filename='load.gif') }}" alt="loading wheel">
</div>
</div> </div>
<br> <br>
<br>
<div class="card">
<h3 class="card-header">Print an image</h3>
<div class="card-body">
<form enctype="multipart/form-data" class="form-group" action="/api/print/img" method="post">
<input class="form-control" type="file" name="img" required><br>
<input class="form-control" type="text" name="signature" placeholder="Signature or pseudo" maxlength="200" required><br>
<input class="btn btn-primary float-right" type="submit" value="Imprimer" name="imprimer">
</form>
</div>
<!-- <div id="pbar" style="display:none">
<img src="{{ url_for('static',filename='load.gif') }}" alt="loading wheel">
</div> -->
</div>
<hr> <hr>
<footer>Little Prynter is built by <a href="https://n07070.xyz/about-me/">n07070</a> for fun :) </footer> <footer>Little Prynter is built by <a href="https://n07070.xyz/about-me/">n07070</a> for fun :) </footer>
</div> </div>

View File

@ -1,4 +1,5 @@
from flask import Flask, request from flask import Flask, request
from werkzeug.utils import secure_filename
from printer import Printer from printer import Printer
import time import time
import os import os
@ -19,11 +20,49 @@ class Web(object):
return self.printer.print_sms(texte, sign) return self.printer.print_sms(texte, sign)
def print_image(self, image): def print_image(self, image, sign: str) -> bool:
pass self.app.logger.debug("Uploading file")
try:
self.app.logger.debug("Uploading file from " + str(sign))
if self.upload_file(image):
self.app.logger.debug("File has been uploaded, printing...")
self.printer.print_img(os.path.join(self.app.config['UPLOAD_FOLDER'], secure_filename(image.filename)), sign)
return True
else:
return False
except Exception as e:
self.app.logger.error(e)
raise Exception
else:
flash("Could not upload file.",'error')
return False
def login(username: str,password: str) -> bool: def login(username: str,password: str) -> bool:
pass pass
def logout(username: str, password: str) -> bool: def logout(username: str, password: str) -> bool:
pass pass
def allowed_file(self, filename) -> bool:
self.app.logger.debug("Is the filename allowed ?")
return '.' in filename and filename.rsplit('.', 1)[1].lower() in self.app.config['ALLOWED_EXTENSIONS']
def upload_file(self, image)-> bool:
self.app.logger.debug("Validating file")
if image and self.allowed_file(image.filename):
filename = secure_filename(image.filename)
self.app.logger.debug("File valid")
try:
image.save(os.path.join(self.app.config['UPLOAD_FOLDER'], filename))
self.app.logger.debug("File saved")
except Exception as e:
self.app.logger.error("Could not save file")
flash(e,'error')
return False
self.app.logger.debug("File saved to " + str(os.path.join(self.app.config['UPLOAD_FOLDER'], filename)))
return True
else:
self.app.logger.error("Could not save file " + str(filename))
return False