Comment tourne une action
Ce que Vovó fait d'une action dès qu'une scène l'affiche : ce qu'elle garantit, et ce sur quoi une action ne doit pas compter. Pour qui écrit des actions à transmettre ou à vendre ; la façon d'en écrire une se trouve dans Actions.
Une page à elle
Chaque action est un document à part, dans un cadre que la page de la scène lui donne : son HTML, avec son style.css et son script.js écrits dedans, le script en dernier, si bien qu'il s'exécute une fois le HTML en place. Une action cassée ne fait pas tomber les autres. Une scène affiche ses actions les unes sur les autres, dans son ordre, la première en bas.
Le cadre ne reçoit ni clic ni touche : un overlay se regarde, il ne se presse jamais. Ce que fait la personne qui mène le live, elle le fait sur le pad, dans le panneau de l'action ou dans Personnaliser, et cela parvient à l'action comme un événement.
Une action n'entend un événement que tant qu'une page ouverte l'affiche, et seulement dans le profil actif. Sans aucune page ouverte, rien ne compte les follows.
Pas de réseau, rien de gardé dans le navigateur
Une action atteint ses propres fichiers et ce que Vovó lui sert, et rien d'autre. Les images, les sons, les vidéos et les polices viennent des fichiers de l'action, par leur nom, ou d'une adresse qu'un événement a donnée (une emote, une pochette, un fichier choisi dans Personnaliser), jamais d'Internet. Son code et ses styles sont script.js et style.css, et eux seuls : aucun script ni feuille de style venant d'un fichier ou d'ailleurs, pas de eval, pas de worker. fetch, XMLHttpRequest et WebSocket sont refusés, même pour les fichiers de l'action, si bien que les données dont elle a besoin vont dans son script. Le stockage du navigateur (localStorage, cookies, IndexedDB) n'existe pas ici : ce qu'une action garde, elle le garde dans vovo.state.
Ce dont elle a besoin de l'extérieur, elle le demande à Vovó, dans les limites des autorisations qu'elle déclare : une ligne dans le chat, un webhook par son nom, un son du pad, un clip dans son lecteur officiel.
Chaque page ouverte l'exécute
La même action tourne partout où sa scène est ouverte : dans OBS, dans la fenêtre d'aperçu de la scène, dans un onglet du navigateur, dans deux sources à la fois. Chaque copie reçoit les mêmes événements et dessine la même chose. Une page ne fait donc que dessiner et jouer, et ce qui doit arriver une seule fois se demande à Vovó, qui le fait une fois pour les pages qui le demandent en traitant un même événement : une ligne dans le chat, un tirage, une voix, un webhook, un son du pad, une source dans OBS, un événement propre à l'action.
Vovó distingue l'appel d'une page de celui d'une autre par l'événement en cours de traitement. Un appel fait après le retour de l'écouteur (après un await, à la fin d'une animation, dans un minuteur) indique l'événement auquel il répond avec { event: e }, l'objet que l'écouteur a reçu ; sans cela, Vovó ne peut pas distinguer une deuxième page d'une deuxième fois.
vovo.on("raid", async (e) =>
{
const gift = await vovo.draw(["a hug", "a song", "a dance"]);
vovo.show("raid", { user: e.user, gift: gift.value });
vovo.chat.send(e.user + " gets " + gift.value + "!", { event: e });
});
Ce qu'une page lance d'elle-même n'a aucun événement derrière lui : un setInterval de l'action tourne dans chaque page, tout comme ce qu'elle fait en se chargeant. Vovó ne peut alors se fier qu'à ce qui est demandé (la même ligne dans le chat, la même voix, le même webhook ou événement propre en 5 secondes n'en font qu'un), donc ce qui compte, ou doit arriver une seule fois, a sa place dans le déclencheur timer, qui est celui de l'appli et unique pour toutes les pages.
L'état, jamais de rediffusion
vovo.state est gardé par Vovó, pas par la page. Une page qui se charge (OBS qui démarre, une source actualisée, la panique qui se termine, Vovó rouverte) l'a en entier avant que son script s'exécute, et une clé définie dans une page parvient aussitôt aux autres, sans un mot, puisqu'elles ont traité le même événement. Les événements ne sont jamais rejoués : une page qui n'était pas là quand un raid est arrivé n'en saura rien plus tard. Une action garde donc dans son état tout ce qu'elle doit afficher de nouveau (le compte d'un objectif, les noms d'un tirage au sort, la fin d'un compte à rebours) et dessine à partir de lui en se chargeant.
function draw()
{
document.getElementById("count").textContent = vovo.state.count || 0;
}
vovo.on("follow", () =>
{
vovo.state.count = (vovo.state.count || 0) + 1;
draw();
});
draw();
- Définissez de nouveau la clé quand ce qu'elle contient change :
vovo.state.names = names.concat([user]). Une liste ou un objet modifié sur place (push) n'est pas enregistré. - Changez l'état dans l'écouteur même, avant tout
await: chaque page part alors du même état et arrive au même. Après unawait, une autre page a peut-être déjà écrit, alors écrivez ce qui ne dépend pas de ce qui s'y trouve (le nom d'un gagnant, une heure), jamais un de plus que ce qu'il contient. - Une clé contient 64 Ko au plus, en JSON, et son nom 64 caractères.
Ce qui est actuel, et ce qui est mesuré en continu
Quatre événements sont une valeur autant qu'un moment : obs.scene, media.track, twitch.viewers et screen.shared. Une action qui se charge reçoit aussitôt la plus récente de chacun de ceux qu'elle écoute, comme si cela venait d'arriver, et affiche donc le morceau en cours sans attendre le suivant. Elle revient à chaque chargement, dans chaque page, sans événement derrière elle : dessinez à partir d'elle, et n'y répondez pas par ce qui doit arriver une seule fois.
audio.level, screen.color, input.move et speech.text sont des mesures, jusqu'à 60 par seconde : livrées puis oubliées, lues seulement tant qu'une page ouverte affiche une action qui les écoute. Gardez leurs écouteurs légers, et laissez le CSS faire le mouvement.
Chargement, rechargement et panique
Une page peut se charger à tout moment, et plus d'une fois : OBS démarre une source quand sa scène s'affiche, peut l'arrêter tant qu'elle est masquée et l'actualiser quand elle réapparaît, et Vovó recharge une action quand son code ou ses fichiers changent. La panique vide toutes les scènes d'un coup et recharge leurs actions, ce qui arrête tout ce qu'elles faisaient ; quand elle se termine, elles se chargent encore une fois et repartent de leur état. Rien ne parvient à une action pendant la panique, et rien de ce qui est arrivé entre-temps n'est joué ensuite ; la valeur d'un contrôle est gardée, puisque c'est de l'état.
Une action revient donc correctement à partir de son seul état, à tout moment, sans compter sur rien de ce que la page précédente a laissé à moitié fait.
La scène du profil
Une action dessine sur une scène en pixels CSS dont le petit côté fait 1080 : 1920 × 1080 pour un live en 16:9, quelle que soit sa résolution (une source 4K la dessine nette à deux fois la taille), 1080 × 1920 pour un live vertical, une autre forme pour un écran d'une autre forme. Son document a la taille de cette scène et il est transparent, si bien que 100vw par 100vh couvre toute l'image, et ce qu'elle ne dessine pas laisse voir le jeu. Une action faite pour les lives des autres ne présume pas de la forme : placez les éléments à partir des bords et en pourcentages, et utilisez @media (orientation: portrait) pour un live vertical.
Le navigateur d'OBS
OBS affiche les scènes dans un navigateur à lui, plus ancien que celui de l'éditeur et de la fenêtre d'aperçu de Vovó (Chromium 127 dans OBS 32), qui peut tourner sans l'aide de la carte graphique. Vérifiez une nouveauté de CSS ou de JavaScript avec cette version avant de compter dessus, et regardez l'action dans OBS même avant de la transmettre. Le son y joue aussitôt, sans clic, et part dans le live : rien de ce qui est destiné au seul streamer n'a sa place dans une action.
Une page n'a pas de console dans OBS. Les erreurs que l'action n'attrape pas, et ce que Vovó a à dire de ses appels, arrivent dans la console de Vovó sous le nom de l'action, avec la ligne ; pas console.log. Pour cela, ouvrez l'adresse de l'aperçu dans un navigateur (vovo dev l'affiche) et utilisez ses outils de développement.
Cadences et limites
- Chat : une ligne de 500 caractères ; 20 lignes en 30 secondes pour toutes les actions, une par seconde pour chacune.
- Webhooks : 16 Ko de JSON ; 30 par minute, un par seconde pour chaque action.
- Clips et vidéos : 3 en attente d'approbation pour chaque action.
- Caméras : 4 affichées à la fois, et 4 paramètres de caméra par action.
- Un appel vocal : 300 secondes au plus.
- Un bouton affiché en cours : 15 minutes au plus.
- État : 64 Ko par clé.
- Un paquet : 200 fichiers, 32 Mo chacun et 128 Mo en tout ;
index.html,style.cssetscript.js, 2 Mo chacun.
Un appel au-delà de sa cadence est laissé de côté, jamais mis en file : sa promesse est rejetée, en disant pourquoi.
Avant de la transmettre
- Rechargez la scène au milieu de tout ce qu'elle fait : elle revient correctement, à partir de son état.
- Ouvrez la scène deux fois, dans OBS et dans la fenêtre d'aperçu, et activez chaque déclencheur : une ligne dans le chat, un son, un compte.
- Activez puis désactivez la panique pendant qu'elle affiche quelque chose.
- Essayez-la sur un profil vertical, et dans OBS même.
- Ne demandez que les autorisations qu'elle utilise, et dites ce qu'elle est sous Comportement : son auteur, son site et une ligne.
- Donnez-lui un id, et une version plus élevée chaque fois que vous la remettez, pour que sa mise à jour retrouve l'action qu'elle remplace et dise quelle version elle apporte.
- Dites sous quelle licence vous la remettez, avec son texte complet quand les conditions sont les vôtres.