Real-time fact-checking powered by VideoDB Capture and Gemini AI.
Play a YouTube video, join a Google Meet, or stream a podcast — Fact Checker captures the audio, transcribes it live, and generates community-style notes on every factual claim it hears.
Three commands — the setup script handles everything else (Node.js, Python, dependencies).
git clone https://github.com/video-db/fact-checker.git
cd fact-checker./scripts/setup.shThis will:
- Check for Python 3.12+ and Node.js 18+ (installs via Homebrew on macOS if missing)
- Install uv for fast Python package management
- Prompt for your VideoDB and Gemini API keys (both free)
- Install all Node.js and Python dependencies
- Create a Python virtual environment
Get your API keys:
- VideoDB — free at console.videodb.io
- Gemini — free at aistudio.google.com/apikey
./scripts/start.shA magnifying glass icon appears in your menu bar. Click it to open the popup.
- Microphone — a macOS dialog will appear; click Allow
- Screen Recording — must be granted manually:
- Open System Settings → Privacy & Security → Screen & System Audio Recording
- Click +, press Cmd+Shift+G, and paste:
node_modules/electron/dist/Electron.app - Toggle it on and restart the app
- Pick a source — YouTube, Google Meet, local file, or live stream
- Paste the URL
- Click Start Fact-Checking
- Play the video/audio — make sure it is not muted
Alerts appear after 30–60 seconds. The tray icon turns orange while actively fact-checking.
Click Stop in the popup. To quit entirely, click X or Quit.
Audio Source (YouTube, Meet, podcast, etc.)
↓
VideoDB Capture (records system audio)
↓
Real-time Transcription (WebSocket)
↓
Transcript Buffer (~20 seconds of text)
↓
Gemini AI (extract claims → verify → score confidence)
↓
Alerts (tray app + terminal + log files)
Every 20 seconds, the system:
- Collects transcribed text
- Sends it to Gemini for fact-checking
- Labels each claim as Verified, Misleading, or Needs Context
- Surfaces high-confidence results
| Color | Label | Meaning |
|---|---|---|
| Green | Verified | Factually accurate |
| Red | Misleading | Contains inaccuracies |
| Orange | Needs Context | Partially true, missing important context |
Left: App waiting to start | Right: Live fact-checking with community notes
- Menu bar app — runs as a tray icon, no dock clutter
- Activity indicators — live status for audio capture, transcription, and fact-checking
- Live transcript ticker — latest transcribed text in real-time
- Tray icon states — orange when active, gray when idle
- Copy to clipboard — hover over any alert and click the copy icon
- Stats footer — running totals for verified, misleading, and needs-context notes
- Auto-reconnect — reconnects automatically if the connection drops
- Session recovery — reopen the popup anytime without losing your session
All settings go in .env. Only the API keys are required — everything else has sensible defaults.
| Setting | Default | Description |
|---|---|---|
VIDEO_DB_API_KEY |
required | Your VideoDB API key |
GEMINI_API_KEY |
required | Your Gemini API key |
PORT |
5002 |
Backend server port |
FACT_CHECK_INTERVAL |
20 |
Seconds between each fact-check cycle |
MIN_WORDS_FOR_CHECK |
15 |
Minimum words before triggering a check |
CONFIDENCE_THRESHOLD |
high |
Minimum confidence level for alerts (high, medium, low) |
CONTEXT_WINDOW_WORDS |
150 |
Words of previous context carried forward |
ALERT_COOLDOWN_SECONDS |
30 |
Minimum gap between alerts for similar claims |
Active VideoDB streams consume credits even after the app is closed. If you force-quit or your machine crashes, clean up manually:
# Kill the backend server
lsof -ti:5002 | xargs kill -9
# Stop active streams on VideoDB
cd backend
source venv/bin/activate
python cleanup.py --forcefact-checker/
├── package.json # Electron config (main: app/main.js)
├── entitlements.mac.plist # macOS entitlements (audio, JIT, library validation)
├── .env.example # API key template
├── scripts/
│ ├── setup.sh # One-time setup (installs everything)
│ └── start.sh # Launch the app
├── app/ # Electron frontend
│ ├── main.js # Main process: tray, window, permissions, process management
│ ├── preload.js # Secure bridge between Electron and UI
│ ├── index.html # Popup UI with permission flow
│ └── icons/ # Tray and app icons
├── backend/ # Python backend
│ ├── backend.py # Flask server — sessions, transcription, fact-checking
│ ├── client.py # Capture client — records system audio
│ ├── config.py # Configuration settings
│ ├── cleanup.py # Stop active streams and free port
│ ├── requirements.txt # Python dependencies
│ ├── pipeline/
│ │ ├── __init__.py # Orchestrator: preprocess → verify → generate → filter
│ │ ├── claim_detector.py # Transcript cleaning, context window
│ │ ├── verifier.py # Gemini API: extract and verify claims
│ │ ├── note_generator.py # Format results as community notes
│ │ └── alert_manager.py # Confidence filtering, dedup, throttling
│ └── amd_mx/ # Native recording binaries
└── resources/
├── icon.icns # App icon (used by electron-builder)
└── logo.jpeg
"No alerts yet" after starting
This is normal for the first 30–60 seconds. The system needs time to:
- Start audio capture
- Accumulate enough transcript (at least 15 words)
- Send it to Gemini for analysis
Make sure your video is actually playing with audio unmuted.
"Error: Invalid session" on startup
Your VideoDB API key is expired or invalid. Get a fresh key from console.videodb.io and update .env:
nano .envPort 5002 is already in use
A previous run left a process on the port. The start script cleans this up automatically, but you can also run:
lsof -ti:5002 | xargs kill -9No audio captured / No transcripts appearing
- Grant Microphone permission — the app requests this automatically on startup
- Grant Screen Recording permission manually:
- System Settings → Privacy & Security → Screen & System Audio Recording
- Add
Electron.appfromnode_modules/electron/dist/ - Toggle it on and restart the app
- Make sure the video/audio source is playing and not muted
- Check that system volume is not at zero
macOS blocks the capture binary
Find and unquarantine the binary:
cd backend
source venv/bin/activate
RECORDER=$(python -c "import videodb_capture_bin; print(videodb_capture_bin.get_binary_path())")
xattr -d com.apple.quarantine "$RECORDER"Backend won't start
Make sure both API keys are set in .env:
VIDEO_DB_API_KEY=...
GEMINI_API_KEY=...
The backend exits immediately if either key is missing.
- VideoDB Capture — System audio capture and real-time transcription
- Gemini AI — Claim extraction, verification, and confidence scoring
- Flask — Backend server
- Electron — Desktop tray app
- pycloudflared — Webhook tunneling

