diff --git a/.dockerignore b/.dockerignore index e762069..b5be57f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,3 +8,5 @@ data/ *.md .env .venv/ +venv/ +uv.lock diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8c9d9e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,57 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +FFmpeg Worker is a dockerized REST API service that executes arbitrary FFmpeg commands. Jobs are submitted via API, processed sequentially by a background worker, and clients poll for status/progress. + +## Commands + +```bash +# Run with Docker (recommended) +docker-compose up -d --build + +# Install dependencies locally (requires uv: https://docs.astral.sh/uv/) +uv sync + +# Run tests (excludes integration test) +uv run pytest tests/ -v --ignore=tests/test_integration.py + +# Run single test +uv run pytest tests/test_api.py::test_create_job -v + +# Run integration test (requires Docker running) +uv run pytest tests/test_integration.py -v -s + +# Run locally (requires ffmpeg installed) +uv run uvicorn app.main:app --reload +``` + +## Architecture + +**Request Flow:** +1. `POST /jobs` → creates Job, stores in JobStore, enqueues job_id in JobQueue +2. Background `worker_loop` dequeues job_id, runs FFmpeg, updates job status/progress +3. `GET /jobs/{id}` → polls job status and real-time progress + +**Key Components:** +- `app/main.py` - FastAPI app with lifespan manager that starts/stops worker +- `app/queue.py` - JobQueue (asyncio.Queue wrapper), run_ffmpeg(), worker_loop() +- `app/ffmpeg.py` - Command parsing, path resolution, progress parsing, ffprobe duration +- `app/models.py` - Pydantic models (Job, JobStatus, Progress, CreateJobRequest, JobResponse) +- `app/store.py` - In-memory JobStore (dict-based) + +**File Path Handling:** +- API accepts relative paths (e.g., `input/video.mp4`) +- `resolve_paths()` converts to absolute using DATA_PATH environment variable +- Output files returned as relative paths + +## Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| DATA_PATH | /data | Mount point for media files | +| FFMPEG_TIMEOUT | 3600 | Max job duration in seconds | +| HOST | 0.0.0.0 | Server bind address | +| PORT | 8000 | Server port | diff --git a/Dockerfile b/Dockerfile index ecb4050..9333fea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,19 @@ FROM python:3.14-slim -# Install FFmpeg +# Install FFmpeg and curl (for uv installation) RUN apt-get update && \ - apt-get install -y --no-install-recommends ffmpeg && \ + apt-get install -y --no-install-recommends ffmpeg curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* +# Install uv +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv + WORKDIR /app # Install Python dependencies -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt +COPY pyproject.toml . +RUN uv pip install --system --no-cache . # Copy application COPY app/ ./app/ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2bf2662 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,30 @@ +[project] +name = "ffmpeg-worker" +version = "1.0.0" +description = "Dockerized FFmpeg worker with REST API" +requires-python = ">=3.12" +dependencies = [ + "fastapi>=0.123.0", + "uvicorn[standard]>=0.38.0", + "pydantic>=2.12.5", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.3.0", + "pytest-asyncio>=0.24.0", + "httpx>=0.28.1", + "requests>=2.32.0", +] + +[tool.pytest.ini_options] +asyncio_mode = "auto" +testpaths = ["tests"] + +[tool.uv] +dev-dependencies = [ + "pytest>=8.3.0", + "pytest-asyncio>=0.24.0", + "httpx>=0.28.1", + "requests>=2.32.0", +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 9e12b46..0000000 --- a/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -fastapi==0.123.0 -uvicorn[standard]==0.38.0 -pydantic==2.12.5 -pytest==8.3.0 -pytest-asyncio==0.24.0 -httpx==0.28.1 -requests==2.32.0