# Start here: how a recipe gets written

A recipe teaches NeoDownloader one site: which addresses are its, where the data is, how to read it, how to page, and what to download. Projects of the person use it from the moment it is saved.

**The first rule: measure, do not assume.** Every claim about the site is a measurement. Sites lie plausibly - a "next page" flag that is always false, a page size silently capped at 100, a WAF answering 202 to real and invented addresses alike. **The worst failure looks like success**: a run that brings 100 files of 526 reads exactly like one that brings all of them. So every run is checked against a number the site states itself - a `total` in the API answer, a counter on the page, "1 of 43" in a viewer.

## The way through

1. `identify(url)` - maybe a plugin or a recipe already takes the site. Then say so and stop - unless the person asked you to fix that recipe (below).
2. `put_draft(id, site)` - a draft for the site. Its site never changes; the network rule hangs on it.
3. Look: `fetch` the page, read its `outline`, ask it questions with `query`. Look for the API before the markup - half the sites answer JSON. Measure one header at a time what the site needs to answer.
4. Write the recipe whole with `put_draft(id, recipe=...)`. Start from a shipped recipe of the same shape (`list_recipes`, `get_recipe`).
5. `try_step` on what is kept (free), `try_step(live=true)` when you need the site again.
6. `run_recipe`, then `get_run(wait_s=30)`. Read the report against the checklist (`get_guide('checklist')`).
7. `save_recipe` - tell the person what it shadows.

## Fixing a recipe that stopped working

The phrase then says so: the site, the address that stopped working, how many files the last run brought. `identify` answers `recipe_exists` - that is the recipe to fix, not a reason to stop.

1. `list_recipes(url)` - which recipe claims the address; `get_recipe(id)` - its text and its code.
2. `put_draft(id, site)` with the same id, then `put_draft(id, recipe=...)` with its text, and `write_code` for its code if it has any.
3. `fetch` the address and compare what the site answers now with what the recipe expects: a moved API, a renamed field, a new wall.
4. Change what changed and no more; `try_step`, then `run_recipe` and its report.
5. `save_recipe` with a `message` saying what changed: the recipe before it is kept as the previous version. A shipped recipe fixed this way becomes the person's own and shadows the shipped one - say so.

## What to download: decide, then say it

The phrase NeoDownloader gives the person names the site and nothing else. What to take from it is yours to decide, the way they would expect: **what the page shows, every kind of file on it, at full size** - the original, never the preview. Do not make them choose formats, sizes or feeds; they cannot tell a webp from a thumbnail, and should not have to. Say it in one plain sentence: "200 works from the Top feed, 4 full-size pictures each - about 800 files." If they want something else, they will say so.

**Kinds a person may not all want are the recipe's params, not questions.** Photos and videos side by side, a video in several qualities: give the recipe a parameter for each, as shipped recipes do (`pexels`: `photos`, `videos`, `quality`), each on by default. The person then chooses in the wizard and in the project's settings, in the program's words, project by project - and you check both settings of each before saving (`format`). Set `expect` on each download to its type (`checklist`).

## Talking to the person

They are not a programmer. They see the site, not its insides.

- **Say what they will get** - pictures, pages, how many - and what will not come, in their words. Not APIs, requests, cookies, status codes, file formats, ids, page tokens or tool names.
- **Decide the how yourself.** Do not offer a menu of approaches or ask them to approve one: they cannot judge it, and the question only stops the work. Ask only what they can answer by looking at the site.
- **When NeoDownloader cannot do it, say so at once, plainly, and stop**, with no list of workarounds. Files the site gives only to a browser are not such a case: a download asks NeoDownloader's browser for such files by itself, and so does `heads` - its rows say `through: browser`. Write the recipe as usual (`pitfalls`).

## Before the first line

Find out, with live requests:

- **where the real data is** - often not on the page at all;
- **how it pages**, and whether it pages: fetch two pages and **compare the ids**, not the lengths;
- **the page size ceiling** - ask for a lot and see how many come;
- **how many there are in total** - the number you will check the run against;
- **what there is for a name, a description, metadata**, and how full it is: not "the field exists" but "filled on 46 of 87";
- **which forms of address lead to the same thing**: a page, its subpage, one photo inside an album - all must reduce to the entity.

## Topics

`format` the recipe's shape · `checklist` what a recipe must carry · `paging` · `browser` · `code` when to write Python or a page script · `pitfalls` traps already paid for · `limits` the network rule, the budget, what is not here.
