# Chopify full documentation Source: https://github.com/mehbul/chopify --- Source: https://github.com/mehbul/chopify/blob/main/README.md

Chopify — local video clipping and captions

# Chopify — free, local AI video clipper **Turn long videos into captioned clips for YouTube Shorts, Instagram Reels, and TikTok, on your own computer.** Chopify is an open-source Python app with a local browser dashboard and a CLI. Import a recording or a video URL, find moments, correct captions, review drafts, and export MP4 clips with SRT subtitles. No subscription, API key, cloud upload, or watermark. [Get started](#install-and-open-the-dashboard) · [Workflow guide](docs/WORKFLOWS.md) · [Agent/API reference](docs/AGENT-API.md) · [Troubleshooting](docs/TROUBLESHOOTING.md) · [Changelog](CHANGELOG.md) ## What can Chopify do? | Feature | How it helps | | --- | --- | | **Local browser dashboard** | Queue videos, watch job progress, review candidates, edit words and timestamps, and download exports. | | **Local video input** | Use MP4, MOV, MKV, WebM, M4V, or AVI recordings directly, or download a yt-dlp-supported URL. | | **Resumable projects** | Reuse matching downloads, transcripts, and completed renders. Retry a failed job without discarding successful clips. | | **Batch queue** | Add one source per line in Studio or use a JSON list in the CLI. Each video has an isolated project folder. | | **Caption editor and SRT export** | Fix transcription mistakes before rendering. Burnt captions and SRT files share the same timeline, including tightened cuts. | | **Reusable brand presets** | Save caption font, text/highlight colors, size, position, and an optional logo in JSON. | | **Semantic clip search** | Search for a concept such as “handling rejection” using optional local Ollama embeddings, even if those exact words are absent. | | **Podcast split-screen** | Stack two adjustable horizontal crops in a vertical clip for fixed-camera two-person footage. | | **Clip selection and review** | Heuristic scoring, optional local LLM scoring, keyword search, numbered selection, editable start/end times, and fast draft previews. | | **Social video exports** | 16:9, 9:16, or 1:1; H.264 video, AAC when source audio exists; SRT, poster, title/description/hashtag JSON. | | **Face tracking and cleanup** | Single-frame vertical crops follow a detected face; optional filler/silence removal and audio loudness normalization. | Clip selection favors sentence endings with punctuation. Scores are editing suggestions, **not predictions or guarantees of virality**. Review the results before publishing. ## Install and open the dashboard Windows is the primary target. Use **Python 3.11+** and **ffmpeg/ffprobe with libx264, AAC, and subtitle rendering support**. Linux CI also checks the Python and ffmpeg workflows. ```powershell git clone https://github.com/mehbul/chopify.git cd chopify winget install Gyan.FFmpeg python -m venv .venv .venv\Scripts\python -m pip install -r requirements.txt .venv\Scripts\python dashboard.py ``` Studio opens at **http://127.0.0.1:8765**. Paste a local video path or URL, then click **Find clips**. Add multiple sources on separate lines to queue them. After processing, review candidates, edit captions, save your settings, and export selected clips. Stop the server with Ctrl+C. To change the port: ```powershell .venv\Scripts\python dashboard.py --port 8766 --projects-dir projects ``` The dashboard has no extra web framework or JavaScript installation step. It binds to loopback and is intended for one trusted user on the same computer, not as a public server. YouTube downloads may also require Deno: `winget install DenoLand.Deno`. A preflight check reports missing Python packages and encoders. Restart your terminal after installing tools so PATH changes take effect. ## CLI: long video to short clips After installing dependencies, run with the virtual environment's Python (or activate it and use `python`): ```powershell # Local recording or online video python chopify.py "C:\Videos\podcast.mp4" --aspect 9:16 python chopify.py "https://youtube.com/watch?v=VIDEO_ID" # Prepare once, inspect candidates, then render selected clips python chopify.py "C:\Videos\podcast.mp4" --workdir work --action prepare python chopify.py --project work --action render --clips 1,3 --preview python chopify.py --project work --action render --clips 1,3 # Search the transcript using keywords or meaning python chopify.py "recording.mp4" --search "pricing" python chopify.py "recording.mp4" --semantic "advice about handling rejection" # Brand and podcast framing python chopify.py "podcast.mp4" --brand examples/brand.json --tighten --loudnorm python chopify.py "podcast.mp4" --aspect 9:16 --layout split --centers 0.25 0.75 # Process a queue of videos into separate projects python chopify.py --batch examples/batch.json --projects-dir projects ``` CLI single-video runs save state under `work/` and copy the latest exports to `clips/`. Set `--out DIR` or `CHOPIFY_OUT` to change the export folder. Dashboard and batch jobs save outputs inside `projects//clips/` and drafts inside `previews/`. **Replace the batch sample's sources with your own paths or URLs.** Successful stages resume by default. Use `--no-resume` to reprocess, or `--project projects/ --action render` to retry a batch project. Use `--action render` after manual caption/clip edits; `--action prepare` recalculates candidates when selection inputs change. A failed batch job does not prevent later videos from processing; the CLI exits nonzero if any job fails. ## Semantic search and optional local AI Default clip selection needs no LLM. For semantic search, install [Ollama](https://ollama.com), run it locally, and download an embedding model: ```powershell ollama pull nomic-embed-text python chopify.py "podcast.mp4" --semantic "turning failure into a lesson" ``` The embedding model runs on `127.0.0.1:11434`. Sentence embeddings are cached per transcript/model. Use `--embed-model MODEL` for another compatible local embedding model. Semantic search ranks matches by similarity; it does not guarantee that the topic is present. Missing models produce setup instructions rather than a cloud fallback. For optional LLM-based selection, download a local chat model separately and pass `--llm MODEL`. If it is unavailable, selection falls back to the heuristic. The GPU backend for faster-whisper is CUDA/NVIDIA; CPU transcription works without an NVIDIA GPU. `--device cpu|cuda|auto` controls transcription. ## Is Chopify an alternative to Opus Clip? Chopify is a free, MIT-licensed option for creators who want local clipping, captions, reframing, and review. It requires local installation and uses your computer's resources. It does not include cloud collaboration, automatic social publishing, or hosted processing. Compare the workflows and privacy requirements that matter to you; feature sets and pricing of other products change. ## Frequently asked questions ### Does video leave my computer? Chopify does not upload footage or transcripts to a hosted processing service. Video URLs require downloads. Whisper models download on first use; face tracking may download a YuNet model; optional Ollama models need an initial download. **Local-file processing can run offline once the required dependencies and models are available.** ### Can I correct captions and export subtitles? Yes. Open a prepared project in Studio, choose **Transcript**, edit word text or start/end times, and **Save captions**. Export again to apply the edits. Each draft and finished clip includes an SRT file relative to that clip's timeline. See the [caption workflow](docs/WORKFLOWS.md#edit-captions-and-export-srt). ### Does split-screen identify who is speaking? No. Split-screen uses two adjustable horizontal crop centers for a fixed camera and stacks the crops vertically. It is not speaker diarization or automatic two-face tracking. Use a draft to tune the positions before final export. ### What happens if processing fails or Studio closes? Completed stages and clips remain on disk. Restart Studio and queue the saved project again, or rerun the CLI with `--project PATH`. Interrupted jobs are marked for explicit retry. Changing the source, transcription model, selection settings, words, brand, or render options invalidates the relevant cached work. Do not run two writers against the same project directory at once. ### Can it edit translated or multilingual subtitles? Whisper detects spoken language, and captions retain its transcription. The heuristic cue lists are English-oriented; punctuation and timing can vary by language. This update does not add translation, bilingual captions, or dubbing. Caption fonts must contain the glyphs you need. ## Developer and agent documentation - [Workflow guide and project layout](docs/WORKFLOWS.md) - [CLI, Python interfaces, and local HTTP API](docs/AGENT-API.md) - [Setup and troubleshooting](docs/TROUBLESHOOTING.md) - [Machine-readable product facts](docs/product.json) and [llms.txt](llms.txt) - [Search discovery and documentation deployment](docs/DISCOVERABILITY.md) ```powershell python -m pip install -r requirements-dev.txt python -m pytest tests/ -q python -m compileall -q chopify.py dashboard.py projects.py captions.py branding.py semantic.py download_and_transcribe.py score_clips.py render_clips.py tighten.py ``` Tests include real ffmpeg renders when ffmpeg is installed; speech models and Ollama are not required for the automated suite. Keep videos, transcripts, generated clips, and local brand presets out of Git. Use footage you have permission to process. Maintained by [mehbul](https://github.com/mehbul). [MIT license](LICENSE). Last documentation update: **2026-10-23**. --- Source: https://github.com/mehbul/chopify/blob/main/docs/WORKFLOWS.md # Chopify workflow guide Chopify processes local recordings or supported video URLs into short, captioned clips. All transcription, scoring, editing, and rendering run on your computer. ## Review a video in Studio 1. Run `python dashboard.py` and open the printed `127.0.0.1` address. 2. Enter an absolute local video path or URL. Add one source per line for a batch. 3. Choose aspect ratio, speech model, and caption style. Click **Find clips**. 4. Watch stage progress in the project list and open **Job log** for details. 5. Click **Watch** beside a candidate to play that range in the source video. 6. Change titles, start/end times, and selected checkboxes. Save clip edits. 7. Save settings, then **Preview selected** to render small drafts. 8. **Export selected** creates MP4 + SRT + PNG poster + metadata JSON. Jobs execute serially to avoid simultaneous model/encoder contention. You can add more videos while one is processing. Each project appears in the left rail. Current jobs cannot be edited. After a server restart, interrupted jobs can be queued again explicitly with **Find / refresh clips**, **Preview**, or **Export**. ## Local input and batch files Supported local extensions: `.mp4`, `.mov`, `.mkv`, `.webm`, `.m4v`, `.avi`. Local footage is read in place, so keep the source available until you finish. The browser accepts paths, not file uploads; the path must exist on the server's computer. Use absolute paths in Studio and quote paths containing spaces in CLI. For the CLI, create a UTF-8 JSON file (Windows paths need doubled backslashes): ```json ["C:\\Videos\\episode-1.mp4", "C:\\Videos\\episode-2.mov"] ``` ```powershell python chopify.py --batch batch.json --projects-dir projects --aspect 9:16 ``` Each invocation creates separate projects, even for repeated sources. To retry one without starting a new project, use its saved directory: ```powershell python chopify.py --project projects/PROJECT_ID --action all ``` Relative paths resolve from the current working directory. Batch output stays in each project folder; `--out` applies to single-project runs. ## Resume and stage invalidation Each `project.json` records the source, options, status, stage, successful stage signatures, and completed render paths. Writes use atomic replacement. Resume is enabled by default; `--no-resume` redoes the chosen action. | Change | Recomputed work | | --- | --- | | Source path/URL, local file size or modification timestamp | Source resolution, transcription, candidates, renders | | Speech model or compute device | Transcription and downstream stages | | Caption words/times | Candidate selection on next prepare; rendered captions on export | | Keyword/semantic query, embedding model, scoring settings | Candidates and renders | | Style, brand, logo, layout, tightening, normalization | Renders | | Selected clip subset | Only requested clips are rendered | Cache identity uses path, size, and nanosecond modification time, not a full hash of large videos. Replacing files without changing those values is not detected. Rendered variants have distinct names so styles do not overwrite earlier cached variants. Missing MP4/sidecar files cause that clip to rerender. A failure retains successful clips and exits nonzero. Use only one writer per project directory. Use `--action prepare` to discover candidates, `--action render` to preserve your manual clip edits, and `--action all` for the whole pipeline. If a selection input has changed, preparing again can replace manually edited candidate ranges. ## Edit captions and export SRT Studio's **Transcript** tab edits individual word text and start/end times in seconds. Words must be nonempty, have finite times within the video's duration, and remain chronological without overlaps. The table is paginated in 100-word pages. Click a word's number to seek the source video to it. Save captions before export. Saving rebuilds sentence text from edited words. Candidate ranges stay available until you request new selection. Every draft and final clip writes `.srt`. Subtitle times begin at zero for that exported clip, are clamped to its bounds, and group up to eight words per cue. `--tighten` remaps both burnt captions and SRT to the shortened timeline. Changing caption text does not change spoken audio. Translation and dubbing are not included. ## Create a reusable brand preset Use Studio's **Create a brand preset**, then select it and **Save settings**. Presets live under `projects/brands/` and are excluded from Git. CLI presets are ordinary JSON files; see [examples/brand.json](../examples/brand.json). | Field | Accepted value | | --- | --- | | `font` | Installed font family name, up to 80 characters; letters, digits, spaces, `_`, `.`, `-` | | `color`, `highlight` | `#RRGGBB` colors | | `font_size` | Integer 12–200, measured at 1080px output height | | `margin` | Integer 0–700, bottom caption margin at 1080px height | | `logo` | Existing PNG/JPEG/WebP path, or `null`; relative paths resolve beside the JSON | | `logo_width` | Integer 20–600, measured at 1920px output width | Sizes scale to the export resolution, including drafts. The logo is placed at the upper right. Brand caption settings override the selected caption style. Colors, font names, and ranges are validated before rendering. ```powershell python chopify.py recording.mp4 --brand examples/brand.json --aspect 9:16 ``` ## Find clips by meaning Install and start Ollama, then download an embedding model: ```powershell ollama pull nomic-embed-text python chopify.py recording.mp4 --semantic "advice about handling rejection" ``` Choose **Search by meaning** in Studio, enter a topic, save settings, and refresh candidates. Sentences are embedded in batches through local Ollama's `/api/embed`, ranked by cosine similarity, and grown into sentence-ended clip windows. `embeddings.json` caches document vectors by model and sentence text; query embeddings are recalculated for each search. Similarity is relative to the input video; a ranked result is not evidence that the topic is discussed. Missing models fail with setup instructions, without a cloud fallback or silent keyword substitution. Keyword and semantic search are mutually exclusive. A chat model for `--llm` is separate from embedding search. ## Frame a two-person podcast ```powershell python chopify.py podcast.mp4 --aspect 9:16 --layout split --centers 0.25 0.75 ``` Centers are horizontal fractions of the source width. The first controls the top panel, the second the bottom panel. Both range from 0 to 1; crops are clamped inside the source frame. For a host at 30% and guest at 70%, try `--centers 0.30 0.70`, preview, then adjust. Each panel is vertically centered. Split-screen is for fixed-camera footage. It does not identify speakers, follow two moving faces, or change panels at speaker turns. Single-frame mode retains automatic face tracking for horizontal crops. ## Files on disk ```text projects/ brands/channel.json / project.json # source, settings, stage status, cache records transcript.json # word timestamps, sentences, source path segments.json # editable clip candidates embeddings.json # optional semantic-search cache job.log # dashboard job output source.mp4 # downloaded footage; local files stay in place previews/*.mp4 # drafts, each with .srt clips/*.mp4 # final exports, .srt, .png, .meta.json ``` The default single-video CLI directory is `work/`; latest exports are also copied to `clips/` (or `--out`). Filenames include a clip number and render signature, followed by the title. Studio shows saved variants for comparison. Automatic social uploads and scheduling are not included. --- Source: https://github.com/mehbul/chopify/blob/main/docs/AGENT-API.md # Chopify interfaces for developers and local agents Chopify is a local Python video clipper. Use the CLI for scripts; the dashboard HTTP API is an authenticated loopback interface. No hosted API is provided. Documentation updated 2026-10-23; implementation is the source of truth. ## CLI contract ```powershell python chopify.py SOURCE --action prepare --workdir work python chopify.py --project work --action render --clips 1,3 --preview python chopify.py --project work --action render --clips 1,3 python chopify.py --batch batch.json --projects-dir projects python chopify.py --help ``` `SOURCE` is a local video path or yt-dlp-supported HTTP(S) URL. Actions: `prepare` resolves/downloads, transcribes and scores; `render` uses saved transcript and candidates; `all` does both. Resume is enabled unless `--no-resume`. Existing projects keep settings unless explicit CLI flags change them. Use absolute project paths when working from another directory. Single-project output defaults to `clips/` or `CHOPIFY_OUT`; `--out` overrides it. Batch outputs stay inside isolated project folders. Batch JSON is a nonempty array of source strings. Exit 0 means the requested action completed; errors exit nonzero, including partial render/batch failures. Standard output is a human log, not a JSON-only protocol. Read project files for structured state. ## Project data `project.json` contains `id`, `source`, `options`, `status`, `stage`, `created`, `updated`, `cache`, `renders`, `error`, and optionally `video`, `source_identity`, and `last_outputs`. Timestamps are Unix seconds. Statuses include `new`, `queued`, `running`, `review`, `complete`, `failed`, and `interrupted`. `transcript.json` contains `video` (absolute path), `duration` (seconds), `language`, `model`, `segments`, and `words`: ```json {"word": "Hello", "start": 1.0, "end": 1.4} ``` `segments.json` is an array with `start`, `end`, `hook`, and `overall`; semantic selection may include `similarity`. Times are source-relative seconds. `--clips` uses one-based row numbers. Scores are suggestions, not audience performance data. ## Python entry points ```python from pathlib import Path from projects import create_project, project_path, run_project root = Path("projects").resolve() state = create_project(root, "C:/Videos/podcast.mp4", {"aspect": "9:16"}) path = project_path(root, state["id"]) run_project(path, action="prepare") run_project(path, action="render", pick="1,3", preview=True) ``` Other interfaces: `projects.run_batch(sources, root, options, **kwargs)`, `projects.edit_transcript(path, words)`, `captions.update_transcript(data, words)`, `branding.load_brand(path)`, and `semantic.search(sentences, query, model, cache_dir)`. `projects.validate_options` validates options. These repository-level interfaces are not a separately versioned SDK. ## Local HTTP API Start `python dashboard.py --no-browser`. Use the printed origin exactly (`http://127.0.0.1:8765` by default). No CORS access or remote binding is supported. Requests with another Host are rejected. `GET /api/config` returns `token`, `defaults`, and saved brand names. The random token is scoped to the server process. All POST requests require `X-Chopify-Token: TOKEN` and JSON bodies. Browser POST requests also require a matching Origin. Never publish a running session's token. | Method | Path | Body / result | | --- | --- | --- | | GET | `/api/projects` | `{ "projects": [project, ...] }` | | POST | `/api/projects` | `{ "source": "C:/video.mp4", "options": {} }`; creates without starting | | POST | `/api/batch` | `{ "sources": ["C:/video.mp4"], "options": {} }`; creates and queues preparation, up to 100 sources | | GET | `/api/projects/ID` | `{ "project": ..., "busy": false, "candidates": [], "transcript": ..., "files": [], "log": "..." }` | | POST | `/api/projects/ID/settings` | Partial settings, e.g. `{ "aspect": "9:16", "layout": "split" }`; `brand_name` selects a preset | | POST | `/api/projects/ID/transcript` | `{ "words": [{ "word": "Hello", "start": 0, "end": 0.5 }] }` | | POST | `/api/projects/ID/candidates` | `{ "candidates": [{ "start": 0, "end": 20, "hook": "Title", "overall": 7 }] }` | | POST | `/api/projects/ID/run` | `{ "action": "render", "preview": true, "pick": "1,3" }`; queues job | | POST | `/api/brands` | `{ "name": "channel", "preset": { "font": "Arial", "highlight": "#FFFF00" } }` | | GET | `/media/ID/source` | Source video for a prepared project; byte-range support | | GET | `/media/ID/clips/FILE` | Project exports; `previews` also supported | Mutations return `{ "ok": true }` unless a created object is documented. Errors use `{ "error": "message" }` with 400, 403, or 404 status. Busy projects reject edits and duplicate runs. Poll detail for completion; enqueueing is not success. On restart, unfinished jobs become `interrupted` and need explicit retry. Project files expose local paths and transcripts; keep them private. Studio is for one trusted local user, not a multi-tenant service. Do not expose it through a proxy or tunnel. --- Source: https://github.com/mehbul/chopify/blob/main/docs/TROUBLESHOOTING.md # Chopify setup and troubleshooting ## Preflight fails Check `ffmpeg -version` and `ffprobe -version`. The ffmpeg build needs libx264, AAC, and the `subtitles` filter (libass). Install a full build, reopen the terminal, and use the same interpreter to install dependencies and run Chopify: ```powershell winget install Gyan.FFmpeg .venv\Scripts\python -m pip install -r requirements.txt ``` `--no-preflight` bypasses checks; it does not install missing dependencies. For Studio, open the Job log for subprocess errors. ## Studio says connection lost Keep `python dashboard.py` running. Use its exact `http://127.0.0.1:PORT` address; `localhost` and network IPs are intentionally rejected. If the port is busy, use `--port 8766`. Reload after restarting because the session token changes. ## Local file is not found Use an absolute path to a supported video on this computer. Relative CLI paths resolve from the shell's current directory. Keep recordings in place. If a source was replaced, prepare again before rendering so timestamps stay valid. ## Model or YouTube download fails The first Whisper run downloads a model. Establish internet access and retry; completed downloads/transcripts are reused. URL input also needs internet. Supported sites change independently of Chopify. Update with `python -m pip install --upgrade yt-dlp`. YouTube may require Deno: `winget install DenoLand.Deno`. Source access depends on the site's rules; Chopify does not bypass restrictions. ## Semantic search cannot connect Start Ollama and run `ollama pull nomic-embed-text`. Check `ollama list`; a chat model alone is not an embedding model. Requests use `http://127.0.0.1:11434/api/embed`. Custom models must support that endpoint. Changing model/text refreshes document vectors. There is no cloud fallback. Use `--search` for keyword matching without Ollama. ## Transcription is slow or CUDA fails Try `--whisper-model small` or `tiny` and `--device cpu`. faster-whisper GPU acceleration requires compatible NVIDIA/CUDA; AMD systems can use CPU. Studio executes jobs serially to avoid loading multiple speech models at once. ## Captions have missing glyphs or wrong timing Choose an installed font with the language's glyphs. Review word times in Studio and save before exporting. Times must be finite, ordered, and within the source. SRT uses clip-relative time; tightening remaps both subtitle forms. Recognition and punctuation are imperfect, so review final clips. ## Split-screen crops the wrong person Choose `9:16`, layout `split`, and tune centers from 0 to 1. First center: top; second: bottom. Preview first. This is fixed framing, not speaker recognition. ## A render fails midway Check disk space, ffmpeg, source availability, and logo paths. Retry with `--project PATH --action render`. Matching completed clips are reused. Preview and final exports have separate caches. Batch jobs continue after failures and report a failure exit code. Avoid two writers using one project directory.