Lorsque vous demandez à un opérateur de télévision en direct ce qu'il aimerait voir dans un outil de planification, il répond presque invariablement : « Laissez-moi créer une chaîne comme j'organise des fichiers dans un dossier. Je glisse un programme, je le dépose où je veux, c'est fait. »
Le problème est qu'une grille de programme de chaîne n'est pas un dossier. C'est un document juridique avec des contraintes strictes. Deux programmes ne peuvent pas être diffusés en même temps. L'encodeur ne peut pas changer d'entrée plus rapidement que toutes les cinq secondes. Un programme qui a déjà commencé ne peut pas être modifié. Et un film qui dépasse minuit doit être traité comme un seul programme, et non découpé à la limite du jour.
Chaque action de glisser-déposer est une violation potentielle de contrainte en attente de se produire. Voici l'histoire d'ingénierie de la façon dont le planificateur de mStudio transforme le glisser-déposer convivial pour l'opérateur en une chronologie de chaîne légale garantie sur AWS MediaLive — y compris le moment où un opérateur dépose un programme directement sur un autre — et pourquoi le système résout ces conflits automatiquement au lieu de remettre à l'opérateur une erreur et une énigme à résoudre.
Aperçu Rapide
| Aspect | Détail |
|---|---|
| Domaine | Planification par glisser-déposer pour les chaînes FAST en direct sur AWS MediaLive |
| Résolution des conflits | Décalage automatique via un algorithme d'alignement à 4 règles |
| Écart minimal entre voisins | 6 secondes (minimum de 5s de MediaLive + 1s de sécurité pour la dérive d'horloge) |
| Gestion des cascades | Heures originales mémorisées à la première touche, de sorte que les décalages en chaîne produisent un seul appel de nettoyage par programme |
| Sécurité des dates passées | Garde en deux phases — rejet de l'entrée, plus annulation silencieuse après alignement |
| Sécurité de la réinitialisation du jour | Tampon de lecture en direct de 2 minutes, plus préservation du report |
| Statut | En production |
Le Problème Commercial : Un Calendrier Qui N'en Est Pas Un
La planification de chaînes ressemble à un logiciel de calendrier. Les opérateurs s'attendent à ce qu'il se comporte comme tel — glisser un film dans un créneau de 21h, faire glisser les programmes sur la chronologie, ajouter une série par lots et voir les épisodes s'agencer les uns après les autres. Mais une grille de programme de chaîne en direct comporte des contraintes qu'un calendrier n'a tout simplement pas :
- Les programmes ne sont pas autorisés à se chevaucher. La télévision en direct diffuse exactement une chose à la fois.
- L'encodeur a un espacement minimum d'actions. AWS MediaLive ne changera pas d'entrée plus rapidement que toutes les 5 secondes. Planifiez deux programmes à 4 secondes d'intervalle, et le déploiement est rejeté.
- Les programmes passés ne peuvent pas être modifiés. Le temps d'antenne est déjà passé ; les bits sont déjà sur les écrans des téléspectateurs.
- Les programmes à cheval sur minuit sont une seule unité. Un film diffusé de 23h30 à 01h15 doit être traité comme un seul programme, et non comme deux demi-programmes séparés à la limite du jour.
Un « quasi-chevauchement » de deux secondes n'est pas une erreur de l'opérateur — c'est le résultat naturel du glissement de deux programmes qui s'ajustent presque, mais pas tout à fait. Le véritable défi d'ingénierie est de traduire « glisser un film à 21h » en « une grille de programme de chaîne légale ». Si cela est mal fait, l'opérateur se heurte à un mur d'erreurs à chaque dépôt, ou — pire — découvre au moment de la diffusion que l'encodeur a silencieusement rejeté une partie de la grille.
Ce que « Légal » Signifie sur AWS MediaLive
Pour qu'une grille de programme soit légale sur MediaLive, les programmes adjacents doivent être soit :
- Dos à dos — zéro écart entre eux, ou
- Séparés par au moins 5 secondes — l'espacement minimum d'actions de l'encodeur.
Le piège est tout ce qui se trouve entre les deux. Un écart d'une seconde, un écart de 3 secondes, un écart de 4,9 secondes — tous semblent parfaitement corrects dans une interface utilisateur, et tous sont rejetés au moment du déploiement. Pire encore, le rejet n'est pas un échec propre et atomique ; il peut en résulter une chaîne partiellement déployée, où certaines actions de planification ont été effectuées sur MediaLive et d'autres non.
Ajoutez une marge de sécurité d'une seconde pour la dérive d'horloge entre les serveurs d'applications et AWS, et le seuil pratique devient 6 secondes, pas 5. Ce chiffre unique — MIN_NEIGHBOUR_GAP_MS = 6000 — est la seule constante autour de laquelle tout le système de résolution de conflits est construit.
Pourquoi les Approches Évidentes Échouent
Avant d'opter pour la résolution automatique, plusieurs stratégies plus évidentes ont été envisagées et rejetées :
« Rejeter tout désaccord et demander à l'opérateur de le résoudre. » Pour cette raison, les opérateurs doivent effectuer manuellement des calculs d'alignement à chaque dépôt. Un glissement de cinq secondes devient une énigme de cinq minutes, et l'énigme devient plus difficile à mesure que la liste de lecture s'allonge. En pratique, les opérateurs abandonnent entièrement le glisser-déposer et reviennent aux feuilles de calcul.
« Aligner tout sur des intervalles de 5 minutes pour que rien ne se chevauche jamais. » Cela résout le problème technique en détruisant l'intention de l'opérateur. Un programme censé commencer à 21:03:15 ne devrait pas passer silencieusement à 21:05:00. La grille appartient à l'opérateur, pas à une fonction d'arrondi.
« Détecter les conflits au moment du déploiement plutôt qu'au moment du dépôt. » Cela semble plus rapide dans l'interface utilisateur, mais cela déplace l'échec au pire moment possible. Au moment où l'opérateur clique sur Déployer et voit « planification rejetée au programme 47 », il est déjà passé mentalement à autre chose par rapport à la modification qui l'a causé.
« Autoriser les micro-écarts dans l'interface utilisateur et laisser MediaLive les rejeter. » Cela renvoie directement les erreurs opaques de l'encodeur à l'opérateur et peut laisser la chaîne dans un état de déploiement partiel dont il est vraiment difficile de récupérer.
Le levier qui a réellement fonctionné a été de résoudre les conflits automatiquement, au moment du dépôt, en utilisant des règles déterministes — et de refléter immédiatement la chronologie corrigée à l'opérateur.
La Solution : Un Algorithme d'Alignement à Quatre Règles
À chaque dépôt, un algorithme d'alignement examine chaque paire de programmes adjacents sur la chaîne affectée — paires nouveau-à -nouveau, nouveau-à -existant, ou existant-à -existant dont l'écart a changé à cause du dépôt — et applique exactement l'une des quatre règles basées sur l'écart entre eux :
- Écart = 0 → aucune action. Le dos à dos est légal et est presque certainement ce que l'opérateur avait l'intention de faire.
- Écart ≥ 6 secondes → aucune action. L'opérateur a délibérément laissé de la place, probablement pour une mire ou un bloc publicitaire.
- 0 < écart < 6 secondes → décaler le second programme en arrière pour réduire l'écart à zéro.
- Écart négatif (chevauchement) → décaler le second programme en avant de la durée du chevauchement.
De manière cruciale, l'algorithme traite les paires adjacentes en un seul passage avant sur la chronologie triée. Chaque programme décalé devient immédiatement l'élément « précédent » pour la comparaison suivante — ainsi, un dépôt qui déclenche une réaction en chaîne de décalages se résout en un seul parcours linéaire, sans nécessiter de récursion.
Diagramme 1 · L'arbre de décision d'alignement

Architecture du Système
Le planificateur est construit autour d'un petit ensemble de composants ciblés :
- Le backend NestJS (
schedule.service.ts) gère le parcours d'alignement, la mémoïsation en cascade et le contrat d'écriture avec MediaLive. - Requête de chevauchement de plage horaire. Lorsqu'un dépôt arrive, le backend récupère chaque programme existant dont la fenêtre horaire touche la plage du nouveau lot, plus un tampon de 6 secondes de chaque côté. Parce que cette requête opère sur des plages horaires plutôt que des dates calendaires, les programmes à cheval sur minuit sont traités identiquement à tout autre programme — aucune logique spéciale de limite de date n'existe nulle part dans le système.
- Chronologie fusionnée. Les DTO des nouveaux programmes et les programmes existants interrogés sont fusionnés en une seule liste triée. Le parcours d'alignement s'exécute sur cette chronologie combinée.
- Carte
shiftedExistings. Pour tout programme existant touché par un décalage, cette carte capture ses heures de début et de fin originales la première fois qu'il est touché — et ne les écrase jamais lors des touches ultérieures. Cette structure de données est ce qui rend les cascades multi-étapes sûres. - Lambda
DELETE_PROGRAM. Pour tout programme décalé qui était déjà déployé sur MediaLive, ses heures originales sont envoyées à une fonction Lambda pour nettoyage avant la mise à jour de MongoDB. MIN_NEIGHBOUR_GAP_MS = 6000— la seule constante à laquelle chaque règle, chaque requête de chevauchement et chaque tampon de sécurité fait référence.
Décisions d'Ingénierie Clés
1. Quatre règles, un parcours, pas de cas spéciaux. Les mêmes quatre règles couvrent tous les scénarios qu'un opérateur peut créer : un nouveau programme déposé entre deux existants, deux nouveaux programmes qui entrent en conflit l'un avec l'autre, ou un programme existant qui est poussé en chevauchement par un décalage antérieur dans le même dépôt. Il n'y a pas de chemin de code séparé pour aucun de ceux-ci — chaque cas se réduit à « examiner l'écart entre les programmes adjacents et appliquer la règle. »
2. Six secondes, pas cinq. MediaLive impose un espacement minimum de 5 secondes entre les actions de planification ; planifier deux changements d'entrée à 4,9 secondes d'intervalle provoque un rejet de déploiement. Le système impose 6 secondes — une marge de sécurité d'une seconde pour la dérive d'horloge entre l'horloge du backend et celle d'AWS. Soumettre une action à exactement 5,000 secondes, alors que l'horloge de l'encodeur la lit comme 4,997 secondes, produit des rejets intermittents qui ressemblent à des pannes réseau et donnent l'impression de bugs non reproductibles. La seconde supplémentaire convertit un mode de défaillance intermittent en un mode qui ne se déclenche tout simplement jamais.
Cela s'accompagne d'un compromis délibéré : un seuil de 6 secondes signifie que les petits écarts consécutifs de 3 ou 4 secondes sont réduits à zéro plutôt que préservés. Ce compromis a été accepté intentionnellement — les transitions dos à dos sont propres sur MediaLive, et un écart visible de 3 secondes a tendance à ressembler à un pépin pour les téléspectateurs de toute façon.
3. La mémoïsation des heures originales rend les cascades sûres. Un seul dépôt peut déclencher une chaîne de décalages — le programme A décale B, B décale C, C décale D. L'appel de nettoyage à MediaLive doit cibler l'heure de déploiement originale de chaque programme, et non son heure décalée en cascade ; utiliser la mauvaise heure fait que MediaLive répond « aucune action trouvée », faisant échouer silencieusement le nettoyage. Le parcours maintient une carte de programId → {oldStartTime, oldEndTime}, capturée la première fois que chaque programme est touché. Les décalages en cascade ultérieurs mettent à jour uniquement la chronologie en mémoire ; les originaux mémoïsés restent intacts, et le nettoyage utilise toujours exactement ce que MediaLive a réellement enregistré.
Diagramme 2 · Un exemple de cascade

4. Gardes de dates passées, appliquées en deux phases distinctes. Les modifications de dates passées sont bloquées deux fois, délibérément :
- Phase 0, avant l'exécution du parcours d'alignement : tout nouveau programme avec une heure de début antérieure à « maintenant » rejette purement et simplement l'ensemble du lot, avec une erreur claire. Le parcours d'alignement ne s'exécute même jamais contre une entrée impossible.
- Phase 4, après le parcours d'alignement : deux sous-cas distincts sont traités différemment. Un nouveau programme que l'alignement a accidentellement tiré dans le passé (rare, mais possible aux limites d'horloge au moment de la requête) est silencieusement annulé à son heure originale, avant alignement — l'intention de l'opérateur est préservée, et l'alignement n'est tout simplement pas appliqué. Un programme existant qu'un décalage pousserait dans le passé rejette plutôt l'ensemble du lot — toucher un programme qui a déjà commencé à être diffusé n'est jamais quelque chose que le système absorbera silencieusement.
5. Un contrat d'écriture Lambda d'abord, base de données ensuite. Lorsqu'un alignement décale un programme qui a déjà été déployé sur MediaLive, MongoDB et MediaLive sont brièvement désynchronisés, et l'ordre de réconciliation est important. Le contrat : Lambda d'abord, MongoDB ensuite. Le backend appelle DELETE_PROGRAM sur Lambda en utilisant les heures originales du programme ; si un appel échoue, le backend lève une erreur avant toute écriture en base de données. Ce n'est qu'une fois que chaque appel de suppression réussit qu'un seul bulkWrite met à jour MongoDB avec les nouvelles heures et réinitialise isDeployed: false.
Cela produit un invariant propre : si MongoDB affiche un programme à une nouvelle heure, MediaLive a déjà accepté ce déplacement. Si l'opérateur voit une erreur à la place, aucun des deux systèmes n'a été touché. Il n'existe aucun état possible où MongoDB et MediaLive sont silencieusement en désaccord sur l'heure d'un programme.
6. La réinitialisation du jour a son propre filet de sécurité dédié. La « réinitialisation du jour » supprime chaque programme sur une chaîne pour un jour calendaire donné — l'opération la plus destructive du système — elle comporte donc deux protections spécifiques.
- Un tampon de 2 minutes (
SAFETY_BUFFER_MS = 120000) exempte tout programme commençant dans les deux prochaines minutes, donnant à la lecture en direct une fenêtre de grâce afin qu'une réinitialisation ne puisse jamais entrer en concurrence avec un programme sur le point d'être diffusé. - La préservation des reports exclut les programmes qui ont commencé la veille mais qui débordent sur aujourd'hui — ceux-ci appartiennent à la grille d'hier, pas à celle d'aujourd'hui.
Un mécanisme de repli élégant est également disponible : si une chaîne n'a jamais été déployée sur MediaLive du tout, Lambda renvoie une certaine chaîne d'erreur que le backend comprend, enregistre comme no_infrastructure, puis effectue une suppression douce uniquement sur MongoDB. La réinitialisation réussit toujours ; l'étape AWS devient simplement une opération nulle.
Pourquoi cette Combinaison de Choix de Conception
| Décision | Pourquoi elle a été prise | Alternative envisagée | Compromis accepté |
|---|---|---|---|
| Résolution automatique au moment du dépôt vs. rejet et demande | Maintient le glisser-déposer utilisable à l'échelle ; le calcul d'alignement manuel ne survit pas à une liste de lecture croissante | Rejeter en cas de conflit, demander à l'opérateur de corriger | Exige du système, et non de l'opérateur, de garantir l'exactitude |
| Seuil de 6 secondes vs. minimum de 5 secondes déclaré par MediaLive | Absorbe la dérive d'horloge entre le backend et AWS, prévenant les échecs de déploiement intermittents | Imposer exactement 5 secondes | Les petits écarts intentionnels (3-4s) sont réduits à zéro au lieu d'être préservés |
| Parcours en un seul passage avant vs. résolution récursive des conflits | Les cascades se résolvent de manière déterministe sans problèmes de profondeur de récursion | Décalage et revérification récursifs | Nécessite un ordonnancement minutieux de la chronologie triée en amont |
| Lambda d'abord / BD ensuite vs. BD d'abord / Lambda ensuite | Garantit que MongoDB et MediaLive ne peuvent jamais être silencieusement en désaccord | Mettre à jour MongoDB de manière optimiste, synchroniser MediaLive après | Latence légèrement plus élevée par programme décalé et déployé, en échange d'un risque de dérive nul |
Ce qui Est Toujours Surveillé
L'ingénierie honnête signifie nommer les lacunes qui restent ouvertes, pas seulement celles qui sont résolues.
- Modifications concurrentes sur la même chaîne. Si deux opérateurs cliquent sur Déployer sur la même chaîne à quelques centaines de millisecondes d'intervalle, les deux chargeront la même capture d'écran, les deux exécuteront le parcours d'alignement indépendamment, et les deux écriront dans MongoDB. Il n'y a pas de verrouillage par chaîne ou de vérification de version optimiste aujourd'hui. La mitigation actuelle est opérationnelle — un opérateur possède une chaîne à la fois — tandis que la correction technique, un champ de version sur le document de la chaîne vérifié au moment de l'écriture, est sur la feuille de route.
- Pas de retour d'information dans l'interface utilisateur sur ce qui a été aligné. Lorsque le parcours décale un programme de trois secondes, la vue de l'opérateur se rafraîchit à l'état corrigé, mais ne montre pas encore ce qui a bougé et pourquoi. Les données existent déjà dans la charge utile de la réponse ; une notification (toast), une barre latérale ou une vue de différence est prévue pour la prochaine itération de l'interface utilisateur du planificateur.
Résultats
- Les opérateurs peuvent déposer un programme n'importe où sur la chronologie, et le système rend la grille de programme résultante légale en un seul passage déterministe — pas de modales de conflit, pas de murs d'erreur, pas de calcul d'alignement manuel.
- Un seul algorithme à quatre règles couvre tous les cas — nouveau-vs-nouveau, nouveau-vs-existant, et décalages en cascade à travers les limites du jour — sans traiter aucun d'eux comme un cas spécial.
- Les programmes à cheval sur minuit et les transitions d'heure d'été/hiver passent par la même requête de chevauchement de plage horaire que tout autre cas ; il n'y a pas de « chemin de code de minuit » séparé à maintenir.
- La réinitialisation du jour ne peut pas accidentellement retirer un programme en direct de l'antenne — le tampon de 2 minutes et la préservation des reports s'appliquent à chaque chaîne, à chaque fois.
- Le contrat Lambda d'abord / base de données ensuite rend la dérive silencieuse de la grille de programme impossible : MongoDB et MediaLive sont garantis de s'accorder, ou l'opérateur voit une erreur explicite.
MIN_NEIGHBOUR_GAP_MSest le seul bouton réglable. Chaque marge de sécurité, chaque règle d'alignement et chaque fenêtre de chevauchement y fait référence, de sorte qu'ajuster la définition de « légal » de la plateforme est un changement d'une seule ligne.
Réflexions Finales
La chose la plus intéressante à propos de ce système n'est pas une seule règle — c'est le petit nombre de règles nécessaires. Quatre conditions sur une valeur d'écart, appliquées en un seul passage avant, couvrent tous les conflits qu'un opérateur peut créer, y compris les cascades multi-étapes à travers les limites de minuit. C'est un résultat de conception délibéré : la complexité a été mise à obtenir les règles correctes une seule fois, plutôt que dans la gestion d'une liste toujours croissante de cas spéciaux.
La leçon plus large se généralise au-delà des logiciels de planification : lorsqu'un système a des contraintes externes strictes — l'espacement minimum d'un encodeur, les garanties de cohérence d'une base de données, une diffusion en direct qui ne peut pas être "dé-diffusée" — l'endroit le plus sûr pour faire respecter ces contraintes est un petit nombre de règles déterministes appliquées de manière cohérente, et non une gestion ad hoc dispersée dans le code. Et lorsque deux systèmes d'enregistrement (ici, MongoDB et MediaLive) doivent rester synchronisés, ordonner les écritures de manière à ce que l'échec les laisse toujours dans un état connu et concordant vaut la latence supplémentaire qu'il coûte.
Ă€ Propos de MicrocosmWorks
Chez MicrocosmWorks, nous développons des logiciels de qualité production pour les organisations qui résolvent des problèmes d'ingénierie complexes.
Notre expertise comprend les applications d'IA, les plateformes SaaS, les logiciels d'entreprise, les systèmes cloud-native, les technologies média et l'architecture backend personnalisée.
À travers notre blog d'ingénierie, nous partageons les leçons pratiques tirées de la conception et de l'exploitation de systèmes de production réels.
Poursuivre la Lecture
Si vous avez apprécié cet article, vous pourriez également trouver ces sujets utiles :

