Skip to content

Repository files navigation

HomeTimeline

CI codecov Docker Python License: MIT

A lightweight, self-hosted web application for browsing and managing event-based security camera recordings.

Does not do continuous recording or motion detection — those are handled by external systems (e.g. Home Assistant). HomeTimeline provides the library, timeline, and playback interface.


Features

  • Timeline — multi-camera, zoomable, date-range navigation, thumbnail preview on bars, click-to-play; the player has previous/next controls (and ← / → keys) to step through a camera's clips in time order
  • Recordings — sortable table, date/camera filtering, thumbnail preview, inline playback, download
  • Scanner — auto-discovers recordings on NAS; deduplicates by hash; generates thumbnails via ffmpeg; runs on a per-camera schedule (or Never for manual-only) or on demand per-camera
  • Hikvision cameras — a camera can be typed Hikvision with host/credentials; the app pulls clips directly over ISAPI (per-day YYYY-MM-DD folders), indexes them like scanned files, shows live device details (model/firmware/RTSP/snapshot), and downloads on a per-camera schedule (or Never for manual-only) via a Download Videos button
  • Aqura cameras — a camera can be typed Aqura with 3 user-configured RTSP stream URLs and RTSP credentials; clips are stored by the camera itself onto a NAS in YYYYMMDD folders and are discovered by the scanner (no download or purge). Live view shows all 3 streams (Channel1/Channel2/Channel3) via go2rtc
  • Purge old videos — Hikvision cameras can auto-delete clips older than a per-camera retention window (delete older than N days, default Never); each purge removes the video file, its thumbnail, and the index entry, and runs on its own per-camera schedule (or Never for manual-only) via a Purge Old Videos button. All runs land in Activity
  • Live view — real-time WebRTC video for Hikvision (main/sub) and Aqura (3 channels) cameras via an embedded go2rtc bridge, with a quality stream switch; the camera page puts the live feed on top over Timeline / Details / Commands tabs
  • Live View wall — a dedicated Live View page shows every live-capable camera at once in an NVR-style grid, with a selectable cameras-per-row layout (Auto / 1× / 2× / 3× / 4×, persisted); each tile has a per-camera stream selector dropdown (Main/Sub for Hikvision, Channel1/2/3 for Aqura) and links back to its camera page
  • Activity — unified feed of scan / download / purge runs; runs left unfinished by a restart are reconciled to an interrupted state on startup (rather than spinning forever)
  • Dashboard — storage stats, recent recordings, health summary, bulk Download Videos (enabled Hikvision cameras with host/username/password) and Purge Videos (enabled Hikvision cameras with retention > 0) actions
  • Settings — general app settings (display timezone), per-camera config (type, clip storage strategy, scan schedule, Hikvision connection + download schedule + purge retention/schedule, Aqura RTSP URLs + credentials), location management
  • Timezone — all timestamps stored as UTC; displayed in any IANA timezone configured in General Settings

Stack

Layer Choice
Backend FastAPI + Peewee + SQLite (WAL)
Frontend React 18 + TypeScript + Vite + shadcn/ui + Tailwind
Video ffmpeg (probe + thumbnails) + HTML5 range streaming; go2rtc (live WebRTC)
Container Multi-stage Dockerfile (apt ffmpeg by default; optional custom FFmpeg target)
CI/CD GitHub Actions — lint, test, build, push to ghcr.io on main

Quick Start

Requires SSH access to a Linux server with Podman installed.

podman run -d --name hometimeline \
  -p 8080:8080 \
  -p 8555:8555 \
  -v /opt/hometimeline/data:/opt/hometimeline/data \
  -v /nas/camera:/nas/camera \
  -e DATABASE_URL=sqlite:////opt/hometimeline/data/cam.db \
  -e RECORDING_LOCATIONS=/nas/camera \
  -e THUMBNAIL_DIR=/opt/hometimeline/data/thumbnails \
  -e LOG_FILE=/opt/hometimeline/data/app.log \
  -e GO2RTC_WEBRTC_CANDIDATE=<server-lan-ip>:8555 \
  ghcr.io/dk307/hometimeline:latest

The recordings volume is mounted read-write (no :ro): Hikvision cameras download clips into it. Use :ro only if you have no Hikvision cameras.

Port 8555 and GO2RTC_WEBRTC_CANDIDATE=<server-lan-ip>:8555 are needed for live view (WebRTC): inside a container go2rtc can't detect the host's LAN address, so it's passed explicitly. scripts/deploy.py sets both automatically. Omit them if you don't need live view.

App served at http://server:8080. Display timezone and other app settings can be changed live from Settings → General; each camera's scan schedule is configured per-camera under Settings → Cameras.


Deployment (from source)

Requires SSH key access to the server (see scripts/deploy.sh for setup).

./scripts/deploy.sh

This validates locally, rsyncs source to the server, rebuilds the container via podman-compose up --build, and verifies health.

Persisted data (survives rebuilds)

All persistent data lives on the host, mounted into the container:

Path Purpose
data/cam.db SQLite database (recordings, cameras, scan events, settings)
data/app.log Application log
data/thumbnails/ Generated video thumbnails

Environment Variables

Variable Default Description
DATABASE_URL sqlite:///./data/cam.db SQLite path
RECORDING_LOCATIONS /mnt/recordings Colon-separated list of root recording dirs
THUMBNAIL_DIR ./data/thumbnails Thumbnail output directory
LOG_FILE ./data/app.log Log file path (rotating, 5×5 MB). Point it inside the mounted data volume so logs survive container restarts.
LOG_LEVEL INFO Logging verbosity

Display timezone is configurable at runtime via Settings → General, and each camera's scan schedule via Settings → Cameras — no restart needed.


Local Development

# Create and activate virtual environment
python3 -m venv .venv
source .venv/bin/activate

# Install project with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/unit tests/integration -v

The .venv/ directory is gitignored. Remember to activate it (source .venv/bin/activate) before running any Python commands. Do not pip install --break-system-packages — the venv is the only supported way to run tests locally.


Running Tests

# Unit + integration (isolated tmp DB, safe any time)
pytest tests/unit tests/integration -v

# E2E (needs a running container)
pytest tests/e2e -v --base-url=http://localhost:8080

CI / CD

Trigger What runs
Every push / PR Backend lint (ruff), unit + integration tests, frontend type-check + build
PR only Docker build smoke test (no push)
Merge to main Docker build + push to ghcr.io/dk307/hometimeline:latest and :<sha>

Project Structure

app/               FastAPI backend
  api/             Route handlers (cameras, recordings, timeline, settings, …)
  models/          Peewee ORM models
  schemas/         Pydantic request/response schemas
  services/        Scanner, log buffer, timezone utilities
  workers/         APScheduler background job
frontend/src/      React frontend
  api/             Typed API clients
  hooks/           Custom React hooks (useTimezone)
  lib/             Utility modules (tz.ts — timezone-aware date formatting)
  pages/           Page components (Dashboard, Timeline, Recordings, Settings)
  components/      Shared components (VideoPlayer)
  store/           Zustand UI state
tests/
  unit/            Pure unit tests (scanner, models, tz utilities)
  integration/     FastAPI TestClient against in-memory DB
  e2e/             Playwright browser tests against live container
docker/Dockerfile  Multi-stage: node:26-slim → python:3.14-slim + ffmpeg targets
docs/              Architecture design and agent guide

See docs/DESIGN.md for full architecture decisions.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages