Pre-share fixes from final review pass
Blockers - http://birdnet.local/avian/ now serves the collage (added avian/index.html redirect to frontend/; previously the symlink target had no top-level index, so users saw a directory listing of api/ assets/ forwarding/ frontend/ scripts/). - Gemini model bumped from gemini-2.5-flash-image-preview (deprecated 2026-01-15) to the stable gemini-2.5-flash-image. Pregen was non- functional out of the box. - avian-mqtt.service: dropped %h (which expands to /root for system-mode units regardless of User=). Use WorkingDirectory=~ which DOES respect User=, plus a relative ExecStart. - birdnet-api.php case 'lifelist' now aliases COUNT(*) AS n (was 'total'), matching what apt.js reads for atlas card counts. Atlas previously always displayed 0. Bugs - Retry-After parsing handles HTTP-date format without crashing. - apt.js no longer fetches firstseen/timeseries (declared in state but never consumed). Two wasted round-trips per page load removed. - update_birdnet.sh defaults branch=avian-visitors so the (opt-in) Sunday auto-update cron doesn't reset HEAD to upstream main and delete the avian/ overlay. - recording.php: removed dead $BIRDS_JSON_CANDIDATES. Polish - Pregen --sleep default raised 4→6s (more headroom under free-tier RPM). - en-dash in README BOM (~$35–80) replaced with hyphen for style match. - Slop sweep across user-facing docs: no em-dashes, no en-dashes, no AI-slop word hits (robust/comprehensive/seamless/etc.).
This commit is contained in:
+12
-8
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""AvianVisitors — pre-generate kachō-e illustrations for a region.
|
||||
"""AvianVisitors - pre-generate kachō-e illustrations for a region.
|
||||
|
||||
Reads a species list (BirdNET-Pi's labels.txt, eBird, or stdin),
|
||||
generates an illustration for each via the Gemini 2.5 Flash Image API,
|
||||
@@ -7,7 +7,7 @@ and saves PNGs into avian/assets/illustrations/.
|
||||
|
||||
Each species gets two poses: <slug>.png (perched) and <slug>-2.png
|
||||
(flight). Edit avian/scripts/prompt.template.md to change the visual
|
||||
style — the prompt body is re-sent verbatim per request with
|
||||
style - the prompt body is re-sent verbatim per request with
|
||||
{sci_name}, {com_name}, and {pose} substituted.
|
||||
|
||||
Usage:
|
||||
@@ -43,7 +43,7 @@ from pathlib import Path
|
||||
# get a 404 here, check Google's model catalog and bump this.
|
||||
GEMINI_URL = (
|
||||
"https://generativelanguage.googleapis.com/v1beta/models/"
|
||||
"gemini-2.5-flash-image-preview:generateContent"
|
||||
"gemini-2.5-flash-image:generateContent"
|
||||
)
|
||||
POSES = {1: "perched", 2: "in flight with wings spread"}
|
||||
|
||||
@@ -117,7 +117,7 @@ def gen_one(api_key: str, prompt: str, sci: str, com: str, pose: int) -> bytes:
|
||||
# rejecting the request shape (image-only sometimes errors).
|
||||
"generationConfig": {"responseModalities": ["TEXT", "IMAGE"]},
|
||||
}
|
||||
# API key as header, NOT URL — keeps the key out of Google's
|
||||
# API key as header, NOT URL - keeps the key out of Google's
|
||||
# request logs, proxy logs, and shell history.
|
||||
req = urllib.request.Request(
|
||||
GEMINI_URL,
|
||||
@@ -134,7 +134,11 @@ def gen_one(api_key: str, prompt: str, sci: str, com: str, pose: int) -> bytes:
|
||||
break
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code in (429, 500, 502, 503, 504) and attempt < 3:
|
||||
retry_after = float(e.headers.get("Retry-After") or backoff)
|
||||
ra = e.headers.get("Retry-After")
|
||||
try:
|
||||
retry_after = float(ra) if ra else backoff
|
||||
except (TypeError, ValueError):
|
||||
retry_after = backoff # HTTP-date format, fall back
|
||||
time.sleep(retry_after)
|
||||
backoff *= 2
|
||||
continue
|
||||
@@ -151,7 +155,7 @@ def gen_one(api_key: str, prompt: str, sci: str, com: str, pose: int) -> bytes:
|
||||
inline = part.get("inlineData") or part.get("inline_data")
|
||||
if inline and inline.get("data"):
|
||||
return base64.b64decode(inline["data"])
|
||||
# No image — surface the blocking reason so users know what to fix.
|
||||
# No image - surface the blocking reason so users know what to fix.
|
||||
finish = (resp.get("candidates", [{}])[0]).get("finishReason", "?")
|
||||
block = resp.get("promptFeedback", {}).get("blockReason", "")
|
||||
raise RuntimeError(f"no image (finish={finish} block={block})")
|
||||
@@ -180,8 +184,8 @@ def main() -> int:
|
||||
choices=list(POSES.keys()),
|
||||
help="Which poses to render. 1=perched, 2=flight. Default: both.")
|
||||
ap.add_argument("--force", action="store_true", help="Re-render even if file exists")
|
||||
ap.add_argument("--sleep", type=float, default=4.0,
|
||||
help="Seconds between API calls (default 4 = under free-tier RPM cap)")
|
||||
ap.add_argument("--sleep", type=float, default=6.0,
|
||||
help="Seconds between API calls (default 6 = headroom under free-tier RPM cap)")
|
||||
ap.add_argument("--limit", type=int, default=0, help="Cap species count for testing")
|
||||
args = ap.parse_args()
|
||||
|
||||
|
||||
@@ -1,42 +1,25 @@
|
||||
# Bird illustration prompt
|
||||
|
||||
This is the prompt sent to Gemini for every bird illustration. Edit it
|
||||
to change the style — the entire visual feel of the collage flows
|
||||
through this template.
|
||||
The prompt sent to Gemini for every illustration. Edit the body to change the style.
|
||||
|
||||
Three placeholders get replaced per request:
|
||||
|
||||
- `{sci_name}` — the binomial Latin name, e.g. `Calypte anna`
|
||||
- `{com_name}` — the English common name, e.g. `Anna's Hummingbird`
|
||||
- `{pose}` — either `perched` (pose 1) or `in flight with wings spread` (pose 2)
|
||||
- `{sci_name}` is the binomial Latin name, e.g. `Calypte anna`
|
||||
- `{com_name}` is the English common name, e.g. `Anna's Hummingbird`
|
||||
- `{pose}` is either `perched` (pose 1) or `in flight with wings spread` (pose 2)
|
||||
|
||||
The default style below is **kachō-e** — Edo-period Japanese
|
||||
flower-and-bird woodblock prints, rendered in ink and watercolor.
|
||||
Replace the body below with whatever style feels right for your
|
||||
apartment.
|
||||
The default below is kachō-e (Edo-period Japanese flower-and-bird woodblock prints). Replace it with whatever style fits your apartment.
|
||||
|
||||
---
|
||||
|
||||
## Prompt
|
||||
|
||||
Generate a {pose} {com_name} ({sci_name}) in the style of an Edo-period
|
||||
Japanese kachō-e woodblock print. Confident sumi-e ink linework with
|
||||
soft watercolor washes. Earthy, restrained palette — burnt umber,
|
||||
ochre, indigo, vermillion, muted greens. Plumage details rendered with
|
||||
short directional brush strokes; eye, beak, and feet drawn with crisp
|
||||
ink. The bird should be the only subject — NO background, NO branch
|
||||
unless the pose requires it (a single sparse twig is fine for
|
||||
perched), NO border or frame, NO text or signature.
|
||||
Generate a {pose} {com_name} ({sci_name}) in the style of an Edo-period Japanese kachō-e woodblock print. Confident sumi-e ink linework with soft watercolor washes. Earthy, restrained palette: burnt umber, ochre, indigo, vermillion, muted greens. Plumage details rendered with short directional brush strokes; eye, beak, and feet drawn with crisp ink. The bird is the only subject. NO background, NO branch unless the pose requires it (a single sparse twig is fine for perched), NO border or frame, NO text or signature.
|
||||
|
||||
Anatomy must be biologically accurate for the named species:
|
||||
|
||||
- Exactly two wings. Two legs. One head. One beak. One tail.
|
||||
- Posture and feather pattern matching {com_name} field-guide
|
||||
references — color, markings, and body proportions must match the
|
||||
real bird.
|
||||
- For perched poses: one wing folded against the body, the other
|
||||
tucked behind. For flight: both wings extended in a natural flapping
|
||||
position.
|
||||
- Posture, color, markings, and body proportions matching {com_name} field-guide references.
|
||||
- For perched poses: one wing folded against the body, the other tucked behind. For flight: both wings extended in a natural flapping position.
|
||||
|
||||
Render at high resolution on a fully transparent background. The bird
|
||||
must be cut out cleanly — no shadow, no paper texture, no caption.
|
||||
Render at high resolution on a fully transparent background. Cut the bird out cleanly. No shadow, no paper texture, no caption.
|
||||
|
||||
Reference in New Issue
Block a user