Aller au contenu principal

Développer un overlay

Une fois familiarisé avec le concept d'overlay, vous aurez probablement la volonté de développer les vôtres. Cette page fait donc office de "tutoriel" rapide afin que vous puissiez maîtriser les blocs principaux constituant la réalisation d'un overlay capable de recevoir et d'émettre des événements mais également d'utiliser l'API JavaScript d'OBS.

Pour savoir comment héberger votre overlay, rendez-vous sur la page dédiée.

Squelette

Le squelette de base d'un overlay Multistream Tools est le suivant :

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Custom overlay example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>

<style>
/* Your own CSS code goes here. */
</style>
</head>
<body>
<main id="logContainer"></main>

<script type="module">
import {} from 'https://obs.multistream.tools/v1/overlay.js';

// Your own JavaScript code goes here.
</script>
</body>
</html>
info

La ligne <meta name="robots" content="noindex,nofollow"/> empêche l'indexation de votre overlay par les différents moteurs de recherche.

Ce squelette utilise notre framework CSS mais, bien que conseillé, ceci n'est pas obligatoire.

Vous pouvez lier autant de fichiers/bibliothèques JS et CSS externes que vous le souhaitez, gardez simplement en tête que plus vous en aurez et plus le chargement de votre overlay pourra être lent. C'est pourquoi ce squelette intègre directement votre code JavaScript et CSS, évitant ainsi des requêtes HTTP inutiles.

attention

Pensez à modifier l'attribut lang de la balise html ainsi que le contenu de la balise title pour correspondre à vos besoins. Vous pouvez également tirer parti de notre système d'internationalisation, voir ci-dessous.

danger

Nous vous recommandons fortement de mettre en place sur votre page les mesures habituelles de sécurité telles que la Content Security Policy, surtout lorsque vous exécutez du code tiers.

Styles

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Custom overlay example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>

<style>
:root {
--fontSize: 40px;
--containerBackgroundColor: red;
}

#logContainer {
background-color: var(--containerBackgroundColor);
white-space: pre;
}
</style>
</head>
<body>
<main id="logContainer"></main>

<script type="module">
import { loadFonts } from 'https://obs.multistream.tools/v1/overlay.js';

// Optional.
loadFonts('Tangerine');

// Your own JavaScript code goes here.
</script>
</body>
</html>

Utilisez les variables CSS pour styliser votre overlay tout en facilitant l'élaboration de thèmes applicables sur ce dernier. L'exemple ci-dessus montre un changement de la taille globale du texte de la page ainsi que l'affectation de la couleur rouge en arrière plan du bloc qui affichera des événements reçus par la suite.

astuce

Nommez vos variables de façon cohérente, prévisible et logique en utilisant par exemple des préfixes et/ou suffixes afin de les regrouper par catégories.

Vous pouvez optionnellement charger des polices de caractères par défaut en utilisant la fonction loadFonts exposée par notre framework JavaScript.

Tout est possible, donnez libre cours à votre imagination !

Paramètres

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Custom overlay example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>

<style>
:root {
--fontSize: 40px;
--containerBackgroundColor: red;
}

#logContainer {
background-color: var(--containerBackgroundColor);
white-space: pre;
}
</style>
</head>
<body>
<main id="logContainer"></main>

<script type="module">
import {
loadFonts,
parameters,
validateNumber,
Queue
} from 'https://obs.multistream.tools/v1/overlay.js';

// Optional.
loadFonts('Tangerine');

let { delay } = parameters;

delay = validateNumber(delay, 5);

const queue = new Queue(delay);
</script>
</body>
</html>

Utilisez les paramètres pour rendre votre overlay facilement configurable à l'utilisation, sans avoir à modifier son code source. Nous définissions ici un paramètre personnalisé nommé delay correspondant à un nombre de secondes (5 par défaut) dont la valeur servira à initialiser une file d'attente (instance de la classe Queue) qui nous permettra par la suite d'afficher des événements reçus pendant un certain temps.

attention

Nommez convenablement vos paramètres en faisant attention à ce qu'ils n'entrent pas en conflit avec ceux faisant partie du système.

Événements

