Skip to content

refactor(pipeline): replace docker-compose with native macOS execution to enable Metal and VideoToolbox #83

Description

@natashaannn

User story

As a developer running the video pipeline on an M3 Mac, I want all pipeline scripts to run natively on macOS rather than inside Docker, so that Metal, VideoToolbox, and Core ML are accessible and Phase 1 GPU acceleration (Issues #22, #23) actually takes effect.

Background

Phase 1 of the production refactor plan adds hardware detection (scripts/config/hardware.ts) and typed FFmpeg commands that branch on M3/NVIDIA/CPU. But this is currently meaningless: all pipeline scripts run inside Docker via docker-compose, and Docker on macOS runs in a Linux VM with no access to Metal, VideoToolbox, or Core ML. The hardware detection will always report CPU-only inside a Docker container on macOS.

The existing Dockerfile was built for the NVIDIA/Linux path and the public deployment. It should stay — but it should not be the default local runtime on macOS.

What runs in Docker today (pipeline services in docker-compose.yml):

  • wizard — main pipeline orchestrator
  • transcribe — whisper.cpp transcription
  • diarize — pyannote speaker diarization (CPU PyTorch in Docker, could use MPS natively)
  • setup-camera — face detection (CPU MediaPipe in Docker, could use Core ML natively)
  • color-match — colour correction
  • hook-qa — hook quality review
  • thumbnail — thumbnail generation (rembg background removal)
  • remotion — Remotion Studio preview

What already works natively: The Python scripts (run_diarize.py, align-transcript.js) already accept a --python flag pointing to a local binary. The wizard prompts for a Python path interactively. So native execution is partially supported — the missing piece is removing Docker as the assumed default and providing a native setup path.

Relationship to Phase 1: This issue is a prerequisite for Phase 1's hardware acceleration to work on M3. Issues #22 (hardware detection) and #23 (FFmpeg command builder) should be implemented against natively-running scripts, not Docker. The Phase 1 status check npm run video:optimize must contain h264_videotoolbox — that is only possible if the script runs outside Docker.

Acceptance criteria

Happy path

Given the native environment is set up on an M3 Mac (per the setup script/guide)
When a developer runs npm run video:wizard
Then the wizard runs natively on macOS (not via docker-compose) and the hardware profile detects appleSilicon

Given the native environment is set up
When the diarization step runs
Then PyTorch uses the MPS backend (not CPU) on M3 — observable via device: mps in diarize output logs

Given the native environment is set up
When npm run video:optimize runs an FFmpeg encode (after Phase 1 #23 is implemented)
Then the ffmpeg command contains h264_videotoolbox, not libx264

Given a developer on an NVIDIA Linux machine wants to use Docker
When they reference docker-compose.yml
Then the pipeline services still exist and work (Docker is kept as the NVIDIA/Linux path, not deleted)

Edge case

Given a developer has not set up the native Python environment
When they run a script that requires Python (diarize, align)
Then they receive a clear error message pointing to the setup guide, not a silent failure

Out of scope

Technical context

npm script cleanup

12 :docker`` variants exist in package.json. These should be:

  • Renamed to :linux`` to signal they are for the NVIDIA/Linux path, not the default
  • Or removed if the non-suffixed native script is sufficient for all environments

Affected scripts:

camera:color-match:docker   → :linux
camera:setup:docker         → :linux
carousel:pdf:docker         → :linux  (carousel is for public app, may be removed anyway)
carousel:wizard:docker      → :linux
shorts:camera-setup:docker  → :linux
shorts:wizard:docker        → :linux
review:hooks:docker         → :linux
thumbnail:candidates:extract:docker → :linux
thumbnail:cutouts:generate:docker   → :linux
thumbnail:frames:select:docker      → :linux
thumbnail:simple:docker             → :linux
thumbnail:wizard:docker             → :linux
video:wizard:docker         → :linux

Python environment

The Python scripts already accept --python <path> (see wizard.js:561, Diarizer.js:10, align-transcript.js:44). Native setup requires:

  • conda or venv with Python 3.11/3.12
  • PyTorch with MPS backend: pip install torch torchvision torchaudio (macOS wheels include MPS)
  • whisperx, pyannote-audio, faster-whisper (same as requirements.txt but without CUDA variants)
  • rembg, mediapipe (thumbnail + camera)
  • Set PYTHON_PATH=.venv/bin/python in .env.local so wizard reads it without interactive prompt

PYTHON_PATH in wizard

wizard.js:561 currently prompts interactively for the Python path. Add a fallback: read PYTHON_PATH from the environment before prompting. This makes CI and scripted runs non-interactive.

docker-compose.yml

Keep docker-compose.yml but document it as the NVIDIA/Linux path. Remove or rename pipeline services on the macOS-native path. Consider adding a comment header to docker-compose.yml:
# This file is for NVIDIA Linux deployment. Run scripts natively on macOS — see docs/NATIVE_SETUP.md

The app service (Next.js dev server) can stay or be removed from docker-compose — Next.js already runs fine natively via npm run dev.

requirements.txt

The current requirements.txt should split into:

  • requirements.txt — platform-agnostic (used for native macOS; PyTorch uses MPS automatically on Apple Silicon)
  • requirements.nvidia.txt — CUDA variants (torch+cu124, faster-whisper with CUDA) for the Docker/Linux path

Implementation details

  1. Create docs/NATIVE_SETUP.md — step-by-step guide: install conda/homebrew, create venv, install Python deps, set PYTHON_PATH in .env.local, verify MPS with a one-liner (python3 -c "import torch; print(torch.backends.mps.is_available())")

  2. Create scripts/setup-native-env.sh — automates the venv creation and dep install for macOS; reads requirements.txt (not the CUDA variant)

  3. In wizard.js: before the interactive Python path prompt, check process.env.PYTHON_PATH; use it if set; only prompt if unset

  4. In package.json: rename all :docker script variants to `:`linux

  5. In docker-compose.yml: add a header comment scoping it to NVIDIA/Linux; optionally remove pipeline services that are now run natively (keep the file for the Linux path)

  6. Split requirements.txtrequirements.txt + requirements.nvidia.txt; update Dockerfile to use requirements.nvidia.txt

  7. Add PYTHON_PATH to .env.example with a comment: # Path to the native Python binary — set to .venv/bin/python after running scripts/setup-native-env.sh

Additional test scenarios

  • python3 -c "import torch; print(torch.backends.mps.is_available())" returns True after running the setup script on M3
  • Diarization log output shows device: mps (or equivalent) when running natively on M3
  • npm run diarize (non-docker) completes successfully with the native venv Python
  • A developer on Linux running npm run diarize:linux via Docker still works

Hard constraints

  • Do not delete Dockerfile or docker-compose.yml — they are the NVIDIA/Linux deployment path
  • The setup script must be idempotent (running it twice does not break the environment)
  • PYTHON_PATH must never be hardcoded in any script — always read from env or CLI flag

Dependency issues

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions