<!-- 0ab6f78b-28ea-4bf7-8a7d-0e9a7d9c1cf7 -->
---
todos:
  - id: "add-pillow-dep"
    content: "Add Pillow dependency to pyproject.toml and bump version"
    status: pending
  - id: "create-thumbnail-module"
    content: "Create thumbnail.py with frame extraction and text overlay functions"
    status: pending
  - id: "add-cli-command"
    content: "Add thumbnail subcommand to cli.py"
    status: pending
  - id: "test-command"
    content: "Test the thumbnail command with a sample video"
    status: pending
isProject: false
---
# Instagram Reel Thumbnail Command

## Overview

Add a new CLI subcommand `avsync thumbnail` that:
1. Extracts a frame from the middle of a video using ffmpeg
2. Overlays styled text (title + artist) using Pillow
3. Outputs an Instagram-ready thumbnail (1080x1920 or original video size)

## Implementation

### 1. Add Pillow Dependency

In [`pyproject.toml`](pyproject.toml), add:
```python
"Pillow>=10.0.0",
```

### 2. Create New Thumbnail Module

Create `src/audio_video_sync/thumbnail.py` with:

- `extract_frame(video_path, timestamp) -> bytes` - Use ffmpeg to extract a single frame as PNG
- `create_thumbnail(image_bytes, title, artist, output_path)` - Use Pillow to:
  - Load the frame
  - Add a semi-transparent dark gradient at the bottom for text readability
  - Render title text (large, bold)
  - Render artist text (smaller, below title)
  - Position text in lower third (Instagram-friendly)
  - Save as PNG/JPEG

### 3. Text Styling

- **Font**: Use a bundled font or system font (e.g., Arial Bold, Helvetica)
- **Position**: Lower third, left-aligned with padding
- **Effects**: Text shadow for readability on any background
- **Format**:
  ```
  Castle of Glass
  Linkin Park
  ```

### 4. Update CLI

In [`cli.py`](src/audio_video_sync/cli.py), add a new subcommand:

```python
@app.command()
def thumbnail(
    video: Path,
    title: str = typer.Option(..., "-t", "--title", help="Song title"),
    artist: str = typer.Option(..., "-a", "--artist", help="Artist name"),
    output: Optional[Path] = typer.Option(None, "-o", "--output"),
    time: Optional[float] = typer.Option(None, "--time", help="Frame timestamp in seconds"),
):
```

Default behavior:
- Extracts frame from video middle if `--time` not specified
- Output defaults to `{video_stem}_thumb.png`

### 5. Example Usage

```bash
avsync thumbnail video.mp4 -t "Castle of Glass" -a "Linkin Park"
# Creates: video_thumb.png

avsync thumbnail video.mp4 -t "Numb" -a "Linkin Park" -o cover.png --time 10
# Creates: cover.png from 10s mark
```

## Files to Create/Modify

- **Create**: `src/audio_video_sync/thumbnail.py` - Frame extraction and text overlay logic
- **Modify**: [`src/audio_video_sync/cli.py`](src/audio_video_sync/cli.py) - Add `thumbnail` subcommand
- **Modify**: [`pyproject.toml`](pyproject.toml) - Add Pillow dependency, bump version
