veza/veza-stream-server/docs/STREAM_API.md
okinrev b7955a680c P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.

Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.

Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).

Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.

Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 11:14:38 +01:00

8.9 KiB

🌐 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):

{
  "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:

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

Exemple 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):

{
  "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:

curl http://localhost:8082/v1/stream/job/550e8400-e29b-41d4-a716-446655440000

Exemple 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:

#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:

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

Exemple 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:

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

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

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

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

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

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.