Architecture Overview¶
Sapari is an async-first video processing platform. The web server handles API requests and dispatches work to background workers. Real-time updates flow back to the frontend via Server-Sent Events.
System Architecture¶
flowchart TB
subgraph Frontend["Frontend (React)"]
UI[Dashboard / Timeline / Export]
end
subgraph Landing["Landing (Astro)"]
LP[Marketing Site]
end
subgraph Backend["FastAPI Backend"]
API[API Routes]
SVC[Services]
SSE[SSE Endpoint]
end
subgraph Storage["Data Stores"]
PG[(PostgreSQL)]
RD[(Redis)]
RMQ[(RabbitMQ)]
R2[(R2 Storage)]
end
subgraph Workers["Background Workers"]
DW[Download Worker]
PW[Proxy Worker]
AW[Analysis Worker]
RW[Render Worker]
AEW[Asset Edit Worker]
EW[Email Worker]
end
UI -->|REST API| API
SSE -->|Events| UI
API --> SVC
SVC --> PG
SVC -->|Queue Tasks| RMQ
SVC -->|Presigned URLs| R2
RMQ -->|Consume| DW
RMQ -->|Consume| PW
RMQ -->|Consume| AW
RMQ -->|Consume| RW
RMQ -->|Consume| AEW
DW -->|Store| R2
PW -->|Read/Write| R2
AW -->|Read| R2
RW -->|Read/Write| R2
AEW -->|Read/Write| R2
DW -->|Publish Events| RD
PW -->|Publish Events| RD
AW -->|Publish Events| RD
RW -->|Publish Events| RD
RD -->|Subscribe| SSE
Request Flow¶
A typical video editing flow from upload to export:
sequenceDiagram
participant C as Client
participant API as FastAPI
participant R2 as R2 Storage
participant Q as RabbitMQ
participant W as Worker
Note over C,W: 1. Upload
C->>API: POST /clips/presign
API->>C: Presigned PUT URL
C->>R2: PUT (direct upload)
C->>API: POST /clips/confirm
API->>R2: HEAD object (actual size for quota recheck)
API->>Q: Queue process_clip_artifacts
Note over C,W: 2. Process
W->>R2: Download clip
W->>W: Extract audio, waveform
W->>R2: Store artifacts
W-->>C: ClipReadyEvent (SSE)
Note over C,W: 3. Analyze
C->>API: POST /projects/{id}/analyze
API->>Q: Queue analyze_project
W->>W: Whisper + LLM detection
W->>API: Create Edit records
W-->>C: AnalysisCompleteEvent (SSE)
Note over C,W: 4. Render
C->>API: POST /projects/{id}/exports
API->>Q: Queue render_export
W->>R2: Download clips
W->>W: Apply cuts (FFmpeg)
W->>R2: Upload export
W-->>C: ExportCompleteEvent (SSE)
Note over C,W: 5. Download
C->>API: GET /exports/{id}/download
API->>C: Presigned download URL
C->>R2: GET (direct download)
Tech Stack¶
| Layer | Technology |
|---|---|
| API | FastAPI + SQLAlchemy (async) |
| Database | PostgreSQL |
| Cache + Events | Redis (pub/sub, sessions, result backend) |
| Message Broker | RabbitMQ (priority queues for task routing) |
| Task Workers | TaskIQ |
| Object Storage | Cloudflare R2 (S3-compatible) |
| Video Processing | FFmpeg |
| Transcription | OpenAI Whisper API |
| LLM Analysis | DeepSeek Reasoner + GPT-5 + GPT-5 Mini |
| Frontend | React 19 + Vite + React Query |
| Landing | Astro |
Core Patterns¶
flowchart LR
subgraph Pattern1["Presigned URLs"]
A1[Client] -->|1. Request URL| A2[API]
A2 -->|2. Signed URL| A1
A1 -->|3. Direct upload/download| A3[R2]
end
Presigned URLs: Clients upload and download directly from R2. The API never touches file bytes, just generates signed URLs with 1-hour expiry.
flowchart LR
subgraph Pattern2["Event-Driven Updates"]
B1[Worker] -->|Publish| B2[Redis Pub/Sub]
B2 -->|Subscribe| B3[SSE Endpoint]
B3 -->|Stream| B4[Frontend]
B4 -->|Invalidate| B5[React Query]
end
Event-driven updates: Workers publish events to Redis pub/sub. The SSE endpoint streams them to connected clients. This avoids polling and gives instant feedback.
flowchart LR
subgraph Pattern3["Pipeline Architecture"]
C1[Load Audio] --> C2[Transcribe]
C2 --> C3[Detect Silences]
C2 --> C4[Detect False Starts]
C3 --> C5[Validate]
C4 --> C5
C5 --> C6[Create Edits]
end
Pipeline architecture: Analysis and render use DAG-based pipelines where each step is a class with execute(). Steps can run in parallel when they don't depend on each other.
Soft deletes: Most entities have is_deleted and deleted_at fields. We don't actually delete data.
Key Files¶
| Component | Location |
|---|---|
| API routes | backend/src/interfaces/api/v1/ |
| Business logic | backend/src/modules/ |
| Workers | backend/src/workers/ |
| Event system | backend/src/infrastructure/events/ |
| Storage client | backend/src/infrastructure/storage/client.py |
| Settings | backend/src/infrastructure/config/settings.py |