Appearance
Préférences de médias et de placement
Pour demander au streamer un fichier ou une zone à l'écran, utilise les champs natifs. Le contrat de lecture/lecture média est détaillé dans medias ; ces formulaires appartiennent aux préférences des applis.
Choisir un média : mediaFile
Fragment à ajouter dans groupItems :
json
{
"alertSound": {
"type": "mediaFile",
"label": { "fr": "Son d'alerte", "en": "Alert sound" },
"wuOptions": { "allowedTypes": ["sound"], "withPosition": false }
}
}allowedTypes accepte sound, video, image. withPosition: true ajoute le placement visuel. Le picker propose les médias du channel et la bibliothèque publique Kappapps, sans copier les médias publics dans le channel.
Les types autorisés sont présélectionnés dans les filtres du gestionnaire, que le streamer peut ensuite modifier librement. Seuls les médias compatibles proposent Utiliser ce média, dans la liste comme dans l'aperçu. Après un upload, les fichiers créés sont mis en évidence et l'aperçu du dernier s'ouvre, même s'il est incompatible avec le champ. Cette navigation ne change pas la préférence : il faut utiliser explicitement un média compatible, puis enregistrer les réglages.
Un défaut doit être portable : default: { library: 'slug-public-verifie', volume: 70 }. Remplace ce slug d'exemple par un slug réellement trouvé dans le catalogue (kapp.medias.library(), champ slug). Aucun ID de média de channel en défaut. position et timeline peuvent compléter ce défaut.
Un slug inexistant produit une erreur CRIT ; un asset dépublié produit WARN (encore jouable, absent du catalogue). Valide aussi chaque langue de defaults utilisée par l'appli.
Valeur reçue
La lecture renvoie { file, volume, position, timeline } ou null. file peut lui-même être null si le média n'existe plus. La référence stockée (library ou ID interne de média) ne doit pas être confondue avec ce résultat hydraté.
js
// Fragment à appeler dans une fonction async.
const media = kapp.preferences.get('alertSound');
if (media?.file) await kapp.medias.playMedia(media);Passe l'objet complet au service, pour conserver le volume, le placement et le segment de lecture. Le volume est sur l'échelle 0–100. Les temps timeline.startAt / endAt sont en millisecondes, comme le champ duration de préférence.
Placer une zone : containerArea
Le champ retourne des coordonnées { x, y, w, h }. Sa configuration se place dans settings au niveau du champ, pas dans wuOptions.
Fragment à ajouter dans groupItems :
json
{
"displayArea": {
"type": "containerArea",
"label": { "fr": "Zone d'affichage", "en": "Display area" },
"settings": {
"container": { "width": 1920, "height": 1080 },
"bounds": { "left": 0, "right": 1920, "top": 0, "bottom": 1080 },
"area": { "minWidth": 100, "maxWidth": 1920, "minHeight": 100, "maxHeight": 1080 },
"appearance": {
"customStyle": { "backgroundColor": "#9146FF", "textColor": "#FFFFFF" },
"label": "Zone"
},
"snaps": { "x": [0, 960, 1920], "y": [0, 540, 1080] }
},
"default": { "x": 100, "y": 100, "w": 640, "h": 360 }
}
}container définit le repère, bounds ses limites, area les contraintes de taille et de ratio (minRatio, maxRatio), et snaps les lignes d'alignement. Ces coordonnées sont à appliquer dans le repère prévu par l'appli.
Pour plusieurs zones nommées, containerAreas partage settings.container, bounds et snaps, puis décrit chaque zone dans settings.areas.nom, avec ses propres area, bounds, appearance ou snaps. La valeur est un objet { nom: { x, y, w, h }, ... }, pas une collection libre.
Les placements peuvent avoir une variante portrait. Utilise le contrat de layout vertical pour choisir les coordonnées du bon layout ; ne supprime pas les données portrait en réécrivant une zone paysage.