Skip to content

Repository files navigation

FrameScout logo

FrameScout

A self-hosted filming-location workspace for filmmakers and small production teams.

FastAPI · PostgreSQL · SQLAlchemy 2 · Leaflet · OpenAI · Raspberry Pi

Why FrameScout?

FrameScout turns scattered map bookmarks, phone photos and production notes into one focused workspace. A filmmaker can save a location, document access and sound considerations, attach reference images, inspect wind and natural light, and ask an AI assistant to compare saved places.

This is a production-minded portfolio project rather than a mockup. It covers the full lifecycle of a web application: authenticated REST APIs, relational data modelling, external integrations, privacy controls, safe image handling, responsive frontend work and a self-hosted Raspberry Pi deployment.

Application preview

FrameScout map with saved filming locations and current shooting conditions

Quick start with Docker Compose

Docker Compose starts the FastAPI application and PostgreSQL. The initial database schema is applied automatically when the PostgreSQL volume is created for the first time.

Copy-Item .env.example .env
# Edit .env and replace DB_PASSWORD and AUTH_SECRET_KEY with real values.
docker compose up --build

Open http://localhost:8010. Stop the stack with Ctrl+C. The application stores uploaded photos in the local uploads/ directory and PostgreSQL data in a named Docker volume.

For a clean disposable database during local development:

docker compose down -v

That command removes the Docker database volume and is not intended for a production deployment.

Features

Location scouting workspace

  • Leaflet map with deliberate add-location mode
  • Create, list, edit and delete saved filming locations
  • Production notes, coordinates and drone airspace reminders
  • Google Street View links
  • Responsive desktop and mobile layouts

Conditions and light planning

  • Current temperature and wind from OpenWeather
  • Sunrise, sunset, golden hour and blue hour from Sunrise-Sunset API
  • PostgreSQL-backed weather cache with stale-data fallback
  • Same-day sunlight cache
  • Manual weather refresh with frontend and backend cooldown protection
  • Visible timestamps showing whether weather came from cache or is stale

Photos and privacy

  • JPEG, PNG, WebP, HEIC and MPO upload support
  • 10 MB file-size and 50-million-pixel limits
  • EXIF removal and server-side image normalization
  • In-page gallery and lightbox viewer
  • Per-user ownership checks for locations, photos and conditions
  • Account export as a ZIP file
  • Account and associated data deletion
  • Privacy notice and terms of use pages

AI Assistant

The FrameScout AI assistant is grounded in the signed-in user's saved locations. It can compare places, interpret current conditions and answer follow-up questions in the selected UI language.

The assistant includes several practical safeguards:

  • only the current user's locations are placed in the context;
  • at most 50 locations are considered and live conditions are fetched for at most 10 of them;
  • weather and sunlight data use the PostgreSQL cache when possible;
  • obvious unrelated requests are rejected before an OpenAI request;
  • per-user, global daily and concurrent-request limits protect cost and abuse;
  • conversation history is bounded and treated as untrusted reference data;
  • saved locations are returned as safe clickable UI references rather than raw HTML or Markdown.

The API key stays on the server in .env; it is never sent to the browser.

Architecture

flowchart LR
    Browser["Responsive browser UI<br/>HTML, CSS, JavaScript, Leaflet"]
    Funnel["Tailscale Funnel<br/>public HTTPS"]
    API["FastAPI application<br/>REST, auth, validation"]
    DB[("PostgreSQL")]
    Cache["Conditions cache<br/>AI request logs"]
    Files[("Private photo storage")]
    Google["Google OpenID Connect"]
    Weather["OpenWeather API"]
    Sun["Sunrise-Sunset API"]
    OpenAI["OpenAI Responses API"]

    Browser --> Funnel --> API
    API --> DB
    DB --> Cache
    API --> Files
    API <--> Google
    API --> Weather
    API --> Sun
    API --> OpenAI
Loading

The browser never talks directly to PostgreSQL or external AI/weather providers. FastAPI is the boundary for authentication, ownership checks, validation, rate limits and integration secrets.

For the engineering decisions behind the system, see the technical overview.

Project structure

app/
├── main.py                 # FastAPI routes and application orchestration
├── database.py             # PostgreSQL engine and request-scoped sessions
├── models.py               # SQLAlchemy models
├── schemas.py              # Pydantic request and response validation
├── security.py             # Argon2 hashing and JWT cookies
├── oauth.py                # Google OpenID Connect configuration
├── services/
│   ├── ai.py               # OpenAI Responses API integration
│   ├── ai_context.py       # Scope checks and bounded location context
│   ├── ai_conditions.py    # Concurrent AI condition enrichment
│   ├── ai_limits.py        # AI usage limits and request logs
│   ├── conditions_cache.py # PostgreSQL weather and sun cache
│   ├── weather.py          # OpenWeather integration
│   └── sun.py              # Sunrise-Sunset integration
└── templates/              # Privacy and terms pages

database/
├── schema.sql              # Complete initial PostgreSQL schema
└── migrations/             # Incremental SQL migrations

