Médias (images & vidéo)

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/).
  • Les images piste/article sont compressées à l'upload (POST/PATCH /statics : orientation EXIF appliquée, grand côté ≤ 1300 px, même format, JPEG/PNG uniquement → 415 sinon). Les fichiers déjà en bucket ont été recompressés par un backfill one-shot.
  • Les photos de profil sont compressées à l'upload (POST /statics/profile-picture : même helper compressImage, grand côté ≤ 256 px, JPEG) ; celles déjà en bucket ont été recompressées par un backfill one-shot.
  • 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.

Affichage des images (plus de resizer)

  • Il n'y a plus de redimensionnement à la volée : le module ResizerModule (/resizer/*) a été supprimé. Les images sont compressées à l'upload (voir plus haut) puis servies telles quelles par le CDN Minio ; le recadrage se fait en CSS (object-fit: cover).
  • horizontalPosition (0-100, défaut 50 sur Media) pilote l'object-position horizontal du carrousel (SPA et SSR) ; il n'existe pas de position verticale persistée, le vertical reste toujours à 50.

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).