# Hook Transcode Test Pipeline

A testing pipeline for video processing that runs glockenspiel's Modal workers (video upload, hook video generation) and processes videos through AWS MediaConvert. It takes an existing video hook ID from the database and runs it through the complete video processing pipeline.

This uses the local files from glockenspiel, not the deployed version of these workers. You can test local updates to modal and media-convert-handler with this project

## Overview

This project orchestrates:
1. **Video Upload Worker** - Processes and transcodes uploaded videos
2. **Hook Video Generation Worker** - Creates hook videos with audio mixing
3. **MediaConvert** - Transcodes the final video to HLS format for streaming

## Prerequisites

- Python 3.12+
- Access to the glockenspiel repository
- PostgreSQL database credentials (prod/staging)
- AWS credentials with S3 and MediaConvert access
- FFmpeg installed (for video concatenation)

## Setup

1. **Install dependencies:**
```bash
uv sync
```

2. **Configure environment:**
```bash
cp .env.template .env
# Edit .env with your credentials:
# - DATABASE_URL_PROD/STAGING - PostgreSQL connection strings
# - MEDIACONVERT_ROLE - IAM role ARN for MediaConvert
# - AWS_ACCESS_KEY_ID/SECRET_ACCESS_KEY - AWS credentials
# - GLOCKENSPIEL_REPO - Path to glockenspiel repo (optional)
```

## Usage

### Basic Usage

Run the pipeline with a hook ID from your database:

```bash
# Use staging database (default)
uv run python main.py <hook_id>

# Use production database
uv run python main.py <hook_id> --db prod
```

### Options

- `--db staging|prod` - Database to use (default: staging)
- `--skip-upload` - Skip the video upload worker step
- `--skip-hook-gen` - Skip the hook video generation step
- `--skip-mediaconvert` - Skip the MediaConvert transcoding step

### Examples

```bash
# Full pipeline test with staging hook
python main.py a7cc0a7b-1dee-4799-8256-e301df69c602 --db staging

# Test only MediaConvert (skip Modal workers)
python main.py a7cc0a7b-1dee-4799-8256-e301df69c602 --skip-upload --skip-hook-gen

# Test only video upload worker
python main.py a7cc0a7b-1dee-4799-8256-e301df69c602 --skip-hook-gen --skip-mediaconvert
```

## Pipeline Steps

1. **Database Lookup**:
   - Connects to PostgreSQL and fetches hook metadata
   - Retrieves render configuration and video references
   - Downloads original raw video from S3

2. **Video Upload Worker** (Modal):
   - Transcodes video to standard format
   - Handles B-frames and timestamp normalization
   - Applies quality optimizations
   - Outputs to `studio/uploads/` in S3

3. **Hook Video Generation** (Modal):
   - Applies hook-specific transformations
   - Mixes audio based on render schema
   - Creates the final hook video
   - Outputs to `tests/hook-transcode-tests/` in S3

4. **MediaConvert**:
   - Creates multi-bitrate HLS streams
   - Generates streaming-ready segments
   - Downloads and concatenates 720p version for verification
   - Outputs to `tests/hook-transcode-tests/<hook_id>/` in S3

## Output Structure

All outputs are saved to `outputs/<hook_id>/`:

```
outputs/
└── a7cc0a7b-1dee-4799-8256-e301df69c602/
    ├── 0_raw_upload.mp4           # Original from S3
    ├── 1_upload_processed.mp4     # After upload worker
    ├── 2_hook_generated.mp4       # After hook generation
    └── 3_mediaconvert_720p.mp4    # Concatenated HLS 720p
```

## Architecture

```
pipeline/
├── constants.py           # Configuration constants and paths
├── database.py            # PostgreSQL connection and queries
├── s3_operations.py       # S3 upload/download operations
├── video_upload_worker.py # Modal upload worker integration
├── hook_video_worker.py   # Modal hook gen worker integration
└── mediaconvert.py        # MediaConvert job management
```

## Known Issues & Solutions


1. **Check video timestamps:**
```bash
# Check for timestamp offsets
ffprobe -v error -show_entries stream=start_time,start_pts outputs/<hook_id>/1_upload_processed.mp4

# Check for B-frames
ffprobe -v error -select_streams v:0 -show_entries stream=has_b_frames outputs/<hook_id>/0_raw_upload.mp4

# Check first packet DTS (reveals negative values from B-frames)
ffprobe -v error -select_streams v:0 -show_entries packet=pts_time,dts_time -of csv=p=0 outputs/<hook_id>/0_raw_upload.mp4 | head -5
```

2. **View Modal worker logs:**
The pipeline streams Modal worker output in real-time with prefixes like `[MODAL]` and `[HOOK-GEN]`

## Environment Variables

Required in `.env`:
- `DATABASE_URL_PROD` - Production PostgreSQL connection string
- `DATABASE_URL_STAGING` - Staging PostgreSQL connection string
- `MEDIACONVERT_ROLE` - IAM role ARN for MediaConvert jobs
- `AWS_ACCESS_KEY_ID` - AWS access key with S3/MediaConvert permissions
- `AWS_SECRET_ACCESS_KEY` - AWS secret access key

Optional:
- `GLOCKENSPIEL_REPO` - Path to glockenspiel repository (defaults to ~/dev/glockenspiel)

## S3 Buckets Used

- `suno-uploads` - Raw video uploads
- `suno-data-uploads` - Processed videos from Modal workers
- `suno-media-sour` - MediaConvert source bucket
- `suno-media-dest` - MediaConvert destination bucket


## Notes

- The pipeline uses the actual render metadata from the database hook
- Modal workers run locally via `uv run modal run` commands
- Intermediate files are preserved for debugging
- The pipeline continues even if some downloads fail (with warnings)
- MediaConvert jobs are tagged with "transcode-test" for easy identification