Skip to main content

SpectatorDB

SpectatorDB is an embeddable, offline-first media-intelligence store for Python. It keeps each media file and its structured metadata and its vector embedding behind a single interface — the union that neither an object store nor a vector database gives you alone — with zero required runtime dependencies and no server to run.

It is designed for per-device autonomy on commodity hardware (e.g. a Raspberry Pi): each device owns its own data and works with no cloud, no network, and no AI model present. AI enrichment is optional and applied after a capture is stored, so a slow or missing model never costs you data.

Features

  • One store for media + metadata + embeddings, backed by the local file system and SQLite.
  • One-line folder import with EXIF/mtime capture times and SHA-256 content-hash deduplication.
  • Composable queries by time range, media type, device id, and labels (any-match), newest-first, with limit/offset, plus count() for paging.
  • Semantic similarity search over embeddings, scoped to a single embedding model — pure-Python cosine, no extra dependencies.
  • Store-first, enrich-later: insert immediately, attach labels/description/ embedding afterward via update_enrichment.
  • Durable by design: atomic insert/delete with compensating actions, reconcile() to sweep orphans, schema versioning with forward migrations, and UTC-normalized timestamps at the API boundary.
  • Pluggable backends via the Storage and MetadataStore ABCs.

Privacy & guarantees

  • Offline always. No network calls, no telemetry — nothing ever leaves the device. A test in the suite asserts the package opens no sockets.
  • Zero required runtime dependencies. The core is stdlib-only. The optional [exif] extra pulls in Pillow for richer photo formats; the core never imports it.
  • Two-folder portability. Your data is just a folder of media files plus one SQLite catalog — open, inspectable, and readable decades from now.

Requirements

  • Python 3.11+ (runs on Raspberry Pi OS Bookworm)

Installation

$ python -m pip install spectator-db            # zero-dependency core
$ python -m pip install "spectator-db[exif]"    # + Pillow for richer EXIF/HEIC

To work on the library itself, clone the repository and install it in editable mode with the development tools:

$ git clone https://github.com/iot-spectator/spectator-db.git
$ cd spectator-db
$ python -m pip install -e .
$ python -m pip install -r requirements.txt  # dev/test/docs tools

Quick start

import pathlib
from datetime import datetime, timezone

from spectatordb import SpectatorDB, MediaType, LocalStorage, SQLiteMetadataStore

# Compose a store from a file-storage backend and a metadata backend.
db = SpectatorDB(
    storage=LocalStorage(pathlib.Path("./media")),
    metadata_store=SQLiteMetadataStore(pathlib.Path("./spectator.db")),
)

# 1. Import a whole folder in one line. Capture times come from EXIF (falling
#    back to file mtime), and duplicates are skipped by content hash.
ids = db.import_dir(pathlib.Path("~/Pictures").expanduser())

# 2. Or store a single capture. captured_at is optional — omit it and it is
#    read from EXIF, then the file's mtime. Returns None if skip_duplicates
#    skipped it.
record_id = db.insert(
    pathlib.Path("/path/to/snapshot.jpg"),
    media_type=MediaType.IMAGE,
    device_id="pi-01",
    labels=["person"],
    skip_duplicates=True,
)

# 3. Enrich later (store-first, enrich-later). embedding and embedding_model
#    must be set together.
db.update_enrichment(
    record_id,
    description="A person at the front door",
    embedding=[0.10, 0.20, 0.30],
    embedding_model="clip-vit-b32",
)

# 4. Query with composable filters, newest-first; count() pages without
#    loading rows.
hits = db.query(media_type=MediaType.IMAGE, labels=["person"], limit=10)
total = db.count(media_type=MediaType.IMAGE, labels=["person"])

# 5. Semantic similarity search, scoped to one embedding model.
similar = db.search_similar([0.11, 0.19, 0.31], model="clip-vit-b32", limit=5)

# 6. Fix a wrong capture time or device after the fact.
db.update_metadata(record_id, captured_at=datetime(2025, 6, 15, tzinfo=timezone.utc))

# 7. Read a record and copy its file back out.
record = db.get(record_id)
db.retrieve(record_id, pathlib.Path("./out.jpg"))

Example: a personal photo library

examples/photos.py is a complete command-line tool built on the public API — import a folder, tag it, search it, find look-alikes:

$ python examples/photos.py import ~/Pictures
imported 2153 file(s) from /home/you/Pictures
library now holds 2153 record(s)

$ python examples/photos.py list --type image --since 2026-01-01 --limit 3
7e281c56  2026-03-14 09:12  image     2.4 MB  -
1e457344  2026-02-02 17:40  image     1.9 MB  -
cd4f7678  2026-01-08 11:05  image     3.1 MB  -

$ python examples/photos.py tag 7e281c56 --add beach --add sunset
$ python examples/photos.py list --label beach
$ python examples/photos.py similar 7e281c56

Every command except embed runs on the stdlib-only core. embed needs an image decoder, so it requires the [exif] extra:

$ python -m pip install "spectator-db[exif]"
$ python examples/photos.py embed

The embedding it computes is a 64-dimension color-layout signature — a 4x4 grid of mean RGB plus a luma histogram. It matches on color and composition, not meaning: it will find your other beach photos, but it does not understand what a beach is. It exists so the similarity path is exercised end to end with no dependencies to speak of. Swapping in a real model such as CLIP means replacing one function; storage and search do not change.

Public API

The supported, semver-protected surface is exported from the spectatordb package root:

from spectatordb import (
    SpectatorDB,         # the facade orchestrating storage + metadata
    MediaRecord,         # the stored-item data model
    MediaType,           # IMAGE | VIDEO
    ReconcileReport,     # result of SpectatorDB.reconcile()
    UNSET,               # sentinel for partial update_enrichment() updates
    Storage,             # file-storage backend ABC
    LocalStorage,        # local-filesystem Storage backend
    SaveMode,            # COPY | MOVE
    MetadataStore,       # metadata backend ABC
    SQLiteMetadataStore, # SQLite MetadataStore backend (default)
)

SpectatorDB methods: insert, import_dir, exists, update_enrichment, update_metadata, get, retrieve, query, count, search_similar, delete, reconcile.

Concurrency

A SQLiteMetadataStore uses one WAL-mode connection per process and serializes writes with a lock; reads are concurrent. It is thread-safe for the expected low-write workload — use one instance per process.

Metadata

Release files for spectator-db 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spectator-db 0.2.0
File Size Uploaded Provenance
spectator_db-0.2.0.tar.gz 39.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for spectator-db 0.2.0
File Interpreter ABI Platform
spectator_db-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 70.0 kB

Release files / spectator_db-0.2.0.tar.gz

Download URL spectator_db-0.2.0.tar.gz
Size 39.4 kB
Tags Source
SHA-256 checksum
How to use checksums
b9c93f166f1fdfd026237e718916483f8cb28c24d5db5c70f871c1b79c91d09d
BLAKE2b-256 checksum
How to use checksums
f9c1d419937c7088f5854fb8be3868b0f9772341bd7216942ae2dbdbf9128a71
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 7, 2026.

Transparency log

Release files / spectator_db-0.2.0-py3-none-any.whl

Download URL spectator_db-0.2.0-py3-none-any.whl
Size 30.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
57522edc9dc2ee2f0ab0b195fca90f7dc25a50b11b4a29a56e9c0b2a090f6f36
BLAKE2b-256 checksum
How to use checksums
815ed34fa120731aa78eac38d11c0685fb1cc6531efe02650f613840fa68bff3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 7, 2026.

Transparency log

Security for spectator-db 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for spectator_db-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 7, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page