Stockage et traitement des images et vidéos. Schéma des tables medias / media-images / media-videos : voir entities/medias.md.

Qui stocke quoi

  • Images → Minio (module statics), un seul bucket. Deux flux distincts y écrivent : les médias piste/article (media-images, via POST /statics) et les photos de profil (flux séparé postProfilePictureToMinio, dossier profile-pictures/).
  • Vidéo → Mux entièrement. Les octets vidéo ne transitent jamais par Minio.
  • Pas de Minio ni de Mux en local : docker-compose.yml ne lance aucun conteneur Minio, le dev pointe vers une instance distante via les variables d'env. Les credentials Mux ne sont même pas déclarées dans docker-compose.yml et ne sont pas validées au démarrage (lues directement dans process.env, pas via ConfigService/Joi) — une vidéo en local dépend donc de vraies credentials Mux présentes dans l'environnement ; sans elles, l'appel échoue au moment de l'upload, pas au démarrage.

Redimensionnement (resizer)

  • À la demande, pas à l'upload : GET /resizer/:width/:height[/crop/:horizontal-:vertical]/:path télécharge l'original depuis Minio et le redimensionne/recadre avec sharp. Sert du WebP si le client l'accepte via Accept.
  • Le résultat est mis en cache sur le disque local du conteneur (cache/resized-images/), pas dans Minio. Le cache est donc propre à chaque instance et n'est jamais invalidé quand l'original est remplacé au même chemin (PATCH /statics/:id) — une image obsolète continue d'être servie jusqu'à un appel manuel à GET /resizer/cache/clear ou un redémarrage du conteneur.
  • horizontalPosition (0-100, défaut 50 sur Media) ne pilote que le recadrage horizontal ; il n'existe pas de position verticale persistée, le vertical reste toujours à 50.
  • Toute erreur (image absente, Minio injoignable, sharp qui échoue) ressort comme la même 404 générique Image not found or processing failed — impossible de distinguer la cause sans regarder les logs serveur.

Vidéo (Mux)

  • Upload direct navigateur → Mux (librairie upchunk), l'API ne voit jamais les octets. Elle se contente de créer l'URL d'upload (muxUploadUrl) et d'attendre.
  • Pas de webhook Mux : un cron (monitorMuxAssets, toutes les 2 secondes, voir cron-tasks.md) interroge l'état de l'asset et met à jour le statut (ready/errored) ainsi que muxPlaybackId.
  • Un statut Mux non reconnu par le code laisse la vidéo bloquée en preparing indéfiniment, sans alerte — d'où le bouton admin "vérifier sur Mux" (check-mux), qui interroge directement la miniature Mux pour confirmer qu'une vidéo est vraiment jouable quand le statut local semble en décalage.
  • L'upload direct dépend de FRONTEND_URL (envoyée à Mux comme cors_origin) — une valeur incorrecte casse l'upload avec une erreur CORS qui n'a rien à voir avec Minio ni l'API.

Pièges

  • proccessing (faute d'orthographe) est la vraie valeur stockée en base, pas juste un nom d'identifiant : c'est le défaut de la colonne medias.status, dupliqué tel quel dans plusieurs fichiers front qui comparent à cette chaîne. Corriger la faute demande une vraie migration d'enum Postgres (renommer la valeur + mettre à jour le défaut) et une mise à jour synchronisée de tous les endroits qui comparent à 'proccessing', pas un simple renommage d'identifiant.
  • Supprimer un média (flux normal piste/article) ne supprime pas le fichier Minio. Seul un soft-delete de la ligne media-images est effectué. Le fichier reste orphelin dans le bucket indéfiniment. Seule la suppression via DELETE /statics/:id, ou la suppression d'une photo de profil, retire réellement l'objet Minio.
  • media.url est vide pour les images ajoutées via le flux standard piste/article — l'URL réelle est reconstruite côté front à partir de image.folder/image.name, pas de media.url. Ce champ n'est effectivement renseigné que pour les photos de profil (URL publique complète) et les vidéos prêtes (URL de stream Mux).