# 🌐 API REST - Documentation complète

**Date**: 2025-01-27  
**Version**: 0.2.0  
**Base URL**: `http://localhost:8082`

---

## Vue d'ensemble

L'API REST du stream-server permet de:
- Soumettre des fichiers audio pour transcodage
- Récupérer l'état des jobs
- Servir les fichiers HLS générés

---

## Endpoints

### POST `/v1/stream/transcode`

Soumet un fichier audio pour transcodage en HLS.

**Request**:
- **Method**: `POST`
- **Content-Type**: `multipart/form-data`
- **Body**:
  - `file` (required): Fichier audio (WAV, MP3, FLAC, etc.)
  - `codec` (optional): Codec cible (`aac`, `mp3`, `opus`, `flac`)
  - `bitrate` (optional): Bitrate en bps (ex: `192000`)
  - `quality_profile` (optional): Profil (`hi_res`, `high`, `medium`, `low`)
  - `priority` (optional): Priorité (`urgent`, `normal`, `background`)

**Response** (200 OK):
```json
{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued",
  "message": "Job submitted successfully"
}
```

**Erreurs**:
- `400 Bad Request`: Fichier manquant ou invalide
- `500 Internal Server Error`: Erreur serveur

**Exemple cURL**:
```bash
curl -X POST http://localhost:8082/v1/stream/transcode \
  -F "file=@audio.wav" \
  -F "quality_profile=high" \
  -F "priority=normal"
```

**Exemple JavaScript**:
```javascript
const formData = new FormData();
formData.append('file', audioFile);
formData.append('quality_profile', 'high');

const response = await fetch('http://localhost:8082/v1/stream/transcode', {
  method: 'POST',
  body: formData
});

const { job_id, status } = await response.json();
```

---

### GET `/v1/stream/job/{id}`

Récupère l'état d'un job de transcodage.

**Request**:
- **Method**: `GET`
- **Path Parameter**: `id` (UUID du job)

**Response** (200 OK):
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "track_id": "my_track",
  "status": "processing",
  "progress": 45.5,
  "created_at": "SystemTime { tv_sec: 1737979200, tv_nsec: 0 }",
  "started_at": "Some(SystemTime { tv_sec: 1737979205, tv_nsec: 0 })",
  "completed_at": null,
  "error": null
}
```

**Statuts possibles**:
- `"Pending"`: Job en attente dans la queue
- `"Processing"`: Job en cours de traitement
- `"Completed"`: Job terminé avec succès
- `"Failed(String)"`: Job échoué (le message d'erreur est dans `error`)
- `"Cancelled"`: Job annulé

**Erreurs**:
- `404 Not Found`: Job introuvable

**Exemple cURL**:
```bash
curl http://localhost:8082/v1/stream/job/550e8400-e29b-41d4-a716-446655440000
```

**Exemple JavaScript**:
```javascript
const response = await fetch(`http://localhost:8082/v1/stream/job/${jobId}`);
const status = await response.json();

if (status.status === 'Completed') {
  console.log('Job terminé!');
} else if (status.status.startsWith('Failed')) {
  console.error('Erreur:', status.error);
}
```

---

### GET `/v1/stream/hls/{job_id}/index.m3u8`

Sert le manifest HLS pour un job terminé.

**Request**:
- **Method**: `GET`
- **Path Parameter**: `job_id` (UUID du job)
- **Headers**: Aucun requis

**Response** (200 OK):
- **Content-Type**: `application/vnd.apple.mpegurl`
- **Body**: Contenu du fichier `index.m3u8`

**Exemple de contenu**:
```m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:6.0,
segment_00001.ts
#EXTINF:6.0,
segment_00002.ts
#EXTINF:6.0,
segment_00003.ts
#EXT-X-ENDLIST
```

**Erreurs**:
- `404 Not Found`: Job introuvable ou manifest non généré
- `400 Bad Request`: Job non terminé

**Exemple cURL**:
```bash
curl http://localhost:8082/v1/stream/hls/550e8400-e29b-41d4-a716-446655440000/index.m3u8
```

**Exemple JavaScript**:
```javascript
const manifestUrl = `http://localhost:8082/v1/stream/hls/${jobId}/index.m3u8`;

// Utiliser avec hls.js
import Hls from 'hls.js';
const hls = new Hls();
hls.loadSource(manifestUrl);
hls.attachMedia(audioElement);
```

---

### GET `/v1/stream/hls/{job_id}/{segment}`

Sert un segment HLS (.ts).

**Request**:
- **Method**: `GET`
- **Path Parameters**:
  - `job_id`: UUID du job
  - `segment`: Nom du segment (ex: `segment_00001.ts`)

**Response** (200 OK):
- **Content-Type**: `video/mp2t`
- **Body**: Contenu binaire du segment

**Erreurs**:
- `404 Not Found`: Segment introuvable

**Exemple cURL**:
```bash
curl http://localhost:8082/v1/stream/hls/550e8400-e29b-41d4-a716-446655440000/segment_00001.ts \
  --output segment_00001.ts
