449 lines
23 KiB
Markdown
449 lines
23 KiB
Markdown
# Stream Control
|
|
|
|
Plan de contrôle web pour piloter des enregistrements OBS répartis sur plusieurs VM/VPS
|
|
(Windows et Ubuntu) depuis une seule interface.
|
|
|
|
```
|
|
┌────────────┐ HTTPS + WS ┌──────────────────┐ WS sortant ┌──────────────────┐
|
|
│ Navigateur │ ───────────────►│ Serveur de │◄────────────────│ Agent (VM) │
|
|
│ dashboard │◄─── temps réel ─│ contrôle │──── commandes ─►│ └► OBS (4455) │
|
|
└────────────┘ │ Express + SQLite│ └──────────────────┘
|
|
└──────────────────┘ ┌──────────────────┐
|
|
◄────────────────│ Agent (VM) … │
|
|
└──────────────────┘
|
|
```
|
|
|
|
**Le sens des connexions est le point clé** : ce sont les agents qui se connectent au
|
|
serveur, pas l'inverse. Aucun port entrant à ouvrir sur les VM d'enregistrement, aucune
|
|
IP publique nécessaire, et obs-websocket reste sur `127.0.0.1`.
|
|
|
|
## Fonctionnalités
|
|
|
|
- Découverte automatique des agents par jeton d'enrôlement, ou provisionnement manuel.
|
|
- État temps réel par VM : OBS connecté, enregistrement en cours, durée, taille du
|
|
fichier, scène active, FPS, frames perdues, CPU, espace disque restant.
|
|
- Commandes unitaires ou groupées : démarrer/arrêter/mettre en pause/découper un
|
|
enregistrement, démarrer/arrêter un stream, changer de scène, de profil, de collection,
|
|
changer le dossier d'enregistrement.
|
|
- Journal d'évènements horodaté, persistant et diffusé en direct, plus un
|
|
[historique par VM](#historique-par-vm) filtrable (🕘 sur la fiche de l'agent).
|
|
- Reconnexion automatique de bout en bout (agent → serveur, agent → OBS, dashboard → serveur).
|
|
- [Pause automatique pendant les shows privés](#pause-automatique-pendant-les-shows-privés)
|
|
(Stripchat), avec reprise et rappel du plein écran au retour du flux public.
|
|
- [Clôture différée sur passage hors-ligne](#passage-hors-ligne) : une coupure brève
|
|
ne découpe pas le fichier, une vraie fin de diffusion le termine.
|
|
- [Enregistrement automatique](#enregistrement-automatique) d'un streamer sur une VM
|
|
dédiée, dès qu'il est en direct depuis assez longtemps.
|
|
- [Presets d'enregistrement](#presets-denregistrement) appliqués à OBS depuis l'interface,
|
|
réglables VM par VM : de « Économe » à « Sans perte ».
|
|
|
|
## Prérequis
|
|
|
|
- Node.js **22+** sur le serveur et sur chaque VM (le serveur utilise `node:sqlite`).
|
|
- OBS **28+** sur chaque VM, avec *Outils → Paramètres du serveur WebSocket* activé.
|
|
Note le port (4455 par défaut) et le mot de passe.
|
|
- Pour le rappel du plein écran sous Ubuntu : `xdotool` et une session X11
|
|
(voir [Prérequis pour le rappel du plein écran](#prérequis-pour-le-rappel-du-plein-écran)).
|
|
|
|
## Démarrage rapide (développement)
|
|
|
|
```bash
|
|
npm install
|
|
cp .env.example .env # renseigne ADMIN_PASSWORD, SESSION_SECRET, ENROLLMENT_TOKEN
|
|
npm run dev # serveur :8080 + dashboard Vite :5173
|
|
```
|
|
|
|
Puis, dans un autre terminal, un agent local :
|
|
|
|
```bash
|
|
cd packages/agent
|
|
cp agent.config.example.json agent.config.json # colle le ENROLLMENT_TOKEN dans "token"
|
|
npm run dev
|
|
```
|
|
|
|
L'agent s'enrôle, reçoit son jeton permanent, le réécrit dans `agent.config.json`, et
|
|
apparaît dans le dashboard sur <http://localhost:5173>.
|
|
|
|
## Déploiement
|
|
|
|
La procédure complète est dans **[DEPLOY.md](DEPLOY.md)** : interface en Docker Compose,
|
|
agents en une commande, artefacts construits et publiés par Gitea Actions.
|
|
|
|
En résumé :
|
|
|
|
```bash
|
|
# Interface (machine du plan de contrôle)
|
|
docker compose up -d
|
|
|
|
# Agent Ubuntu
|
|
curl -fsSL <gitea>/api/packages/jeanbon/generic/stream-control-agent/latest/install-agent.sh \
|
|
| sudo bash -s -- --registry <gitea> --server ws://<IP_DU_CONTROLE>:8080/ws/agent --token <JETON>
|
|
|
|
# Agent Windows (PowerShell administrateur)
|
|
& ([scriptblock]::Create((irm '<gitea>/api/packages/jeanbon/generic/stream-control-agent/latest/install-agent.ps1'))) `
|
|
-Registry '<gitea>' -Server 'ws://<IP_DU_CONTROLE>:8080/ws/agent' -Token '<JETON>'
|
|
```
|
|
|
|
Les agents ne sont volontairement pas conteneurisés : ils doivent voir l'OBS local et la
|
|
fenêtre du navigateur.
|
|
|
|
Pour vérifier une installation à tout moment :
|
|
|
|
```bash
|
|
node /opt/stream-control-agent/agent.cjs --check
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Serveur — `.env` (voir [.env.example](.env.example))
|
|
|
|
| Variable | Rôle |
|
|
| --- | --- |
|
|
| `PORT`, `HOST` | Écoute HTTP/WebSocket |
|
|
| `ADMIN_PASSWORD` | Mot de passe du dashboard |
|
|
| `SESSION_SECRET` | Clé HMAC des sessions (32+ octets aléatoires) |
|
|
| `ENROLLMENT_TOKEN` | Jeton d'auto-enregistrement ; vide = désactivé |
|
|
| `DB_PATH` | Fichier SQLite |
|
|
| `STATUS_INTERVAL_MS` | Fréquence de remontée d'état des agents |
|
|
| `AGENT_TIMEOUT_MS` | Délai avant de déclarer un agent hors-ligne |
|
|
|
|
### Agent — `agent.config.json` (ou variables d'environnement)
|
|
|
|
| Clé | Variable | Rôle |
|
|
| --- | --- | --- |
|
|
| `serverUrl` | `SERVER_URL` | `wss://control.exemple.com/ws/agent` |
|
|
| `token` | `AGENT_TOKEN` | Jeton d'enrôlement puis jeton propre à l'agent |
|
|
| `name` | `AGENT_NAME` | Nom affiché (défaut : hostname) |
|
|
| `obs.host/port/password` | `OBS_HOST` / `OBS_PORT` / `OBS_PASSWORD` | Repli local ; le serveur pousse la config de référence |
|
|
| `insecureTls` | `INSECURE_TLS=1` | Accepter un certificat auto-signé |
|
|
|
|
Les paramètres OBS édités dans le dashboard sont poussés à chaud vers l'agent : pas
|
|
besoin de se connecter à la VM pour changer un mot de passe obs-websocket.
|
|
|
|
## Pause automatique pendant les shows privés
|
|
|
|
Quand un streamer bascule en show privé, le flux public est remplacé par un écran
|
|
d'attente : l'enregistrement continue mais ne capte plus rien d'utile, et le lecteur
|
|
sort du plein écran. L'agent peut surveiller le statut du streamer et réagir seul.
|
|
|
|
Configuration par agent, dans *Configuration → Surveillance du stream* :
|
|
|
|
| Réglage | Effet |
|
|
| --- | --- |
|
|
| Pseudo du streamer | Celui de l'URL de sa page |
|
|
| Intervalle de sonde | Fréquence d'interrogation de l'API (10 s par défaut, plancher 3 s) |
|
|
| Lectures avant pause | Lectures « privé » consécutives exigées avant d'agir (2 par défaut) |
|
|
| Statuts « privé » | Statuts déclenchant la pause — retire `groupShow` pour continuer à enregistrer les shows de groupe |
|
|
| Touche / fenêtre / délai | Raccourci plein écran à renvoyer au lecteur après le show |
|
|
|
|
### Ce que fait l'agent
|
|
|
|
1. Il interroge `GET /api/front/v2/models/username/{pseudo}/cam` et lit `user.user.status`.
|
|
Valeurs relevées en production : `public`, `private`, `p2p`, `groupShow`, `idle`.
|
|
2. Statut privé confirmé → `PauseRecord`, en mémorisant que **c'est lui** qui a mis en pause.
|
|
3. Retour au public → `ResumeRecord`, puis envoi de la touche plein écran après le délai
|
|
configuré, le temps que le lecteur ait rechargé le flux.
|
|
|
|
Trois garde-fous, parce qu'une automatisation qui coupe un enregistrement au mauvais
|
|
moment coûte plus cher que quelques secondes d'écran d'attente enregistrées :
|
|
|
|
- **La pause exige plusieurs lectures consécutives, la reprise agit immédiatement.** Une
|
|
fausse pause perd du contenu réel ; une fausse reprise ne coûte rien.
|
|
- **Une sonde en échec ne déclenche jamais rien.** API injoignable ou réponse inattendue :
|
|
l'agent conserve le dernier état connu et ne touche pas à l'enregistrement.
|
|
- **Une pause manuelle n'est jamais reprise automatiquement.** L'agent ne reprend que ce
|
|
qu'il a lui-même mis en pause.
|
|
|
|
Un passage `hors-ligne` (`idle`) ne provoque ni pause ni reprise : seul le retour effectif
|
|
du flux public relance l'enregistrement.
|
|
|
|
### Prérequis pour le rappel du plein écran
|
|
|
|
L'envoi de touche se fait au niveau du système, pas via OBS.
|
|
|
|
| OS | Mécanisme | À prévoir |
|
|
| --- | --- | --- |
|
|
| Ubuntu | `xdotool windowactivate` + XTEST | `apt install xdotool`, session **X11** (pas Wayland), `DISPLAY` accessible à l'agent |
|
|
| | | Le cookie X est résolu à l'exécution : `$XAUTHORITY`, puis `/run/user/<uid>/gdm/Xauthority`, les cookies Xwayland, puis `~/.Xauthority` |
|
|
| Windows | `SetForegroundWindow` + `SendKeys` | L'agent doit tourner dans la session interactive — d'où la tâche planifiée plutôt qu'un service |
|
|
| macOS | AppleScript System Events | Autorisation Accessibilité (prévu pour le développement) |
|
|
|
|
Dans les deux cas la fenêtre du lecteur passe **au premier plan** : les navigateurs
|
|
ignorent les évènements clavier synthétiques envoyés sans focus (`XSendEvent`). Sans
|
|
conséquence sur une VM d'enregistrement dédiée, gênant si quelqu'un s'en sert en même
|
|
temps.
|
|
|
|
Le bouton ⛶ sur la fiche de l'agent renvoie la touche à la demande, et ⟳ force une sonde
|
|
immédiate — les deux servent à valider le titre de fenêtre sans attendre un vrai show privé.
|
|
|
|
Le **titre de fenêtre** à renseigner est celui de la fenêtre, pas le nom du processus. Sous
|
|
Firefox il vaut `<titre de la page> — Mozilla Firefox`, et le titre d'une page Stripchat se
|
|
termine par `| Stripchat` : `Stripchat` comme `Firefox` conviennent donc. En cas d'échec,
|
|
l'agent liste les fenêtres qu'il voit réellement, et `agent.cjs --check` fait de même sans
|
|
rien déclencher.
|
|
|
|
Trois causes distinctes produisaient autrefois le même message « aucune fenêtre ne
|
|
correspond » ; elles sont maintenant séparées :
|
|
|
|
| Symptôme | Cause |
|
|
| --- | --- |
|
|
| `Authorization required` / `Invalid MIT-MAGIC-COOKIE-1 key` | cookie X introuvable ou périmé |
|
|
| `Session Wayland : seule la fenêtre technique du compositeur est visible` | voir ci-dessous |
|
|
| `Aucune fenêtre visible sur DISPLAY=:0` | navigateur lancé hors de la session de l'agent |
|
|
| Liste des fenêtres ouvertes | titre mal renseigné — recopier un fragment de la liste |
|
|
|
|
### Wayland
|
|
|
|
Ubuntu démarre en session Wayland par défaut, et Firefox y tourne en client Wayland natif.
|
|
Une telle fenêtre est **invisible à xdotool** : ni activation, ni envoi de touche. Le
|
|
symptôme est net — côté X11, seule `mutter guard window` apparaît, la fenêtre technique du
|
|
compositeur. L'agent reconnaît cette signature et le dit explicitement.
|
|
|
|
Deux issues :
|
|
|
|
- **Rester en Wayland, passer Firefox sous XWayland.** L'agent pose `MOZ_ENABLE_WAYLAND=0`
|
|
en lançant le navigateur, ce qui suffit — *à condition qu'aucune instance Firefox ne
|
|
tourne déjà*. Firefox délègue l'URL à l'instance existante, qui garde ses propres
|
|
variables d'environnement : ferme toutes ses fenêtres avant de lancer la capture.
|
|
- **Ouvrir une session Xorg.** Sur l'écran de connexion GDM, roue dentée en bas à droite →
|
|
« Ubuntu sur Xorg ». Tout redevient pilotable, y compris une fenêtre ouverte à la main.
|
|
|
|
Si le rappel du plein écran s'avère fragile sur ta VM, l'alternative sans clavier est de
|
|
lancer le navigateur en mode kiosque (`chromium --kiosk`) : il n'y a alors plus de plein
|
|
écran à restaurer.
|
|
|
|
## Presets d'enregistrement
|
|
|
|
Chaque VM peut être réglée sur un compromis qualité / charge CPU différent, depuis la
|
|
fiche de l'agent (⚙ → « Preset d'enregistrement »). Tant que la case n'est pas cochée,
|
|
l'agent ne touche à rien et OBS garde sa configuration manuelle.
|
|
|
|
| Preset | Sortie | Qualité | Conteneur | CPU |
|
|
| --- | --- | --- | --- | --- |
|
|
| Économe | 720p 30 fps | haute (CRF ~23) | MP4 | ● |
|
|
| Équilibré | 1080p 30 fps | très haute (CRF ~16) | MP4 | ●● |
|
|
| Qualité maximale | définition de la scène, 60 fps | très haute, encodage lent | MKV | ●●● |
|
|
| Sans perte | définition de la scène, 60 fps | aucune perte | MKV | ●●●● |
|
|
|
|
L'encodeur se choisit séparément : x264 (le seul disponible sur un VPS sans GPU), NVENC,
|
|
Quick Sync, AMF ou VideoToolbox. Le preset fournit la valeur de vitesse adaptée à la
|
|
famille retenue — `veryfast` pour x264, `p5` pour NVENC, etc.
|
|
|
|
### Ce qui se passe à l'application
|
|
|
|
L'agent écrit dans le profil OBS courant (`SetProfileParameter`, section `SimpleOutput`)
|
|
puis ajuste la sortie vidéo (`SetVideoSettings`). Trois comportements à connaître :
|
|
|
|
- **La qualité prend effet au prochain démarrage d'enregistrement.** OBS relit ces
|
|
paramètres à ce moment-là ; l'interface d'OBS, elle, ne les rafraîchit qu'au changement
|
|
de profil.
|
|
- **Changer d'encodeur exige un redémarrage d'OBS.** L'objet encodeur n'est instancié
|
|
qu'au lancement. L'agent le détecte et le signale dans son compte rendu.
|
|
- **Un preset n'est jamais appliqué pendant une capture** : cela la corromprait. La
|
|
demande est refusée, ou différée jusqu'à l'arrêt de l'enregistrement.
|
|
|
|
Le mode de sortie du profil est forcé sur « Simple » : c'est la section que ces réglages
|
|
pilotent. Un paramètre refusé par la version d'OBS installée n'interrompt pas les autres —
|
|
il apparaît dans le compte rendu affiché sous le bouton « Appliquer maintenant ».
|
|
|
|
Un preset trop lourd pour la VM fait chuter les images par seconde : la qualité perçue
|
|
baisse alors malgré un meilleur CRF. Après un changement, surveille « FPS » et « Frames
|
|
perdues » sur la fiche de l'agent.
|
|
|
|
## Historique par VM
|
|
|
|
Le journal en bas de page mélange toutes les machines. Le bouton 🕘 de la fiche d'un agent
|
|
ouvre son historique à lui : mêmes entrées, filtrées sur cette VM, groupées par jour et du
|
|
plus récent au plus ancien.
|
|
|
|
Chaque entrée porte un **type d'évènement** — `record.paused`, `obs.disconnected`,
|
|
`fullscreen.restored`, `preset.applied`… — qui lui donne son pictogramme et sa couleur, et
|
|
qui alimente les filtres : Enregistrement, OBS, Surveillance, Problèmes.
|
|
|
|
Ce champ est facultatif dans le protocole. Une entrée écrite avant cette version, ou
|
|
envoyée par un agent qui n'a pas encore été mis à jour, s'affiche sans pictogramme plutôt
|
|
que de disparaître.
|
|
|
|
Les pauses détectées valent d'être soulignées : elles proviennent de l'évènement
|
|
`RecordStateChanged` d'OBS, pas de la commande envoyée. L'historique montre donc aussi
|
|
les pauses déclenchées depuis l'interface d'OBS sur la VM, que le dashboard n'aurait
|
|
aucun autre moyen de connaître.
|
|
|
|
La rétention est celle du journal global (`LOG_RETENTION`) : les entrées les plus
|
|
anciennes sont purgées, toutes VM confondues.
|
|
|
|
## Statuts Stripchat
|
|
|
|
Le champ autoritatif est `user.user.status`. Deux familles, deux traitements :
|
|
|
|
| Statut brut | Signification | Traitement |
|
|
| --- | --- | --- |
|
|
| `public` | diffusion publique | enregistré |
|
|
| `private`, `p2p`, `groupShow`, `virtualPrivate`, `ticketShow` | show payant | pause, sans clôture |
|
|
| `idle` | **« revient bientôt »** — connecté mais ne diffuse pas | pause, sans clôture |
|
|
| `off`, `notFound`, `deleted` | déconnecté | pause puis [clôture différée](#passage-hors-ligne) |
|
|
|
|
`idle` se distingue de `off` par `isLive: false` mais `isOnline: true` : le streamer est
|
|
toujours là, il s'est simplement absenté. Le flux revient, donc l'enregistrement se met en
|
|
pause et attend, exactement comme pendant un show privé — il ne se clôt pas.
|
|
|
|
Ne pas confondre avec `offlineStatus`, un champ voisin : c'est le message d'absence libre
|
|
du modèle (« I'll be back soon »), qui reste renseigné pendant qu'il diffuse. Il ne dit
|
|
rien de l'état courant et n'est pas utilisé.
|
|
|
|
La liste des statuts mis en pause est modifiable par agent, dans « Surveillance du stream ».
|
|
Retirer `groupShow` pour continuer à enregistrer les shows de groupe, par exemple. Les
|
|
agents créés avant l'ajout d'`idle` sont migrés au démarrage du serveur, sauf si leur liste
|
|
a été personnalisée.
|
|
|
|
## Passage hors-ligne
|
|
|
|
Quand le statut brut passe à `off`, deux temps distincts :
|
|
|
|
1. **Pause immédiate.** L'écran d'attente n'a rien à faire dans le fichier. Aucun délai,
|
|
aucune confirmation : reprendre ne coûte rien si la lecture était fausse.
|
|
2. **Clôture différée**, après le délai réglé sur la fiche de l'agent (1 h par défaut).
|
|
Une coupure de quelques minutes est fréquente ; clore tout de suite découperait le
|
|
fichier en deux. Le compte à rebours est annulé dès que le flux revient, public **ou**
|
|
privé, et l'enregistrement reprend dans le même fichier.
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
## Enregistrement automatique
|
|
|
|
Sur la fiche d'un streamer (onglet Streamers), assigne un agent puis coche
|
|
**Enregistrer automatiquement**. Dès que le profil est en direct depuis `AUTO_RECORD_DELAY_MS`
|
|
(60 s par défaut), le serveur lance la capture sur cet agent, exactement comme le bouton
|
|
« Enregistrer » — surveillance des shows privés comprise.
|
|
|
|
Trois règles cadrent l'automatisme :
|
|
|
|
- **Un streamer par VM.** Activer l'automatisme sur un agent déjà pris est refusé, avec le
|
|
nom du profil qui l'occupe.
|
|
- **Aucune préemption.** Si la VM enregistre déjà quoi que ce soit, l'automatisme passe son
|
|
tour plutôt que d'écraser la capture en cours.
|
|
- **Trois tentatives par diffusion.** Les échecs restants une fois ces garde-fous passés
|
|
(OBS injoignable, par exemple) sont surtout persistants ; au-delà, l'automatisme
|
|
abandonne jusqu'à la diffusion suivante et le dit dans le journal.
|
|
|
|
Le délai d'amorçage n'est pas une précaution de style : un modèle qui sort d'un show privé
|
|
repasse « public » quelques secondes avant de se remettre en place. Déclencher sur la
|
|
première lecture produirait des fichiers de dix secondes.
|
|
|
|
## Capture de fenêtre OBS et réutilisation
|
|
|
|
La source « capture de fenêtre » d'OBS mémorise un identifiant de fenêtre X11. Fermer la
|
|
fenêtre du navigateur, ou en ouvrir une nouvelle, invalide cet identifiant : la source
|
|
devient noire et il faut la repointer à la main.
|
|
|
|
L'agent évite donc les deux :
|
|
|
|
- **aucun argument** par défaut (`--new-window` est proscrit) : Firefox confie l'URL à la
|
|
fenêtre déjà ouverte ;
|
|
- **à l'arrêt, page vide** plutôt que fermeture : la fenêtre survit, et le lecteur cesse de
|
|
décoder la vidéo.
|
|
|
|
Les agents configurés avant ce correctif sont migrés au démarrage du serveur, sauf si leurs
|
|
arguments ou leur comportement d'arrêt ont été personnalisés.
|
|
|
|
### « Firefox est déjà ouvert »
|
|
|
|
Ce dialogue signifie que le processus lancé par l'agent n'a pas trouvé l'instance déjà en
|
|
cours : il bute alors sur le verrou de profil au lieu de lui confier l'URL.
|
|
|
|
Le passage de relais se fait par le **bus de session D-Bus** — le seul mécanisme disponible
|
|
sous Wayland, le protocole X de remoting n'y existant pas. Or un service systemd « system »
|
|
n'hérite pas de ce bus, pas plus qu'il n'hérite du cookie X. L'agent résout donc lui-même,
|
|
à chaque lancement :
|
|
|
|
| Variable | Origine |
|
|
| --- | --- |
|
|
| `DISPLAY` | valeur héritée, sinon la socket X présente dans `/tmp/.X11-unix` |
|
|
| `XAUTHORITY` | `$XAUTHORITY` s'il existe, puis GDM, Xwayland, `~/.Xauthority` |
|
|
| `XDG_RUNTIME_DIR` | valeur héritée, sinon `/run/user/<uid>` |
|
|
| `DBUS_SESSION_BUS_ADDRESS` | valeur héritée, sinon la socket `$XDG_RUNTIME_DIR/bus` |
|
|
|
|
`agent.cjs --check` affiche les quatre. Si le bus ressort en avertissement, vérifie que
|
|
l'agent tourne bien sous **le même compte** que la session graphique : l'utilisateur est
|
|
choisi par `--user` à l'installation.
|
|
|
|
### Éviter l'accumulation d'onglets
|
|
|
|
Avec les réglages d'usine de Firefox, un lien venu de l'extérieur ouvre un **nouvel
|
|
onglet** : chaque cycle d'enregistrement en laisse donc derrière lui, et les anciens
|
|
continuent de décoder leur page. Sur une VM d'enregistrement, règle une fois pour toutes
|
|
dans `about:config` :
|
|
|
|
```
|
|
browser.link.open_newwindow = 1
|
|
```
|
|
|
|
L'URL remplace alors le contenu de l'onglet courant. Ouverture et page vide réutilisent le
|
|
même onglet, dans la même fenêtre — rien ne s'accumule et OBS ne perd jamais sa cible.
|
|
|
|
Dans OBS, règle aussi la **priorité de correspondance** de la source sur « Faire
|
|
correspondre le titre, sinon trouver une fenêtre du même type » : le titre suit l'onglet
|
|
actif et change à chaque streamer.
|
|
|
|
## API HTTP
|
|
|
|
Toutes les routes hors `/api/login` exigent `Authorization: Bearer <jeton de session>`.
|
|
|
|
| Méthode | Route | Rôle |
|
|
| --- | --- | --- |
|
|
| `POST` | `/api/login` | Ouvre une session (`{ password }`) |
|
|
| `GET` | `/api/agents` | Liste des agents et de leur état |
|
|
| `POST` | `/api/agents` | Provisionne un agent, renvoie son jeton **une seule fois** |
|
|
| `PATCH` | `/api/agents/:id` | Nom, notes, paramètres OBS, auto-connexion, surveillance, navigateur, preset |
|
|
| `PATCH` | `/api/watchlist/:id` | Libellé, agent assigné, notifications, automatisme |
|
|
| `POST` | `/api/agents/:id/token` | Régénère le jeton (coupe la session en cours) |
|
|
| `DELETE` | `/api/agents/:id` | Supprime l'agent |
|
|
| `POST` | `/api/agents/:id/command` | `{ action, params }`, attend le résultat de l'agent |
|
|
| `POST` | `/api/commands/bulk` | Même action sur plusieurs agents, résultat par agent |
|
|
| `GET` | `/api/logs?limit=` | Journal récent, toutes VM confondues |
|
|
| `GET` | `/api/agents/:id/logs?limit=` | Historique d'une VM, ordre chronologique |
|
|
| `GET` | `/healthz` | Sonde de vie (non authentifiée) |
|
|
|
|
Actions disponibles : `obs.connect`, `obs.disconnect`, `obs.refresh`, `record.start`,
|
|
`record.stop`, `record.pause`, `record.resume`, `record.split`, `stream.start`,
|
|
`stream.stop`, `scene.set`, `profile.set`, `collection.set`, `recordDirectory.set`,
|
|
`watch.check`, `hotkey.fullscreen`, `browser.open`, `browser.close`, `capture.start`,
|
|
`capture.stop`, `preset.apply`, `agent.update`, `agent.ping`.
|
|
|
|
## Structure
|
|
|
|
| Paquet | Rôle |
|
|
| --- | --- |
|
|
| [packages/shared/](packages/shared/) | Types du protocole, partagés par les trois autres |
|
|
| [packages/server/](packages/server/) | API REST, passerelle agents, diffusion dashboard, SQLite |
|
|
| [packages/agent/](packages/agent/) | Binaire à déployer sur chaque VM, pilote OBS |
|
|
| [packages/web/](packages/web/) | Dashboard React/Vite |
|
|
|
|
Points d'entrée utiles : [packages/shared/src/index.ts](packages/shared/src/index.ts)
|
|
(le protocole), [packages/server/src/hub.ts](packages/server/src/hub.ts) (état central et
|
|
dispatch), [packages/agent/src/obs.ts](packages/agent/src/obs.ts) (traduction
|
|
action → obs-websocket), [packages/agent/src/watcher.ts](packages/agent/src/watcher.ts)
|
|
(sonde de statut et machine à états pause/reprise),
|
|
[packages/agent/src/hotkey.ts](packages/agent/src/hotkey.ts) (envoi de touche par OS).
|
|
|
|
## Sécurité
|
|
|
|
- Les jetons d'agent ne sont stockés qu'en SHA-256 ; le clair n'est affiché qu'à la création.
|
|
- Le mot de passe obs-websocket n'est jamais renvoyé au navigateur (masqué en `********`).
|
|
- Les sessions dashboard sont des jetons HMAC à durée limitée, sans état serveur.
|
|
- **Sers l'application en HTTPS/WSS** : jetons d'agent et de session circulent dans les
|
|
en-têtes et les URL de WebSocket.
|
|
- Après un `POST /api/agents/:id/token`, l'agent est déconnecté jusqu'à ce que son
|
|
`agent.config.json` soit mis à jour.
|
|
|
|
## Pistes d'évolution
|
|
|
|
Enregistrements programmés (cron par agent), rapatriement automatique des fichiers
|
|
(rclone/S3 déclenché à `record.stop`), alertes sur seuil d'espace disque ou de frames
|
|
perdues, comptes utilisateurs multiples, groupes d'agents.
|