Scoring Bullpadel Show 06
Pour qui est cette page ?

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

Besoin d'une version PDF imprimable ?

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.

Lecture ponctuelle (snapshot REST)
/api/tournoi/snapshot

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

Flux temps réel (Server-Sent Events)
/api/stream/tournoi

Abonnement 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

bash
# 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/tournoi

Exemple JavaScript / navigateur

js
// 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

ChampTypeDescription
idchaîneIdentifiant interne du tournoi
nomchaîneNom du tournoi à afficher (« Bullpadel Show 06 » par défaut)
etat« programme » · « en-cours » · « cloture »État global du tournoi
scoreGlobalobjetScore 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)
scoreGlobalAffichageobjetJumeau 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
scoreParJourobjetScore 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
scoreParJourAffichageobjetJumeau texte de scoreParJour (jour1 et jour2 stringifiés, jamais null, jamais vide)
epreuveCouranteIdchaîne ou nullIdentifiant de l'épreuve actuellement sélectionnée par la tablette arbitre. null = aucune sélection
equipesobjetLes deux équipes en lice (voir section suivante)
epreuvestableauToutes les épreuves du tournoi dans l'ordre (matchs + concours + Jeu en Or)
transitionsobjetSnapshots 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

ChampTypeDescription
id« equipe1 » ou « equipe2 »Position de l'équipe dans le tournoi en cours (rôle, pas identité métier)
equipeIdchaîneIdentifiant 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
nomchaîneNom à afficher (modifiable dans /admin)
couleur« bleu » ou « rose »Couleur officielle de l'équipe pour vos animations graphiques
joueurstableauRoster 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.

ChampTypeDescription
typevoir liste ci-dessous« match-frenchies », « match-galactic », « magic-puerta », « crazy-volley », « epic-defense » ou « jeu-en-or »
idchaîneIdentifiant de l'épreuve
libellechaîneLibellé prêt à afficher (« Match Frenchies — Jour 1 », « Match Galacticas — Jour 2 », « Magic Puerta (Galacticas J1) », etc.)
jour1 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 » · nullVainqueur si l'épreuve est terminée, sinon null
pointsRapportesnombreCombien de points cette épreuve apporte au score global (2 pour un match, 1 pour un concours)
compositionobjetLes 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)

ChampTypeDescription
scoring.pointsobjetPoints 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.pointsAffichageobjetJumeau 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.jeuxobjetJeux gagnés dans le set courant, en CHIFFRES. Champs equipe1 / equipe2
scoring.jeuxAffichageobjetJumeau TEXTE de scoring.jeux. Chiffre stringifié quand le match a démarré, tiret « — » sinon
scoring.setsobjetSets gagnés dans le match, en CHIFFRES
scoring.setsAffichageobjetJumeau TEXTE de scoring.sets. Chiffre stringifié quand le match a démarré, tiret « — » sinon
scoring.setActuelnombreNumé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.puntoDeOroFinalbooléenVrai pendant le Punto de Oro à 4-4 du super tie-break : le prochain point gagne le match
scoring.setsArchivestableauSets précédents avec leur score final, en CHIFFRES (utile pour afficher « set 1 : 6-3 »)
scoring.setsArchivesAffichagetableauJumeau 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)

ChampTypeDescription
timerobjetChrono du match (détail des sous-champs juste en dessous)
puntoDeOroChronoFinbooléenVrai 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).

ChampTypeDescription
chronoAffichagechaî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
tempsRestantMsnombreTemps restant en millisecondes, calculé par le moteur au moment de l'envoi (utile si vous voulez vos propres formats d'affichage)
dureeInitialeMsnombreDurée totale du chrono à l'origine (30 min pour Frenchies, 30 s ou 40 s pour les concours)
tempsEcouleMsnombreTemps écoulé sauvegardé au dernier point de synchronisation (sert au recalcul côté template, voir conseil)
enCoursbooléenVrai si le chrono tourne en ce moment, faux à l'arrêt ou en pause
derniereSyncMsnombreHorodatage 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)

ChampTypeDescription
scoreobjetScore brut du concours, en CHIFFRES (nombre entier de réussites par équipe)
scoreAffichageobjetJumeau 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)
timerobjetChrono dédié (30s, 30s, 40s selon le concours)
chronoExpirebooléenVrai 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
modeCorrectionbooléenVrai si l'arbitre a réactivé la saisie après l'expiration du chrono pour corriger un point
joueurCibleIndex0 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
switchEffectueboolé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.

