# AGENTS.md Guidance for AI coding agents working in this repository. ## What this is `wallhaven-dl`: single-file PyQt6 desktop app to search wallhaven.cc, preview and download wallpapers, and find better-resolution replacements for low-res images already on disk. The only dependency is PyQt6 (QtWidgets, QtNetwork); everything else is stdlib. Keep it a single file and do not add dependencies. On the maintainer's machine the script, menu entry and icon are symlinks into the repo, so edits are live on the next launch: ~/bin/wallhaven-dl -> wallhaven-dl ~/.local/share/applications/wallhaven-dl.desktop -> wallhaven-dl.desktop ~/.local/share/icons/hicolor/scalable/apps/wallhaven-dl.svg -> wallhaven-dl.svg ## wallhaven API facts (verified, not in the docs) - Rate limit is 45 API calls per minute. The app caps itself at 40 (`API_LIMIT`) with a sliding-window limiter in `get()`, and debounces filter changes by 400 ms. Thumbnails (`th.wallhaven.cc`) and full images (`w.wallhaven.cc`) do not count. Keep test runs cheap: stub `Main.search` when no network is needed, and never loop over the API. - With an API key, every parameter the request omits falls back to the account's saved search settings (categories, resolutions, ...). That is why `categories`, `purity` and `atleast` are always sent explicitly (`atleast=1x1` for "any size"). - The key goes in the `X-API-Key` header, only for URLs under `API`, never in the query string. It is read from `pass` (`WALLHAVEN_PASS_ENTRY`, default `wallhaven.cc/api-key`) at startup. - `like:` similarity search is behind a Cloudflare bot challenge (403, for the API and the site alike). Do not try to get around it. The low-res feature uses the wallpaper's first tag (`id:`) instead, under the wallpaper's own purity. - `colors` accepts only the 29 palette values in `COLORS`. Each wallpaper stores five colors and a color search matches any of the five. The local extraction (`palette_histogram`, redmean distance) agrees with the site's list well: measured on 72 wallpapers, the picked color was in the site's list 72/72 times. - There is no reverse image search on wallhaven. Sending local images to third-party services was rejected for privacy. ## Library and files - `LIBRARY` (`~/Pictures/wallpapers`, recursive, hidden dirs skipped) is what "downloaded" means. Files are matched by the wallhaven id in their name (`WALL_ID`, also `wallhaven-_.png` copies), never by path. - Writes go to a `.part` file renamed into place. Replacing an original saves the new file first and only then moves the old one to the desktop trash (`QFile.moveToTrash`). Never delete user images outright. ## Conventions - Non-GUI logic lives in module-level functions (`read_apikey`, `api_delay`, `target`, `scan_library`, `nearest_color`, `search_pick`, ...) so it can be tested without a display. - Async replies check `gen` before touching grid items: `grid.clear()` deletes them, and a stale callback would hit a dead C++ object. - Do not name methods after `QWidget` ones (`show` once shadowed `QWidget.show()` and broke startup). - Placeholders only in code, docs, tests and commits: no real account names, file names from the maintainer's library, or `pass` entry names beyond the generic default. ## Testing python3 test_wallhaven_dl.py Covers the non-GUI functions with plain asserts and a fake `pass` on `PATH`. For GUI checks, run headless and keep settings away from the real config: XDG_CONFIG_HOME=/tmp/whdl-cfg QT_QPA_PLATFORM=offscreen python3 -I