Le cœur de notre système est basé sur la réception et l'envoi d'événements ; pour ce faire on utilisera respectivement les classes Observer et Emitter de notre framework JavaScript.

Réception

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Custom overlay example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>

<style>
:root {
--fontSize: 40px;
--containerBackgroundColor: red;
}

#logContainer {
background-color: var(--containerBackgroundColor);
white-space: pre;
}
</style>
</head>
<body>
<main id="logContainer"></main>

<script type="module">
import {
loadFonts,
parameters,
validateNumber,
Queue,
setText,
stringify,
removeChildren,
Observer
} from 'https://obs.multistream.tools/v1/overlay.js';

// Optional.
loadFonts('Tangerine');

let { duration } = parameters;

duration = validateNumber(duration, 5);

const queue = new Queue(duration);

const { logContainer } = window;

const log = data => setText(stringify(data, null, ' '), logContainer);

const clear = () => removeChildren(main);

const addToQueue = data => queue.add(() => log(data), clear);

const observer = new Observer({
load: addToQueue,
session: addToQueue,
alerts: addToQueue,
chat: addToQueue,
other: addToQueue
});
</script>
</body>
</html>

Nous définissons ici :

  • une fonction utilitaire, nommée log, chargée de remplacer le contenu de la balise main par une représentation textuelle indentée d'un objet JSON
  • une fonction utilitaire, nommée clear, chargée de détruire le contenu de la balise main
  • une fonction utilitaire, nommée addToQueue, chargée d'ajouter l'affichage et la destruction d'un log à la file d'attente précédemment crée

Ensuite nous observons la réception de tous les événements - provenant d'adaptateurs - de catégorie load, session, alerts, chat et other en appellant à chaque fois la fonction addToQueue. Ceci aura pour effet d'afficher à l'écran les données liées à ces événements uniquement pendant le laps de temps souhaité. Les événements pause et skip, émis par notre dock, sont automatiquement observés dans le but d'agir sur l'exécution de la file d'attente.

Émission

Commencez par créer un nouveau fichier JavaScript, nommé par exemple emitter.js, dans le même répertoire que la page HTML de votre overlay. Il contiendra un émetteur et le nom d'un événement personnalisé :

import { Emitter } from 'https://obs.multistream.tools/v1/overlay.js';

export const customEvent = 'customEvent';

export const emitter = new Emitter();

Modifiez ensuite votre overlay pour utiliser ces derniers :

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Custom overlay example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>

<style>
:root {
--fontSize: 40px;
--containerBackgroundColor: red;
}

#logContainer {
background-color: var(--containerBackgroundColor);
white-space: pre;
}
</style>
</head>
<body>
<main id="logContainer"></main>

<script type="module">
import {
loadFonts,
parameters,
validateNumber,
Queue,
setText,
stringify,
removeChildren,
Observer,
Emitter
} from 'https://obs.multistream.tools/v1/overlay.js';

import { customEvent, emitter } from './emitter.js';

// Optional.
loadFonts('Tangerine');

let { duration } = parameters;

duration = validateNumber(duration, 5);

const queue = new Queue(duration);

const { logContainer } = window;

const log = data => {
setText(stringify(data, null, ' '), logContainer);
emitter.emit(customEvent, data);
};

const clear = () => removeChildren(logContainer);

const addToQueue = data => queue.add(() => log(data), clear);

const observer = new Observer({
load: addToQueue,
session: addToQueue,
alerts: addToQueue,
chat: addToQueue,
other: addToQueue
});
</script>
</body>
</html>

Les données liées à chaque événement reçu, provenant des différentes plateformes, seront émises à nouveau vers d'autres overlays observant l'événement personnalisé nommé customEvent. Ainsi, chaque overlay Multistream Tools peut dialoguer dans les deux sens avec plusieurs autres overlays Multistream Tools !

attention

Un overlay donné ne reçoit jamais les événements personnalisés qu'il émet lui-même. Ceci évite notamment les boucles infinies.

Créez maintenant une nouvelle page HTML, nommée par exemple receiver.html, dans le même répertoire que celle de votre overlay. Il s'agit simplement d'un second overlay capable de traiter l'événement personnalisé émis par le premier :

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Custom events receiver example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>
</head>
<body>
<script type="module">
import { Observer } from 'https://obs.multistream.tools/v1/overlay.js';