ChampTypeDescription
pointsobjetScore du super tie-break final, en CHIFFRES (entiers 0 à 5). Champs : equipe1, equipe2
pointsAffichageobjetJumeau 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
puntoDeOroFinalbooléenVrai 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 » · nullVainqueur du Jeu en Or (et donc du tournoi) une fois les 5 points atteints
compositionMixteValideebooléenLe 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.

ChampTypeDescription
transitions.dernierJeuFiniobjet ou nullSnapshot du dernier jeu qui vient de se terminer (mode normal, tie-break ou super tie-break)
transitions.dernierSetFiniobjet ou nullSnapshot du dernier set qui vient de se terminer
transitions.derniereEpreuveFinieobjet ou nullSnapshot de la dernière épreuve qui vient de se terminer (match, concours ou Jeu en Or)

Sous-champs de transitions.dernierJeuFini

ChampTypeDescription
epreuveIdchaîneIdentifiant du match dans lequel le jeu s'est terminé
libelleEpreuvechaîneLibellé prêt à afficher du match (« Match Galacticas — Jour 1 », etc.)
numeroSetnombreNumé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
scoreFinalobjetScore à 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
horodatageMsnombreTimestamp Unix (ms) du moment exact où le jeu s'est terminé. Comparez avec Date.now() pour calculer l'ancienneté

Sous-champs de transitions.dernierSetFini

ChampTypeDescription
epreuveIdchaîneIdentifiant du match dans lequel le set s'est terminé
libelleEpreuvechaîneLibellé prêt à afficher du match
numeroSetnombreNuméro du set qui vient de se terminer
vainqueur« equipe1 » ou « equipe2 »Équipe qui a remporté le set
jeuxFinauxobjetJeux finaux du set (ex : 6-4, 7-5, ou 7-6 si tie-break). Champs equipe1 et equipe2
parTieBreakbooléenVrai si le set s'est terminé sur un tie-break
parSuperTieBreakbooléenVrai si le set s'est terminé sur un super tie-break (cas du 3ᵉ set d'un match Galactic après 1-1)
horodatageMsnombreTimestamp Unix (ms) du moment exact où le set s'est terminé

Sous-champs de transitions.derniereEpreuveFinie

ChampTypeDescription
epreuveIdchaîneIdentifiant 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
libellechaîneLibellé prêt à afficher de l'épreuve
vainqueur« equipe1 » ou « equipe2 »Équipe qui a remporté l'épreuve
scoreFinalLibellechaîneScore 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)
horodatageMsnombreTimestamp Unix (ms) du moment exact où l'épreuve s'est terminée

3 · Cas particuliers et conseils

Contrat « zéro null, zéro vide » — version texte de chaque score
Pour éviter qu'un template broadcast (Ross XPression, vMix, Singular.live, Caspar, Chyron…) affiche par accident « null », « undefined » ou un blanc à l'antenne, chaque score numérique du JSON est livré en double : une version CHIFFRE (le champ historique) et une version TEXTE (champ suffixé 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).
Le chrono : champ déjà calculé vs chrono fluide entre deux messages
Le moteur vous envoie 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 :
js
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)
}
La formule reproduit exactement ce que font la tablette et la régie télé, écart d'horloge compris.
Affichage des points selon le mode de scoring
En mode normal, 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), utilisezscoring.pointsAffichage qui livre directement « 0 », « 15 », « 30 », « 40 » ou le nombre stringifié en tie-break — voir le conseil « Contrat zéro null » plus haut.
Quelle épreuve afficher ?
Le champ 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.
Composition par épreuve
Chaque épreuve porte sa propre composition : 2 joueurs par équipe. Pour afficher « Pierre & Marc contre Sophie & Léa », croisez epreuve.composition.equipe1 (paire d'identifiants) avec tournoi.equipes.equipe1.joueurs pour récupérer prénom + nom.
Garder le score précédent à l'antenne pendant qu'un nouveau jeu démarre
Le bloc 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é :
js
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}`)
  }
}
Politique : un champ rempli reste rempli jusqu'à la prochaine transition du même type. À l'ouverture du tournoi, les trois champs valent 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.
Reconnexion et reprise
Le flux SSE est conçu pour survivre aux coupures réseau. Votre logiciel n'a rien de spécial à faire : il se reconnecte automatiquement et reçoit l'état complet à jour. Si vous voulez une garantie supplémentaire, vous pouvez aussi appeler le snapshot REST en parallèle toutes les 5 secondes : la donnée sera toujours cohérente entre les deux canaux.

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.

JSON en direct
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.