| backend | ||
| deployment | ||
| frontend | ||
| .env.development | ||
| .env.example | ||
| .env.production | ||
| .gitignore | ||
| .htaccess | ||
| CLAUDE.md | ||
| DEPLOYMENT.md | ||
| DEPLOYMENT_phase2.md | ||
| ENVIRONMENT_SETUP.md | ||
| oliver_box_config.json | ||
| README.md | ||
| start.sh | ||
Video Optimizer for Social Media Platforms
Creative video optimization tool built for CDMO workflows. Converts videos to platform-specific specifications using FFmpeg, with two operating modes: an interactive web UI and a fully automated Box.com pipeline.
Modes of Operation
| Mode | How it works | When to use |
|---|---|---|
| Web UI | User uploads video → selects platform → downloads optimised file | Ad-hoc individual conversions |
| Box Automation | Drop video in Box IN folder → auto-processes → delivers to OUT_SUCCESS |
Batch / unattended workflows |
Supported Platforms
| Platform | Codec | Aspect Ratios | Bitrate Range |
|---|---|---|---|
| Meta (Facebook/Instagram) | H264 | 1:1, 16:9, 4:5, 9:16 | 840–1400 kbps |
| H264 | 1:1, 16:9, 2:3, 4:5, 9:16 | 1100–1690 kbps | |
| Snapchat | H264 | 16:9, 9:16 | 1100–1400 kbps |
| TikTok | H265 | 1:1, 16:9, 9:16 | 840–1300 kbps |
| YouTube & DV360 | VP9 | 1:1, 16:9, 4:5, 9:16 | 1300–2000 kbps |
| YouTube CTV | VP9 | 16:9 | 3300–7000 kbps |
| Amazon Prime | H264 | 16:9 | 15000 kbps fixed |
| Amazon Freevee | H264 | 16:9 | 4500–7000 kbps |
Prerequisites
- Python 3.8+
- FFmpeg — required for all video processing
# macOS
brew install ffmpeg
# Ubuntu/Debian
sudo apt-get install ffmpeg
Installation
# Clone and create virtual environment
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r backend/requirements.txt
Configuration
Copy the example env file and fill in your credentials:
cp .env.example .env
Required: Microsoft SSO (Azure AD)
AZURE_CLIENT_ID=your-client-id
AZURE_TENANT_ID=your-tenant-id
REDIRECT_URI=http://localhost:3000
Create an app registration in Azure Portal:
- Supported account types: Single tenant
- Redirect URI platform: Single-page application (SPA)
- Add URIs:
http://localhost:3000andhttp://localhost:3000/admin.html - Grant Microsoft Graph → User.Read permission
Optional: Box.com Automation
BOX_CONFIG_PATH=../oliver_box_config.json
BOX_VIDEO_OPTIMIZER_FOLDER_ID=362124323515
BOX_AS_USER_ID= # leave blank — service account has direct access
BOX_WEBHOOK_SECRET= # only needed for webhook mode
BOX_USE_POLLING=false # set true for dev/testing (no public URL needed)
BOX_POLL_INTERVAL_SECONDS=60
BOX_PROCESSOR_PORT=5001
Running the Web UI
# One-command start (backend + frontend)
./start.sh
# Or manually in two terminals:
# Terminal 1 — backend (port 5000)
cd backend && python app.py
# Terminal 2 — frontend (port 3000)
cd frontend && python3 -m http.server 3000
Open http://localhost:3000 and sign in with your Microsoft account.
Admin panel: http://localhost:3000/admin.html
Running the Box Automation Service
Step 1 — Verify setup (run once)
cd backend
python box_setup.py
This authenticates with Box, lists accessible folders, discovers IN / OUT_SUCCESS / OUT_FAILED sub-folders and prints a full configuration summary.
Step 2 — Start the service
Box automation runs inside the same app.py process as the web UI — no separate service needed.
Polling mode (recommended for development — no public URL needed):
# Set in .env: BOX_USE_POLLING=true
./start.sh # or: python backend/app.py
Webhook mode (for production):
# Expose local port via ngrok (development)
ngrok http 5000
# Start the app as normal
python backend/app.py
Configure a Box webhook in the Box Admin Console:
- Target URL:
https://<your-url>/webhooks/box - Trigger:
FILE.UPLOADEDon theINfolder - Copy the webhook secret into
BOX_WEBHOOK_SECRETin.env
Step 3 — Test a file
Upload a video with a valid naming convention to the Box IN folder, or trigger manually:
curl -X POST http://localhost:5001/manual-trigger \
-H "Content-Type: application/json" \
-d '{"file_id": "PASTE_BOX_FILE_ID", "filename": "campaign_tiktok_9x16.mp4"}'
Box Naming Convention
Filenames must include both a platform and an aspect ratio pattern for the automation to process them. Files without both patterns are skipped and an error report is placed in OUT_FAILED.
Platform Patterns
| Platform | Patterns |
|---|---|
| Meta | _meta_, _fb_, _ig_, _facebook_, _instagram_ |
_pinterest_, _pin_ |
|
| Snapchat | _snapchat_, _snap_ |
| TikTok | _tiktok_, _tt_ |
| YouTube | _youtube_, _yt_ |
| YouTube CTV | _youtube_ctv_, _yt_ctv_, _ctv_ |
| Amazon Prime | _prime_, _amazon_prime_ |
| Amazon Freevee | _freevee_, _amazon_freevee_ |
Aspect Ratio Patterns
| Ratio | Patterns |
|---|---|
| 1:1 | _1x1_, _square_ |
| 16:9 | _16x9_, _landscape_ |
| 9:16 | _9x16_, _vertical_, _portrait_ |
| 4:5 | _4x5_ |
| 2:3 | _2x3_ |
Valid examples:
campaign_tiktok_9x16.mp4 → TikTok 9:16
product_meta_1x1_v2.mov → Meta 1:1
hero_youtube_ctv_16x9_final.mp4 → YouTube CTV 16:9
Box Automation Pipeline
When a valid file is detected in the IN folder:
- Filename validated (platform + aspect ratio must be detectable)
- Video downloaded from Box to server temp directory
- FFmpeg conversion with platform-specific codec settings
- JSON report generated with full before/after metadata
- Optimised video uploaded to
OUT_SUCCESS - JSON report uploaded to
OUT_SUCCESSalongside the video - Original file deleted from
IN - Conversion logged to daily audit log (
backend/logs/conversions/) - Temp files cleaned up
On failure or invalid filename, an error report JSON is placed in OUT_FAILED.
Box Folder Structure
VIDEO_OPTIMIZER/ (ID: 362124323515)
├── IN/ (ID: 362125331342) ← drop raw videos here
├── OUT_SUCCESS/ (ID: 362127206346) ← optimised video + report.json
└── OUT_FAILED/ (ID: 362119054851) ← error_report.json
Success Report Format (*_report.json)
{
"status": "success",
"timestamp": "2026-02-20T14:32:15",
"processing_time_seconds": 48.3,
"original_file": {
"filename": "campaign_tiktok_9x16.mp4",
"size_mb": 47.6,
"codec": "h264",
"resolution": "1920x1080",
"bitrate": "8000k",
"duration_seconds": 30.5
},
"optimised_file": {
"filename": "campaign_tiktok_9x16_optimized.mp4",
"size_mb": 12.1,
"size_reduction_percent": 74.6,
"savings_mb": 35.5
},
"conversion_details": {
"platform": "tiktok",
"aspect_ratio": "9:16",
"resolution": "1080x1920",
"codec": "libx265",
"bitrate": "1000k"
}
}
Web UI Usage
1. Upload
Drag and drop or browse for a video (MP4, MOV, AVI, MKV, WEBM, FLV, WMV, M4V — max 500MB).
2. Auto-detection
Platform and aspect ratio are detected from the filename. Override via dropdowns if needed.
3. Convert
Click Convert Video. FFmpeg processes with platform-optimised settings.
4. Compare & Download
- Side-by-side playback of original vs optimised
- Synchronized and independent playback controls
- Mute controls for each player
- Full codec/bitrate/resolution specs for both videos
- File size reduction percentage
- Download either version
Aspect Ratio Warnings
- Yellow (config page): selected ratio differs from original
- Red (comparison page): conversion changed the aspect ratio
API Reference
Main Backend (port 5000)
| Endpoint | Method | Description |
|---|---|---|
/api/health |
GET | FFmpeg status check |
/api/config |
GET | Azure AD config for frontend SSO |
/api/platforms |
GET | All platform specifications |
/api/detect |
POST | Detect platform/ratio from filename |
/api/upload |
POST | Upload video file |
/api/convert |
POST | Convert to target platform/format |
/api/stream/<type>/<id> |
GET | Stream video for browser playback |
/api/download/<type>/<id> |
GET | Download original or optimised |
/api/cleanup/<id> |
DELETE | Delete files after session |
Admin (port 5000)
| Endpoint | Method | Description |
|---|---|---|
/api/admin/platforms |
POST/PUT/DELETE | Manage platform specs |
/api/admin/export |
GET | Export all specs as JSON |
/api/admin/import |
POST | Import specs from JSON |
/api/admin/reset-factory |
POST | Restore factory defaults |
/api/admin/naming-conventions |
GET/POST | Manage detection patterns |
Box Automation (port 5000 — same as main API)
| Endpoint | Method | Description |
|---|---|---|
/webhooks/box |
POST | Receive Box webhook events |
/api/box/trigger |
POST | Manually trigger processing {file_id, filename} |
/api/box/health |
GET | Box initialisation status + folder config |
Project Structure
loreal-video-optimizer/
├── backend/
│ ├── app.py # Flask API + Box automation (single process, port 5000)
│ ├── video_processor.py # FFmpeg conversion engine
│ ├── platform_specs.py # 21 platform configurations + detection
│ ├── conversion_logger.py # Daily JSON audit logging
│ ├── file_cleanup.py # File retention management
│ ├── box_client.py # Box SDK wrapper (JWT auth, upload/download)
│ ├── box_processor.py # BoxProcessor + BoxPoller classes (imported by app.py)
│ ├── box_setup.py # Setup diagnostic script
│ ├── test_box_processor.py # Box automation test suite
│ ├── requirements.txt
│ ├── uploads/ # Temp upload storage (auto-cleaned)
│ ├── outputs/ # Temp converted output (auto-cleaned)
│ └── logs/conversions/ # Daily conversion audit logs
├── frontend/
│ ├── index.html # Main UI (requires Microsoft SSO)
│ ├── admin.html # Admin panel (requires Microsoft SSO)
│ ├── app.js # Upload, convert, compare logic
│ ├── admin.js # Admin CRUD operations
│ ├── auth.js # MSAL Browser SSO wrapper
│ ├── config.js # API base URL (auto-detects env)
│ ├── style.css # Main styles
│ └── admin.css # Admin styles
├── deployment/
│ ├── deploy.sh # Production deployment automation
│ ├── video-optimizer-backend.service # Systemd: main backend
│ ├── box-processor.service # Systemd: Box automation service
│ └── apache-complete.conf # Apache vhost config
├── oliver_box_config.json # Box JWT credentials (gitignored)
├── .env # Environment variables (gitignored)
├── .env.example # Environment variable template
├── start.sh # Dev startup script
└── CLAUDE.md # AI assistant project instructions
Codec Settings Reference
H264 (Meta, Pinterest, Snapchat, Amazon):
preset=medium,crf=23,profile=main,pix_fmt=yuv420p
H265 (TikTok):
preset=medium,crf=28,pix_fmt=yuv420p
VP9 (YouTube, YouTube CTV):
deadline=good,cpu-used=2,row-mt=1
Audio:
- AAC 128 kbps (mobile platforms)
- AAC/Opus 192 kbps (CTV platforms)
Troubleshooting
Box automation not working
# Run diagnostic script
python backend/box_setup.py
# Check Box status via API
curl http://localhost:5000/api/box/health
# Check BOX_VIDEO_OPTIMIZER_FOLDER_ID is set in .env
# Verify oliver_box_config.json exists in project root
Port already in use
lsof -ti:5000 | xargs kill # Kills both web API and Box automation (same process)
FFmpeg not found
ffmpeg -version
brew install ffmpeg # macOS
Box file not being processed
- Check filename includes both platform and aspect ratio pattern
- Verify the service is running (
/healthendpoint) - Check
OUT_FAILEDfolder for error report - In polling mode, check logs for
[POLLER]output
SSO login fails
- Verify
AZURE_CLIENT_IDandAZURE_TENANT_IDin.env - Confirm redirect URIs match in Azure AD app registration
- Backend must be running for
/api/configto be accessible
Production Deployment
Services are managed via systemd on the production server:
# Main backend
sudo systemctl start video-optimizer-backend
sudo systemctl enable video-optimizer-backend
# Box automation service
sudo systemctl start box-processor
sudo systemctl enable box-processor
# View logs
sudo journalctl -u video-optimizer-backend -f
sudo journalctl -u box-processor -f
See deployment/ folder for full deployment scripts and Apache configuration.
Design
- Colors: Black (
#000000) background + Yellow (#FFC407) accents - Font: Montserrat (Google Fonts)
- Theme: Dark UI
Version
Current Version: 2.0 Last Updated: February 2026
Phase 1 — Web UI:
- Microsoft SSO authentication
- 21 platform configurations across 8 platforms
- Automatic filename-based platform/aspect ratio detection
- Side-by-side video comparison with synchronized playback
- Admin panel with full CRUD for platforms and naming conventions
- Conversion audit logging
Phase 2 — Box.com Automation:
- Automated pipeline triggered by Box webhook or polling
- JWT service account authentication (
oliver_box_config.json) - 8-step processing pipeline with full error handling
- Original file deleted from
INafter successful processing
Phase 3 — Automated Reporting:
- JSON report generated per conversion with full metadata
- Original video metadata (codec, resolution, bitrate) captured
- Reports delivered to
OUT_SUCCESSalongside optimised video - Error reports delivered to
OUT_FAILEDfor invalid/failed files