import { customEvent, emitter } from './emitter.js';

const customEventKey = emitter.key(customEvent);

const observer = new Observer({
[customEventKey](data) {
// Do whatever you want with the data here.
}
});
</script>
</body>
</html>
info

Les noms d'événements personnalisés sont automatiquement préfixés afin d'éviter d'éventuels conflits avec ceux gérés en natif par notre système.

API JavaScript d'OBS

Les overlays Multistream Tools ont la capacité d'utiliser l'API JavaScript d'OBS par le biais de notre wrapper ; il faudra juste bien penser à régler les paramètres obsAPI et obsEvents ainsi que les permissions des sources de type navigateur Web en fonction de vos besoins. Il existe différentes manières de faire appel à cette API, vous pouvez bien entendu les combiner mais voici leur description une par une.

attention

Rappel : les docks Internet personnalisés n'ont pour le moment pas accès à l'API JavaScript d'OBS.

Événements

OBS émet des événements lorsque l'on se sert du logiciel, par exemple à chaque fois que l'on démarre ou arrête un stream, que l'on change de scène, etc. Afin qu'un overlay puisse recevoir ceux-ci, il faut configurer le wrapper associé avec le paramètre nommé obsEvents et simplement utilier la classe Observer pour observer les événements en question.

Par exemple, pour un wrapper configuré tel que :

https://obs.multistream.tools/v1/wrapper?obsEvents=StreamingStarted,StreamingStopped,SceneChanged&url=<overlayURL>

L'overlay correspondant pourra s'écrire de la façon suivante :

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>OBS events exemple</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>
</head>
<body>
<script type="module">
import { Observer } from 'https://obs.multistream.tools/v1/overlay.js';

const observer = new Observer({
obsStreamingStarted() {
// Do whatever you want here.
},
obsStreamingStopped() {
// Do whatever you want here.
},
obsSceneChanged({ data }) {
// Do whatever you want with the data here.
// `data.name` contains the name of the new scene.
}
});
</script>
</body>
</html>
attention

Le préfixe obs devant chaque nom d'événement ne doit pas être présent dans la configuration du wrapper mais ne doit pas être oublié dans l'instance de la classe Observer.

Propriétés

L'API JavaScript d'OBS peut exposer des propriétés qui ne sont pas des méthodes. Le système Multistream Tools vous permet d'accéder aux valeurs de celles-ci en activant le paramètre obsAPI de notre wrapper puis en utilisant la fonction utilitaire également nommée obsAPI et la classe Observer, toutes deux exposées par notre framework JavaScript.

Par exemple, pour un wrapper configuré tel que :

https://obs.multistream.tools/v1/wrapper?obsAPI&url=<overlayURL>

L'overlay correspondant pourra s'écrire de la façon suivante :

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>OBS properties example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>
</head>
<body>
<script type="module">
import {
Observer,
obsAPI
} from 'https://obs.multistream.tools/v1/overlay.js';

const propertyName = 'pluginVersion';

const observer = new Observer({
[propertyName]({ data }) {
// Do whatever you want with the value here.
}
});

obsAPI(propertyName);
</script>
</body>
</html>

L'appel à la fonction utilitaire nommée obsAPI permet donc d'envoyer une requête à OBS qui répondra, sous la forme d'un événement à observer, avec la valeur de la propriété souhaitée ; ici le numéro de version de la brique chargée de l'affichage des sources de type navigateur Web.

Getters

Le fonctionnement des getters est le même que pour les propriétés. Vous ferez simplement appel à des méthodes de l'API JavaScript d'OBS dont le nom commence par get.

attention

Pensez à ajuster la valeur de l'attribut nommé Permissions de la page de la source de type navigateur Web associée.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>OBS getters example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>
</head>
<body>
<script type="module">
import {
Observer,
obsAPI
} from 'https://obs.multistream.tools/v1/overlay.js';

const getterName = 'getCurrentScene';

const observer = new Observer({
[getterName]({ data }) {
// Do whatever you want with the value here.
}
});

