diff --git a/README.md b/README.md new file mode 100644 index 0000000..82e8716 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# Print Every Page (Local Auto-Printer) + +This program watches a folder on your laptop and automatically prints each new supported file it sees. + +## What it prints +- `.pdf` +- `.txt` +- `.png` +- `.jpg` +- `.jpeg` + +## Usage + +```bash +python3 print_every_page.py ~/to-print +``` + +Optional flags: + +```bash +python3 print_every_page.py ~/to-print --interval 1.0 --dry-run +``` + +- `--interval`: how often to check the folder. +- `--dry-run`: preview only (no actual printing). + +## Notes +- Linux/macOS: uses `lp`. +- Windows: uses PowerShell `Start-Process ... -Verb Print`. +- Ensure your default printer is configured before running. diff --git a/print_every_page.py b/print_every_page.py new file mode 100644 index 0000000..8a0ca51 --- /dev/null +++ b/print_every_page.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Automatically print every supported file from a chosen folder. + +This script watches a folder and sends each new file to the default printer. +It is intended for local, opt-in automation only. +""" + +from __future__ import annotations + +import argparse +import platform +import subprocess +import time +from pathlib import Path + +SUPPORTED_EXTENSIONS = {".pdf", ".txt", ".png", ".jpg", ".jpeg"} + + +def print_file(file_path: Path, dry_run: bool = False) -> None: + """Send one file to the default system printer.""" + system = platform.system().lower() + + if dry_run: + print(f"[dry-run] Would print: {file_path}") + return + + if system == "windows": + # Uses the file association print action on Windows. + cmd = ["powershell", "-Command", f"Start-Process -FilePath '{file_path}' -Verb Print"] + elif system == "darwin": + # macOS printing via CUPS. + cmd = ["lp", str(file_path)] + else: + # Linux and most Unix-like systems with CUPS installed. + cmd = ["lp", str(file_path)] + + subprocess.run(cmd, check=True) + print(f"Printed: {file_path}") + + +def watch_and_print(folder: Path, interval_seconds: float, dry_run: bool = False) -> None: + """Continuously watch a folder and print new supported files.""" + printed: set[Path] = set() + + if not folder.exists() or not folder.is_dir(): + raise ValueError(f"Folder does not exist or is not a directory: {folder}") + + print(f"Watching: {folder}") + print(f"Supported types: {', '.join(sorted(SUPPORTED_EXTENSIONS))}") + print("Press Ctrl+C to stop.\n") + + while True: + for file_path in sorted(folder.iterdir()): + if not file_path.is_file(): + continue + if file_path.suffix.lower() not in SUPPORTED_EXTENSIONS: + continue + if file_path in printed: + continue + + try: + print_file(file_path, dry_run=dry_run) + printed.add(file_path) + except subprocess.CalledProcessError as exc: + print(f"Failed to print {file_path}: {exc}") + + time.sleep(interval_seconds) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Watch a folder and print every new page/file using your default printer." + ) + parser.add_argument( + "folder", + nargs="?", + default=".", + help="Folder to watch for files (default: current directory).", + ) + parser.add_argument( + "--interval", + type=float, + default=2.0, + help="Polling interval in seconds (default: 2.0).", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Show which files would be printed without sending print jobs.", + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + watch_and_print(Path(args.folder).expanduser().resolve(), args.interval, args.dry_run) + + +if __name__ == "__main__": + main()