Schéma de séance

Dernière mise à jour : 2026-05-29

Your Trainer est une application de cyclisme en intérieur multi-cyclistes pour tablettes Android. Contrôle du home-trainer avec données locales + contrôle local. Achat unique.

Le fichier de séance .ytw est du JSON brut — rédige-le dans n'importe quel éditeur de texte, importe-le via la feuille de partage, partage-le avec tes amis. L'éditeur visuel gère les cas courants ; le schéma est l'échappatoire quand tu as besoin d'un contrôle total.

Quand rédiger à la main

La plupart des cyclistes n'ont jamais besoin de cette page — l'éditeur visuel de séances et l'AI Workout Coach couvrent tout, d'une séance de 4×8 au Seuil à un empilement de microbursts. Tourne-toi vers le schéma quand :

Exemple minimal

Le plus court fichier .ytw valide est un programme avec un seul intervalle. Enregistre-le avec l'extension .ytw et partage-le vers Your Trainer.

{
  "programId": "my-sweet-spot",
  "programName": "My Sweet Spot 30",
  "description": "A short sweet-spot workout.",
  "totalDuration": 1800,
  "workoutType": "POWER",
  "primaryLocale": "en",
  "intervals": [
    { "id": "warmup",   "duration": 300,  "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Warmup",     "intervalType": "WARMUP"   },
    { "id": "work",     "duration": 1200, "targetPowerPercent": 88, "intensityZone": "Z3", "label": "Sweet Spot", "intervalType": "INTERVAL" },
    { "id": "cooldown", "duration": 300,  "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Cooldown",   "intervalType": "COOLDOWN" }
  ]
}

Champs de premier niveau

L'objet de premier niveau décrit un programme de séance. Les champs requis sont signalés.

ChampTypeDescription
programId requischaîneIdentifiant stable. Utilise le kebab-case (my-sweet-spot). La clé unique de la séance dans ta bibliothèque — réimporter un fichier avec le même programId met à jour l'entrée existante au lieu d'en créer un doublon.
programName requischaîneNom affiché dans la langue principale du cycliste.
description requischaîneDescription d'une ou deux phrases affichée sur la carte de la séance.
totalDuration requisentier (secondes)Durée totale de la séance. L'app la recalcule à partir des intervalles à l'enregistrement, donc tu peux la laisser incohérente pendant la rédaction.
intervals requistableauListe ordonnée d'objets intervalle ou de groupes de répétition.
workoutTypechaîneFamille de séance : POWER (par défaut), HR_ZONE ou ROUTE. Voir Types de séance.
variantchaîneSous-forme à l'intérieur de la famille : STANDARD (par défaut) ou RAMP_TEST.
primaryLocalechaîne (BCP-47)Langue dans laquelle les chaînes ont été rédigées. Par défaut "en". Pilote la chaîne de repli inter-langues.
categorychaîneCatégorisation en texte libre (par exemple "threshold", "endurance"). Optionnel.
difficultyentier (1–5)Difficulté subjective. Apparaît sur la carte de la séance.
isUserCreatedbooléenVrai pour les séances créées dans l'app par le cycliste ; faux pour les séances importées ou fournies. Par défaut false.
isFavoritebooléenIndicateur d'épinglage en haut. Les cyclistes le basculent dans l'app ; généralement omis dans les fichiers partagés.
routeProfiletableau de { distanceMeters, elevationMeters }Profil d'élévation complet pour les séances ROUTE. Null pour POWER et HR_ZONE.
stringsobjet (langue → LocaleStrings)Traductions par langue du nom, de la description, des étiquettes d'intervalles et des indications.

Champs d'intervalle

Chaque objet intervalle décrit un bloc de la séance. La forme du bloc dépend du workoutType parent — les blocs Power utilisent des pourcentages de puissance, les blocs HR-Zone utilisent une cible de zone.