obsAPI(getterName);
</script>
</body>
</html>

Contrôle

Il est possible de contrôler OBS en appelant diverses méthodes d'API prévues à cet effet. Certaines acceptent des paramètres et d'autres non mais aucune ne renvoie de valeur à observer.

attention

Pensez à ajuster la valeur de l'attribut nommé Permissions de la page de la source de type navigateur Web associée.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>OBS control example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>
</head>
<body>
<script type="module">
import {
obsAPI,
parameters,
validateString,
validateNumber,
toMilliSeconds
} from 'https://obs.multistream.tools/v1/overlay.js';

let { endScene, stopDelay } = parameters;

endScene = validateString(endScene, 'End');
stopDelay = validateNumber(stopDelay, 30);

obsAPI('setCurrentScene', endScene);

setTimeout(() => obsAPI('stopStreaming'), toMilliSeconds(stopDelay));
</script>
</body>
</html>

L'exemple ci-dessus montre un code qui, une fois la source correspondante active, débutera une transition vers une scène de fin de stream, configurable (nommée 'End' par défaut), avant d'arrêter complètement celui-ci après un certain délai, également configurable (30 secondes par défaut).

Docks

Vous pouvez aussi développer vos propres docks, il ne s'agit ni plus ni moins que d'overlays intégrés à OBS en tant que dock Internet personnalisés ; la seule limitation étant que ceux-ci n'ont pour l'instant pas directement accès à l'API JavaScript d'OBS pour des raisons indépendantes de notre volonté.

astuce

Vous pouvez néanmoins contourner cette limitation en faisant communiquer si besoin vos docks avec un overlay faisant office de passe-plat.

Voici un exemple de dock utilisant nos frameworks JS et CSS pour afficher un formulaire d'administration ainsi que quelques boutons :

remarque

Nous considérons qu'il existe également un fichier nommé emitter.js, présent dans le même répertoire que la page HTML du dock, contenant le code suivant:

import { Emitter } from 'https://obs.multistream.tools/v1/overlay.js';

export const buttonClicked = 'buttonClicked';

export const settingsSubmitted = 'settingsSubmitted';

export const emitter = new Emitter();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Custom dock example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/dock.css"/>
</head>
<body>
<form id="settingsForm" class="page">
<section class="content">
<label class="fields field">
<input class="input" name="text" required/>
<span class="label">Text input</span>
</label>
<fieldset class="fields">
<legend class="label">Checkboxes</legend>
<div class="row">
<label class="field">
<input class="input" type="checkbox" name="checkbox1" checked/>
<span class="label">Checkbox 1</span>
</label>
<label class="field">
<input class="input" type="checkbox" name="checkbox2"/>
<span class="label">Checkbox 2</span>
</label>
</div>
</fieldset>
<fieldset class="fields">
<legend class="label">Radio buttons</legend>
<label class="field">
<input class="input" type="radio" name="radio" value="radio1" checked/>
<span class="label">Radio button 1</span>
</label>
<label class="field">
<input class="input" type="radio" name="radio" value="radio2"/>
<span class="label">Radio button 2</span>
</label>
</section>
<footer class="toolbar">
<button id="simpleButton" class="button" type="button">Button</button>
<button id="submitButton" class="button" disabled>Submit</button>
<button class="button danger" type="reset">Reset</button>
</footer>
</form>

<script type="module">
import {
ClickListener,
Form,
enable
} from 'https://obs.multistream.tools/v1/overlay.js';

import { buttonClicked, settingsSubmitted, emitter } from './emitter.js';

const { settingsForm, simpleButton, submitButton } = window;
const { text, checkbox1, checkbox2, radio } = settingsForm;

new ClickListener(() => emitter.emit(buttonClicked), simpleButton);

new Form(settingsForm, {
persisted: [checkbox1.name, checkbox2.name],
submit() {
emitter.emit(settingsSubmitted, {
text: text.value,
checkbox1: checkbox1.checked,
checkbox2: checkbox2.checked,
radio: radio.value
});
}
});

enable(submitButton);
</script>
</body>
</html>

