A multi-modal deepfake detection system featuring a Next.js frontend and a Flask backend. Upload a video and the backend runs a real analysis pipeline (facial-landmark analysis, optional audio analysis, and container metadata) through a trained XGBoost classifier to produce an authenticity score with live progress.
Test at https://notreally.vercel.app/
- Multi-Modal Analysis: Combines MediaPipe facial-feature analysis, optional audio (MFCC) processing, and ffmpeg container/metadata extraction.
- Live Progress: Analysis runs in a background thread; the client polls for the real completion percentage and pipeline stage, so the progress bar reflects actual work done.
- Interactive Dashboard: Charts and a detailed feature/metric breakdown (lazily loaded to keep the initial bundle small).
- Explainable Output: A human-readable summary highlights which indicators (blink rate, facial jitter, audio patterns) contributed to the verdict.
- Modern UI: Built with Next.js (App Router), React 19, TypeScript, and Tailwind CSS.
Notreally/
├── frontend/ # Next.js React application
│ ├── src/
│ │ ├── app/ # App Router (layout.tsx, page.tsx, globals.css)
│ │ ├── components/ # FileUpload, LoadingProgress, AnalysisDashboard
│ │ └── types/ # analysis.ts (TypeScript types)
│ ├── next.config.ts
│ └── package.json
├── backend/ # Flask Python API
│ ├── app.py # Flask app, routes, background analysis worker
│ ├── db.py # SQLite job store (jobs table w/ progress + stage)
│ ├── feature_extractor.py # MediaPipe + librosa + ffmpeg feature extraction
│ ├── predictor.py # model.pkl loader (cached) + predict_proba
│ ├── train_model.py # Trains the XGBoost model -> model.pkl
│ ├── model.pkl # Trained classifier (checked in)
│ ├── notreally.db # SQLite database
│ ├── requirements.txt
│ ├── Dockerfile
│ └── uploads/ # Uploaded video storage (created at runtime)
├── docker-compose.yml # Containerized backend
├── DEPLOY.md # Docker deployment guide
├── render.yaml / coolify.yaml
└── README.md
- Next.js 15.5.18 with the App Router (Turbopack dev/build)
- React 19.1.0 / React DOM 19.1.0
- TypeScript 5
- Tailwind CSS 4 for styling
- Recharts 3 for data visualization (lazy-loaded in the dashboard)
- Lucide React for icons
- Axios for API communication
- Flask + Flask-CORS for the REST API
- OpenCV (
opencv-python-headless) for video frame decoding - MediaPipe (
0.10.14) for facial landmark / Face Mesh analysis - Librosa + ffmpeg-python for audio (MFCC) extraction
- XGBoost + scikit-learn for ML classification, loaded via joblib
- NumPy (
<2), pandas for numeric work - SQLite (Python stdlib
sqlite3) for job/result storage — actually used, not planned - Gunicorn for production serving
- Node.js 18+ and npm
- Python 3.8+
- FFmpeg (required for audio extraction and metadata probing)
cd backend
pip install -r requirements.txt
python app.pyThe backend listens on the port from the PORT env var, defaulting to 52513 (see app.py). It binds to 0.0.0.0. The SQLite database and uploads directory are created automatically on startup, and the model is warmed into memory so the first request is fast.
cd frontend
npm install
npm run devThe frontend defaults to talking to http://localhost:52513. To point it elsewhere, set NEXT_PUBLIC_API_BASE_URL (e.g. in frontend/.env.local).
The application will be available at:
- Frontend: http://localhost:3000
- Backend API: http://localhost:52513 (default; overridable via
PORT)
cd backend
# Quotes are needed for the space in the real dataset folder name
python train_model.py "DFD_original sequences" DFD_manipulated_sequences
# This (re)writes backend/model.pklAnalysis is asynchronous. POST /api/analyze saves the file, creates a job in SQLite, starts a background thread, and returns a job_id immediately. The client then polls GET /api/results/<job_id> (about once a second) for the live progress percentage and stage.
The background worker reports real progress as it proceeds through these stages:
- Upload — the file is saved to the uploads directory and a job row is inserted (
stage: queued). - Facial analysis (
analyzing_video, ~5%→80%): OpenCV reads frames and MediaPipe Face Mesh extracts landmarks. It computes Eye Aspect Ratio for blink detection (smoothed, adaptive threshold, refractory period) and nose-displacement-based facial jitter. To keep long videos bounded, only every Nth frame is analyzed (frame_stride=5) and the number of analyzed frames is capped byNOTREALLY_MAX_FRAMES(default600). - Audio analysis (
analyzing_audio, ~82%): optional — disabled by default. When enabled viaNOTREALLY_ENABLE_AUDIO, librosa extracts MFCC mean/std (16 kHz mono, ffmpeg fallback). - Metadata (
reading_metadata, ~90%): ffmpeg probes container format, duration, bitrate, codecs, resolution, fps, and audio sample rate. - Classification (
finalizing, ~95%): features are assembled into a fixed 10-element vector and scored by the XGBoost model (predict_proba) to produceprob_real/prob_fakeand an authenticity score. - Done (
completed, 100%): results (authenticity score, confidence, probabilities, legacy feature summary, human-readable verdict) are written to the job row.
- Drag-and-drop interface with file type/size validation (100 MB max)
- Real upload-progress reporting, then a switch to backend-driven analysis progress
- Two-phase progress bar: a determinate upload phase driven by axios upload events, then an analyzing phase driven by the backend-reported percentage and stage label
- Falls back to an indeterminate animation only before the first backend progress arrives
- Lazy-loaded (
next/dynamic,ssr: false) so the heavy Recharts dependency stays out of the initial bundle - Authenticity score with confidence, feature breakdown charts, metrics, and an explainable summary
| Method | Path | Description |
|---|---|---|
POST |
/api/analyze |
Upload a video (multipart/form-data, field file). Returns immediately with { job_id, status, message } and runs analysis in a background thread. |
GET |
/api/results/<job_id> |
Poll for status. Returns { job_id, status, results, created_at, filename, progress, stage }. status is processing / completed / failed. |
GET |
/api/health |
Health check: { status: "healthy", ... }. |
GET |
/ |
Root/info endpoint listing available endpoints (used for platform health checks). |
Jobs and results are persisted in SQLite (notreally.db by default). The jobs table schema:
| Column | Type | Notes |
|---|---|---|
id |
TEXT (PK) | Job UUID |
filename |
TEXT | Original upload name |
filepath |
TEXT | Saved path on disk |
status |
TEXT | processing / completed / failed |
created_at |
TEXT | ISO timestamp |
results_json |
TEXT | JSON-encoded results (null until done) |
progress |
INTEGER | Live completion 0–100 |
stage |
TEXT | Current pipeline stage |
init_db() creates the table and migrates older databases that predate the progress/stage columns. Connections use a 30s timeout so the analysis thread and polling requests don't collide on "database is locked".
PORT— server port (default52513)NOTREALLY_DB_PATH— SQLite database path (defaultbackend/notreally.db)NOTREALLY_UPLOAD_DIR— upload directory (defaultuploads)NOTREALLY_MAX_FRAMES— max frames analyzed per video (default600)NOTREALLY_ENABLE_AUDIO— set1/trueto enable audio analysis (off by default); legacyNOTREALLY_DISABLE_AUDIOis also honoredMODEL_PATH— path to the model pickle (defaultbackend/model.pkl)
NEXT_PUBLIC_API_BASE_URL— backend base URL (defaulthttp://localhost:52513)
The backend ships with a Dockerfile and docker-compose.yml. See DEPLOY.md for full instructions. Quick version:
docker-compose up -d
curl http://localhost:52513/api/healthrender.yaml and coolify.yaml are provided for those platforms.
- Update types in
frontend/src/types/ - Create/extend components in
frontend/src/components/ - Add API endpoints in
backend/app.py - Extend the analysis pipeline in
backend/feature_extractor.pyand the feature vector / model as needed
- Real-time video streaming analysis
- Batch processing for multiple files
- User authentication and history
- Advanced ML models (CNN, Transformer)
- API rate limiting and security hardening
- Cloud deployment automation
This is a hackathon project, but contributions are welcome:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
This project is created for educational and hackathon purposes.