jeanotx32 37e0c73ab5
Some checks failed
release / build (push) Successful in 27s
release / verify-windows (push) Failing after 56s
Feat : Control streamer
2026-08-11 21:11:04 +02:00
2026-08-11 20:16:08 +02:00
2026-08-11 21:11:04 +02:00
2026-08-11 20:04:08 +02:00
2026-08-11 17:18:54 +02:00
FP
2026-08-11 00:26:56 +02:00
2026-08-11 18:13:50 +02:00
2026-08-11 17:10:08 +02:00
2026-08-11 19:04:32 +02:00
2026-08-11 17:10:08 +02:00
2026-08-11 18:04:28 +02:00
2026-08-11 17:10:08 +02:00
FP
2026-08-11 00:26:56 +02:00
2026-08-11 18:41:19 +02:00
FP
2026-08-11 00:26:56 +02:00

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.
  • Reconnexion automatique de bout en bout (agent → serveur, agent → OBS, dashboard → serveur).
  • Pause automatique pendant les shows privés (Stripchat), avec reprise et rappel du plein écran au retour du flux public.

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).

Démarrage rapide (développement)

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 :

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 : interface en Docker Compose, agents en une commande, artefacts construits et publiés par Gitea Actions.

En résumé :

# 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 :

node /opt/stream-control-agent/agent.cjs --check

Configuration

Serveur — .env (voir .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
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é.

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.

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
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
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, agent.ping.

Structure

Paquet Rôle
packages/shared/ Types du protocole, partagés par les trois autres
packages/server/ API REST, passerelle agents, diffusion dashboard, SQLite
packages/agent/ Binaire à déployer sur chaque VM, pilote OBS
packages/web/ Dashboard React/Vite

Points d'entrée utiles : packages/shared/src/index.ts (le protocole), packages/server/src/hub.ts (état central et dispatch), packages/agent/src/obs.ts (traduction action → obs-websocket), packages/agent/src/watcher.ts (sonde de statut et machine à états pause/reprise), 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.

Description
No description provided
Readme 2.4 MiB
Languages
TypeScript 82.7%
JavaScript 8%
CSS 4.6%
Shell 2.3%
PowerShell 1.8%
Other 0.5%