Drop in videos, PDFs, and notes. Ask anything. Get exact answers with timestamps.
TOBU is a fully offline desktop application that turns your personal archive of videos, PDFs, and plain-text notes into a searchable, queryable knowledge base,no cloud, no subscriptions, no data leaving your machine.
Drop a lecture recording into the vault. Ask "what did they say about gradient descent?" and TOBU will surface the exact transcript segment with a timestamp, along with the visual frame from that moment.
Why it matters: Most personal knowledge tools index filenames and metadata. TOBU indexes what's inside : speech, visual content, and text and retrieves it semantically. It bridges the gap between "I know I watched something about this" and actually finding it.
| Category | Capability |
|---|---|
| Video | Speech-to-text transcription (Whisper), CLIP frame embeddings, thumbnail extraction |
| Documents | PDF page-level extraction (PyMuPDF), Markdown / .txt paragraph chunking |
| Search | Hybrid semantic + keyword search with Reciprocal Rank Fusion (RRF) |
| Summarization | Auto-generated summaries via DistilBART for every indexed file |
| Watch Folder | Drop files in; auto-detection, debounced stability checks, auto-enqueue |
| Desktop App | Electron shell + PyInstaller backend — single installer, zero runtime deps |
| Job Queue | SQLite-backed job queue with retry logic, progress tracking, and cancellation |
| Privacy First | 100% local inference. All models run on-device. |
┌─────────────────────────────────────────────────────────────┐
│ Electron Shell (desktop/) │
│ main.js → spawns Python backend → loads React UI │
└─────────────────┬───────────────────────────────────────────┘
│ http://127.0.0.1:8000
┌─────────────────▼───────────────────────────────────────────┐
│ FastAPI Backend (backend/search_and_index/) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ aural_engine│ │visual_engine │ │ document_engine │ │
│ │ (Whisper) │ │ (CLIP) │ │ (PyMuPDF/frontmatter)│ │
│ └──────┬──────┘ └──────┬───────┘ └─────────┬─────────┘ │
│ │ │ │ │
│ ┌──────▼────────────────▼─────────────────────▼───────────┐ │
│ │ semantic_engine (MiniLM + LanceDB) │ │
│ │ Hybrid RRF Search (semantic + keyword) │ │
│ └────────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────▼────────────────────────────────┐ │
│ │ sql_database.py (SQLite — jobs + media) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌───────────────────────────────┐│
│ │ watchdog FileWatcher│ │ Background Worker Thread ││
│ │ (auto-enqueue files)│ │ (job queue consumer) ││
│ └──────────────────────┘ └───────────────────────────────┘│
└───────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ React Frontend (client/ — Vite + JSX) │
│ Search · Jobs · Ingest · System panels │
└─────────────────────────────────────────────────────────────┘
| Model | Purpose | Source |
|---|---|---|
distil-large-v3 |
Speech-to-text transcription | Faster-Whisper |
clip-ViT-B-32 |
Visual frame understanding & search | CLIP via sentence-transformers |
all-MiniLM-L6-v2 |
Semantic text embeddings | sentence-transformers |
distilbart-cnn-6-6 |
Automatic content summarization | sshleifer/distilbart-cnn-6-6 |
- OS: Windows 10/11 (64-bit) · macOS 12+ · Linux (Ubuntu 20.04+)
- Python: 3.10 or later
- Node.js: 18 or later
- FFmpeg: Must be on your system
PATH(Download here) - RAM: 8 GB minimum (16 GB recommended)
- GPU: Optional — CUDA 12 supported for faster transcription
TOBU runs all AI models locally. Two modes are supported:
| Mode | VRAM | RAM | Speed | Notes |
|---|---|---|---|---|
| CPU-only | 0 GB | 8 GB min (16 GB rec.) | Slow (3-10x realtime) | Works on any machine. Whisper + CLIP run on CPU. |
| GPU (CUDA) | 4 GB min (2 GB usable) | 8 GB | Fast (near-realtime) | NVIDIA GPU with CUDA 12. Whisper + CLIP use GPU; MiniLM + DistilBART stay on CPU. |
Model VRAM breakdown (GPU mode):
| Model | Purpose | VRAM (GPU) | Device |
|---|---|---|---|
| Whisper distil-large-v3 (int8) | Speech-to-text | ~750 MB | GPU (CUDA) |
| CLIP ViT-B/32 (fp32) | Visual frame embeddings | ~600 MB | GPU (CUDA) |
| all-MiniLM-L6-v2 | Semantic text embeddings | 0 MB | CPU (default) |
| distilbart-cnn-6-6 | Summarization | 0 MB | CPU (default) |
| Total resident | ~1.4 GB + ~500 MB working | ~2 GB min, 4 GB recommended |
Any GPU with 4 GB+ VRAM works (RTX 3050, GTX 1660, RTX 4050, etc.). CPU-only mode works with 8 GB RAM but is significantly slower for transcription.
GPU Setup (Windows): If you encounter
RuntimeError: Library cublas64_12.dll is not found, seeGPU_TRANSCRIBTION_TEMPORARY_FIX.mdfor the CUDA DLL workaround.
| Type | Extensions |
|---|---|
| Video | .mp4 .mkv .avi .mov .webm |
| Documents | .pdf |
| Notes | .md .txt |
git clone https://github.com/VishnupiriyanV/TOBU.git
cd TOBUpython -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install -r backend/search_and_index/requirements.txtGPU Acceleration (Optional): If you have an NVIDIA GPU with CUDA 12, uncomment the CUDA lines in
requirements.txtbefore installing.
# Root (Electron shell)
npm install
# React frontend
cd client && npm install && cd ..On first launch, TOBU's onboarding wizard handles model downloads automatically.
To pre-download manually:
python backend/search_and_index/model_downloader.pyThis downloads ~1.5 GB of models into the models/ directory.
# Terminal 1 — Start the FastAPI backend
python -m uvicorn backend.search_and_index.api_app:app --host 127.0.0.1 --port 8000 --reload
# Terminal 2 — Start the full Electron + React app
npm startThe Electron window will open automatically once the React dev server is ready on http://localhost:5173.
A single PowerShell script handles the full build pipeline:
.\build_tobu.ps1This will:
- Build the React frontend (
client/dist/) - Sync the UI into the Electron shell (
desktop/ui/) - Bundle the Python backend with PyInstaller
- Package everything into a desktop installer via
electron-builder
The output installer will be in desktop/release/.
# With the backend running on port 8000
python test_api_smoke.pycurl http://127.0.0.1:8000/api/v1/health# Drop a file into the watch folder; it will be auto-detected and queued
cp my_video.mp4 watch/
# Or POST directly to the ingest API
curl -X POST http://127.0.0.1:8000/api/v1/ingest \
-H "Content-Type: application/json" \
-d '{"file_path": "/absolute/path/to/my_video.mp4"}'curl "http://127.0.0.1:8000/api/v1/search?q=gradient+descent&limit=5"Below is the full indexing pipeline for a video file:
File dropped into watch/ folder
│
▼
FileWatcher (watchdog) detects change
→ debounce 2s → stability check (size + mtime unchanged)
│
▼
Job enqueued in SQLite (status: queued)
│
▼
Background Worker picks up job
1. [5%] Hash check — skip if file unchanged
2. [15%] Extract audio → 16kHz mono WAV (ffmpeg)
3. [22%] Read duration (ffmpeg probe)
4. [45%] Transcribe audio (Whisper distil-large-v3, VAD filtered)
5. [50%] Delete temp WAV
6. [65%] Summarize transcript (DistilBART, chunked 1024 tokens)
7. [75%] Save to SQLite (media record + transcript segments)
8. [85%] Save semantic embeddings to LanceDB (MiniLM, sentence-window chunking)
9. [90%] Save summary embedding to LanceDB
10. [97%] Index visual frames (CLIP, 1 frame / 2 sec, batch 50, thumbnails saved)
11. [100%] Job marked done
For PDFs and notes, steps 2–5 are replaced with page/paragraph extraction respectively.
Every query runs two retrieval strategies in parallel, then merges them:
- Semantic Search — embeds your query with MiniLM and finds nearest neighbors in LanceDB (top 40)
- Keyword Search — full-text FTS5 search in SQLite (top 40)
Results are fused using Reciprocal Rank Fusion (RRF):
score(doc) = Σ 1 / (k + rank_in_source) where k = 60
The final ranked list can be filtered by:
- Source type (
video,pdf,note) - Folder prefix (workspace scoping)
- Date range (
added_atfrom SQLite) - Minimum score threshold
| Scenario | How TOBU Handles It |
|---|---|
| File still being copied when detected | Stability check: waits for size + mtime to stop changing |
Temp/partial files (.tmp, .part, .crdownload) |
Filtered by suffix and name pattern before enqueueing |
| File deleted from vault | Records removed from SQLite + LanceDB + thumbnails cleaned up |
| Job crash mid-processing | Retry counter incremented; re-queued up to max_retries, then marked failed |
Stale running jobs on startup |
reset_stale_running_jobs() re-queues them at boot |
| CUDA unavailable at runtime | Automatically falls back to CPU for Whisper and CLIP |
| Model files missing | Raises RuntimeError with a message pointing to the onboarding flow |
| Video with no valid audio track | extract_audio returns None; job raises RuntimeError and is retried |
| Video with invalid / zero FPS | Logged and skipped in visual_engine |
| Same file re-added unchanged | SHA-256 hash check detects no change; job marked skipped_unchanged |
| Duplicate job for same path | enqueue_job is idempotent; returns existing job ID |
| Empty transcript / document | Guarded with early return — "No valid segments" logged |
The backend exposes a versioned REST API at http://127.0.0.1:8000/api/v1/.
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Health check (DB reachability) |
GET |
/system/status |
System info, job counts, model states |
GET |
/system/models/status |
Per-model download status |
POST |
/system/models/download |
Trigger background model download |
GET |
/system/onboarding-status |
Check if first-launch setup is complete |
POST |
/system/onboarding-completed |
Mark onboarding finished |
GET |
/system/file-tree |
Directory tree of the watch folder |
GET |
/system/browse-file |
Open native OS file picker |
GET |
/system/browse-folder |
Open native OS folder picker |
DELETE |
/system/workspace-folder |
Remove folder from index (disk untouched) |
POST |
/system/cancel-indexing |
Cancel queued jobs for a folder |
GET |
/system/integrity |
Run DB integrity check |
POST |
/system/backup |
Create a labeled backup |
GET |
/search |
Hybrid search (q, limit, source_types, date_from, date_to) |
POST |
/ingest |
Manually enqueue a file for indexing |
GET |
/jobs |
List all indexing jobs with status + progress |
GET |
/media/{id} |
Fetch full transcript and metadata for a file |
All responses follow a consistent envelope:
{ "ok": true, "data": { ... } }
{ "ok": false, "error": { "code": "...", "message": "..." } }TOBU/
├── backend/
│ └── search_and_index/
│ ├── api_app.py # FastAPI app + lifespan (worker + watcher startup)
│ ├── api_routes_*.py # Route handlers (search, jobs, ingest, media, system)
│ ├── aural_engine.py # Whisper transcription + ffmpeg audio extraction
│ ├── visual_engine.py # CLIP frame embedding + thumbnail generation
│ ├── semantic_engine.py # MiniLM embeddings + LanceDB vector store
│ ├── document_engine.py # PDF (PyMuPDF) + Markdown/TXT processing
│ ├── summarizer.py # DistilBART summarization
│ ├── runtime_service.py # Worker loop + hybrid RRF search
│ ├── sql_database.py # SQLite schema, job queue, FTS5 search
│ ├── watch.py # Watchdog filesystem observer + debouncer
│ ├── model_downloader.py # Model download + path management
│ └── requirements.txt
├── client/ # React + Vite frontend
│ └── src/
│ ├── pages/ # Search, Jobs, Ingest, System
│ └── components/
├── desktop/ # Electron shell
│ ├── main.js # Spawns backend, loads UI
│ └── preload.js
├── models/ # Local AI model weights (git-ignored)
├── data/ # SQLite DB, LanceDB vectors, thumbnails
├── watch/ # Default vault drop folder
├── scripts/
│ └── build_backend.py # PyInstaller build script
├── build_tobu.ps1 # One-shot full build script (Windows)
└── tobu-server.spec # PyInstaller spec file
- Multi-vault support — manage multiple named watch folders independently
- In-app playback — click a transcript segment to jump to that timestamp in an embedded video player
- Visual search from image — drag-and-drop an image to find visually similar frames across all indexed videos
- Re-index on demand — force re-processing of a file even when hash is unchanged
- Search filters in UI — expose source type, date range, and folder filters in the frontend
- Audio file support — index
.mp3,.m4a,.wavwithout video - GPU progress reporting — live VRAM usage in the System panel
- Export results — save search results and transcripts to Markdown
- macOS and Linux installers — finalize cross-platform packaging with electron-builder
- Configurable watch folders — add/remove vault folders from within the UI
- Local LLM integration — RAG-style Q&A over your vault using a local LLM (Ollama / llama.cpp)
- Speaker diarization — identify and label different speakers in a recording
- OCR support — index text from scanned PDFs and images
- Mobile companion — remote search via a local network endpoint
- Plugin system — allow custom ingestion engines for new file types
TOBU is built on the shoulders of outstanding open-source projects:
| Project | Role |
|---|---|
| Faster-Whisper by SYSTRAN | Efficient CTranslate2-based Whisper inference |
| sentence-transformers by UKPLab | MiniLM semantic embeddings + CLIP visual embeddings |
| LanceDB | Embedded vector database for semantic search |
| FastAPI by Sebastián Ramírez | High-performance async API framework |
| Uvicorn | ASGI server |
| ffmpeg-python | Python bindings for FFmpeg |
| PyMuPDF (fitz) | PDF text extraction |
| OpenCV | Video frame extraction |
| Watchdog | Filesystem event monitoring |
| Hugging Face Transformers | DistilBART summarization |
| Electron | Cross-platform desktop shell |
| Vite + React | Frontend tooling and UI framework |
| PyInstaller | Python app bundling |
| python-frontmatter | YAML front-matter parsing for Markdown |
MIT License — see LICENSE for details.