add printer out-of-paper warning and track app.py

Show a yellow warning banner on page load if CUPS reports the printer
is stopped with a paper-related reason (e.g. Media Empty).
This commit is contained in:
Connor Rhodes 2026-05-22 16:24:33 -05:00
parent 01e6cf6300
commit 3cd6d20d13
2 changed files with 151 additions and 1 deletions

138
home/print/app.py Normal file
View file

@ -0,0 +1,138 @@
import os
import subprocess
from pathlib import Path
import pypdf
from flask import Flask, flash, redirect, render_template, request, url_for
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = str(Path(__file__).parent / "uploads")
ALLOWED_EXTENSIONS = {"pdf"}
PRINTER_NAME = "HL-2270DW_series"
MAX_PAGES = 10
MAX_COPIES = 10
app = Flask(__name__)
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
app.config["SECRET_KEY"] = os.urandom(24)
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024
def allowed_file(filename):
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
def get_printer_warning():
try:
result = subprocess.run(
["lpstat", "-l", "-p", PRINTER_NAME],
capture_output=True, text=True, timeout=3
)
output = result.stdout.lower()
first_line = result.stdout.splitlines()[0] if result.stdout else ""
if "stopped" in first_line.lower() or "offline" in first_line.lower():
paper_keywords = ("media empty", "out of paper", "paper", "tray empty", "no media")
if any(kw in output for kw in paper_keywords):
return "Printer appears to be out of paper."
return "Printer is stopped or offline and may not accept jobs."
except Exception:
pass
return None
def get_pdf_page_count(filepath):
try:
with open(filepath, "rb") as f:
reader = pypdf.PdfReader(f)
return len(reader.pages)
except Exception as e:
app.logger.error(f"Could not read PDF pages: {e}")
return None
@app.route("/", methods=["GET", "POST"])
def upload_and_print():
if request.method == "POST":
if "file" not in request.files:
flash("No file part in the request.", "error")
return redirect(request.url)
file = request.files["file"]
if file.filename == "":
flash("No file selected. Please choose a PDF to print.", "error")
return redirect(request.url)
if file and allowed_file(file.filename):
try:
copies = int(request.form.get("copies", 1))
except ValueError:
copies = 1
copies = max(1, min(copies, MAX_COPIES))
filename = secure_filename(file.filename)
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename)
file.save(filepath)
try:
page_count = get_pdf_page_count(filepath)
if page_count is None:
flash(
"Could not process the PDF file. It might be corrupt.", "error"
)
return redirect(request.url)
if page_count > MAX_PAGES:
flash(
f"Error: File has {page_count} pages. The maximum allowed is {MAX_PAGES}.",
"error",
)
return redirect(request.url)
app.logger.info(
f"Sending '{filename}' ({page_count} pages, {copies} copies) to printer '{PRINTER_NAME}'"
)
command = [
"lp",
"-d",
PRINTER_NAME,
"-n",
str(copies),
filepath,
]
result = subprocess.run(
command, capture_output=True, text=True, check=True
)
app.logger.info(f"lp command stdout: {result.stdout}")
copy_word = "copy" if copies == 1 else "copies"
flash(f"Success! '{filename}' ({copies} {copy_word}) has been sent to the printer.", "success")
except subprocess.CalledProcessError as e:
app.logger.error(f"Error printing file: {e}")
app.logger.error(f"lp command stderr: {e.stderr}")
flash(
"Error sending file to printer. The printer may be offline or misconfigured.",
"error",
)
except Exception as e:
app.logger.error(f"An unexpected error occurred: {e}")
flash("An unexpected server error occurred.", "error")
finally:
if os.path.exists(filepath):
os.remove(filepath)
app.logger.info(f"Cleaned up temporary file: {filepath}")
return redirect(url_for("upload_and_print"))
else:
flash("Invalid file type. Please upload a PDF file.", "error")
return redirect(request.url)
return render_template("index.html", printer_name=PRINTER_NAME, max_pages=MAX_PAGES, max_copies=MAX_COPIES, printer_warning=get_printer_warning())
if __name__ == "__main__":
app.run(host="127.0.0.1", port=57928, debug=False)