```

**Note**: Généralement appelé automatiquement par le player HLS lors du parsing du manifest.

---

## Workflow complet

### 1. Soumettre un job

```javascript
const formData = new FormData();
formData.append('file', audioFile);
formData.append('quality_profile', 'high');

const submitResponse = await fetch('http://localhost:8082/v1/stream/transcode', {
  method: 'POST',
  body: formData
});

const { job_id } = await submitResponse.json();
console.log('Job soumis:', job_id);
```

### 2. Poller l'état

```javascript
async function pollJobStatus(jobId) {
  while (true) {
    const response = await fetch(`http://localhost:8082/v1/stream/job/${jobId}`);
    const status = await response.json();
    
    console.log(`Statut: ${status.status}, Progression: ${status.progress}%`);
    
    if (status.status === 'Completed') {
      return status;
    } else if (status.status.startsWith('Failed')) {
      throw new Error(status.error);
    }
    
    // Attendre 2 secondes avant le prochain poll
    await new Promise(resolve => setTimeout(resolve, 2000));
  }
}

const completedJob = await pollJobStatus(jobId);
```

### 3. Charger le manifest HLS

```javascript
const manifestUrl = `http://localhost:8082/v1/stream/hls/${jobId}/index.m3u8`;

// Avec hls.js
import Hls from 'hls.js';

if (Hls.isSupported()) {
  const hls = new Hls();
  hls.loadSource(manifestUrl);
  hls.attachMedia(audioElement);
  
  hls.on(Hls.Events.MANIFEST_PARSED, () => {
    console.log('Manifest chargé, prêt à jouer');
    audioElement.play();
  });
} else if (audioElement.canPlayType('application/vnd.apple.mpegurl')) {
  // Support natif (Safari)
  audioElement.src = manifestUrl;
  audioElement.addEventListener('loadedmetadata', () => {
    audioElement.play();
  });
}
```

---

## Codes d'erreur

### 400 Bad Request
- Fichier manquant dans la requête
- Format de fichier invalide
- Paramètres invalides

### 404 Not Found
- Job introuvable
- Manifest HLS introuvable
- Segment introuvable

### 500 Internal Server Error
- Erreur serveur interne
- Échec de soumission du job
- Erreur de traitement

---

## Rate limiting

Actuellement non implémenté (P0). À ajouter en P1.

---

## Authentification

Actuellement non requise (P0). À ajouter en P1 avec JWT.

---

## CORS

CORS configuré pour:
- `http://localhost:5176` (Vite dev)
- `http://localhost:3000` (React dev)

Configurable via variable d'environnement `ALLOWED_ORIGINS`.

---

## Exemples complets

### Python

```python
import requests
import time
import uuid

# 1. Soumettre un job
with open('audio.wav', 'rb') as f:
    response = requests.post(
        'http://localhost:8082/v1/stream/transcode',
        files={'file': f},
        data={'quality_profile': 'high'}
    )
    job_id = response.json()['job_id']

# 2. Poller l'état
while True:
    response = requests.get(f'http://localhost:8082/v1/stream/job/{job_id}')
    status = response.json()
    
    print(f"Statut: {status['status']}, Progression: {status['progress']}%")
    
    if status['status'] == 'Completed':
        break
    elif status['status'].startswith('Failed'):
        raise Exception(status['error'])
    
    time.sleep(2)

# 3. URL du manifest
manifest_url = f'http://localhost:8082/v1/stream/hls/{job_id}/index.m3u8'
print(f"Manifest HLS: {manifest_url}")
```

### Node.js

```javascript
const FormData = require('form-data');
const fs = require('fs');
const fetch = require('node-fetch');

async function transcodeAudio(filePath) {
  // 1. Soumettre
  const form = new FormData();
  form.append('file', fs.createReadStream(filePath));
  form.append('quality_profile', 'high');
  
  const submitRes = await fetch('http://localhost:8082/v1/stream/transcode', {
    method: 'POST',
    body: form
  });
  
  const { job_id } = await submitRes.json();
  
  // 2. Poller
  while (true) {
    const statusRes = await fetch(`http://localhost:8082/v1/stream/job/${job_id}`);
    const status = await statusRes.json();
    
    if (status.status === 'Completed') {
      return `http://localhost:8082/v1/stream/hls/${job_id}/index.m3u8`;
    } else if (status.status.startsWith('Failed')) {
      throw new Error(status.error);
    }
    
    await new Promise(resolve => setTimeout(resolve, 2000));
  }
}

// Usage
transcodeAudio('./audio.wav')
  .then(manifestUrl => console.log('Manifest:', manifestUrl))
  .catch(console.error);
```

---

## Changelog

### v0.2.0 (2025-01-27)
- ✅ Ajout endpoints `/v1/stream/transcode`
- ✅ Ajout endpoint `/v1/stream/job/{id}`
- ✅ Ajout endpoints HLS `/v1/stream/hls/{job_id}/...`
- ✅ Support multipart/form-data pour upload
- ✅ Gestion des erreurs et statuts

---

**Prochaine étape**: Voir `STREAM_PIPELINE.md` pour l'architecture interne.

