from flask import Flask, request, jsonify import os import threading import queue import pdf2image from brother_ql.conversion import convert from brother_ql.backends.helpers import send, status from brother_ql.raster import BrotherQLRaster from PIL import Image import mimetypes import logging from logging.handlers import RotatingFileHandler import time app = Flask(__name__) # Set up logging log_file = 'log.txt' # Create a rotating file handler for logging log_handler = RotatingFileHandler(log_file, maxBytes=10*1024*1024, backupCount=5) # 10MB per log file, keep 5 backups log_handler.setLevel(logging.DEBUG) log_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') log_handler.setFormatter(log_formatter) # Add the log handler to the Flask app's logger app.logger.addHandler(log_handler) # Ensure Flask app uses our logger, not its default one app.logger.setLevel(logging.DEBUG) # Create a queue to hold the files for processing file_queue = queue.Queue() image_queue = queue.Queue() lock = threading.Lock() # Directory to save uploaded files (you can modify this path) UPLOAD_FOLDER = './uploads' IMAGE_FOLDER = './images' os.makedirs(UPLOAD_FOLDER, exist_ok=True) os.makedirs(IMAGE_FOLDER, exist_ok=True) # Printer configuration printer_model = "QL-1100" backend = 'pyusb' printer = 'usb://0x04f9:0x20a7' # Convert PDF to image def convert_pdf_to_images(pdf_path): """Converts a PDF to a PIL Image""" try: images = pdf2image.convert_from_path(pdf_path) if images: return images return None except Exception as e: app.logger.error(f"Error converting PDF: {str(e)}") return None # Function to print address labels def print_address_label(image, pdf_path): try: if 'address' in pdf_path.lower(): target_width = 1660 target_height = 1164 image = image.convert("RGB") image = image.resize((target_width, target_height), Image.LANCZOS) image = image.rotate(90, expand=True) else: target_width = 1164 target_height = 1660 image = image.convert("RGB") image = image.resize((target_width, target_height), Image.LANCZOS) qlr = BrotherQLRaster(printer_model) qlr.exception_on_warning = True app.logger.info("Converting image to printer instructions...") instructions = convert( qlr=qlr, images=[image], label='102x152', threshold=70.0, dither=False, compress=False, red=False, dpi_600=False, hq=True, cut=True ) app.logger.info("Sending to printer...") status = send( instructions=instructions, printer_identifier=printer, backend_identifier=backend, blocking=True ) app.logger.info(f"Printer status: {status}") app.logger.info("Print job sent successfully") except Exception as e: app.logger.error(f"Error during printing: {str(e)}") def save_images(images, pdf_path): uuid = str(int(time.time())) for i, image in enumerate(images): if 'address' in pdf_path.lower(): image.save(os.path.join(IMAGE_FOLDER, f"address_label_{uuid}_{i}.png"), "PNG") elif 'packing' in pdf_path.lower(): image.save(os.path.join(IMAGE_FOLDER, f"shipping_label_{uuid}_{i}.png"), "PNG") else: image.save(os.path.join(IMAGE_FOLDER, f"unknown_{uuid}_{i}.png"), "PNG") image_queue.put((image, pdf_path)) # Worker thread that processes files from the queue def file_worker(): while True: file_path = file_queue.get() if file_path is None: # Shutdown signal break app.logger.info(f"Processing file: {file_path}") images = convert_pdf_to_images(file_path) if images: save_images(images, file_path) else: app.logger.error(f"Failed to convert PDF to images: {file_path}") file_queue.task_done() # Printer worker def printer_worker(): while True: image, path = image_queue.get() if image is None: break with lock: print_address_label(image, path) image_queue.task_done() # Variable to track the printer worker thread printer_worker_thread = None # Ensure only one printer worker is created def start_printer_worker(): global printer_worker_thread if printer_worker_thread is None or not printer_worker_thread.is_alive(): app.logger.info("Starting printer worker thread.") printer_worker_thread = threading.Thread(target=printer_worker, daemon=True) printer_worker_thread.start() else: app.logger.info("Printer worker thread already running.") # Start the file worker thread threading.Thread(target=file_worker, daemon=True).start() # Start the printer worker only once start_printer_worker() @app.route('/upload', methods=['POST']) def upload_file(): # Check if a file is part of the request if 'file' not in request.files: app.logger.warning('No file part in request') return jsonify({'error': 'No file part'}), 400 file = request.files['file'] if file.filename == '': app.logger.warning('No selected file') return jsonify({'error': 'No selected file'}), 400 # Check file type (ensure it's a PDF) mime_type, _ = mimetypes.guess_type(file.filename) if mime_type != 'application/pdf': app.logger.warning(f"Invalid file type: {mime_type}. Only PDF files are allowed.") return jsonify({'error': 'Only PDF files are allowed'}), 400 # Save the file to the uploads folder file_path = os.path.join(UPLOAD_FOLDER, file.filename) file.save(file_path) app.logger.info(f"File {file.filename} uploaded and saved to {file_path}") # Add the file path to the queue for processing file_queue.put(file_path) return jsonify({'message': f'File {file.filename} uploaded and queued for processing'}), 200 if __name__ == '__main__': app.run(debug=True)