feat(aircraft): planespotters.net photo in aircraft popup #16

Merged
sirius merged 1 commit from feat/planespotters-photos into master 2026-08-28 22:58:30 -04:00
Owner

Summary

Adds a planespotters.net latest-photo lookup to the ADS-B aircraft popup on the map.

  • app/live_layers.pyfetch_planespotters_photo() (hex preferred, reg fallback) + _normalize_planespotter_photo(); 24h TTL cache (planespotters ToS cap).
  • app/main.pyGET /api/aircraft/photo?hex=... (or reg=...): 422 on missing/invalid params, 404 when no photo, 502 on upstream failure.
  • app/static/index.html.ps-photo block in the ADS-B popup; lazy-loads on popupopen; thumbnail links to the photo page + photographer credit.

Why server-side, not browser fetch()

Planespotters returns 403 for any request carrying an Origin header — and every browser fetch() sends one. So the JSON must be proxied server-side with the identifying OSINT_USER_AGENT. The thumbnail binary itself is loaded by the browser straight from the planespotters CDN (never re-hosted), per their Terms of Use.

Tests

  • 4 unit tests (test_live_layers.py) + 4 API contract tests (test_api_live_layers.py).
  • 38 passed in the two files.
  • Live-verified: fetch_planespotters_photo(hex_code='e8027e') returned a real photo (LATAM A320, photographer "Philipp Greiml"); a hex absent from their DB returns None and the popup hides the block.
## Summary Adds a planespotters.net latest-photo lookup to the ADS-B aircraft popup on the map. - **`app/live_layers.py`** — `fetch_planespotters_photo()` (hex preferred, reg fallback) + `_normalize_planespotter_photo()`; 24h TTL cache (planespotters ToS cap). - **`app/main.py`** — `GET /api/aircraft/photo?hex=...` (or `reg=...`): 422 on missing/invalid params, 404 when no photo, 502 on upstream failure. - **`app/static/index.html`** — `.ps-photo` block in the ADS-B popup; lazy-loads on `popupopen`; thumbnail links to the photo page + photographer credit. ## Why server-side, not browser fetch() Planespotters returns `403` for any request carrying an `Origin` header — and every browser `fetch()` sends one. So the JSON must be proxied server-side with the identifying `OSINT_USER_AGENT`. The thumbnail binary itself is loaded by the browser straight from the planespotters CDN (never re-hosted), per their Terms of Use. ## Tests - 4 unit tests (`test_live_layers.py`) + 4 API contract tests (`test_api_live_layers.py`). - 38 passed in the two files. - Live-verified: `fetch_planespotters_photo(hex_code='e8027e')` returned a real photo (LATAM A320, photographer "Philipp Greiml"); a hex absent from their DB returns `None` and the popup hides the block.
sirius added 1 commit 2026-08-28 22:56:30 -04:00
Add planespotters.net latest-photo lookup for ADS-B aircraft.

- app/live_layers.py: fetch_planespotters_photo() (hex preferred, reg
  fallback) + _normalize_planespotter_photo(); 24h TTL cache (their ToS cap).
- app/main.py: GET /api/aircraft/photo?hex=...|reg=... (422/404/502).
- app/static/index.html: .ps-photo block in ADS-B popup; lazy load on
  popupopen; thumbnail links to photo page + photographer credit.

Server-side proxy, not browser fetch(): planespotters 403s any request
carrying an Origin header (every browser fetch() sends one). The thumbnail
binary is loaded by the browser straight from their CDN, never re-hosted.

Tests: 4 unit + 4 API contract (38 pass in the two files).
sirius merged commit 680aeea29f into master 2026-08-28 22:58:30 -04:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: sirius/osint-dashboard#16
No description provided.