Added the files of the project,

Added README.md
This commit is contained in:
N07070 2018-10-14 23:12:47 +02:00
parent 4c29866a21
commit b2ec27701e
22 changed files with 326 additions and 0 deletions

60
README.md Normal file
View File

@ -0,0 +1,60 @@
# Little Prynter
---
> Print out shit from the cloud.
## About
This project started when I got a Thermal Printer from a friend. I don't really know if you can do anything more, but I guess it's fun.
## Installation
To make this project work, you will need :
- [A Thermal Printer]( https://www.adafruit.com/product/597 )
- A Raspberry Pi
- Some electric wires.
Start by following the guide [here](https://learn.adafruit.com/networked-thermal-printer-using-cups-and-raspberry-pi) to install the CUPS software needed to print images. You can also get some information from [here](https://learn.adafruit.com/mini-thermal-receipt-printer) and [here](https://learn.adafruit.com/instant-camera-using-raspberry-pi-and-thermal-printer) if you're stuck.
Then, setup the project :
```
git clone https://github.com/N07070/LittlePrynter
cd LittlePrynter
pip install -r requirements.txt
```
Now, edit `the users.json` and add a user. **Don't forget to remove the test user.**
You can now start the web server with
```
export FLASK_APP=littleprynter.py
flask run
```
Voilà !
## More
If you liked this project, feel free to support my work !
BTC : `1GYEiFSS7vbZXxDjKQavdaGsAEtvGjNWqb`
## Licence
```
LittlePrynter
Copyright (C) 2018 N07070
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
```

114
littleprynter.py Normal file
View File

@ -0,0 +1,114 @@
#!/usr/bin/python
import os, random, sys, json
from datetime import datetime
from subprocess import call
from Adafruit_Thermal import *
from flask import Flask, render_template, jsonify, make_response, request, redirect, url_for, abort, session, flash
from flask_limiter import Limiter
from pprint import pprint
from flask_limiter.util import get_remote_address
app = Flask(__name__)
app.secret_key = b'\x98>3nW[D\xa4\xd4\xd0K\xab?oM.`\x98'
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
# session['logged_in'] = False
def error_handler_limiter():
flash("Trop de requêtes !!! CANNOT PRINT !!! HAAAAAAAAAA",'dark')
return redirect(url_for('display_index_page'))
@app.errorhandler(418)
def i_m_a_tea_pot(error):
return make_response('\n', 418)
@app.route('/tea')
def tea():
abort(418)
@app.route('/')
@limiter.exempt
def display_index_page():
if session.get('logged_in'):
return render_template('index.html')
else:
return redirect(url_for('login'))
@app.route('/login', methods=['POST','GET'])
@limiter.limit("100 per minute", error_message=error_handler_limiter)
def login():
if request.method == 'POST':
if not session.get('logged_in'):
if request.form['username'] and request.form['password']:
# Get the json
with open('users.json') as f:
users_file = json.load(f)
for user in users_file["users"]:
if users_file["users"][user] == request.form['password']:
session['logged_in'] = True
session['user'] = request.form['username']
if not session.get('logged_in'):
flash('Mot de passe ou pseudo invalide.','danger')
return redirect(url_for('login'))
else:
return redirect(url_for('display_index_page'))
else:
flash('Incorrect logins')
return render_template('password.html')
else:
return render_template('password.html')
else:
return render_template('password.html')
@app.route("/logout")
def logout():
session['logged_in'] = False
flash('Tu est déconnecté', 'info')
return redirect(url_for('login'))
@app.route('/print/image')
@limiter.limit("5 per minute", error_message=error_handler_limiter)
def print_image():
if session.get('logged_in'):
img = random.choice(os.listdir("static/images/")) #change dir name to whatever
call(["lp", "-o fit-to-page", "static/images/" + img])
# printer = Adafruit_Thermal('/dev/serial0', 19200, timeout=5)
# printer.begin()
# printer.feed(1)
# # printer.printImage(dickbutt, True)
# # printer.feed(2)
return redirect(url_for('display_index_page'))
else:
return redirect(url_for('login'))
@app.route('/print/text', methods=['POST'])
@limiter.limit("3: per minute", error_message=error_handler_limiter)
def print_text():
if session.get('logged_in'):
if len(request.form['message']) < 200:
printer = Adafruit_Thermal('/dev/serial0', 19200, timeout=5)
printer.begin()
printer.justify('L')
printer.println(request.form['message'])
printer.setSize('S')
printer.println(">> " + datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " " + session.get('user'))
printer.justify('C')
printer.println("------------------------------")
printer.feed(2)
printer.sleep() # Tell printer to sleep
printer.wake() # Call wake() before printing again, even if reset
printer.setDefault() # Restore printer to defaults
return redirect(url_for('display_index_page'))
else:
flash('Le text est trop long, 200 caractères au maximum stp !')
return redirect(url_for('display_index_page'))
else:
return redirect(url_for('login'))

View File

1
pip-selfcheck.json Normal file
View File

@ -0,0 +1 @@
{"last_check":"2018-10-10T18:33:22Z","pypi_version":"18.1"}

13
requirements.txt Normal file
View File

@ -0,0 +1,13 @@
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

27
static/css/style.css Normal file
View File

@ -0,0 +1,27 @@
body {
margin-left: 20%;
margin-right: 20%;
}
button {
margin-top: 3em;
}
/*// Small devices (landscape phones, 576px and up)*/
@media (min-width: 576px) {
}
/*// Medium devices (tablets, 768px and up)*/
@media (min-width: 768px) {
margin-left: 0%;
margin-right: 0%;
}
/*// Large devices (desktops, 992px and up)*/
@media (min-width: 992px) {
}
/*Extra large devices (large desktops, 1200px and up)*/
@media (min-width: 1200px) {
}

BIN
static/images/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
static/images/10.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
static/images/11.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

BIN
static/images/12.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
static/images/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
static/images/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

BIN
static/images/4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
static/images/5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
static/images/6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
static/images/7.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
static/images/8.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

BIN
static/images/9.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
static/load.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

75
templates/index.html Normal file
View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>LittlePrynter</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="{{ url_for('static',filename='css/style.css') }}">
</head>
<body class="container">
<div class="col-md-6 offset-md-3">
<h1 class="font-weight-bold">Little Prynter</h1>
<hr>
<br>
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger" role="alert">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<br>
<h3>Imprimer une image aléatoire</h3>
<a class="btn btn-primary btn-lg btn-block" href="/print/image"> Imprimer</a>
<hr>
<br>
<h3>Imprimer ton propre texte</h3>
<form class="form-group" action="/print/text" method="post">
<input class="form-control" type="text" name="message" placeholder="200 caractères ou moins " maxlength="200"><br>
<input class="btn btn-primary float-right" type="submit" value="Imprimer" name="imprimer">
</form>
<div id="pbar" style="display:none">
<img src="{{ url_for('static',filename='load.gif') }}" alt="loading wheel">
</div>
<br>
<br>
<footer><a href="/logout">Se déconnecter</a></footer>
</div>
</body>
<script>
function printImage() {
// Make an ajax call to the flask server on the /print/image route to trigger an image to be printer.
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
var x = document.getElementById("pbar");
if (x.style.display === "none") {
x.style.display = "inline";
}
if (this.readyState == 4 && this.status == 429) {
x.style.display = "none";
alert("Hé, attends un peu avant d'imprimer une autre image, je me repose...");
} else if (this.readyState == 4 && this.status == 200) {
x.style.display = "none";
}
};
xhttp.open("GET", "/print/image", true);
xhttp.send();
}
function printText(){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 400) {
alert("Le text est trop long !");
}
};
xhttp.open("GET", "/print/text", true);
xhttp.send();
}
</script>
</html>

30
templates/password.html Normal file
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Log in</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="{{ url_for('static',filename='css/style.css') }}">
</head>
<body class="container">
<div class="col-md-6 offset-md-3">
<h1>Login</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}" role="alert">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form action="/login" method="post">
<div class="form-group">
<input type="text" name="username" class="form-control" placeholder="Pseudo">
<input type="password" name="password" class="form-control" placeholder="Password">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</body>
</html>

6
users.json Normal file
View File

@ -0,0 +1,6 @@
{
"users": {
"test" : "test",
"Karl" : "Marx"
}
}