The media library for the Gecko Suite — a searchable, persistent home for every image, video, audio file, document and AI generation you accumulate, so that a clip you saw once is findable a year later.
The problem GAM solves is recall. Media piles up; what gets lost is the moment — the ten seconds inside a ninety-minute interview where someone said the thing you now want to quote. GAM transcribes, indexes and searches that material so a plain-English question returns the asset and the timestamp.
Status: early development. The scaffold is being built milestone by milestone. See
docs/plan-of-attack.mdfor the roadmap and the reasoning behind the architecture.
Three loosely-coupled apps under one domain and one login:
| App | Role | Status |
|---|---|---|
| Gecko Notes | The writing hub — scripts, narration, drafts. Source of truth for narrative. | Live at notes.geckopico.com |
| Gecko Asset Manager (GAM) | The media library. Source of truth for assets. | This repository |
| Gecko Video Creator (GVC) | The output engine — combines notes and assets into finished video. | Not yet started |
Assets flow GAM → GVC; narrative flows Notes → GVC. Each app is independently deployable and owns its own data; they share identity and talk over APIs rather than reaching into each other's databases.
Gecko Notes is the identity provider for the suite. GAM has no registration,
password reset or 2FA of its own — it verifies the JWT Notes issues and keeps a shadow
user row. The changes that makes possible are specified in
docs/gecko-notes-integration.md.
- Ingest with no friction — drop files in with a name and nothing else. Everything beyond that is optional and can be filled in later, by hand or by AI.
- Find the moment — full-text search over names, descriptions, summaries, tags and transcripts, fused with semantic search so half-remembered wording still lands. Hits in audio and video come back with a timestamp you can jump straight to.
- Clips and sub-videos — mark a range non-destructively (no new file, always in step with its parent), or physically extract it as a standalone asset.
- Opt-in AI enrichment — transcription, text extraction from PDFs and Office documents, image description, summaries and tag suggestions. Suggestions are reviewed, never applied silently, and a field you edited by hand is not overwritten by a later AI run.
- AI asset creation — generate images from images, and video from one or more images, with the prompt and base assets recorded so a result stays reproducible.
- Cost visibility — an estimate before anything paid runs, and the real cost tracked per asset and per library afterwards.
Matches Gecko Notes, so the two stay maintainable together:
| Layer | Technology |
|---|---|
| Frontend | React 18 + Vite + TypeScript + Zustand + Tailwind CSS v3 |
| Backend | FastAPI + SQLModel (SQLite), Alembic migrations |
| Media | FFmpeg (probing, thumbnails, extraction) |
| Search | SQLite FTS5 + vector embeddings, fused |
| Transcription | Deepgram |
| Generation | fal.ai |
| Container | Docker Compose + Nginx |
Requires Python 3.13, Node 20+, and ffmpeg/ffprobe on PATH.
Dev ports are 8001 (backend) and 5174 (frontend), not the usual 8000/5173: gecko-notes claims those, and the two apps get run side by side.
.env first — it is not only for Docker. backend/app/config.py reads the
repo-root .env by absolute path, so uvicorn uses it too:
cp .env.example .envTwo entries matter before anything works:
JWT_SECRET_KEY— required, and must match gecko-notes'. The app refuses to start without it.CORS_ORIGIN=http://localhost:5174— development only. Behind nginx both halves of the app share one origin, so production needs nothing here. Under Vite they are two: the browser sendsOrigin: http://localhost:5174while the dev proxy rewritesHosttolocalhost:8001, and the origin check compares host and port. Without it every cookie-authenticatedPOST— every upload — returns 403forbidden_origin("Cookie authentication requires an allowed Origin").
Settings are read once at import, so restart uvicorn after editing .env.
Python 3.13 exactly, not "3.13 or newer": the pinned Pillow and numpy publish wheels up
to 3.13 only, and on a newer interpreter pip quietly falls back to compiling them from
source, which fails without a C toolchain and libjpeg headers. .python-version pins it,
so uv venv picks the right interpreter — downloading it if you don't have one — and
your system Python stops mattering.
# Backend — http://localhost:8001
cd backend
uv venv && source .venv/bin/activate # honours .python-version
uv pip install -r requirements-dev.txt
alembic upgrade head # and again after every `git pull`
uvicorn app.main:app --reload --port 8001
# Without uv, name the interpreter explicitly — a bare `python3` is what breaks:
# python3.13 -m venv .venv && source .venv/bin/activate
# pip install -r requirements-dev.txt
# Frontend — http://localhost:5174 (proxies /api and /media to :8001)
cd frontend
npm install
npm run devMigrations are not automatic here. alembic upgrade head runs from
backend/entrypoint.sh, which is the Docker entrypoint — the app itself never
migrates on startup, deliberately, so a failed migration stops the container instead of
leaving a running app serving a schema it does not match. Start uvicorn directly and
nothing runs it for you, which is fine until a pull brings a new table: then every
request touching it dies with sqlite3.OperationalError: no such table: … and several
hundred lines of traceback. Run it after any pull that adds a migration.
Or run the whole stack:
cp .env.example .env # JWT_SECRET_KEY is required
docker compose up --build -dFor a real deployment on gam.geckopico.com, see
docs/deployment.md.
JWT_SECRET_KEY must match the value Gecko Notes uses — that shared secret is what
lets GAM verify a session Notes issued. Generate one with openssl rand -hex 32 if you
are running GAM standalone.
cd backend && pytest -q
cd frontend && npm test && npm run buildTwo things to back up: the SQLite database at ./data/db/ and the media tree at
./data/media/. Media filenames are write-once UUIDs, which is what lets an incremental
backup send only what is new.
See LICENSE.