From 449a6d917f2fc43f946935b2cd57f5ba8f84eae4 Mon Sep 17 00:00:00 2001 From: jeanotx32 Date: Wed, 12 Aug 2026 22:40:58 +0200 Subject: [PATCH] Fix : FS after off status --- README.md | 12 ++++++++ packages/agent/src/watcher.ts | 58 ++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index aa4b1ed..0523acd 100644 --- a/README.md +++ b/README.md @@ -550,6 +550,18 @@ Quand le statut brut passe à `off`, deux temps distincts : Le décompte s'affiche sur la fiche de l'agent. La clôture ferme aussi la fenêtre du navigateur si son pilotage est activé, contrairement à une simple pause. +**Le plein écran est rappelé au retour, comme après un show privé.** Le lecteur en sort aussi +bien sur l'écran d'attente d'un flux coupé que sur l'overlay d'un show privé. Le cas manquait +initialement — seul le show privé armait le rappel — et il ne se limite pourtant pas à une fin +de diffusion : une coupure plus courte que le délai de clôture reprend le **même** +enregistrement, dans la **même** fenêtre, qui a entre-temps perdu son plein écran sans que +rien ne le lui rende. + +Le rappel ne part que si une capture tourne réellement. Le surveillant, lui, tourne en +permanence : sans ce garde-fou, n'importe quel profil surveillé qui passe hors-ligne puis en +direct enverrait une touche à une fenêtre que personne ne filme — et en mode BiDi, irait +jusqu'à lancer Firefox pour l'occasion. + Régler le délai à `0` clôt dès la première lecture hors-ligne ; décocher « Clore l'enregistrement quand le flux passe hors-ligne » désactive les deux temps, y compris la pause. diff --git a/packages/agent/src/watcher.ts b/packages/agent/src/watcher.ts index 6e49d23..cd411e1 100644 --- a/packages/agent/src/watcher.ts +++ b/packages/agent/src/watcher.ts @@ -186,9 +186,18 @@ export class StreamWatcher extends EventEmitter { // Le retour du flux, public ou privé, annule toute clôture programmée. if (next !== 'offline') this.cancelOfflineStop(); - if (next === 'private') { - // Le lecteur quitte le plein écran dès que l'overlay de show privé apparaît. + // Toute interruption du flux public appelle un rappel du plein écran au + // retour : le lecteur en sort aussi bien sur l'overlay de show privé que sur + // l'écran d'attente d'un flux coupé. Le hors-ligne était initialement + // absent d'ici — le cas ne se limite pourtant pas à une fin de diffusion, + // puisqu'une coupure plus courte que `offlineStopDelayMs` reprend le même + // enregistrement, dans la même fenêtre, qui a entre-temps perdu son plein + // écran sans que rien ne le lui rende. + if (next === 'private' || next === 'offline') { this.fullscreenPending = true; + } + + if (next === 'private') { await this.handlePrivate(); return; } @@ -325,9 +334,37 @@ export class StreamWatcher extends EventEmitter { } private async handlePublic(): Promise { - if (this.state.autoPaused && this.settings.resumeOnPublic) { + const resuming = this.state.autoPaused && this.settings.resumeOnPublic; + const recalling = this.fullscreenPending && this.settings.fullscreen.enabled; + // Un rappel armé alors que le réglage est désactivé n'a plus lieu d'être : + // le désarmer tout de suite évite qu'il parte bien plus tard, au premier + // réglage réactivé, pour une interruption depuis longtemps oubliée. + if (!recalling) this.fullscreenPending = false; + if (!resuming && !recalling) return; + + // Une seule lecture pour les deux décisions qui suivent : reprendre une + // capture en pause, et rappeler le plein écran — qui n'a de sens que s'il y + // a justement une capture à cadrer. + let record: { active: boolean; paused: boolean }; + try { + record = await this.actions.recordState(); + } catch (err) { + this.emit( + 'log', + 'error', + `État de l'enregistrement illisible : ${err instanceof Error ? err.message : String(err)}`, + 'command.failed', + ); + // `autoPaused` est relâché quoi qu'il arrive : s'obstiner à reprendre une + // capture dont on ne sait rien reviendrait à réessayer à chaque sonde. Le + // rappel de plein écran, lui, reste armé — le différer ne coûte rien, et + // la sonde suivante retentera. + this.state.autoPaused = false; + return; + } + + if (resuming) { try { - const record = await this.actions.recordState(); if (record.active && record.paused) { await this.actions.resumeRecording(); this.emit('log', 'info', 'Flux public rétabli : enregistrement repris', 'record.resumed'); @@ -344,11 +381,16 @@ export class StreamWatcher extends EventEmitter { } } - if (this.fullscreenPending && this.settings.fullscreen.enabled) { - this.fullscreenPending = false; - this.scheduleFullscreen(); - } else { + if (recalling) { this.fullscreenPending = false; + // Seulement s'il y a une fenêtre à cadrer. Le surveillant tourne en + // permanence, pas seulement pendant une capture : hors enregistrement, le + // rappel s'adresserait à une fenêtre que personne ne filme — et en mode + // BiDi il irait jusqu'à lancer Firefox pour l'occasion. Le cas est + // courant depuis que le hors-ligne arme lui aussi le rappel : un profil + // surveillé sans être enregistré passe hors-ligne puis en direct comme + // n'importe quel autre. + if (record.active) this.scheduleFullscreen(); } }