| --- |
| title: Ffmpeg |
| emoji: 👀 |
| colorFrom: green |
| colorTo: yellow |
| sdk: docker |
| pinned: false |
| license: mit |
| --- |
| |
| # Basyx FFmpeg Automation Hub |
|
|
| FastAPI + Gradio app for common FFmpeg media tasks. It can run directly in |
| Docker/Hugging Face Spaces and exposes both a browser UI and HTTP API. |
|
|
| ## Features |
|
|
| - Task-specific Gradio controls for video, audio, subtitle, image, and social presets. |
| - File and URL inputs, including `yt-dlp` downloads. |
| - Input validation with `python-magic`, upload size limits, and temp-file cleanup. |
| - Synchronous execution at `/execute/{task_id}`. |
| - Background jobs at `/jobs/{task_id}` with `/status/{job_id}` and `/download/{job_id}`. |
| - Recent output history at `/history` with per-item download URLs. |
| - Configurable FFmpeg options: CRF, preset, resolution, audio bitrate, trim times, |
| aspect ratio, GIF settings, watermark settings, speed, frame rate, and text styling. |
| - Persistent SQLite job/history state. |
| - FFmpeg execution timeout, production readiness checks, and URL download limits. |
| - Faceless short-video automation for quote cards, story slides, image narration, |
| b-roll narration, and vertical montage generation. |
| - TikTok/Reels mini-series packaging with numbered episodes, recap cards, and |
| ZIP exports containing publish-order manifests. |
|
|
| ## Tasks |
|
|
| Call `GET /tasks` to list the current task registry with file requirements. |
|
|
| Current tasks: |
|
|
| - `normalize` |
| - `extract_audio` |
| - `resize_916` |
| - `add_subtitles` |
| - `burn_lyrics` |
| - `text_overlay` |
| - `merge_music` |
| - `thumbnail` |
| - `watermark` |
| - `compress` |
| - `batch_compress` |
| - `make_gif` |
| - `tiktok_lyrics` |
| - `tiktok_pro_reframer` |
| - `reels_blur_fit` |
| - `reels_safe_caption` |
| - `reels_hook_title` |
| - `reels_progress_bar` |
| - `reels_loop` |
| - `reels_subtitle_safe` |
| - `reels_reaction_stack` |
| - `reels_audio_duck` |
| - `faceless_quote_card` |
| - `faceless_story_pages` |
| - `faceless_image_narration` |
| - `faceless_video_narration` |
| - `faceless_broll_montage` |
| - `series_split_pack` |
| - `series_episode_badge` |
| - `series_batch_pack` |
| - `series_recap_card` |
| - `concat` |
| - `slideshow` |
| - `trim` |
| - `crop_aspect` |
| - `waveform` |
| - `extract_frames` |
| - `add_intro_outro` |
| - `speed` |
| - `remove_audio` |
| - `replace_audio` |
|
|
| ## API Examples |
|
|
| Run a task immediately and download the returned file: |
|
|
| ```bash |
| curl -X POST \ |
| -F "files=@input.mp4" \ |
| -F "resolution=720p" \ |
| -F "crf=28" \ |
| http://localhost:7860/execute/compress \ |
| --output compressed.mp4 |
| ``` |
|
|
| Run a background job: |
|
|
| ```bash |
| curl -X POST \ |
| -F "files=@input.mp4" \ |
| -F "text=Launch caption" \ |
| http://localhost:7860/jobs/text_overlay |
| ``` |
|
|
| Check the job: |
|
|
| ```bash |
| curl http://localhost:7860/status/<job_id> |
| ``` |
|
|
| Download a completed background job: |
|
|
| ```bash |
| curl http://localhost:7860/download/<job_id> --output result.mp4 |
| ``` |
|
|
| Use a URL input: |
|
|
| ```bash |
| curl -X POST \ |
| -F "url=https://example.com/video.mp4" \ |
| http://localhost:7860/execute/thumbnail \ |
| --output thumbnail.jpg |
| ``` |
|
|
| ## n8n Inputs |
|
|
| Use `POST /n8n/execute/{task_id}` for immediate file output or |
| `POST /n8n/jobs/{task_id}` for background jobs. These endpoints accept common |
| n8n HTTP Request node payload styles: |
|
|
| - multipart form-data with any binary field name |
| - form-data URL fields: `url`, `urls`, `file_url`, `source_url`, `download_url` |
| - JSON base64 files in `files`, `binary`, or `data` |
| - JSON URL lists |
| - raw binary body for single-file tasks, with optional `X-Filename` header |
|
|
| Multipart binary from n8n: |
|
|
| ```bash |
| curl -X POST \ |
| -F "myBinary=@input.mp4" \ |
| -F "text=Episode 1" \ |
| http://localhost:7860/n8n/execute/series_episode_badge \ |
| --output episode.mp4 |
| ``` |
|
|
| JSON base64: |
|
|
| ```json |
| { |
| "text": "Episode title", |
| "files": [ |
| { |
| "fileName": "input.mp4", |
| "mimeType": "video/mp4", |
| "data": "<base64>" |
| } |
| ] |
| } |
| ``` |
|
|
| JSON URLs: |
|
|
| ```json |
| { |
| "urls": ["https://example.com/input.mp4"], |
| "text": "Mini series title", |
| "duration": "60" |
| } |
| ``` |
|
|
| Raw binary: |
|
|
| ```bash |
| curl -X POST \ |
| -H "Content-Type: application/octet-stream" \ |
| -H "X-Filename: input.mp4" \ |
| --data-binary @input.mp4 \ |
| http://localhost:7860/n8n/execute/reels_blur_fit \ |
| --output output.mp4 |
| ``` |
|
|
| ## Configuration |
|
|
| Environment variables: |
|
|
| - `TEMP_DIR`: working directory for uploads and outputs. Default: `temp`. |
| - `STATE_DB_PATH`: SQLite path for job/history state. Default: `TEMP_DIR/state.sqlite3`. |
| - `FONT_PATH`: font used by FFmpeg `drawtext`. Default: |
| `/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf`. |
| - `MAX_UPLOAD_MB`: max upload size per file. Default: `500`. |
| - `MAX_URL_DOWNLOAD_MB`: max URL download size. Default: same as `MAX_UPLOAD_MB`. |
| - `ALLOW_URL_INPUTS`: enable/disable URL downloads. Default: `true`. |
| - `FFMPEG_TIMEOUT_SECONDS`: max runtime for an FFmpeg command. Default: `1800`. |
| - `FILE_TTL_SECONDS`: temp file lifetime. Default: `3600`. |
| - `HISTORY_LIMIT`: number of recent output records to keep. Default: `50`. |
|
|
| ## Production Checks |
|
|
| - `GET /healthz`: process health and dependency check details. |
| - `GET /readyz`: returns `503` if required runtime dependencies are missing. |
|
|
| Required runtime dependencies: |
|
|
| - `ffmpeg` |
| - `ffprobe` |
| - writable `TEMP_DIR` |
| - writable SQLite state database |
| - readable `FONT_PATH` |
|
|
| ## Local Run |
|
|
| ```bash |
| pip install -r requirements.txt |
| python app.py |
| ``` |
|
|
| The app listens on `http://localhost:7860`. |
|
|