## Resume executif Nous utilisons actuellement l'export SQL complet produit par Louise comme source de verite. Cet export est importe dans une base MySQL locale, puis notre application interne consomme les donnees via l'API Mogador. Le probleme n'est pas uniquement l'existence de Mogador. Le probleme principal est que l'API actuelle est trop granulaire pour un besoin de synchronisation et de publication web moderne. Aujourd'hui, pour publier TFO, IDELLO, ONFR et Linear, notre application doit: - recuperer les programmations par plateforme; - boucler sur chaque programme; - recuperer le produit; - recuperer les collections; - recuperer les videos; - recuperer les presses; - recuperer les images; - recuperer la serie; - recuperer les URLs; - recuperer les versions; - recuperer les mots-cles; - recuperer les categories pedagogiques; - recuperer les sous-titres; - recuperer les territoires; - recuperer les pays autorises; - reconstruire elle-meme les relations collection -> saisons -> episodes. Cette logique genere beaucoup d'appels API, rallonge la synchronisation, complique les retries, augmente les risques de timeout et rend la publication difficile a auditer. Notre demande est donc de faire evoluer Mogador vers une API v2 orientee "sync/publication", capable de retourner des snapshots complets, des deltas, un statut d'import et des payloads relationnels prets a consommer. ## Objectif de l'API v2 L'API v2 doit permettre de produire rapidement des donnees de publication pour les sites web sans devoir faire des milliers de petits appels. Objectifs: - reduire fortement le nombre d'appels API; - permettre une synchronisation fiable sur une fenetre J a J+10; - permettre la comparaison entre deux exports; - fournir des donnees relationnelles completes; - exposer le statut du dernier export/import; - supporter une concurrence documentee; - supporter HTTPS avec validation SSL; - fournir des schemas de reponse stables et versionnes. ## Endpoints Mogador actuellement consommes Cette liste provient de l'application Laravel actuelle. ### Programmations ```http GET /programs/search.json?extended=true&channel_key={channel_key}&program_date={date} GET /programs/search.json?extended=true&channel_key={channel_key}&begin_date={from}&end_date={to} GET /programs/search.json?channel_key={channel_key}&id={collection_biznumber} GET /program/{program_key}.json GET /program/{program_key}.json?extended=true GET /program/{program_key}/product.json GET /program/{program_key}/versions.json GET /program/{program_key}/pedagogicalcategories.json GET /program/{program_key}/nonlinearcategories.json GET /program/{program_key}/authorizedcountries.json GET /program/{program_key}/subtitles.json GET /program/{program_key}/territories.json GET /program/{program_key}/othermaterials.json GET /program/{program_key}/videos.json ``` ### Produits ```http GET /product/{product_key}.json GET /product/{product_key}/programs.json GET /product/{product_key}/directcollections.json GET /product/{product_key}/reverselinkedproducts.json GET /product/{product_key}/videos.json GET /product/{product_key}/presses.json GET /product/{product_key}/images.json GET /product/{product_key}/serie.json GET /product/{product_key}/urls.json GET /product/{product_key}/versions.json GET /product/{product_key}/keywords.json GET /product/{product_key}/casting.json ``` ### Recherche produit / metaproduit ```http GET /products/search.json?id={biznumber} GET /metaproducts/search.json?id_equal={biznumber} ``` ### Pedagogie IDELLO ```http GET /pedagogicalcategorytypes.json ``` ## Limites de l'API actuelle ### Trop d'appels par produit Pour un seul produit/programme, l'application peut devoir appeler plusieurs endpoints: ```text product directcollections videos metaproducts presses images serie urls versions keywords program versions program subtitles program territories program countries program materials ``` Sur 8 000 produits TFO ou 22 000 produits IDELLO, cela devient couteux. ### Relations reconstruites cote client Les sites veulent consommer: ```text collection -> saisons -> episodes -> programmations ``` Mais les donnees sont souvent accessibles dans l'autre sens: ```text episode -> programmation -> serie -> collection ``` Notre application doit donc reconstruire elle-meme une structure qui pourrait etre exposee directement par l'API. ### Besoin de programmation future Les sites veulent pouvoir afficher: ```text Prochain episode le 8 aout ``` Nous avons besoin d'une fenetre future maximale de J+10. ### Besoin de statut d'import L'export SQL Louise est importe dans MySQL avant que l'API soit utilisable. L'application doit savoir si l'import est termine et si les donnees sont pretes. ### Besoin d'audit et de comparaison Nous devons pouvoir comparer: ```text ce que Louise/Mogador expose vs ce que notre sync a publie ``` L'API v2 doit donc exposer un identifiant d'export stable et des compteurs. ## API v2 demandee ## 1. Statut du dernier export/import ### Endpoint ```http GET /sync/v2/status ``` ### Objectif Permettre a notre systeme de savoir si les donnees sont pretes avant de commencer la synchronisation. ### Reponse attendue ```json { "schema_version": "2.0", "status": "ready", "latest_export_id": "20260801-0900", "export_started_at": "2026-08-01T09:00:00Z", "export_finished_at": "2026-08-01T09:34:00Z", "import_started_at": "2026-08-01T09:35:00Z", "import_finished_at": "2026-08-01T09:48:00Z", "source": { "file_name": "export_mogador.sql", "file_size_bytes": 306184192, "checksum_sha256": "..." }, "record_counts": { "programs": 1234, "products": 8142, "collections": 320, "series": 980, "videos": 7900, "images": 24000 }, "available_platforms": ["tfo", "idello", "onfr", "linear"] } ``` ### Statuts possibles ```text receiving_export importing ready failed stale ``` ## 2. Snapshot complet par plateforme ### Endpoint ```http GET /sync/v2/platforms/{platform}/snapshot?from={date}&to={date} ``` Exemple: ```http GET /sync/v2/platforms/tfo/snapshot?from=2026-08-01&to=2026-08-11 ``` ### Objectif Retourner toutes les donnees necessaires pour publier une plateforme sur une fenetre donnee, sans devoir faire un appel par produit. ### Parametres ```text platform: tfo | idello | onfr | linear from: date ISO to: date ISO, maximum J+10 pour les besoins web actuels include: optionnel, liste de blocs a inclure ``` Exemple `include`: ```text programs,products,collections,series,presses,images,videos,subtitles,keywords,pedagogy,rights ``` ### Reponse attendue ```json { "schema_version": "2.0", "export_id": "20260801-0900", "platform": "tfo", "from": "2026-08-01", "to": "2026-08-11", "generated_at": "2026-08-01T09:50:00Z", "programs": [], "products": [], "series": [], "collections": [], "presses": [], "images": [], "videos": [], "subtitles": [], "keywords": [], "pedagogical_categories": [], "authorized_countries": [], "territories": [], "other_materials": [] } ``` ### Donnees attendues dans `programs` ```json { "program_key": 9001, "product_key": 1001, "product_biznumber": "GP1001", "channel_key": 6, "platform": "tfo", "begin": "2026-08-01T06:00:00Z", "end": "2026-08-01T06:24:00Z", "duration_seconds": 1440, "title": "Titre programme", "target": "Jeunesse", "live": false, "advertising": "0", "version": {}, "territories": [], "authorized_countries": [], "subtitles": [], "other_materials": [], "nonlinear_categories": [], "pedagogical_categories": [] } ``` ### Donnees attendues dans `products` ```json { "product_key": 1001, "biznumber": "GP1001", "title": "Titre episode", "short_title": "Titre court", "production_year": "2024", "duration": "00:24:00", "distributor": "...", "origin_country": "CA", "type": "Episode", "kind": "...", "subkind": "...", "category": "...", "target": "...", "rating": "...", "serie_key": 2001, "serie_biznumber": "S123", "serie_title": "Saison 1", "collection_key": 3001, "collection_biznumber": "C123", "collection_title": "Collection publique", "episode_number": "1", "episode_count": "12", "season": "1", "is_audio": false, "is_original": true, "is_pedagogical_data_valid": true, "modification_date": "2026-08-01T08:44:00Z", "urls": [], "versions": [], "presses": [], "images": [], "videos": [], "keywords": [], "casting": [], "linked_products": [], "pedagogical_products": [] } ``` ### Donnees attendues dans `series` Dans notre modele web, `serie` correspond souvent a une saison. ```json { "serie_key": 2001, "biznumber": "S123", "title": "Saison 1", "short_title": "S1", "production_year": "2024", "type": "Serie", "kind": "...", "subkind": "...", "category": "...", "target": "...", "season": "1", "episode_count": "12", "collection_key": 3001, "collection_biznumber": "C123", "collection_title": "Collection publique", "presses": [], "images": [], "casting": [] } ``` ### Donnees attendues dans `collections` Dans notre modele web, `collection` correspond souvent a la serie publique. ```json { "collection_key": 3001, "biznumber": "C123", "title": "Collection publique", "short_title": "Collection", "type": "Collection", "kind": "...", "subkind": "...", "category": "...", "presses": [], "images": [], "date_begin": "2026-08-01T06:00:00Z", "date_end": "2026-08-11T23:59:59Z" } ``` ## 3. Changements entre deux exports ### Endpoint ```http GET /sync/v2/platforms/{platform}/changes?since_export_id={export_id} ``` Exemple: ```http GET /sync/v2/platforms/tfo/changes?since_export_id=20260801-0600 ``` ### Objectif Permettre de savoir ce qui a change entre deux exports complets Louise. Meme si Louise continue de fonctionner en full export SQL, Mogador peut exposer un diff entre deux imports. ### Reponse attendue ```json { "schema_version": "2.0", "platform": "tfo", "from_export_id": "20260801-0600", "to_export_id": "20260801-0900", "added": { "programs": [], "products": [], "collections": [], "series": [] }, "updated": { "programs": [], "products": [], "collections": [], "series": [] }, "removed": { "programs": [], "products": [], "collections": [], "series": [] } } ``` Chaque element devrait contenir au minimum: ```json { "entity_type": "program", "entity_key": 9001, "biznumber": "GP1001", "change_reason": "modified", "previous_hash": "...", "current_hash": "..." } ``` ## 4. Collection complete prete pour publication web ### Endpoint ```http GET /sync/v2/platforms/{platform}/collections/{biznumber}/full?from={date}&to={date} ``` Exemple: ```http GET /sync/v2/platforms/tfo/collections/C123/full?from=2026-08-01&to=2026-08-11 ``` ### Objectif Retourner directement la structure attendue par les sites: ```text collection -> saisons -> episodes -> programmations ``` ### Reponse attendue ```json { "schema_version": "2.0", "export_id": "20260801-0900", "platform": "tfo", "collection": { "collection_key": 3001, "biznumber": "C123", "title": "Collection publique", "presses": [], "images": [], "is_original_summary": "mixed", "next_programmation": { "program_key": 9001, "date": "2026-08-08", "begin": "2026-08-08T06:00:00Z", "end": "2026-08-08T06:24:00Z" } }, "seasons": [ { "serie": { "serie_key": 2001, "biznumber": "S123", "title": "Saison 1", "season": "1" }, "episodes": [ { "product": {}, "programmations": [], "media": {}, "images": [] } ] } ] } ``` ### Regles de remontee souhaitees Certaines donnees existent seulement sur les episodes, mais les sites peuvent les vouloir au niveau collection. Exemples: ```text collection.is_original_summary: - original si tous les episodes sont originaux - not_original si aucun episode n'est original - mixed si certains episodes sont originaux collection.next_programmation: - premiere programmation future disponible dans la fenetre demandee collection.all_targets: - liste unique des targets des episodes/programmes collection.all_audiences: - liste unique des audiences/ratings des episodes ``` Ces regles doivent etre documentees dans l'API. ## 5. Produit complet pret pour publication web ### Endpoint ```http GET /sync/v2/platforms/{platform}/products/{product_key}/full?from={date}&to={date} ``` ### Objectif Retourner toutes les donnees utiles pour un produit/episode en un seul appel. ### Reponse attendue ```json { "schema_version": "2.0", "export_id": "20260801-0900", "platform": "tfo", "product": {}, "collection": {}, "serie": {}, "programmations": { "current": [], "upcoming": [], "next": {} }, "presses": [], "images": [], "videos": [], "subtitles": [], "keywords": [], "casting": [], "linked_products": [], "pedagogical_products": [] } ``` ## 6. Endpoint bulk par liste de cles ### Endpoint ```http POST /sync/v2/platforms/{platform}/products/bulk POST /sync/v2/platforms/{platform}/programs/bulk ``` ### Objectif Permettre de recuperer plusieurs produits ou programmes complets sans multiplier les appels unitaires. ### Exemple request ```json { "export_id": "20260801-0900", "product_keys": [1001, 1002, 1003], "include": ["presses", "images", "videos", "keywords", "programs"] } ``` ### Exemple response ```json { "schema_version": "2.0", "export_id": "20260801-0900", "products": [] } ``` ## 7. Endpoint pedagogie IDELLO ### Endpoint ```http GET /sync/v2/platforms/idello/pedagogy ``` ### Objectif Retourner toutes les categories pedagogiques IDELLO et leur hierarchie. ### Reponse attendue ```json { "schema_version": "2.0", "export_id": "20260801-0900", "categories": [ { "pedagogie_key": 1, "type": "...", "name_fr": "...", "name_en": "...", "description_fr": "...", "description_en": "...", "parent_key": null } ] } ``` ## 8. Endpoint media / videos ### Endpoint ```http GET /sync/v2/platforms/{platform}/media/videos?from={date}&to={date} ``` ### Objectif Retourner les informations video utiles pour la publication et la reconciliation avec JWP. ### Reponse attendue ```json { "schema_version": "2.0", "export_id": "20260801-0900", "videos": [ { "program_key": 9001, "product_key": 1001, "product_biznumber": "GP1001", "name": "my_file.mp4", "path": "/my_directory/", "format": "JWP", "file_delivery": "2026-08-01T08:00:00Z", "duration": "00:24:00", "tc_in": "00:00:00:00", "tc_out": "00:24:00:00", "subtitle_tc_ref": "..." } ] } ``` Note: historiquement, les formats Limelight/Uplynk etaient presents. Aujourd'hui, le besoin est surtout lie a JWP. ## 9. Endpoint validation / diagnostics ### Endpoint ```http GET /sync/v2/platforms/{platform}/diagnostics?from={date}&to={date} ``` ### Objectif Retourner des anomalies connues cote Mogador/Louise avant que notre systeme ne publie. ### Reponse attendue ```json { "schema_version": "2.0", "export_id": "20260801-0900", "platform": "tfo", "diagnostics": [ { "severity": "warning", "entity_type": "program", "entity_key": 9001, "message": "No subtitles found" }, { "severity": "error", "entity_type": "product", "entity_key": 1001, "message": "Missing video" } ] } ``` ## Exigences techniques ### HTTPS / SSL L'API v2 doit fonctionner avec une validation SSL standard. Notre application ne devrait pas avoir besoin de `verify=false`. ### Concurrence documentee L'API actuelle semble avoir une limite de concurrence par defaut autour de 5 requetes simultanees, configurable par variable d'environnement. Pour l'API v2, il faudrait documenter officiellement: ```text concurrence recommandee concurrence maximale comportement en cas de rate limit headers de rate limit temps de reponse attendu timeouts recommandes ``` Exemple de headers: ```http X-RateLimit-Limit: 10 X-RateLimit-Remaining: 8 Retry-After: 30 ``` ### Pagination Les endpoints volumineux doivent supporter une pagination stable. Exemple: ```http GET /sync/v2/platforms/idello/snapshot?from=2026-08-01&to=2026-08-11&page_size=1000&cursor=... ``` Reponse: ```json { "data": [], "next_cursor": "abc123", "has_more": true } ``` ### Compression Support attendu: ```text gzip ou brotli ``` ### ETag / checksum Les gros endpoints devraient retourner un ETag ou checksum pour detecter rapidement si le contenu a change. ```http ETag: "sha256-..." ``` ### Versioning Toutes les reponses doivent inclure: ```json { "schema_version": "2.0", "export_id": "20260801-0900" } ``` Les changements cassants doivent passer par une nouvelle version: ```text /sync/v3/... ``` ### Erreurs standardisees Format attendu: ```json { "error": { "code": "IMPORT_NOT_READY", "message": "Latest export is still importing", "retry_after_seconds": 300, "export_id": "20260801-0900" } } ``` Codes utiles: ```text IMPORT_NOT_READY EXPORT_FAILED INVALID_PLATFORM INVALID_DATE_RANGE RATE_LIMITED NOT_FOUND INTERNAL_ERROR ``` ## Vision long terme: publication quasi instantanee La demande immediate est une API v2 orientee snapshot et synchronisation. Cependant, a long terme, les equipes de production voudront probablement une publication quasi instantanee apres une sauvegarde dans Louise. Il faut donc prevoir une trajectoire en deux temps: ```text Court / moyen terme: - API snapshot fiable - API status - API changes - publication JSON controlee toutes les 3h ou sur demande Long terme: - evenements fiables lors des changements dans Louise - replay des evenements - republication ciblee des JSON touches - reconciliation reguliere par snapshot complet ``` Le point important: Louise ne devrait pas publier directement vers les sites. Louise devrait notifier notre systeme qu'une donnee a change. Notre systeme reste responsable de la validation, de la transformation, des rapports, des overrides, du rollback et de la publication. Flux cible long terme: ```text Louise save -> event / webhook signe -> Sync Engine interne -> recuperation du detail via API Mogador/Louise -> validation -> regeneration des JSON impactes -> publication bucket/CDN -> rapport / alerte ``` Et en parallele: ```text Snapshot complet regulier -> compare avec l'etat publie -> detecte les evenements perdus ou incoherents ``` Un systeme event-driven sans snapshot/reconciliation serait trop risque. Le snapshot complet doit rester le filet de securite. ## API d'evenements demandee Pour permettre une publication quasi instantanee, il faudrait ajouter une API d'evenements fiable. ### Webhook de changement Louise peut notifier notre systeme lorsqu'une entite change. Exemples d'evenements: ```text program.created program.updated program.deleted product.created product.updated product.deleted collection.updated serie.updated media.updated rights.updated ``` ### Payload minimal attendu ```json { "schema_version": "2.0", "event_id": "evt_20260801_000001", "sequence": 123456, "event_type": "program.updated", "entity_type": "program", "entity_key": 9001, "biznumber": "GP1001", "platforms": ["tfo"], "occurred_at": "2026-08-01T09:15:00Z", "export_id": "20260801-0900", "changed_fields": ["begin", "end", "product_key"] } ``` Le payload peut rester leger. Il n'est pas obligatoire d'envoyer tout le produit dans le webhook. Par contre, l'evenement doit permettre a notre systeme de recuperer le detail complet ensuite. ### Endpoint replay Un endpoint de replay est obligatoire pour eviter les pertes d'evenements. ```http GET /sync/v2/events?since_sequence=123456 ``` Reponse: ```json { "schema_version": "2.0", "from_sequence": 123456, "to_sequence": 123500, "events": [], "has_more": false, "next_sequence": 123501 } ``` ### Endpoint detail apres evenement Apres reception d'un evenement, notre systeme doit pouvoir recuperer l'entite complete. ```http GET /sync/v2/platforms/{platform}/programs/{program_key}/full GET /sync/v2/platforms/{platform}/products/{product_key}/full GET /sync/v2/platforms/{platform}/collections/{biznumber}/full ``` ### Garanties attendues pour les events Pour que les evenements soient utilisables en production, il faut: - `event_id` unique; - `sequence` monotone; - retry automatique des webhooks; - signature HMAC; - idempotence; - endpoint de replay; - conservation des evenements pendant une periode definie; - environnement staging; - documentation du format; - monitoring cote fournisseur; - garantie que les suppressions sont aussi envoyees; - garantie que les changements de droits sont envoyes. ### Pourquoi le replay est non negociable Un webhook peut echouer pour plusieurs raisons: - reseau indisponible; - maintenance; - timeout; - erreur applicative temporaire; - deploy en cours; - rate limit. Sans replay, une notification perdue peut produire une publication incorrecte. Avec replay, notre systeme peut reprendre depuis la derniere sequence connue. ## Position recommandee sur le temps reel La recommandation est de ne pas aller directement vers: ```text Louise save -> site web ``` La cible devrait etre: ```text Louise save -> event fiable -> Sync Engine interne -> JSON statiques -> site web ``` Cela permet d'avoir une publication rapide tout en gardant: - validation; - audit; - rollback; - rapports; - controle des overrides internes; - cache/CDN maitrise; - reconciliation par snapshot. ## Criteres d'acceptation L'API v2 serait consideree utile si elle permet: - de recuperer TFO J a J+10 en beaucoup moins d'appels; - de recuperer IDELLO J a J+10 sans 22 000 appels produit unitaires; - de connaitre le statut exact du dernier export/import; - de recuperer un export_id stable dans chaque reponse; - de comparer deux exports; - de recuperer une collection complete dans le sens attendu par les sites; - de recuperer un produit complet en un seul appel; - de fonctionner avec SSL valide; - de supporter pagination et compression; - de documenter clairement les limites de concurrence; - de fournir un environnement staging. - de fournir une trajectoire vers des events fiables avec replay pour la publication quasi instantanee. ## Priorites ### Priorite 1 ```text GET /sync/v2/status GET /sync/v2/platforms/{platform}/snapshot ``` Ces deux endpoints reduiraient immediatement la complexite de la synchronisation. ### Priorite 2 ```text GET /sync/v2/platforms/{platform}/collections/{biznumber}/full GET /sync/v2/platforms/{platform}/products/{product_key}/full ``` Ces endpoints reduiraient la complexite des relations collection/saison/episode. ### Priorite 3 ```text GET /sync/v2/platforms/{platform}/changes POST /sync/v2/platforms/{platform}/products/bulk GET /sync/v2/platforms/{platform}/diagnostics ``` Ces endpoints amelioreraient la performance, l'audit et la fiabilite. ### Priorite 4 ```text Webhook signed events GET /sync/v2/events?since_sequence={sequence} GET /sync/v2/platforms/{platform}/programs/{program_key}/full ``` Ces capacites prepareraient la publication quasi instantanee sans exposer les sites directement a Louise. ## Conclusion Nous ne cherchons pas necessairement a contourner Mogador. Nous cherchons surtout a eviter que notre application doive reconstruire une publication web complete a partir de milliers de petits appels API. Une API Mogador v2 orientee synchronisation permettrait de: - garder Louise/Mogador comme source officielle; - reduire la charge sur l'API; - reduire la duree des syncs; - fiabiliser les publications; - faciliter les rapports d'ecarts; - mieux servir les sites web; - eviter une suppression complete de Mogador. - preparer une publication quasi instantanee avec des evenements fiables. La demande principale est donc: ```text Passer d'une API granulaire par entite a une API de snapshot, diff et publication par plateforme. ``` La demande long terme est: ```text Ajouter des evenements fiables avec replay afin de permettre une republication ciblee quelques secondes/minutes apres une sauvegarde dans Louise. ```