frontend/
├── index.html
├── styles.css
├── app.js
└── assets/

Local setup without Docker

Requirements

  • Python 3.13 or newer
  • PostgreSQL 18 or a compatible PostgreSQL server
  • Node.js for JavaScript syntax checks
  • Git

1. Clone and create a virtual environment

git clone https://github.com/Lanque/FrameScout.git
cd FrameScout
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt

On Linux or macOS:

source .venv/bin/activate

2. Create the database

Create a PostgreSQL login named drone_app and a database named drone_locations, owned by that login. Apply database/schema.sql:

createuser --pwprompt drone_app
createdb --owner=drone_app drone_locations
psql --dbname=drone_locations --file=database/schema.sql

If you are upgrading an existing installation, run the relevant migration as the PostgreSQL administrator. For the current AI and conditions changes:

sudo -u postgres psql \
  -v ON_ERROR_STOP=1 \
  -d drone_locations \
  < database/migrations/003_conditions_cache_and_ai_limits.sql

3. Configure environment variables

Copy-Item .env.example .env

At minimum, set the database values and a long random AUTH_SECRET_KEY. OpenWeather, Google login and OpenAI are optional integrations.

Generate a suitable secret with:

python -c "import secrets; print(secrets.token_urlsafe(48))"

Never commit .env. The OpenAI key belongs only in the server-side environment.

4. Start the application

uvicorn app.main:app --reload --host 127.0.0.1 --port 8010

Open http://127.0.0.1:8010 or the interactive API documentation at http://127.0.0.1:8010/docs.

Configuration

Variable Required Purpose
DB_HOST, DB_PORT, DB_NAME Yes PostgreSQL connection target
DB_USER, DB_PASSWORD Yes Restricted application database login
AUTH_SECRET_KEY Yes Signs authentication tokens
OPENWEATHER_API_KEY No Enables current wind and temperature
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET No Enables Google login
OAUTH_SESSION_SECRET No Separate OAuth session secret
COOKIE_SECURE Production Enables secure cookies behind HTTPS
OPENAI_API_KEY No Enables the AI assistant
OPENAI_MODEL No OpenAI model, default gpt-5.4-mini
AI_USER_LIMIT_15_MINUTES No Per-user rolling-window AI limit
AI_USER_DAILY_LIMIT No Per-user daily AI limit
AI_GLOBAL_DAILY_LIMIT No Global daily AI limit
AI_MAX_CONCURRENT_REQUESTS No Concurrent AI request ceiling
AI_TEST_ENABLED No Enables the disabled-by-default /ai/test endpoint
AI_SAFETY_SALT No Stable privacy-preserving provider identifier salt
WEATHER_CACHE_SECONDS No Fresh-weather cache lifetime
WEATHER_STALE_SECONDS No Maximum stale-weather fallback age
WEATHER_REFRESH_COOLDOWN_SECONDS No Manual refresh cooldown
LEGAL_CONTROLLER_NAME, LEGAL_CONTACT_EMAIL Public deployment Legal page contact details

Verification

The same checks run locally and in GitHub Actions:

python -m compileall app
node --check frontend/app.js
python -m pip check
git diff --check

Health endpoints:

GET /health
GET /health/database

Deployment

The current production-style deployment runs on a Raspberry Pi 5:

  • Debian 13
  • PostgreSQL 18
  • Uvicorn managed by systemd
  • application bound to 127.0.0.1:8010
  • public HTTPS provided by Tailscale Funnel
  • secrets stored in a server-side .env
  • uploaded photos stored outside Git under uploads/

Typical update flow:

cd /home/lanque/framescout
git pull --ff-only
sudo systemctl restart framescout
sudo systemctl status framescout --no-pager
curl http://127.0.0.1:8010/health

What this project demonstrates

  • Designing a relational model and authenticated CRUD API
  • Enforcing ownership server-side instead of trusting frontend state
  • Separating ORM models, validation schemas and service integrations
  • Coordinating asynchronous external requests with bounded concurrency
  • Designing a cache with stale-data fallback and explicit refresh protection
  • Handling untrusted photo uploads, EXIF privacy and filesystem cleanup
  • Building a map-based responsive interface with plain JavaScript
  • Integrating OpenAI with bounded context, scope controls and usage limits
  • Deploying, monitoring and debugging a real application on Linux hardware
  • Treating export, deletion, privacy and legal pages as product features

Current limitations and honest next steps

  • There are not yet automated pytest regression tests.
  • SQL migrations are manually managed; Alembic is a future improvement.
  • AI live conditions are currently enriched for only the ten newest locations.
  • OpenStreetMap's public tile servers are suitable for small usage, not high traffic commercial workloads.
  • The repository needs a fresh English UI demo recording and AI-panel screenshot; no placeholder media is linked.

License

FrameScout is released under the MIT License.

The application uses third-party services and assets whose own terms still apply, including OpenStreetMap tiles, OpenWeather, Google and OpenAI.

About

Self-hosted film location scouting app with interactive maps, weather and natural-light planning, secure photo workflows, and user accounts.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages