runarium 0.1.0

Generate animated videos from GPS running/cycling data with real-time statistics
Documentation
# HTTP Server Usage

## Running the Server

### Local Development
```bash
# Build and run the server
cargo run --example server

# Or with release mode for better performance
cargo run --example server --release

# Using Makefile
make server
```

### Docker Deployment
```bash
# Build Docker image
make docker-build

# Start container
make docker-up

# Stop container
make docker-down
```

The server will start on `http://localhost:3000`

## API Endpoints

### 1. Health Check
```bash
GET /health
```

### 2. Generate Video
```bash
POST /generate-video
Content-Type: multipart/form-data
```

**Request Body:**
- `fit_file`: Your GPS data file (.fit format)
- `background`: Background map image (.jpg format)
- `config` (optional): JSON configuration object

**Response:**
```json
{
  "success": true,
  "message": "Video generated successfully",
  "download_url": "/download-video/550e8400-e29b-41d4-a716-446655440000",
  "video_id": "550e8400-e29b-41d4-a716-446655440000",
  "generation_time_ms": 15000
}
```

### 3. Generate Image
```bash
POST /generate-image
Content-Type: multipart/form-data
```

**Request Body:**
- `fit_file`: Your GPS data file (.fit format)
- `background`: Background map image (.jpg format)
- `config` (optional): JSON configuration object

**Response:**
```json
{
  "success": true,
  "message": "Image generated successfully",
  "download_url": "/download-image/550e8400-e29b-41d4-a716-446655440000",
  "image_id": "550e8400-e29b-41d4-a716-446655440000",
  "generation_time_ms": 1200
}
```

### 4. Download Video
```bash
GET /download-video/:video_id
```

Returns the generated MP4 video file. **One-time download only** - file is deleted after download.

### 5. Download Image
```bash
GET /download-image/:image_id
```

Returns the generated PNG image file. **One-time download only** - file is deleted after download.

## Example Usage

### Using Makefile Commands

```bash
# Test video generation with default config
make api-test-video

# Test video generation with full config
make api-test-video-config

# Test image generation with default config
make api-test-image

# Test image generation with full config
make api-test-image-config

# Check API health
make api-health
```

### Using cURL

#### Generate Video (Default Config)
```bash
curl -X POST http://localhost:3000/generate-video \
  -F "fit_file=@source/example.fit" \
  -F "background=@source/example.jpg"
```

#### Generate Video (Custom Config)
```bash
curl -X POST http://localhost:3000/generate-video \
  -F "fit_file=@source/example.fit" \
  -F "background=@source/example.jpg" \
  -F 'config={"scale":0.3,"offset_x_percent":0.15,"offset_y_percent":0.15,"route_line_color":[255.0,0.0,0.0,0.0],"show_lap_data":true}'
```

#### Generate Image (Default Config)
```bash
curl -X POST http://localhost:3000/generate-image \
  -F "fit_file=@source/example.fit" \
  -F "background=@source/example.jpg"
```

#### Generate Image (Custom Config)
```bash
curl -X POST http://localhost:3000/generate-image \
  -F "fit_file=@source/example.fit" \
  -F "background=@source/example.jpg" \
  -F 'config={"scale":0.25,"route_line_color":[0.0,255.0,0.0,0.0]}'
```

#### Download Video
```bash
# Replace VIDEO_ID with the id from response
curl -o output.mp4 http://localhost:3000/download-video/VIDEO_ID
```

#### Download Image
```bash
# Replace IMAGE_ID with the id from response
curl -o output.png http://localhost:3000/download-image/IMAGE_ID
```

### Using JavaScript/Fetch

#### Generate Video
```javascript
const formData = new FormData();
formData.append('fit_file', fitFileBlob);
formData.append('background', imageFileBlob);

// Optional: Add custom config
const config = {
  scale: 0.3,
  route_line_color: [255.0, 0.0, 0.0, 0.0],
  show_lap_data: true
};
formData.append('config', JSON.stringify(config));

const response = await fetch('http://localhost:3000/generate-video', {
  method: 'POST',
  body: formData
});

const data = await response.json();
console.log('Download URL:', data.download_url);
console.log('Generation time:', data.generation_time_ms, 'ms');

// Download video
window.location.href = `http://localhost:3000${data.download_url}`;
```

#### Generate Image
```javascript
const formData = new FormData();
formData.append('fit_file', fitFileBlob);
formData.append('background', imageFileBlob);

const response = await fetch('http://localhost:3000/generate-image', {
  method: 'POST',
  body: formData
});

const data = await response.json();
console.log('Download URL:', data.download_url);

// Download image
window.location.href = `http://localhost:3000${data.download_url}`;
```

### Using Python

#### Generate Video
```python
import requests

# Upload and generate with default config
files = {
    'fit_file': open('source/example.fit', 'rb'),
    'background': open('source/example.jpg', 'rb')
}

response = requests.post('http://localhost:3000/generate-video', files=files)
result = response.json()

print(f"Video ID: {result['video_id']}")
print(f"Download URL: {result['download_url']}")
print(f"Generation time: {result['generation_time_ms']}ms")

# Download video
video_response = requests.get(f"http://localhost:3000{result['download_url']}")
with open('output.mp4', 'wb') as f:
    f.write(video_response.content)
```

#### Generate Video with Custom Config
```python
import requests
import json

files = {
    'fit_file': open('source/example.fit', 'rb'),
    'background': open('source/example.jpg', 'rb')
}

config = {
    'scale': 0.3,
    'route_line_color': [255.0, 0.0, 0.0, 0.0],
    'show_lap_data': True,
    'show_pace': True
}

data = {
    'config': json.dumps(config)
}

response = requests.post('http://localhost:3000/generate-video', 
                        files=files, data=data)
result = response.json()
```

#### Generate Image
```python
import requests

files = {
    'fit_file': open('source/example.fit', 'rb'),
    'background': open('source/example.jpg', 'rb')
}

response = requests.post('http://localhost:3000/generate-image', files=files)
result = response.json()

# Download image
image_response = requests.get(f"http://localhost:3000{result['download_url']}")
with open('output.png', 'wb') as f:
    f.write(image_response.content)
```

### Using Postman

#### Generate Video
1. Create a new POST request to `http://localhost:3000/generate-video`
2. Go to "Body" tab → Select "form-data"
3. Add fields:
   - Key: `fit_file`, Type: File, Value: Select your .fit file
   - Key: `background`, Type: File, Value: Select your .jpg file
   - Key: `config` (optional), Type: Text, Value: JSON config string
4. Click "Send"
5. Copy the `download_url` from response
6. Create a new GET request to `http://localhost:3000{download_url}`
7. Click "Send and Download"

#### Generate Image
1. Create a new POST request to `http://localhost:3000/generate-image`
2. Follow same steps as video generation
3. Download from the returned `download_url`

## Configuration Options

All configuration parameters are optional. If not provided, default values will be used.

### Available Config Parameters

```json
{
  "scale": 0.2,                    // Route scale (default: 0.2)
  "offset_x_percent": 0.1,         // X offset percentage (default: 0.1)
  "offset_y_percent": 0.1,         // Y offset percentage (default: 0.1)
  
  "route_line_color": [0.0, 0.0, 255.0, 0.0],           // Red (BGRA format)
  "current_position_color": [0.0, 255.0, 0.0, 0.0],     // Green
  "text_color": [255.0, 255.0, 255.0, 0.0],             // White
  "lap_bars_color": [0.0, 165.0, 255.0, 0.0],           // Orange
  
  "pace_font_scale": 0.6,          // Pace text size (default: 0.6)
  "pace_thickness": 2,             // Pace text thickness (default: 2)
  
  "lap_position_x": 0.5,           // Lap data X position (default: 0.5)
  "lap_position_y": 0.09,          // Lap data Y position (default: 0.09)
  "lap_font_scale": 0.5,           // Lap text size (default: 0.5)
  "lap_thickness": 1,              // Lap text thickness (default: 1)
  
  "show_bottom_bar": true,         // Show bottom bar (default: true)
  "show_route": true,              // Show route line (default: true)
  "show_lap_data": true,           // Show lap information (default: true)
  "show_pace": true,               // Show pace (default: true)
  "show_distance": true,           // Show distance (default: true)
  "show_heart_rate": true,         // Show heart rate (default: true)
  "show_stride_length": true,      // Show stride length (default: true)
  "show_pace_bars": true           // Show pace bars (default: true)
}
```

### Color Format (BGRA)
Colors are specified in BGRA format (Blue, Green, Red, Alpha):
- Red: `[0.0, 0.0, 255.0, 0.0]`
- Green: `[0.0, 255.0, 0.0, 0.0]`
- Blue: `[255.0, 0.0, 0.0, 0.0]`
- White: `[255.0, 255.0, 255.0, 0.0]`
- Orange: `[0.0, 165.0, 255.0, 0.0]`

## File Storage

- **In-Memory Only**: Videos and images are stored in memory (not saved to disk)
- **One-Time Download**: Files are automatically deleted after download
- **Temporary Processing**: Files are temporarily written during generation, then cleaned up
- **System Temp Directory**: Uses OS temp directory for cross-platform compatibility

## Limits

- Maximum file size: 100MB (configurable via `DefaultBodyLimit`)
- Concurrent requests: Handled by tokio async runtime
- Memory usage: Videos/images stored in RAM until downloaded

## Docker Configuration

The Docker image is optimized for production:
- Multi-stage build (~600MB final size)
- Based on `debian:bookworm-slim`
- Includes OpenCV runtime libraries
- Exposes port 3000
- Memory limits: 2GB max, 512MB reserved