Fix : FS after off status
All checks were successful
release / build (push) Successful in 23s
release / verify-windows (push) Successful in 1m27s

This commit is contained in:
jeanotx32
2026-08-12 22:40:58 +02:00
parent d44a7c4fb6
commit 449a6d917f
2 changed files with 62 additions and 8 deletions

View File

@@ -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 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. 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 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 l'enregistrement quand le flux passe hors-ligne » désactive les deux temps, y compris la
pause. pause.

View File

@@ -186,9 +186,18 @@ export class StreamWatcher extends EventEmitter {
// Le retour du flux, public ou privé, annule toute clôture programmée. // Le retour du flux, public ou privé, annule toute clôture programmée.
if (next !== 'offline') this.cancelOfflineStop(); if (next !== 'offline') this.cancelOfflineStop();
if (next === 'private') { // Toute interruption du flux public appelle un rappel du plein écran au
// Le lecteur quitte le plein écran dès que l'overlay de show privé apparaît. // 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; this.fullscreenPending = true;
}
if (next === 'private') {
await this.handlePrivate(); await this.handlePrivate();
return; return;
} }
@@ -325,9 +334,37 @@ export class StreamWatcher extends EventEmitter {
} }
private async handlePublic(): Promise<void> { private async handlePublic(): Promise<void> {
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 { try {
const record = await this.actions.recordState();
if (record.active && record.paused) { if (record.active && record.paused) {
await this.actions.resumeRecording(); await this.actions.resumeRecording();
this.emit('log', 'info', 'Flux public rétabli : enregistrement repris', 'record.resumed'); 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) { if (recalling) {
this.fullscreenPending = false;
this.scheduleFullscreen();
} else {
this.fullscreenPending = false; 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();
} }
} }