Some checks failed
build-and-deploy / build (push) Failing after 5s
- camera_scraper: public-directory-only discovery (Insecam-style HTML + plain-text lists), hard private-range guard (fail closed), per-host rate limiting, Nominatim geocoding at <=1 req/s, TTL'd local snapshot cache, sha256 url_hash dedupe with Postgres upsert - cameras table (migration 002) + /api/cameras?bbox= + snapshot endpoint with cache passthrough in main.py - run_camera_service: long-running cycle worker following NATS->ingester pattern; publishes events.camera for shared ingester - docker-compose camera-service profile, .env.example knobs Verified E2E against TimescaleDB+PostGIS: private-range entries dropped, dedupe across cycles holds, bbox query returns expected rows.
43 lines
1.6 KiB
Python
43 lines
1.6 KiB
Python
"""Open IP camera discovery — models and migration helpers.
|
|
|
|
Cameras are stored in their own `cameras` table (TimescaleDB hypertable on
|
|
last_seen) alongside the existing `events` feed, deduped by URL hash.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from sqlalchemy import (
|
|
Column, Float, Index, Integer, String, Text, DateTime, JSON,
|
|
func, Table, BigInteger,
|
|
)
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
|
|
import uuid
|
|
|
|
from database import metadata
|
|
|
|
|
|
cameras = Table(
|
|
"cameras",
|
|
metadata,
|
|
Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4),
|
|
# sha256 hex of the camera's source/snapshot URL — the dedupe key.
|
|
Column("url_hash", String(64), nullable=False, index=True),
|
|
Column("source_url", Text, nullable=False),
|
|
# Snapshot endpoint (may differ from the directory listing URL).
|
|
Column("snapshot_url", Text),
|
|
# Which discovery feed produced this record (e.g. "insecam", "camera_list").
|
|
Column("discovery_source", String(128), nullable=False, index=True),
|
|
Column("location_lat", Float),
|
|
Column("location_lon", Float),
|
|
Column("location_name", String(512)),
|
|
# Vendor / device type when detectable from the directory page or URL.
|
|
Column("vendor", String(128)),
|
|
Column("device_type", String(64)), # e.g. ip-cam, rtsp, mjpeg
|
|
Column("first_seen", DateTime(timezone=True), server_default=func.now(), nullable=False),
|
|
Column("last_seen", DateTime(timezone=True), server_default=func.now(), nullable=False),
|
|
Column("raw", JSON),
|
|
)
|
|
|
|
Index("ix_cameras_location", cameras.c.location_lat, cameras.c.location_lon)
|
|
Index("ix_cameras_last_seen", cameras.c.last_seen)
|