Studio : ordre de passage du plan de test et socle d'upload
Le plan de test gagne un § 0bis : le lot 0 a réécrit tout le chemin d'upload (URL V4 signée puis POST /ingest) et rien n'a jamais été déposé depuis un navigateur, alors que les § 1 à 5 en dépendent tous. Ajout aussi de l'ordre de passage en quatre séances, de la façon de reporter un échec, et des résultats déjà obtenus sur le § 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d879766cae
commit
a588c53549
@ -9,20 +9,67 @@
|
||||
|
||||
---
|
||||
|
||||
## Comment passer ce plan — ajouté le 2026-09-16
|
||||
|
||||
**Quatre séances, dans cet ordre.** Chacune est arrêtée par la précédente : inutile de chercher un bug
|
||||
de narration si l'upload est cassé en dessous.
|
||||
|
||||
| Séance | Sections | Durée | Ce qu'un ❌ arrête |
|
||||
|---|---|---|---|
|
||||
| **1. Le socle** | § 0, § 0bis | ~45 min | Tout. Rien d'autre ne se teste |
|
||||
| **2. L'argent et l'image** | § 1, § 2, § 2bis | ~1 h | Les § 3 à 5 (pas de génération fiable, pas de narration) |
|
||||
| **3. La narration** | § 3, § 4 | ~1 h 30 | Les § 5 et 5bis côté visiteur |
|
||||
| **4. Les trois fronts** | § 5, § 5bis, § 6 | ~1 h | Rien — c'est la fin |
|
||||
|
||||
**Comment reporter.** Un ✅ tout court suffit quand c'est conforme. Un ❌ demande trois choses et pas
|
||||
une de plus : *ce que tu as fait*, *ce que tu as vu*, *ce que tu attendais*. Une capture vaut mieux
|
||||
qu'une description pour tout ce qui est visuel. N'essaie pas de diagnostiquer — c'est mon travail, et
|
||||
un diagnostic dans la colonne ✓ masque souvent le vrai symptôme.
|
||||
|
||||
**Ce qui n'est pas un bug** : la liste des limites connues en fin de document. À lire **avant** de
|
||||
commencer, pas après avoir signalé l'une d'elles.
|
||||
|
||||
---
|
||||
|
||||
## 0. Prérequis — une fois
|
||||
|
||||
| # | Étape | Attendu | ✓ |
|
||||
|---|---|---|---|
|
||||
| 0.1 | `winget install Gyan.FFmpeg`, rouvrir le terminal, `ffmpeg -version` | Une version s'affiche. Sans ffmpeg, la génération audio échoue et les crédits sont remboursés | |
|
||||
| 0.2 | `dotnet user-secrets set "AI:ApiKey" "<clé Gemini>" --project manager-service/ManagerService` | `dotnet user-secrets list` montre `AI:ApiKey` (et `Studio:Fal:Key`) | |
|
||||
| 0.3 | Lancer Postgres puis `dotnet ef database update` avec `MIGRATIONS_CONNECTION` (voir la note mémoire EF) | Migrations appliquées : `AddAiWatermarkBurned`, `StudioCalibrationEngravingStyle`, `StudioCreditUnitPerReference`, `AddNarrators`, `AddGeoPointAudio`, `AddCasting` | |
|
||||
| 0.1 | `winget install Gyan.FFmpeg`, rouvrir le terminal, `ffmpeg -version` | Une version s'affiche. Sans ffmpeg, la génération audio échoue et les crédits sont remboursés | ✅ 16/09 — 9.0.1-full_build, alias winget dans le Path persistant |
|
||||
| 0.2 | `dotnet user-secrets set "AI:ApiKey" "<clé Gemini>" --project manager-service/ManagerService` | `dotnet user-secrets list` montre `AI:ApiKey` (et `Studio:Fal:Key`) | ✅ 16/09 — « Successfully saved AI:ApiKey » (Thomas) |
|
||||
| 0.3 | Lancer Postgres puis `dotnet ef database update` avec `MIGRATIONS_CONNECTION` (voir la note mémoire EF) | Migrations appliquées : `AddAiWatermarkBurned`, `StudioCalibrationEngravingStyle`, `StudioCreditUnitPerReference`, `AddNarrators`, `AddGeoPointAudio`, `AddCasting` | ✅ 16/09 — les 6 appliquées en une passe, « Done. » |
|
||||
| 0.4 | `dotnet run` (manager-service), `flutter run -d chrome` (manager-app) | Les deux démarrent, connexion SuperAdmin OK | |
|
||||
| 0.5 | Dialogue d'instance (SuperAdmin) : assistant **et** Studio activés, **100 crédits** accordés | Solde 100 dans la jauge du menu | |
|
||||
| 0.5 | **Dialogue d'instance** — il n'est pas dans un écran : **pied du menu de gauche → icône ⇄ « Changer d'instance »** (visible seulement en SuperAdmin) → dans la liste, le **crayon** « Configurer le plan » de la ligne voulue. Activer **Assistant IA**, puis **Studio**, puis accorder **100 crédits** | Solde 100 dans la jauge du menu | |
|
||||
| 0.5b | ⚠️ Dans ce même dialogue, **décocher Assistant IA** | L'interrupteur **Studio se désactive tout seul** et devient grisé : pas de Studio sans assistant (décision 12). Recocher les deux avant de continuer | |
|
||||
| 0.6 | Guide IA › Personnages : un personnage **Léon** avec une **voix** (Umbriel) et une intonation, un second **La sentinelle** avec une autre voix (Kore) | Les deux apparaissent dans la liste, sans « (sans voix) » | |
|
||||
| 0.7 | Donner un **portrait** à Léon (onglet Visage → générer ou choisir une vue Portrait) | La vue Portrait est enregistrée | |
|
||||
|
||||
---
|
||||
|
||||
## 0bis. Le socle d'upload — ajouté le 2026-09-16
|
||||
|
||||
> **Pourquoi cette section n'existait pas, et pourquoi elle doit exister.** Le lot 0 a **réécrit tout le
|
||||
> chemin d'upload** de manager-app : plus aucun appel au SDK Firebase, tout passe par une URL V4 signée
|
||||
> puis `POST /ingest`. C'est codé depuis le 13/09 et couvert par des tests unitaires, mais **aucun
|
||||
> fichier n'a jamais été déposé depuis un navigateur**. Les § 1 à 5 supposent tous que ça marche : la
|
||||
> médiathèque reçoit les images générées, les MP3 de narration, les portraits. Tester la narration
|
||||
> avant ce socle, c'est chercher une fuite au toit avant d'avoir regardé les fondations.
|
||||
|
||||
| # | Étape | Attendu | ✓ |
|
||||
|---|---|---|---|
|
||||
| 0b.1 | Médiathèque : déposer une **image JPEG** de plus de 2560 px | Elle monte (barre de progression), apparaît dans la grille, s'ouvre. Réduite à 2560 px côté long | |
|
||||
| 0b.2 | Déposer un **PNG à transparence** | Reste un PNG, l'alpha est préservé (le voir sur fond coloré) | |
|
||||
| 0b.3 | Déposer un **MP3**, un **PDF** | Montent tels quels, lisibles / téléchargeables depuis la fiche | |
|
||||
| 0b.4 | **Remplacer** le fichier d'une ressource existante | Le nouveau s'affiche partout où la ressource était utilisée, l'ancien ne traîne pas | |
|
||||
| 0b.5 | **Supprimer** une ressource non utilisée | Disparaît de la grille ; recharger la page ne la ramène pas | |
|
||||
| 0b.6 | ⚠️ Fichier **au-dessus du plafond de son type** (ex. une image > 30 Mo) | Refusé avec un message traduit, **pas** une erreur brute ni un échec muet. Tester en FR, EN **et** NL | |
|
||||
| 0b.7 | ⚠️ **Sélecteur de ressource** : champ image d'une section → ajouter un fichier depuis la modale → Enregistrer | Le fichier arrive **et** le champ de la section le pointe. C'est le piège historique : la modale embarque `ResourcesScreen`, une régression ici touche les 13 types de section | |
|
||||
| 0b.8 | Ouvrir une ressource déposée depuis **visitapp-web** et depuis **mymuseum-visitapp** | L'URL s'ouvre des deux côtés (le jeton de téléchargement est bien posé à l'écriture) | |
|
||||
| 0b.9 | Remplacer l'image d'une visite **déjà téléchargée** dans mymuseum-visitapp | L'app la re-télécharge, n'affiche pas l'ancienne | |
|
||||
| 0b.10 | Sélecteur de langues d'une configuration | **Le luxembourgeois (LB) est proposé**, avec son drapeau (ajouté au lot 0, jamais vu à l'écran) | |
|
||||
|
||||
---
|
||||
|
||||
## 1. Mention IA dans le fichier (lot 6)
|
||||
|
||||
| # | Étape | Attendu | ✓ |
|
||||
@ -49,6 +96,25 @@
|
||||
|
||||
---
|
||||
|
||||
## 2bis. Les crédits, pour de vrai — ajouté le 2026-09-16
|
||||
|
||||
> Le § 2 vérifie que **l'estimation** affichée est juste. Cette section-ci vérifie que **le solde**
|
||||
> bouge juste, ce qui n'est pas la même chose : le mécanisme est une réservation puis un débit réel,
|
||||
> avec remboursement en cas d'échec, et rien de tout ça n'a tourné en conditions réelles.
|
||||
|
||||
| # | Étape | Attendu | ✓ |
|
||||
|---|---|---|---|
|
||||
| 2b.1 | Noter le solde, lancer une génération de 3 variantes, **regarder la jauge pendant** le travail | Le solde baisse **dès le lancement** (réservation), pas à l'arrivée des images | |
|
||||
| 2b.2 | À la fin | Le débit définitif égale l'estimation annoncée, ni plus ni moins | |
|
||||
| 2b.3 | Fermer le panneau **sans valider** aucune variante | Les crédits restent dépensés (l'appel a coûté), mais **rien** ne s'ajoute au quota de stockage | |
|
||||
| 2b.4 | Lancer **deux générations coup sur coup** sans attendre la première | Les deux réservations se cumulent ; le solde ne descend jamais sous zéro | |
|
||||
| 2b.5 | Solde insuffisant pour 3 variantes mais suffisant pour 1 | Refus **avant** l'appel, message clair, aucun crédit pris | |
|
||||
| 2b.6 | ⚠️ Génération qui échoue (couper le réseau du serveur vers fal, ou fausse clé) | Le solde **revient** à sa valeur d'avant. C'est le point le plus important de la section | |
|
||||
| 2b.7 | Abonnement › journal des crédits | Une ligne par mouvement (réservation, débit, remboursement), export CSV lisible | |
|
||||
| 2b.8 | SuperAdmin : accorder 50 crédits à l'instance | Solde +50, et la **date d'expiration repoussée à +12 mois sur tout le solde** (pas seulement sur les 50) | |
|
||||
|
||||
---
|
||||
|
||||
## 3. Qui raconte quoi (lot 8a et 8c)
|
||||
|
||||
Préparer une configuration avec un **parcours de 4 étapes** (titre + description remplis en FR et NL) et un **article** avec du texte.
|
||||
@ -159,3 +225,23 @@ ajouter **La cuisinière sans portrait** comme narratrice d'une étape).
|
||||
- Hors ligne, mymuseum-visitapp ne montre pas le portrait.
|
||||
- Le portrait affiché est le portrait **actuel** du personnage ; le nom et la voix sont ceux de l'audio entendu.
|
||||
- Les extraits d'écoute des voix (lot 7) ne sont pas encore produits.
|
||||
|
||||
---
|
||||
|
||||
## Ce que ce plan ne couvre **pas** — ajouté le 2026-09-16
|
||||
|
||||
À savoir avant de conclure « le Studio est testé » :
|
||||
|
||||
- **L'infra du bucket de prod.** Le lot 0 est codé mais son volet infra reste ouvert : CORS des origines
|
||||
de prod, cycle de vie `incoming/`, et surtout **fermeture des règles Storage en écriture** — qui doit
|
||||
se faire *après* le déploiement du nouveau manager-app, sinon l'upload de la version encore en ligne
|
||||
casse. Rien de tout ça ne se voit depuis un poste de dev.
|
||||
- **Le plafond journalier par utilisateur** : il *est* configurable, champ « Plafond par utilisateur et
|
||||
par jour (crédits, 0 = aucun) » dans le dialogue d'instance, sous l'interrupteur Studio. Il vaut 0 par
|
||||
défaut, donc rien ne se déclenche tant qu'on n'y met pas une valeur — à tester le jour où on veut
|
||||
vérifier le garde-fou « stagiaire », pas dans cette passe.
|
||||
- **Le webhook fal** en conditions réelles : en local `Studio:WebhookBaseUrl` est vide, la relecture se
|
||||
fait par sondage. La vérification de signature ED25519 ne sera exercée qu'en préprod.
|
||||
- **La charge** : tout ce plan se passe à un seul utilisateur. Le sémaphore à 2 images simultanées de
|
||||
l'ingestion ne se voit pas à cette échelle.
|
||||
- **Les lots 9 à 11** (avant/après, vidéo, 3D) : pas commencés.
|
||||
|
||||
@ -161,6 +161,7 @@ Tout sur `Instance`, dupliqué depuis `SubscriptionPlan` : `StorageQuotaBytes`,
|
||||
| 22 | Plafond dur et dotation de plan — *13/09, lot 1* | **Retirés.** Avec des crédits prépayés (décision 17), le solde *est* le plafond : un « plafond dur organisation » n'a plus rien à borner. Seul reste `StudioPerUserDailyCap`, en crédits. `SubscriptionPlan.HasStudio` / `StudioCreditsGranted` et `Instance.StudioProviderRegion` ne sont pas créés tant que rien ne les lit (prix différés, décision 16 ; région déclarative) |
|
||||
| 23 | Écarts des lots 2 à 4 — *13/09* | **Fournisseur interchangeable** : `IGenerationProvider` (soumission, sondage, lecture du webhook) + `GenerationModel.ProviderKey` en base ; changer de fournisseur = une classe et une ligne de catalogue. **Une requête par variante** (FLUX.2 pro n'a pas de `num_images`). **Référence source avant celles de l'identité** dans l'ordre d'envoi. Migration unique `AddStudioGeneration` pour les lots 2 et 3. **Non livrés** : `preview`, `GET /jobs`, `cancel`, `reject`, `regenerate`, l'interrupteur UI de `CanValidateAssets`. **Pas de `target` serveur** : l'onglet « Générer » vit dans le sélecteur (`showSelectResourceModal`, mode sélection) et la ressource validée revient au champ qui l'a ouvert — donc absent du bouton « Ajouter » de la Médiathèque, qui relève du lot 5. Surcharges d'identité en puces, pas en onglets ; `ModelKey` non exposé (un seul modèle). **`GuidedStep.ImageUrl` conservée** et remplie par le client avec `ImageResourceId` (motif `GeoPoint`) : visitapp-web n'a rien à changer, mymuseum-visitapp lit l'id pour l'hors-ligne, tablet-app n'a pas d'étapes. `CreditCost = 10` provisoire jusqu'au calibrage || 24 | Modèles 3D et multi-vues — *15/09* | **Tout reste sur fal** : Meshy (v5, v6-preview, v7), Hi3D, Tripo3D, Hunyuan3D et Trellis y sont exposés — aucun second fournisseur à intégrer, aucune seconde clé. **Deux lignes de catalogue, pas une** : `3d-default` mono-image (~0,02–0,05 $) et `3d-multiview` = `meshy/v7/multi-image-to-3d` (~1,20 $). Le facteur ~25 sur le coût impose que le basculement de l'un à l'autre soit **annoncé avant la génération, jamais silencieux**. Hi3D n'expose pas de multiview : excellent en fidélité structurelle, il ne couvre pas le cas d'usage central. Ids et prix à revérifier au calibrage (§9.4) |
|
||||
| 25 | Casting visiteur — *15/09* | **Retenu** — lève le « hors périmètre » de §3.8. `ConfigurationPersona { ConfigurationId, PersonaId, Order, IsFeatured }` ne porte **que** l'ordre et la mise en avant : l'appartenance reste **calculée** depuis les narrateurs assignés. Un personnage ne s'assigne qu'à **un seul endroit — le contenu** ; l'écran casting est une *vue*, jamais une seconde saisie. Rien ne s'affiche côté visiteur sans `Configuration.IsCastingShownToVisitor` (défaut off). Lot 8bis |
|
||||
| 26 | Prise de vue mobile — *16/09* | **Page de dépôt dédiée ouverte par QR code, pas un manager-app responsive.** Le conservateur est devant la vitrine avec son téléphone, pas devant son poste ; rendre responsive un back-office Flutter web pour un seul écran revient à le rendre responsive tout court — refus déjà posé sur le routing (décision 23) — et CanvasKit se charge mal sur la 4G d'une salle. La page vit sur **la même surface web non-Flutter que l'éditeur de POI** (décision n°9 d'[immersif-frontiere-plan.md](immersif-frontiere-plan.md)) : aucune nouvelle cible de déploiement. Capture par `<input type="file" accept="image/*" capture="environment">`, qui ouvre l'appareil photo natif — **jamais `getUserMedia`**, dont le flux est sans autofocus ni traitement constructeur, alors que c'est le seul gabarit du Studio où la qualité des photos pèse plus lourd que le prompt. Accès par **token de dépôt** borné (§3.7bis), jamais un JWT manager. **Lot 11bis, après le lot 11** : le dépôt de fichiers depuis le poste couvre déjà le besoin fonctionnel |
|
||||
|
||||
|
||||
---
|
||||
@ -731,6 +732,39 @@ publiques exposées en JWKS :
|
||||
répond `200` sans rien refaire. D'où le téléchargement des octets dans un job, pas dans la requête ;
|
||||
- corps : `{ request_id, gateway_request_id, status: "OK" | "ERROR", payload | error }`.
|
||||
|
||||
### 3.7bis Token de dépôt — le seul chemin d'écriture non authentifié
|
||||
|
||||
Réservé au lot 11bis (décision 26). À lire avant de l'écrire : ce serait **le seul endroit de la
|
||||
solution où un client sans session peut faire écrire dans le bucket**, et les règles Storage de prod
|
||||
sont ouvertes en écriture depuis 2024. Un token laxiste transformerait le QR en hébergeur de fichiers
|
||||
gratuit pour qui le photographie par-dessus l'épaule.
|
||||
|
||||
```
|
||||
StudioSourceToken
|
||||
Token -- aléatoire 32 octets, indexé, jamais dérivé d'un id
|
||||
JobDraftId -- le brouillon de génération 3D, et lui seul
|
||||
InstanceId
|
||||
SlotsLeft -- 4 au départ, décrémenté à chaque URL délivrée
|
||||
ExpiresAt -- maintenant + 30 min : la durée d'une prise de vue
|
||||
CreatedBy -- l'utilisateur qui a affiché le QR, pour le journal
|
||||
```
|
||||
|
||||
Quatre bornes, toutes côté serveur :
|
||||
|
||||
- **portée d'un seul brouillon** — il n'autorise que le dépôt dans
|
||||
`studio-sources/{instanceId}/{draftId}/{slot}`, aucune lecture, aucun autre chemin ;
|
||||
- **durée courte** — 30 min, cohérent avec les 15 min des URLs signées de la décision 14 ;
|
||||
- **compteur borné** — 4 URLs délivrées, puis le token est mort, même avant expiration ;
|
||||
- **il ne signe rien lui-même** — il ouvre `POST /api/Studio/source-upload-url`, qui applique le
|
||||
plafond de taille (`x-goog-content-length-range`) et le quota exactement comme le flux authentifié.
|
||||
Le token est une autorisation, pas une capacité.
|
||||
|
||||
**Les photos sources ne sont pas des `Resource`.** Ce sont des entrées de génération : elles vivent
|
||||
sous `studio-sources/`, hors quota de médiathèque, et tombent avec une règle de cycle de vie comme
|
||||
celle posée sur `incoming/` au lot 0. Elles ne passent donc **pas** par `ResourceIngestionService` —
|
||||
ce qui veut dire que **le lot 0, déjà codé le 13/09, n'est pas à rouvrir**. Seul le GLB produit
|
||||
devient une `Resource`.
|
||||
|
||||
### 3.8 Personnages — fusion des trois plans, arrêtée le 2026-09-01
|
||||
|
||||
> **Trois plans décrivaient le même objet sans se croiser.** Le canon visuel (ce plan), les 3 frames
|
||||
@ -1526,6 +1560,10 @@ l'écran doit être construit autour de ça.
|
||||
Chaque case porte une silhouette d'exemple. Sous le bloc, **une seule consigne**, courte :
|
||||
*même objet, même lumière, fond neutre, objet entier dans le cadre.* C'est la recommandation du
|
||||
modèle, et elle pèse plus lourd que n'importe quel réglage.
|
||||
|
||||
Chaque case accepte un fichier **ou** une photo prise sur place (lot 11bis). ⚠️ **La grille de
|
||||
quatre est aussi un plafond, pas une mise en forme** : `meshy/v7/multi-image-to-3d` prend **2 à 4
|
||||
vues**, une cinquième fait rejeter l'appel.
|
||||
3. **Le coût se met à jour en direct, et le changement de modèle est annoncé** : 1 photo → modèle
|
||||
mono-image ; dès la 2ᵉ → Meshy v7 multi, **~25× plus cher**. Une ligne explicite au moment où
|
||||
la bascule se produit, jamais une facture découverte après coup (même principe que le coût de
|
||||
@ -1533,6 +1571,22 @@ l'écran doit être construit autour de ça.
|
||||
4. À l'inverse, **ne pas culpabiliser le mono-image** : une seule photo reste un usage légitime et
|
||||
bon marché pour une illustration. La formulation est « plus de vues = plus fidèle », pas
|
||||
« vous avez mal fait ».
|
||||
4bis. **Les N photos sources ne touchent pas `GenerationJob.SourceResourceId`.** Vérifié dans le code le
|
||||
16/09 : cette colonne est **singulière** et porte un id de `Resource`
|
||||
(`AddStudioGeneration:47`, `StudioGenerationService.cs:238`) — l'élargir en liste rouvrirait les lots 3
|
||||
et 4, leurs tests et le chemin image qui fonctionne. À la place, une **table fille** dans la migration
|
||||
du lot 11 :
|
||||
|
||||
```
|
||||
StudioSourceFile
|
||||
JobDraftId -- le brouillon de génération
|
||||
Slot -- Face | Profil | Dos | TroisQuarts
|
||||
StoragePath -- studio-sources/{instanceId}/{draftId}/{slot}
|
||||
```
|
||||
|
||||
`SourceResourceId` reste intact pour le chemin image. C'est le pendant en entrée du multi-fichier en
|
||||
sortie, et ça suit la décision 21, qui réserve déjà le job composite à la 3D.
|
||||
|
||||
5. Génération asynchrone en **job composite** (parent + enfants, décision 21), puis **normalisation
|
||||
canonique du GLB** à l'ingestion — échelle, pivot, orientation (décision n°2 d'
|
||||
`immersif-frontiere-plan` §5) : sans elle les POI posés sur un modèle sautent à la
|
||||
@ -1543,6 +1597,33 @@ l'écran doit être construit autour de ça.
|
||||
7. La ressource validée revient au champ qui a ouvert le sélecteur, et les **POI se posent dans
|
||||
l'éditeur three.js en iframe** (décision n°9) — le vrai morceau non trivial du chantier.
|
||||
|
||||
### Lot 11bis — Prise de vue mobile par QR *(petit, juste après le lot 11)*
|
||||
|
||||
Décision 26. Le lot 11 suppose que le conservateur a déjà ses quatre JPEG sur son disque ; devant une
|
||||
vitrine, il a un téléphone. Ce lot ferme l'écart **sans toucher au responsive de manager-app**.
|
||||
|
||||
1. Bouton « **Photographier avec mon téléphone** » à côté de la grille des quatre emplacements. Il
|
||||
crée un `StudioSourceToken` (§3.7bis) et affiche le QR de l'URL de dépôt.
|
||||
2. **Page de dépôt** — même surface web non-Flutter que l'éditeur de POI, route
|
||||
`/studio/capture/{token}`. Quatre cases (Face / Profil / Dos / 3/4), la même consigne unique que
|
||||
sur le poste, un `<input capture="environment">` par case. Pas de session, pas de routing, pas
|
||||
d'état : demander l'URL signée, `PUT`, afficher la vignette.
|
||||
3. **Le poste sert d'écran de contrôle** : les vignettes se remplissent en direct pendant la prise de
|
||||
vue, par le **polling 2 s déjà retenu au §3.7** — aucun SSE à introduire pour ça.
|
||||
4. Token expiré ou épuisé → la page le dit et renvoie vers le poste pour régénérer le QR. Le bouton
|
||||
« Générer » reste sur le poste : **le téléphone dépose, il ne dépense pas de crédits.**
|
||||
|
||||
⚠️ **Le réseau est le point faible, pas la photo.** Une salle d'exposition en sous-sol coupe la 4G,
|
||||
et une page web sans service worker perd les clichés déjà pris. Acceptable ici — l'utilisateur est un
|
||||
conservateur qui recommence, pas un visiteur — mais c'est *cet* argument qui ferait revenir un jour
|
||||
l'idée d'une application native, jamais la qualité d'image.
|
||||
|
||||
**Ne pas inverser l'ordre avec le lot 11.** « Je photographie, je m'envoie les photos, j'uploade
|
||||
depuis le poste » coûte zéro et marche dès le premier jour. La valeur du chantier est dans la qualité
|
||||
des quatre vues, l'annonce du basculement mono→multi à ×25 et la normalisation canonique du GLB.
|
||||
|
||||
---
|
||||
|
||||
⚠️ **Ce lot faisait quatre lignes et se contentait de renvoyer au plan VR, qui lui-même renvoyait
|
||||
ici.** La frontière est tranchée depuis le 2026-09-11 dans
|
||||
[immersif-frontiere-plan.md](immersif-frontiere-plan.md), qui ajoute : `Scene3D` comme
|
||||
@ -1592,6 +1673,9 @@ la première migration du Studio, pas en arrivant au lot 11.
|
||||
| « Tout par le serveur » (première version de la décision 14) | `MemoryBufferThreshold = int.MaxValue` (`Startup.cs:112`) tient tout `IFormFile` en RAM, et une vidéo 360 de 1 Go traverserait le VPS | Abandonné le jour même : les octets vont directement chez Google |
|
||||
| Objet écrit par le SDK GCS | Pas d'URL de téléchargement Firebase sans la métadonnée `firebaseStorageDownloadTokens` | Jeton posé à l'écriture, URL construite (§5) |
|
||||
| Trois décisions d'`immersif-frontiere-plan` §5 « à écrire avant la première migration » | Seule la n°5 (crédits) avait été reportée | N°1, 4 et 6 reportées le 13/09 : décisions 14, 20, 21 |
|
||||
| « Rendre manager-app responsive pour photographier l'objet » | Back-office Flutter web : le responsive d'un seul écran entraîne celui du routing, déjà exclu du périmètre (décision 23) — et CanvasKit à charger sur la 4G d'une salle pour faire un `<input type="file">` | Page de dépôt dédiée ouverte par QR, sur la surface web de l'éditeur de POI (décision 26, lot 11bis) |
|
||||
| « Capture par `getUserMedia` » | Flux vidéo sans autofocus ni traitement constructeur, et manager-app n'a aucun binding caméra JS-interop | `<input type="file" accept="image/*" capture="environment">` : appareil photo natif, JPEG pleine résolution, retombe sur le sélecteur de fichiers sur poste |
|
||||
| « Les photos sources passent par l'ingestion du lot 0 » | Le lot 0 est **codé depuis le 13/09**, et une photo source n'est pas une `Resource` : c'est une entrée de génération | `studio-sources/`, hors quota médiathèque, balayé par le cycle de vie. Lot 0 non rouvert |
|
||||
|
||||
---
|
||||
|
||||
@ -1662,6 +1746,13 @@ la première migration du Studio, pas en arrivant au lot 11.
|
||||
`x-goog-content-length-range` — sans quoi le navigateur refuse l'envoi.
|
||||
- **Cycle de vie** : règle `age: 1` sur le préfixe `incoming/` — un envoi jamais ingéré disparaît seul,
|
||||
sans job Hangfire. S'ajoute à la règle des versions non courantes posée le 07/09.
|
||||
- **Cycle de vie à prévoir dans le même geste** : règle équivalente (`age: 7`) sur `studio-sources/`, le
|
||||
préfixe des photos sources de la 3D (lot 11bis, §3.7bis). Rien ne les balaie autrement : ce ne sont
|
||||
pas des `Resource`. À poser maintenant coûte une ligne, plus tard coûte de le redécouvrir.
|
||||
- **Origine de la page de capture dans le CORS** — case à cocher, pas encore une valeur : la config CORS
|
||||
est au niveau du bucket et ne liste que les origines du manager. Tant que l'hébergement de la surface
|
||||
web non-Flutter (éditeur POI + page de capture) n'est pas tranché, on ne sait pas si c'est une origine
|
||||
de plus ou la même. À revérifier au lot 11bis.
|
||||
- **Règles Storage : écriture fermée à tous les clients**, lecture inchangée. Relever les règles
|
||||
actuelles avant. ⚠️ À appliquer **après** la mise en prod du nouveau manager-app : fermer avant
|
||||
casse l'upload de la version encore déployée.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user