ChampTypeDescription
duration requisentier (secondes)Durée du bloc en secondes.
targetPowerPercent requis pour powerentier (% de la FTP)Cible de puissance pour les séances POWER. Mutuellement exclusif avec targetHrZone.
targetPowerEndPercententier (% de la FTP)Puissance de fin de rampe optionnelle. Lorsqu'elle est présente, le wattage cible s'interpole linéairement de targetPowerPercenttargetPowerEndPercent sur la durée du bloc.
targetHrZone requis pour hrentier (1–5)Zone de FC pour les séances HR_ZONE. Mutuellement exclusif avec targetPowerPercent.
intensityZone requischaîneJeton visuel de zone : Z1Z5. Pilote la couleur sur la visualisation du terrain. Voir Zones d'entraînement.
intervalTypechaîneWARMUP, COOLDOWN ou INTERVAL (par défaut). Les blocs d'Échauffement et de Retour au calme sont exclus des résumés portant sur le seul travail (puissance moyenne des blocs de travail, temps en zone pour la portion de travail, etc.).
label requischaîneTexte affiché sur le bloc dans la langue principale du cycliste. Les variantes inter-langues se trouvent dans strings.<locale>.labels.
idchaîneSlug stable — la clé utilisée dans strings.<locale>.labels et dans la composition des clés d'indication. Recommandé pour toute séance livrée avec des traductions.
autoLabelbooléenVrai lorsque l'étiquette a été générée par un préréglage de l'éditeur plutôt que saisie par le cycliste. Les étiquettes automatiques sont localisées par Your Trainer lui-même et n'ont pas besoin d'entrées par langue dans strings. Par défaut false.
cadenceTargetentier (RPM)Cible de Cadence optionnelle pour le bloc (par exemple 60 pour de la grimpée à basse Cadence, 100 pour des exercices d'accélération de pédalage).
cuestableau de CoachingCueIndications de coaching qui se déclenchent pendant le bloc.

Indications de coaching

Une indication de coaching est un court texte qui apparaît en surimpression sur le cockpit pendant une sortie. Chaque indication a un décalage à l'intérieur de son intervalle parent, le texte à afficher et la durée pendant laquelle elle reste à l'écran.

ChampTypeDescription
offsetSec requisentier (secondes)Secondes depuis le début de l'intervalle parent auxquelles l'indication se déclenche.
text requischaîneTexte de l'indication dans la langue principale de la séance. Les variantes inter-langues se trouvent dans strings.<locale>.cues, indexées par <intervalId>:<cueIndex>.
durationSecentier (secondes)Durée pendant laquelle l'indication reste à l'écran. Par défaut 5.

Exemple d'intervalle avec trois indications (la composition de clé utilise l'id de l'intervalle parent + l'index de l'indication dans le tableau) :

{
  "id": "work",
  "duration": 600,
  "targetPowerPercent": 95,
  "intensityZone": "Z4",
  "label": "Threshold",
  "intervalType": "INTERVAL",
  "cues": [
    { "offsetSec": 0,   "text": "Settle in — find your rhythm." },
    { "offsetSec": 300, "text": "Halfway. Stay smooth.",  "durationSec": 8 },
    { "offsetSec": 540, "text": "One minute. Hold form." }
  ]
}

Chaînes localisées et chaîne de repli

Le bloc strings porte les traductions par langue de chaque chaîne visible par le cycliste dans la séance. Chaque entrée de langue a la même forme :

"strings": {
  "en": {
    "name": "Sweet Spot 30",
    "description": "A short sweet-spot workout.",
    "labels": { "warmup": "Warmup", "work": "Sweet Spot", "cooldown": "Cooldown" },
    "cues":   { "work:0": "Settle in", "work:1": "Halfway" }
  },
  "de": {
    "name": "Sweet Spot 30",
    "description": "Ein kurzes Sweet-Spot-Training.",
    "labels": { "warmup": "Aufwärmen", "work": "Sweet Spot", "cooldown": "Ausrollen" },
    "cues":   { "work:0": "Locker einrollen", "work:1": "Halbzeit" }
  }
}

Les clés d'indication suivent le motif <intervalId>:<cueIndex> — donc la première indication sur l'intervalle work a la clé work:0.

Pour chaque chaîne visible par le cycliste, l'app choisit la meilleure correspondance de langue dans cet ordre :

  1. strings[<rider-locale>] — la propre langue du cycliste.
  2. strings[primaryLocale] — la langue de l'auteur.
  3. strings["en"] — repli universel.
  4. Le champ de premier niveau (programName, label d'intervalle, text d'indication).

Les chaînes affichées depuis toute langue autre que celle du cycliste apparaissent en italique sur les cartes de séance et le cockpit, afin que le cycliste sache quelles chaînes n'ont pas encore été traduites.

See also: Compétences de prompt IA — the AI Coach can generate strings blocks for additional locales when prompted.

Groupes de répétition

Pour les structures répétitives, un groupe de répétition rédige l'unité une seule fois et indique à l'app combien de fois la jouer. Les groupes de répétition sont déployés en blocs individuels à l'import, afin que le cycliste voie chaque bloc dans la bande des intervalles à venir pendant la sortie.

{
  "intervals": [
    { "id": "warmup", "duration": 600, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Warmup", "intervalType": "WARMUP" },
    {
      "repeat": 4,
      "intervals": [
        { "id": "on",  "duration": 480, "targetPowerPercent": 95, "intensityZone": "Z4", "label": "Threshold" },
        { "id": "off", "duration": 240, "targetPowerPercent": 55, "intensityZone": "Z1", "label": "Recovery" }
      ]
    },
    { "id": "cooldown", "duration": 600, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Cooldown", "intervalType": "COOLDOWN" }
  ]
}

L'exemple ci-dessus se déploie en 1 Échauffement + 4×(Seuil + Récupération) + 1 Retour au calme = 10 blocs. Les groupes de répétition peuvent être imbriqués, mais le plat est préférable pour la lisibilité.

Types de séance et variantes

Le champ workoutType sélectionne le paradigme ; variant sélectionne une sous-forme à l'intérieur.

workoutTypeDe quoi il s'agitForme d'intervalle requise
POWER (par défaut)Intervalles ancrés sur la FTP. Le home-trainer roule au wattage cible en ERG, ou suit des courbes de résistance en SIM.Chaque intervalle a targetPowerPercent (et optionnellement targetPowerEndPercent pour les rampes).
HR_ZONEIntervalles pilotés par la Fréquence cardiaque. Le home-trainer ajuste le wattage en direct pour maintenir la FC du cycliste dans la zone cible — utile lorsque la charge cardiovasculaire est la métrique d'entraînement (Récupération, base, travail polarisé).Chaque intervalle a targetHrZone (1–5). Le bouton START affiche LIER HRM jusqu'à ce que le HRM soit connecté.
ROUTESorties de simulation pilotées par la pente. Le home-trainer suit le profil d'élévation depuis routeProfile ; le cycliste choisit la Cadence et le braquet.Les intervalles sont généralement vides ou contiennent un seul espace réservé pleine longueur. Le vrai contenu de la sortie se trouve dans routeProfile.

Le champ variant sélectionne une sous-forme :

Packs (.ytwpack)

Quand tu veux un lot organisé de séances en une seule fois, le format .ytwpack regroupe plusieurs fichiers .ytw avec un manifeste par pack. Un .ytwpack est une archive ZIP sous le capot — renomme-le en .zip et décompresse-le pour voir le contenu, ou importe-le via l'installateur de packs de Your Trainer pour installer toutes les séances en un seul tap.

Une archive .ytwpack contient :

Le manifeste du pack contient assez d'informations pour une fiche de décision d'installation sans avoir besoin d'ouvrir chaque séance. Champs de premier niveau :

ChampTypeDescription
schema_version requisentierActuellement toujours 1.
pack_id requischaîneIdentifiant stable en kebab-case (par exemple sweet-spot).
name requischaîneNom affiché dans le catalogue de packs.
description requischaîneRésumé d'une ou deux phrases visible avant que le cycliste ne tape pour installer.
version requischaîne (SemVer)MAJOR.MINOR.PATCH, -prerelease optionnel. Patch pour les correctifs de contenu ; mineure pour des séances additives ; majeure pour des changements de schéma cassants. Intégré dans le nom de fichier publié (v1.0.2.ytwpack).
content_hash requischaînesha256: sur la concaténation triée par slug des octets de chaque fichier .ytw. Stable d'une régénération à l'autre pour un contenu inchangé ; augmente chaque fois que les séances à l'intérieur changent.
generated_at requischaîne (ISO 8601)Horodatage UTC.
set requischaînepower ou hr-zone — la famille de séances à laquelle ce pack appartient.
category requischaîneSous-taxonomie à l'intérieur du set (par exemple sweet-spot).
workout_count requisentierNombre d'entrées .ytw dans le pack.
total_ride_time_seconds requisentierSomme des durées de chaque séance dans le pack.
experience_level requischaîneCalculé à partir de la plage de difficulté des contenus — l'un de beginner / intermediate / advanced / mixed. Format de fil en minuscules ; l'app met une majuscule pour l'affichage.
hrm_required requisbooléenVrai si une séance du pack utilise HR_ZONE.
type_mix requisobjetPourcentage par catégorie du temps total de sortie (somme à 100). Pilote le donut de répartition par type sur la fiche d'installation dans l'app.
duration_histogram requisobjetNombre de séances par tranche de durée : 0-30, 30-60, 60-90, 90+ (minutes). Pilote le graphique de durée de la fiche d'installation.
contents requistableauEntrées complètes par séance — sur-ensemble de la forme d'entrée du manifeste de bibliothèque ; chacune porte slug, name, duration_seconds, des métriques de résumé, plus un tableau sparkline pour le rendu de la vignette.

Deux chemins d'installation pour un .ytwpack téléchargé :

Exemple de manifeste de pack (avec contents tronqué pour la lisibilité) :

{
  "schema_version": 1,
  "pack_id": "sweet-spot",
  "name": "Sweet Spot",
  "description": "26 sweet-spot sessions across classic intervals, sustained stacks, and over-unders.",
  "version": "1.0.2",
  "content_hash": "sha256:9432a3a76015158dc71ec63…",
  "generated_at": "2025-05-28T00:00:00Z",
  "set": "power",
  "category": "sweet-spot",
  "workout_count": 26,
  "total_ride_time_seconds": 119340,
  "experience_level": "intermediate",
  "hrm_required": false,
  "type_mix":   { "sweet-spot": 100 },
  "duration_histogram": { "0-30": 0, "30-60": 5, "60-90": 12, "90+": 9 },
  "contents": [
    { "slug": "sweet-spot-3x10min-at-88pct-ftp",  "name": "Sweet Spot 3×10min @ 88% FTP",  "duration_seconds": 3300, "tss": 55.8,  "intensity_factor": 0.764, "sparkline": […] },
    { "slug": "sweet-spot-3x15min-at-90pct-ftp",  "name": "Sweet Spot 3×15min @ 90% FTP",  "duration_seconds": 4500, "tss": 81.6,  "intensity_factor": 0.808, "sparkline": […] }
    /* … 24 more workouts … */
  ]
}

Manifestes de bibliothèque et de pack

Deux manifestes sont publiés aux côtés des artefacts téléchargeables. Les deux sont en JSON brut ; les deux sont décrits par des documents JSON Schema que tu peux récupérer directement.

URLCe qu'il listeJSON Schema
library/manifest.json Chaque .ytw organisé dans la bibliothèque — métadonnées par séance pour les clients de navigation / recherche / filtre. Liste également les téléchargements .ytwpack disponibles (chemin de fichier, version, hash de contenu, résumé de répartition par type, URL d'icône). /schemas/workout-manifest.json
packs/manifest.json Point d'accès du catalogue de packs : chaque .ytwpack publié avec ses métadonnées de résumé. Même forme d'entrée par pack que le tableau packs du manifeste de bibliothèque ; la Pack Library intégrée à l'app récupère cela sur rafraîchissement initié par le cycliste. /schemas/workout-manifest.json
(à l'intérieur de chaque .ytwpack) Manifeste par pack transporté en tant que manifest.json à la racine de l'archive — le tableau ci-dessus en documente la forme. /schemas/workout-pack-manifest.json

Si tu construis un outil qui consomme la bibliothèque — un navigateur de séances sur mesure, un convertisseur de séances qui cible .zwo, un tableau de bord d'entraîneur qui fait apparaître des packs — ce sont les contrats à valider. La même forme d'entrée par séance apparaît dans les tableaux contents / workouts des deux manifestes, donc un client qui gère l'un gère l'autre.

Exemples détaillés

Intervalle en rampe

Un Échauffement de 5 minutes qui monte de 40 % de FTP à 75 % de FTP par interpolation linéaire :

{
  "id": "rampup",
  "duration": 300,
  "targetPowerPercent": 40,
  "targetPowerEndPercent": 75,
  "intensityZone": "Z1",
  "label": "Ramp up",
  "intervalType": "WARMUP"
}

Over-under

Trois séries de 2 minutes à 95 % de FTP / 1 minute à 105 % de FTP, exprimées sous forme de groupe de répétition :

{
  "repeat": 3,
  "intervals": [
    { "id": "under", "duration": 120, "targetPowerPercent": 95,  "intensityZone": "Z4", "label": "Under" },
    { "id": "over",  "duration": 60,  "targetPowerPercent": 105, "intensityZone": "Z5", "label": "Over"  }
  ]
}

Séance par zones de FC

Sortie d'Endurance Z2 de 30 minutes avec une accélération Z4 de 3 minutes au milieu :

{
  "programId": "hr-z2-with-surge",
  "programName": "Z2 with a Z4 surge",
  "description": "Steady Zone 2 with a single 3-minute Zone 4 surge.",
  "totalDuration": 1800,
  "workoutType": "HR_ZONE",
  "primaryLocale": "en",
  "intervals": [
    { "id": "warmup",  "duration": 300,  "targetHrZone": 1, "intensityZone": "Z1", "label": "Warmup", "intervalType": "WARMUP" },
    { "id": "endure1", "duration": 600,  "targetHrZone": 2, "intensityZone": "Z2", "label": "Endurance" },
    { "id": "surge",   "duration": 180,  "targetHrZone": 4, "intensityZone": "Z4", "label": "Surge" },
    { "id": "endure2", "duration": 420,  "targetHrZone": 2, "intensityZone": "Z2", "label": "Endurance" },
    { "id": "cooldown","duration": 300,  "targetHrZone": 1, "intensityZone": "Z1", "label": "Cooldown", "intervalType": "COOLDOWN" }
  ]
}

Multilingue

L'exemple minimal Sweet Spot avec des blocs strings EN + DE + NL. Même séance, trois expériences natives :

{
  "programId": "my-sweet-spot",
  "programName": "Sweet Spot 30",
  "description": "A short sweet-spot workout.",
  "totalDuration": 1800,
  "workoutType": "POWER",
  "primaryLocale": "en",
  "intervals": [
    { "id": "warmup",   "duration": 300,  "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Warmup",     "intervalType": "WARMUP"   },
    { "id": "work",     "duration": 1200, "targetPowerPercent": 88, "intensityZone": "Z3", "label": "Sweet Spot", "intervalType": "INTERVAL",
      "cues": [
        { "offsetSec": 0,   "text": "Settle in" },
        { "offsetSec": 600, "text": "Halfway" }
      ]
    },
    { "id": "cooldown", "duration": 300,  "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Cooldown",   "intervalType": "COOLDOWN" }
  ],
  "strings": {
    "en": {
      "name": "Sweet Spot 30",
      "description": "A short sweet-spot workout.",
      "labels": { "warmup": "Warmup", "work": "Sweet Spot", "cooldown": "Cooldown" },
      "cues":   { "work:0": "Settle in", "work:1": "Halfway" }
    },
    "de": {
      "name": "Sweet Spot 30",
      "description": "Ein kurzes Sweet-Spot-Training.",
      "labels": { "warmup": "Aufwärmen", "work": "Sweet Spot", "cooldown": "Ausrollen" },
      "cues":   { "work:0": "Locker einrollen", "work:1": "Halbzeit" }
    },
    "nl": {
      "name": "Sweet Spot 30",
      "description": "Een korte sweet-spot-training.",
      "labels": { "warmup": "Inrijden", "work": "Sweet Spot", "cooldown": "Uitrijden" },
      "cues":   { "work:0": "Rustig inrijden", "work:1": "Halverwege" }
    }
  }
}

Pièges courants

Référence

Pour des recherches programmatiques de spec de format (tables de champs, exemples, contraintes, glossaire), la source canonique lisible par machine est le Your Trainer MCP — appelez get_format_spec, get_canonical_examples, get_format_constraints ou get_format_glossary depuis n'importe quel client MCP. Ces outils servent depuis le même registre de connaissances qui alimente cette page ; si la réponse d'un outil diverge un jour de cette page, le MCP est canonique et cette page est obsolète.

JSON Schemas (contrats lisibles par machine pour les clients qui construisent sur ces formats) :

← Retour au Manuel et Guides