## Print Dog

Print Dog watches a download directory and automatically sends finished PDF files to your printer. It is built with cross-platform support in mind: macOS and Linux use CUPS (`lp`/`lpr`), while Windows can rely on either the default shell printer verb or SumatraPDF for robust silent printing.

---

### Highlights

- Watches a folder for new PDFs and prints them as soon as the download completes (with stability checks for `.crdownload`, `.part`, etc.).
- Default filename prefix allow-list (`--allow-prefix`) set to `RELR`; extend it to permit more patterns.
- Deletes printed PDFs by default; keep them with `--keep-files`.
- Flexible printing backends: system default, custom command (`--print-command`), or SumatraPDF auto-discovery (`--use-sumatra`).
- Download handling controls (`--download-timeout`, `--stable-checks`, `--poll-interval`) tuned for slow or unstable networks.
- Dry-run mode for testing (`--dry-run`).
- Packaged as an installable CLI (`print-dog`) with helper scripts for Windows and macOS.

---

### Requirements

- Python 3.12 or newer.
- `watchdog` (installed automatically via the package).
- A configured system printer:
  - **macOS / Linux**: CUPS (`lp` or `lpr`).
  - **Windows**: Either a default PDF handler that supports the `print` verb or SumatraPDF (recommended for silent printing).

---

### Installation

#### Via pip

```bash
pip install print-dog
```

For local development:

```bash
pip install -e .
```

#### Via uv

```bash
uv pip install print-dog
# or for development
uv pip install -e .
```

`uv` respects the `UV_PYTHON` environment variable if you want to pin the interpreter (e.g. `export UV_PYTHON=3.14`).

---

### Quick Start

```bash
print-dog /path/to/watch --log-level INFO
```

- The folder argument is optional and defaults to `~/Downloads`.
- Output is logged to stdout; use `--log-level DEBUG` for verbose diagnostics.
- Without extra flags the system default printer is used.

#### Common Options

- `--printer NAME` – target a specific printer queue (macOS/Linux).
- `--print-command "command {file}"` – run a custom command; `{file}` is replaced with the PDF path. Environment variables and `~` are expanded.
- `--use-sumatra` – on Windows, auto-detect `SumatraPDF.exe` (searches `%LOCALAPPDATA%`, `%PROGRAMFILES%`, `SUMATRAPDF_PATH`, and the PATH). Combine with:
  - `--sumatra-path PATH` to point to a specific executable.
  - `--sumatra-args "-print-to \"Printer\" -silent"` to override default flags.
- `--allow-prefix PREFIX` – require filenames to start with one or more prefixes (repeatable). Defaults to `RELR`.
- `--keep-files` – prevent automatic deletion after a PDF is printed. Default behaviour deletes the file once spooled.
- `--dry-run` – log matched PDFs without printing.
- `--download-timeout`, `--stable-checks`, `--poll-interval` – tune how long Print Dog waits for downloads to stabilise before printing.

---

### Windows Printing Tips

- To avoid per-user path issues, let Print Dog discover SumatraPDF:

  ```powershell
  print-dog "C:\Users\%USERNAME%\Downloads" --use-sumatra
  ```

- Set `SUMATRAPDF_PATH` if SumatraPDF is installed in a non-standard location, or pass `--sumatra-path` on the command line.
- When relying on `--print-command`, wrap paths with quotes and include `{file}`. Example:

  ```powershell
  print-dog --print-command "\"%LOCALAPPDATA%\SumatraPDF\SumatraPDF.exe\" -print-to-default -silent {file}"
  ```

---

### macOS / Linux Printing Tips

- By default, Print Dog shells out to `lp`/`lpr`. Ensure CUPS is running and the desired printer is set as default (`lpoptions -d <printer>`).
- Use `--printer` to choose a specific printer without changing the system default.
- All CLI arguments accept `~` expansion; `--print-command "echo {file}"` is handy for smoke-testing on macOS.

---

### Helper Scripts

Located in the [`scripts`](scripts) directory:

- `scripts/run-print-dog-py.bat` – Windows bootstrap using `py -3.14` to manage Python and Print Dog.
- `scripts/run-print-dog.bat` – Windows bootstrap when `python` on PATH already points at Python 3.14.
- `scripts/run-print-dog-uv.bat` – Windows bootstrap powered entirely by `uv` (installs/upgrades and runs the watcher in an isolated environment).
- `scripts/run-print-dog-macos.sh` – macOS helper using the system Python; override `PYTHON_BIN`, `WATCH_DIR`, or `PRINT_ARGS` as needed.
- `scripts/run-print-dog-uv-macos.sh` – macOS helper backed by `uv` for isolated installs.
- `scripts/build-macos-executable.sh` – Builds a standalone macOS binary using PyInstaller (via `uv`); output lands in `dist/`.
- `scripts/release.sh` – Maintainer script that bumps the version, builds distributions, runs `twine check`, uploads to PyPI, and tags the release. Run from a clean git tree with `TWINE_USERNAME=__token__` and `TWINE_PASSWORD=<pypi-token>`.

All scripts are meant to be copied to target machines, edited to taste (e.g. printer names, prefixes), and launched via shortcut/Automator/Task Scheduler.

---

### Testing

Install the development extras once:

```bash
pip install -e .[dev]
# or with uv
uv pip install -e .[dev]
```

Then run the suite:

```bash
python -m pytest
```

Alternatively, without installing extras permanently:

```bash
uv run --with pytest --with pytest-mock pytest
```

The tests cover CLI argument parsing, download stabilisation, Sumatra detection, print command construction, and handler behaviours.

---

### Building & Releasing

Manual build:

```bash
python -m build            # or: uv run --with build python -m build
python -m twine upload dist/*
```

Automated release:

```bash
./scripts/release.sh 0.1.x
```

The release script updates `pyproject.toml`, builds artefacts, checks them with `twine`, uploads to PyPI, creates a Git tag, and pushes. Ensure `uv` is installed and the working tree is clean before running.

---

### Troubleshooting

- **Access denied on Windows** – increase `--download-timeout`/`--stable-checks` so the watcher waits longer for the browser to close the file handle.
- **No printers detected** – use `--print-command` or `--use-sumatra` with explicit paths.
- **Need to observe without printing** – run with `--dry-run` to verify detection logic.
- **Want verbose insight** – use `--log-level DEBUG` to see every event processed by the watcher.

Happy printing!
