Cette page s'adresse à l'équipe technique régie qui veut afficher le scoring à l'antenne via son propre logiciel de diffusion (vMix, OBS, CasparCG, Singular Live, etc.). Elle décrit comment récupérer les données brutes du tournoi en JSON, ce que contient chaque champ, et comment se brancher.
Si vous cherchez juste un écran de suivi prêt à l'emploi (score géant, chrono, etc.), utilisez plutôt la Régie télé.
Le bouton ouvre une version épurée de la doc dans un nouvel onglet et lance la modale d'impression du navigateur — choisissez « Enregistrer au format PDF » comme imprimante de destination.
1 · Comment vous brancher
Deux URL à connaître. La première vous donne l'état complet du tournoi en une requête. La seconde vous abonne aux mises à jour en temps réel. Vous pouvez utiliser l'une, l'autre, ou les deux ensemble.
/api/tournoi/snapshotRenvoie l'état complet du tournoi en JSON. Idéal pour une interrogation périodique (toutes les 1 à 2 secondes) ou pour récupérer l'état initial au démarrage du logiciel.
/api/stream/tournoiAbonnement push : le moteur vous envoie chaque changement (point marqué, jeu gagné, changement d'épreuve, etc.) au moment où il se produit. Reconnexion automatique en cas de coupure réseau. La plupart des logiciels de diffusion modernes (vMix, OBS Studio, CasparCG, Singular Live) savent consommer ce protocole standard.
Exemple en ligne de commande
# Snapshot ponctuel (vous récupérez l'état complet une fois)
curl /api/tournoi/snapshot
# Flux temps réel (chaque événement s'affiche au fur et à mesure)
curl -N /api/stream/tournoiExemple JavaScript / navigateur
// 1. Récupérer l'état initial
const reponse = await fetch("/api/tournoi/snapshot")
const { data } = await reponse.json()
const tournoi = data.tournoi
// 2. S'abonner aux mises à jour en temps réel
const flux = new EventSource("/api/stream/tournoi")
flux.addEventListener("snapshot", (e) => {
// Premier message : état complet du tournoi
const { tournoi } = JSON.parse(e.data)
monTemplate.mettreAJour(tournoi)
})
flux.addEventListener("evenement", (e) => {
// Chaque changement métier (point, jeu, set, fin de match, etc.)
const { tournoi, ev } = JSON.parse(e.data)
monTemplate.mettreAJour(tournoi)
})2 · Format des données
Le JSON renvoyé par les deux URL est identique en structure : il contient toujours un objet tournoi avec tous les champs ci-dessous.
Niveau racine — objet tournoi
| Champ | Type | Description |
|---|---|---|
| id | chaîne | Identifiant interne du tournoi |
| nom | chaîne | Nom du tournoi à afficher (« Bullpadel Show 06 » par défaut) |
| etat | « programme » · « en-cours » · « cloture » | État global du tournoi |
| scoreGlobal | objet | Score général cumulé sur tout le tournoi (2 jours) : 2 points par match gagné, 1 point par concours gagné. Max théorique 24 points (6 matchs + 12 concours). Champs : equipe1 (nombre), equipe2 (nombre) |
| scoreGlobalAffichage | objet | Jumeau texte de scoreGlobal pour les templates qui attendent une chaîne. Toujours stringifié (« 0 », « 3 »…), jamais null, jamais vide. Le score global est toujours significatif dès le début du tournoi, donc pas de tiret ici |
| scoreParJour | objet | Score décomposé par journée du tournoi. Champs : jour1 (objet equipe1/equipe2), jour2 (objet equipe1/equipe2). Utile pour afficher un bandeau récap du jour 1 le jour 2 |
| scoreParJourAffichage | objet | Jumeau texte de scoreParJour (jour1 et jour2 stringifiés, jamais null, jamais vide) |
| epreuveCouranteId | chaîne ou null | Identifiant de l'épreuve actuellement sélectionnée par la tablette arbitre. null = aucune sélection |
| equipes | objet | Les deux équipes en lice (voir section suivante) |
| epreuves | tableau | Toutes les épreuves du tournoi dans l'ordre (matchs + concours + Jeu en Or) |
| transitions | objet | Snapshots figés des dernières transitions du tournoi (dernier jeu, dernier set, dernière épreuve qui viennent de se terminer). Permet de garder à l'antenne le score finalisé pendant qu'un nouveau jeu démarre. Voir détail plus bas. |
Champs additionnels à ignorer côté régie : le JSON contient aussi poolJoueurs,poolEquipes, equipe1Id et equipe2Id. Ces champs servent à la page d'administration pour gérer un pool global d'équipes et de joueurs (création/réservation pour de futures compétitions, joueurs partagés entre plusieurs équipes). La régie n'en a pas besoin :tournoi.equipes.equipe1 et equipe2 exposent déjà tout ce qu'il faut afficher avec les joueurs résolus directement.
Une équipe — tournoi.equipes.equipe1 / equipe2
| Champ | Type | Description |
|---|---|---|
| id | « equipe1 » ou « equipe2 » | Position de l'équipe dans le tournoi en cours (rôle, pas identité métier) |
| equipeId | chaîne | Identifiant métier stable de l'équipe dans le pool global. À utiliser si vous voulez un identifiant qui ne change pas même si l'organisation renomme l'équipe ou la déplace de position |
| nom | chaîne | Nom à afficher (modifiable dans /admin) |
| couleur | « bleu » ou « rose » | Couleur officielle de l'équipe pour vos animations graphiques |
| joueurs | tableau | Roster complet de l'équipe (au moins 2 joueurs, par défaut 6). Chaque joueur a id, prenom, nom, genre, et un booléen frenchy=true si c'est un joueur français du Match Frenchies (lever de rideau) |
Une épreuve — un élément de tournoi.epreuves
Il y a quatre types d'épreuves, repérés par le champ type. Les champs communs sont listés en premier ; les champs spécifiques varient ensuite.
| Champ | Type | Description |
|---|---|---|
| type | voir liste ci-dessous | « match-frenchies », « match-galactic », « magic-puerta », « crazy-volley », « epic-defense » ou « jeu-en-or » |
| id | chaîne | Identifiant de l'épreuve |
| libelle | chaîne | Libellé prêt à afficher (« Match Frenchies — Jour 1 », « Match Galacticas — Jour 2 », « Magic Puerta (Galacticas J1) », etc.) |
| jour | 1 ou 2 (indéfini sur le Jeu en Or) | Numéro de la journée du tournoi sur laquelle l'épreuve est programmée — utile pour grouper l'affichage par jour ou afficher un récap du jour précédent |
| etat | « programmee » · « en-cours » · « en-pause » · « terminee » | État de l'épreuve |
| vainqueurId | « equipe1 » · « equipe2 » · null | Vainqueur si l'épreuve est terminée, sinon null |
| pointsRapportes | nombre | Combien de points cette épreuve apporte au score global (2 pour un match, 1 pour un concours) |
| composition | objet | Les 2 joueurs de chaque équipe qui jouent cette épreuve. Champs : equipe1 (paire d'identifiants), equipe2 (paire d'identifiants) |
Champs spécifiques aux matchs (Frenchies + Galactic)
| Champ | Type | Description |
|---|---|---|
| scoring.points | objet | Points dans le jeu en cours, en CHIFFRES. En mode normal : 0, 15, 30, 40. En mode tie-break ou super-tie-break : nombre entier |
| scoring.pointsAffichage | objet | Jumeau TEXTE de scoring.points pour les templates qui attendent une chaîne. Garantie jamais null, jamais vide. Chiffre stringifié quand le match a démarré (« 0 », « 15 », « 30 », « 40 »), tiret « — » quand le match n'a pas encore démarré (etat = programmee) |
| scoring.jeux | objet | Jeux gagnés dans le set courant, en CHIFFRES. Champs equipe1 / equipe2 |
| scoring.jeuxAffichage | objet | Jumeau TEXTE de scoring.jeux. Chiffre stringifié quand le match a démarré, tiret « — » sinon |
| scoring.sets | objet | Sets gagnés dans le match, en CHIFFRES |
| scoring.setsAffichage | objet | Jumeau TEXTE de scoring.sets. Chiffre stringifié quand le match a démarré, tiret « — » sinon |
| scoring.setActuel | nombre | Numéro du set en cours (1 ou 2 ; 3 = super tie-break) |
| scoring.serviceEquipe | « equipe1 » ou « equipe2 » | Équipe au service sur le point courant |
| scoring.modeScoring | « normal » · « tie-break » · « super-tie-break » | Type de point en cours — détermine le format d'affichage des points |
| scoring.puntoDeOroFinal | booléen | Vrai pendant le Punto de Oro à 4-4 du super tie-break : le prochain point gagne le match |
| scoring.setsArchives | tableau | Sets précédents avec leur score final, en CHIFFRES (utile pour afficher « set 1 : 6-3 ») |
| scoring.setsArchivesAffichage | tableau | Jumeau TEXTE de scoring.setsArchives, chaque entrée a equipe1 et equipe2 stringifiés. Tableau vide tant qu'aucun set archivé |
Spécifique au Match Frenchies (lever de rideau, chrono 30 min)
| Champ | Type | Description |
|---|---|---|
| timer | objet | Chrono du match (détail des sous-champs juste en dessous) |
| puntoDeOroChronoFin | booléen | Vrai si le chrono a expiré avec égalité aux jeux — le prochain point décide |
Objet timer (présent dans Frenchies et concours)
Le chrono est sérialisé avec deux champs déjà calculés par le moteur à chaque envoi (snapshot REST + heartbeat SSE chaque seconde) : chronoAffichage ettempsRestantMs. Vous pouvez les utiliser directement sans refaire le calcul vous-mêmes. Les champs bruts restent disponibles si vous voulez un chrono qui tique côté template entre deux messages (voir conseil « Chrono fluide entre deux messages » plus bas).
| Champ | Type | Description |
|---|---|---|
| chronoAffichage | chaîne « M:SS » | Chrono déjà formaté prêt à afficher (« 28:21 », « 0:30 », « 0:00 » à expiration). Calculé par le moteur au moment de l'envoi |
| tempsRestantMs | nombre | Temps restant en millisecondes, calculé par le moteur au moment de l'envoi (utile si vous voulez vos propres formats d'affichage) |
| dureeInitialeMs | nombre | Durée totale du chrono à l'origine (30 min pour Frenchies, 30 s ou 40 s pour les concours) |
| tempsEcouleMs | nombre | Temps écoulé sauvegardé au dernier point de synchronisation (sert au recalcul côté template, voir conseil) |
| enCours | booléen | Vrai si le chrono tourne en ce moment, faux à l'arrêt ou en pause |
| derniereSyncMs | nombre | Horodatage Unix (ms) du dernier point de synchronisation. Sert à reconstituer un chrono fluide entre deux messages |
Spécifique aux concours (Magic Puerta, Crazy Volley, Epic Defense)
| Champ | Type | Description |
|---|---|---|
| score | objet | Score brut du concours, en CHIFFRES (nombre entier de réussites par équipe) |
| scoreAffichage | objet | Jumeau TEXTE de score pour les templates qui attendent une chaîne. Chiffre stringifié quand le concours a démarré, tiret « — » quand le concours n'a pas encore démarré (etat = programmee) |
| timer | objet | Chrono dédié (30s, 30s, 40s selon le concours) |
| chronoExpire | booléen | Vrai dès que le chrono atteint zéro |
| phase | « normale » ou « prolongation » | En cas d'égalité après le chrono, une prolongation est lancée |
| modeCorrection | booléen | Vrai si l'arbitre a réactivé la saisie après l'expiration du chrono pour corriger un point |
| joueurCibleIndex | 0 ou 1 (Epic Defense uniquement) | Joueur attaqué actuellement (0 = premier joueur de l'équipe en défense, 1 = second) |
| equipeEnDefense | « equipe1 » ou « equipe2 » (Epic Defense) | Équipe en défense sur ce concours |
| switchEffectue | booléen (Epic Defense) | Vrai après le switch automatique à mi-chrono qui change la cible |
Spécifique au Jeu en Or (finale éventuelle à 12-12 sur le cumul 2 jours)
Le Jeu en Or se joue en super tie-break, premier à 5 points, point en or à 4-4 (mort subite : à 4-4 le point suivant donne la victoire 5-4). L'arbitre marque les points un par un comme sur un tie-break : pas de jeux ni de sets ici, juste un compteur de points par équipe.
| Champ | Type | Description |
|---|---|---|
| points | objet | Score du super tie-break final, en CHIFFRES (entiers 0 à 5). Champs : equipe1, equipe2 |
| pointsAffichage | objet | Jumeau TEXTE de points (jamais null, jamais vide). Chiffre stringifié quand le Jeu en Or a démarré, tiret « — » tant qu'il est programmee. Champs : equipe1, equipe2 |
| puntoDeOroFinal | booléen | Vrai dès 4-4 : mort subite (point en or final). Le prochain point gagne le titre. Affichez un bandeau « POINT EN OR · 4-4 » |
| vainqueurId | « equipe1 » · « equipe2 » · null | Vainqueur du Jeu en Or (et donc du tournoi) une fois les 5 points atteints |
| compositionMixteValidee | booléen | Le Jeu en Or exige une composition mixte (1 H + 1 F par équipe). Ce drapeau confirme que la composition retenue respecte la règle. Tant qu'il est faux, l'arbitre ne peut pas marquer de point |
Bloc tournoi.transitions — snapshots figés des dernières transitions
Ce bloc expose, en parallèle de l'état temps réel, trois snapshots figés des dernières transitions du tournoi : le dernier jeu qui vient de se terminer dans un match, le dernier set qui vient de se terminer, et la dernière épreuve qui vient de se terminer (match, concours ou Jeu en Or). Chaque snapshot porte un horodatage précis de fin (horodatageMs, milliseconde Unix), ce qui vous permet, dans votre habillage, de décider combien de temps vous gardez l'info à l'antenne — par exemple, garder le score finalisé du jeu précédent visible pendant 10 secondes avant de basculer sur le nouveau jeu, surtout utile quand le Jeu en Or démarre brusquement après un 12-12 final au score général.
Politique de conservation : une fois rempli, un champ reste rempli indéfiniment jusqu'à la prochaine transition du même type qui le remplace. À l'ouverture du tournoi (aucune transition encore survenue), les trois champs valent null.
| Champ | Type | Description |
|---|---|---|
| transitions.dernierJeuFini | objet ou null | Snapshot du dernier jeu qui vient de se terminer (mode normal, tie-break ou super tie-break) |
| transitions.dernierSetFini | objet ou null | Snapshot du dernier set qui vient de se terminer |
| transitions.derniereEpreuveFinie | objet ou null | Snapshot de la dernière épreuve qui vient de se terminer (match, concours ou Jeu en Or) |
Sous-champs de transitions.dernierJeuFini
| Champ | Type | Description |
|---|---|---|
| epreuveId | chaîne | Identifiant du match dans lequel le jeu s'est terminé |
| libelleEpreuve | chaîne | Libellé prêt à afficher du match (« Match Galacticas — Jour 1 », etc.) |
| numeroSet | nombre | Numéro du set dans lequel le jeu s'est terminé (1, 2, ou 3 = super tie-break) |
| vainqueur | « equipe1 » ou « equipe2 » | Équipe qui a remporté le jeu |
| scoreFinal | objet | Score à l'instant T de la fin du jeu, avant le reset des points. En mode normal : equipe1 et equipe2 valent 0, 15, 30 ou 40 (le gagnant est à 40). En tie-break / super tie-break : entiers (ex : 7-5, 10-8) |
| modeScoring | « normal » · « tie-break » · « super-tie-break » | Mode dans lequel le jeu s'est terminé — détermine comment afficher scoreFinal |
| horodatageMs | nombre | Timestamp Unix (ms) du moment exact où le jeu s'est terminé. Comparez avec Date.now() pour calculer l'ancienneté |
Sous-champs de transitions.dernierSetFini
| Champ | Type | Description |
|---|---|---|
| epreuveId | chaîne | Identifiant du match dans lequel le set s'est terminé |
| libelleEpreuve | chaîne | Libellé prêt à afficher du match |
| numeroSet | nombre | Numéro du set qui vient de se terminer |
| vainqueur | « equipe1 » ou « equipe2 » | Équipe qui a remporté le set |
| jeuxFinaux | objet | Jeux finaux du set (ex : 6-4, 7-5, ou 7-6 si tie-break). Champs equipe1 et equipe2 |
| parTieBreak | booléen | Vrai si le set s'est terminé sur un tie-break |
| parSuperTieBreak | booléen | Vrai si le set s'est terminé sur un super tie-break (cas du 3ᵉ set d'un match Galactic après 1-1) |
| horodatageMs | nombre | Timestamp Unix (ms) du moment exact où le set s'est terminé |
Sous-champs de transitions.derniereEpreuveFinie
| Champ | Type | Description |
|---|---|---|
| epreuveId | chaîne | Identifiant de l'épreuve qui vient de se terminer |
| type | « match-frenchies » · « match-galactic » · « magic-puerta » · « crazy-volley » · « epic-defense » · « jeu-en-or » | Type d'épreuve |
| libelle | chaîne | Libellé prêt à afficher de l'épreuve |
| vainqueur | « equipe1 » ou « equipe2 » | Équipe qui a remporté l'épreuve |
| scoreFinalLibelle | chaîne | Score final déjà formaté (« Sets 2-1 » pour un match Galactic, « Jeux 4-2 » pour un match Frenchies, « 8-5 » pour un concours, « Jeu en Or remporté » pour le Jeu en Or) |
| horodatageMs | nombre | Timestamp Unix (ms) du moment exact où l'épreuve s'est terminée |
3 · Cas particuliers et conseils
Affichage).La version TEXTE est garantie : jamais null, jamais absente, jamais vide. Elle vaut soit le chiffre stringifié (« 0 », « 15 », « 40 », « 3 », etc.) quand le score a un sens, soit un tiret « — » quand l'épreuve n'a pas encore démarré (état
programmee). Le tiret évite la confusion « match en cours à 0-0 » dans les bandeaux d'épreuves pas encore jouées.Quel champ utiliser dans votre template ?
- Si la case attend un nombre (calcul d'animation, position, barre de progression) → utilisez le champ chiffre historique (
scoring.points,scoring.jeux, etc.). Pour les épreuves pas encore démarrées, il vaudra 0 - Si la case attend du texte (cellule alphanumérique de scoreboard, bandeau lower-third) → utilisez le jumeau Affichage (
scoring.pointsAffichage,scoring.jeuxAffichage, etc.) qui livrera le chiffre ou le tiret. Aucune valeur vide ne pourra apparaître
Champs concernés (CHIFFRE + jumeau TEXTE côte à côte) :
scoring.points,scoring.jeux, scoring.sets, scoring.setsArchives,score (concours), scoreGlobal (tournoi).timer.chronoAffichage (format « M:SS », ex. « 28:21 ») ettimer.tempsRestantMs déjà calculés à chaque envoi. Si vous interrogez le snapshot REST toutes les secondes ou que vous lisez le flux SSE (heartbeat à 1 Hz), c'est le chemin le plus simple : affichez chronoAffichage tel quel, basta.Si vous voulez un chrono qui tique en continu côté template (entre deux messages, à 30 ou 60 Hz), recalculez localement avec la même formule que la tablette et la régie télé. Point crucial :
derniereSyncMs est marqué avec l'horloge du moteur. Si vous le comparez à l'horloge de votre propre machine et que les deux ne sont pas parfaitement synchronisées, votre chrono sera décalé de l'écart entre les deux horloges. Mémorisez donc cet écart à chaque message (le moteur joint son heure dans ts au battement ethorodatage au snapshot) et corrigez-le :let decalageHorloge = 0 // écart entre votre horloge et celle du moteur
// À chaque message reçu (battement ou snapshot) :
// decalageHorloge = Date.now() - new Date(message.ts).getTime()
function tempsRestantMs(timer) {
const maintenantMoteur = Date.now() - decalageHorloge
const ecoule = timer.enCours
? timer.tempsEcouleMs + (maintenantMoteur - timer.derniereSyncMs)
: timer.tempsEcouleMs
return Math.max(0, timer.dureeInitialeMs - ecoule)
}scoring.points vaut 0, 15, 30 ou 40 — affichez tel quel. En mode tie-break ou super tie-break, c'est un entier (0, 1, 2, …) — affichez tel quel aussi. Le champ modeScoring vous permet de savoir dans quel mode on est et d'afficher éventuellement une bannière « TIE-BREAK » ou « PUNTO DE ORO FINAL ».Si votre template a besoin d'une chaîne déjà stringifiée (jamais null), utilisez
scoring.pointsAffichage qui livre directement « 0 », « 15 », « 30 », « 40 » ou le nombre stringifié en tie-break — voir le conseil « Contrat zéro null » plus haut.tournoi.epreuveCouranteId vous donne l'identifiant de l'épreuve sur laquelle l'arbitre est en train de saisir. Récupérez l'épreuve correspondante dans le tableautournoi.epreuves. C'est l'épreuve à mettre en avant à l'antenne.epreuve.composition.equipe1 (paire d'identifiants) avec tournoi.equipes.equipe1.joueurs pour récupérer prénom + nom.tournoi.transitions vous donne en parallèle de l'état temps réel les snapshots figés du dernier jeu / dernier set / dernière épreuve qui viennent de se terminer. Cas typique d'usage : vous voulez garder à l'antenne le score finalisé du jeu précédent pendant que vous préparez l'incrustation du nouveau jeu (en particulier si le Jeu en Or démarre brusquement après un 12-12 au score général). Dans votre template, liseztransitions.dernierJeuFini et calculez l'ancienneté :const t = tournoi.transitions.dernierJeuFini
if (t) {
const ageMs = Date.now() - t.horodatageMs
if (ageMs < 10000) {
// Le jeu s'est terminé il y a moins de 10s — on l'affiche encore
afficherBandeau(`${t.libelleEpreuve} · Set ${t.numeroSet} · ${
t.vainqueur === "equipe1" ? "Équipe 1" : "Équipe 2"
} gagne le jeu ${t.scoreFinal.equipe1}-${t.scoreFinal.equipe2}`)
}
}null. C'est vous, côté template, qui décidez via l'horodatage combien de temps vous gardez chaque information à l'antenne — le moteur ne fait pas ce choix à votre place.4 · Démo live — JSON courant du tournoi
Le bloc ci-dessous affiche exactement ce que reçoit votre logiciel à chaque mise à jour du tournoi. Ouvrez cette page sur un second écran pendant que la tablette arbitre tourne, et vous verrez les champs bouger en temps réel.
Connexion au flux…Une question ou un besoin spécifique côté template régie ? Le superviseur peut adapter le format de réponse : prévenez-le avant le tournoi pour qu'on prépare un ajustement si nécessaire.