# Public Drive folder → scanned-book digest pipeline

Use when a book exceeds chat upload limits and the user supplies a public Google Drive **folder** link.

## Retrieval

1. Fetch the folder HTML anonymously with a browser-like user agent before requesting OAuth.
2. Extract file IDs and names from `data-id` / `data-tooltip` attributes.
3. Download with `https://drive.google.com/uc?export=download&id=FILE_ID`.
4. For large-file warning pages, submit the hidden `confirm` and `uuid` values to `drive.usercontent.google.com/download`.
5. Verify file magic and metadata (`%PDF`, page count, encryption, size) before claiming success.

## Detect scan-only PDFs

Run ordinary text extraction first. If page count is plausible but extracted text is empty or near-empty, treat the PDF as image-only and OCR it; do not build fragments from blank output.

## Rootless local Tesseract fallback

When system installation is unavailable, packages can be downloaded and unpacked into a user-owned prefix. The durable performance lesson is to set:

```bash
OMP_THREAD_LIMIT=1
```

for each Tesseract subprocess, especially when running several pages in parallel. Without this, nested OpenMP threading can oversubscribe CPUs and make each page dramatically slower. A practical pattern is 3–4 page workers, each with `OMP_THREAD_LIMIT=1`.

Set the unpacked library/data locations only for the OCR subprocess environment:

```bash
LD_LIBRARY_PATH=<prefix>/usr/lib/x86_64-linux-gnu
TESSDATA_PREFIX=<prefix>/usr/share/tesseract-ocr/5/tessdata
OMP_THREAD_LIMIT=1
```

Render pages at a bounded size (for example `pdftoppm -scale-to 1800 -jpeg`) rather than an unnecessarily high fixed DPI. Verify several OCR pages manually, including contents and a substantive chapter.

## Digest setup

- Exclude cover, contents, copyright, and other front matter from fragment 1.
- Keep page markers in OCR text and fragments.
- Build a manifest with source page ranges and character counts.
- Test the next-fragment script without advancing state (or back up/restore state during the probe).
- For “every two days at 12:00 Singapore”, use `0 4 */2 * *` when the scheduler is UTC, then verify `next_run_at`.
- If the user asks to start immediately, deliver fragment 1 now and advance state exactly once; otherwise state the first scheduled delivery time clearly.
