44 lines
1.6 KiB
Python
44 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)
|