Le code ci-dessus implémente un formulaire comprenant un champ de texte libre, deux cases à cocher indépendantes et deux boutons radio. Le champ de texte est obligatoire, tout le reste est optionnel et l'état des cases à cocher est automatiquement enregistré à chaque changement pour persister entre chaque rechargement de la page. Trois boutons sont également présents, servant respectivement à émettre des événements et réinitialiser le formulaire.

Internationalisation

En reprenant l'exemple du code ci-dessus, ajoutons la gestion des traductions en français et espagnol à notre interface utilisateur fraîchement créée :

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title data-i18n="title">Custom dock example</title>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/core.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/overlay.css"/>
<link rel="stylesheet" href="https://obs.multistream.tools/v1/dock.css"/>
</head>
<body>
<form id="settingsForm" class="page">
<section class="content">
<label class="fields field">
<input class="input" name="text" required/>
<span class="label" data-i18n="textInput">Text input</span>
</label>
<fieldset class="fields">
<legend class="label" data-i18n="checkboxes">Checkboxes</legend>
<div class="row">
<label class="field">
<input class="input" type="checkbox" name="checkbox1" checked/>
<span class="label" data-i18n="checkbox1">Checkbox 1</span>
</label>
<label class="field">
<input class="input" type="checkbox" name="checkbox2"/>
<span class="label" data-i18n="checkbox2">Checkbox 2</span>
</label>
</div>
</fieldset>
<fieldset class="fields">
<legend class="label" data-i18n="radioButtons">Radio buttons</legend>
<label class="field">
<input class="input" type="radio" name="radio" value="radio1" checked/>
<span class="label" data-i18n="radio1">Radio button 1</span>
</label>
<label class="field">
<input class="input" type="radio" name="radio" value="radio2"/>
<span class="label" data-i18n="radio2">Radio button 2</span>
</label>
</section>
<footer class="toolbar">
<button id="simpleButton" class="button" type="button" data-i18n="button">Button</button>
<button id="submitButton" class="button" disabled data-i18n="submit">Submit</button>
<button class="button danger" type="reset" data-i18n="reset">Reset</button>
</footer>
</form>

<script type="module">
import {
ClickListener,
Form,
enable,
i18n
} from 'https://obs.multistream.tools/v1/overlay.js';

import { buttonClicked, settingsSubmitted, emitter } from './emitter.js';

i18n({
fr: {
title: 'Exemple de dock personnalisé',
textInput: 'Champ de texte',
checkboxes: 'Cases à cocher',
checkbox1: 'Case à cocher 1',
checkbox2: 'Case à cocher 2',
radioButtons: 'Boutons radio',
radio1: 'Bouton radio 1',
radio2: 'Bouton radio 2',
button: 'Bouton',
submit: 'Soumettre',
reset: 'Réinitialiser'
},
es: {
title: 'Ejemplo de dock personalizado',
textInput: 'Campo de texto',
checkboxes: 'Casillas de verificación',
checkbox1: 'Casilla de verificación 1',
checkbox2: 'Casilla de verificación 2',
radioButtons: 'Botones de opción',
radio1: 'Botón de opción 1',
radio2: 'Botón de opción 2',
button: 'Botón',
submit: 'Enviar',
reset: 'Restablecer'
}
});

const { settingsForm, simpleButton, submitButton } = window;
const { text, checkbox1, checkbox2, radio } = settingsForm;

new ClickListener(() => emitter.emit(buttonClicked), simpleButton);

new Form(settingsForm, {
persisted: [checkbox1.name, checkbox2.name],
submit() {
emitter.emit(settingsSubmitted, {
text: text.value,
checkbox1: checkbox1.checked,
checkbox2: checkbox2.checked,
radio: radio.value
});
}
});

enable(submitButton);
</script>
</body>
</html>

Il s'agit tout bonnement de définir des attributs data-i18n sur les balises HTML pour lesquelles traduire le contenu textuel, puis de faire appel à la fonction i18n exposée par notre framework JavaScript.

astuce

Les docks et overlays peuvent être traduits de la même manière.

Vous voyez, c'est très simple en terme de code ; à vous maintenant de développer votres propres overlays et docks !