Show a yellow warning banner on page load if CUPS reports the printer is stopped with a paper-related reason (e.g. Media Empty).
138 lines
4.9 KiB
Python
138 lines
4.9 KiB
Python
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)
|