Percus SmartEmbed SDK
El Percus SmartEmbed SDK (percus-embed-sdk) es la librería cliente que se utiliza para embeber el Percus Player en cualquier página web. Se encarga de la creación del iframe, el protocolo de comunicación por postMessage y expone una API JavaScript limpia para controlar la reproducción.
Instalación
Mediante <script> (IIFE)
<script src="https://cdn.percus.example/percus-embed.js"></script>
El SDK queda expuesto como window.PercusEmbed.
Mediante npm / bundler (ESM)
npm install @percus/percus-embed-sdk
import { PercusEmbed } from "@percus/percus-embed-sdk";
Archivos de salida del build
| Archivo | Formato | Caso de uso |
|---|---|---|
dist/percus-embed.js | IIFE | Tag <script> simple |
dist/percus-embed.esm.js | ESM | Webpack, Vite, Rollup |
dist/percus-embed.d.ts | Declaraciones TypeScript | Uso con tipado estricto |
Inicio rápido
<div id="player-host" style="width: 800px; height: 450px;"></div>
<script src="./dist/percus-embed.js"></script>
<script>
const controller = PercusEmbed.init({
target: "#player-host",
playerUrl: "https://player.percus.example/runtime",
templateUrl: "https://cdn.example.com/template.json",
manifestUrl: "https://cdn.example.com/manifest.json",
data: { userId: "abc123", name: "María" },
onReady: (msg) => console.log("Player listo", msg),
onProgress: (msg) => console.log("Tiempo:", msg.payload.time),
onError: (msg) => console.error("Error:", msg.payload.message),
});
// Controlar la reproducción
controller.play();
controller.pause();
controller.seek(30); // saltar a 30 segundos
controller.destroy(); // limpiar
</script>
Referencia de API
initEmbed(params) / PercusEmbed.init(params)
Crea e inyecta un <iframe>, luego inicializa la comunicación con el Player Runtime.
function initEmbed(params: EmbedInitParams): EmbedController
Retorna un EmbedController.
EmbedInitParams
| Propiedad | Tipo | Requerido | Descripción |
|---|---|---|---|
target | string | HTMLElement | Sí | Selector CSS o elemento DOM que contendrá el iframe. |
playerUrl | string | No | URL del runtime del Percus Player (por defecto "about:blank"). |
templateUrl | string | No | URL del template de animación Lottie JSON. |
manifestUrl | string | No | URL del manifest de bindings en JSON. |
data | unknown | No | Objeto de datos de personalización inline. Mutuamente exclusivo con dataUrl. |
dataUrl | string | No | URL desde donde obtener los datos de personalización. Mutuamente exclusivo con data. |
options | Record<string, unknown> | No | Configuración adicional que se reenvía al Player Runtime. |
onReady | (event: PercusReadyMessage) => void | No | Callback que se dispara cuando el player termina de cargar. |
onProgress | (event: PercusProgressMessage) => void | No | Callback que se dispara cada ≈500 ms durante la reproducción. |
onError | (event: PercusErrorMessage) => void | No | Callback que se dispara cuando el player encuentra un error. |
EmbedController
El objeto retornado por initEmbed().
| Miembro | Firma | Descripción |
|---|---|---|
play() | () => void | Envía PERCUS/PLAY al iframe del player. |
pause() | () => void | Envía PERCUS/PAUSE al iframe del player. |
seek(seconds) | (seconds: number) => void | Salta a seconds dentro de la animación. |
destroy() | () => void | Elimina el iframe, desregistra todos los event listeners y libera recursos. |
iframe | HTMLIFrameElement | Referencia directa al elemento iframe inyectado. |
Unidades de tiempo:
seek()recibe segundos. El SDK convierte a milisegundos antes de reenviar al Player Runtime.
Eventos (callbacks Player → Host)
onReady
Se dispara una vez después de que el player cargó el template y aplicó los bindings.
{
version: 1;
type: "PERCUS/READY";
payload: {
playerVersion?: string; // ej. "0.1.0"
};
}
onProgress
Se dispara aproximadamente cada 500 ms durante la reproducción activa.
{
version: 1;
type: "PERCUS/PROGRESS";
payload: {
time: number; // Posición actual en segundos
duration?: number; // Duración total en segundos (si se conoce)
state?: "playing" | "paused" | "ended"; // Estado actual de reproducción
};
}
onError
Se dispara cuando el player encuentra un error irrecuperable.
{
version: 1;
type: "PERCUS/ERROR";
payload: {
code: string; // Código legible por máquina (ej. "LOAD_FAILED")
message: string; // Descripción legible por humano
details?: unknown; // Contexto estructurado opcional
};
}
Funciones auxiliares del contrato de mensajes
El SDK exporta funciones de utilidad usadas internamente (y disponibles para casos de uso avanzados):
import {
isPercusMessage,
makeInitMessage,
makePlayMessage,
makePauseMessage,
makeSeekMessage,
} from "@percus/percus-embed-sdk";
isPercusMessage(value) // type-guard: true si value es un PercusMessage válido
makeInitMessage(payload) // construye un sobre PERCUS/INIT
makePlayMessage() // construye un sobre PERCUS/PLAY
makePauseMessage() // construye un sobre PERCUS/PAUSE
makeSeekMessage(time) // construye un sobre PERCUS/SEEK (time en segundos)
La constante de versión del protocolo se exporta como:
PERCUS_MESSAGE_VERSION = 1
Configuración avanzada
options (pasado mediante EmbedInitParams.options) soporta claves internas adicionales:
| Clave | Tipo | Por defecto | Descripción |
|---|---|---|---|
postMessageTargetOrigin | string | "*" | Origen destino para las llamadas postMessage salientes. Fijarlo al dominio del player en producción. |
iframe.allow | string | "autoplay; fullscreen" | Valor para el atributo allow del iframe. |
iframe.referrerPolicy | string | — | Política de referrer para el iframe. |
iframe.title | string | "Percus Player" | Título accesible para el elemento iframe. |
iframe.className | string | — | Clase CSS añadida al elemento iframe. |
iframe.style | CSSStyleDeclaration | border:0; width:100%; height:100% | Estilos inline aplicados al iframe. |
Configuración por defecto del iframe
Cuando el SDK inyecta el <iframe>, establece:
<iframe
src="{playerUrl}"
title="Percus Player"
allow="autoplay; fullscreen"
style="border: 0; width: 100%; height: 100%;"
></iframe>
El iframe se añade como único hijo del elemento target.
Ciclo de vida
initEmbed(params)
└── Crear <iframe src="playerUrl">
└── Adjuntar al elemento target
└── Registrar listener "message" en window
└── Al cargar el iframe ("load")
└── postMessage PERCUS/INIT → player
└── Al recibir PERCUS/READY → llamar onReady(event)
└── Al recibir PERCUS/PROGRESS → llamar onProgress(event)
└── Al recibir PERCUS/ERROR → llamar onError(event)
controller.play() → postMessage PERCUS/PLAY
controller.pause() → postMessage PERCUS/PAUSE
controller.seek(s) → postMessage PERCUS/SEEK { time: s }
controller.destroy()
└── Eliminar <iframe> del DOM
└── Eliminar listener "message"
Soporte TypeScript
El SDK incluye declaraciones TypeScript completas. Todos los tipos de mensajes, formas de payload y firmas de funciones están exportados:
import type {
EmbedInitParams,
EmbedController,
PercusReadyMessage,
PercusProgressMessage,
PercusErrorMessage,
PercusInitMessage,
PercusPlayMessage,
PercusPauseMessage,
PercusSeekMessage,
PercusMessageType,
} from "@percus/percus-embed-sdk";
Funcionalidades planificadas
Las siguientes capacidades aún no están implementadas. Las formas de API propuestas a continuación son provisionales y están pensadas para guiar las discusiones de diseño.
Analíticas y tracking
Se añadirá un bloque de configuración tracking a EmbedInitParams. Cuando está habilitado, el SDK recolecta eventos del player y los reenvía a un backend configurable sin requerir código adicional en la página host.
// Adición propuesta a EmbedInitParams
tracking?: {
enabled: boolean;
trackerGroupKey?: string; // Agrupa eventos entre sesiones/campañas
googleAnalytics?: {
enabled: boolean;
eventName?: string; // Nombre de evento GA personalizado (default: "percus_player")
};
endpoint?: string; // URL personalizada de ingesta de analíticas
};
Se generará un trackerKey por sesión y se incluirá en todos los eventos de analíticas salientes para correlación.
Gestión de consentimiento
El tracking quedará gateado detrás del consentimiento del usuario. Se añadirán dos nuevos callbacks:
// Adiciones propuestas a EmbedInitParams
consentRequired?: boolean; // Default: false
onConsentAccepted?: (event: PercusConsentMessage) => void;
onConsentDeclined?: (event: PercusConsentMessage) => void;
Cuando consentRequired es true, el SDK retrasa toda actividad de tracking hasta que se dispare onConsentAccepted. Esto soporta los requisitos de GDPR, Ley 19.628 (Chile) y LGPD (Brasil) aplicables a los clientes de Percus.
Callbacks de finalización
Dos nuevos callbacks se dispararán al final de la reproducción:
// Adiciones propuestas a EmbedInitParams
onPlayComplete?: (event: PercusPlayCompleteMessage) => void; // El usuario vio hasta el final
onPlayIncomplete?: (event: PercusPlayIncompleteMessage) => void; // El usuario salió antes
El payload de onPlayIncomplete incluirá hasta dónde llegó el usuario (time y duration) para habilitar puntuaciones de visionado parcial.
Callback de llamada a la acción (CTA)
// Adición propuesta a EmbedInitParams
onCTA?: (event: PercusCTAMessage) => void;
Se dispara cuando la animación alcanza un marcador CTA. La página host maneja la acción (abrir un modal, redirigir, registrar una conversión) sin necesitar saber cómo están definidos los CTAs en el template.
Callbacks de eventos adicionales
onChapterEnter?: (event: PercusChapterMessage) => void;
onChapterExit?: (event: PercusChapterMessage) => void;
onEvent?: (event: PercusEventMessage) => void; // Evento genérico dentro del video
onAutoplayFailure?: (event: PercusAutoplayFailureMessage) => void;
Opciones de reproducción en la inicialización
// Adiciones propuestas a EmbedInitParams
autoplay?: boolean; // Intentar autoplay al estar listo (default: false)
mute?: boolean; // Iniciar muteado — requerido para autoplay en la mayoría de navegadores
loop?: boolean; // Repetir la animación (default: false)
aspectRatio?: string; // ej. "16x9", "9x16", "1x1" — el SDK maneja todo el CSS responsivo
Modo modal / lightbox
// Adición propuesta a EmbedInitParams
modal?: {
enabled: boolean;
autoOpenTime?: number; // Milisegundos antes de que el modal se abra automáticamente (0 = manual)
};
Cuando modal.enabled es true, el SDK renderiza una superposición a pantalla completa con un botón de cierre en lugar de embeber el player de forma inline.
Múltiples instancias de player
Una API de registro PercusEmbed.get(id) / PercusEmbed.getAll() permitirá a la página host recuperar y controlar cualquier player previamente inicializado mediante un id personalizado pasado al momento de la inicialización.
// Propuesto
const c1 = PercusEmbed.init({ id: "hero-player", target: "#hero", ... });
const c2 = PercusEmbed.init({ id: "sidebar-player", target: "#sidebar", ... });
PercusEmbed.get("hero-player").pause();
PercusEmbed.getAll().forEach(c => c.destroy());
Consideraciones de seguridad
| Aspecto | Recomendación |
|---|---|
postMessageTargetOrigin | Fijarlo al dominio real del player (ej. "https://player.percus.example") en lugar de "*". |
| Validación de origen | El SDK valida que los mensajes entrantes provengan del contentWindow del iframe inyectado. |
| Sandbox del iframe | Agregar atributos sandbox restrictivos en el contenedor si el origen del player no es de plena confianza. |
PII en data | Los datos de personalización nunca son almacenados por el SDK — se reenvían una sola vez al player al inicializar. |
Ejemplos
ESM / TypeScript con tipado completo
import { PercusEmbed } from "@percus/percus-embed-sdk";
import type { PercusProgressMessage } from "@percus/percus-embed-sdk";
const controller = PercusEmbed.init({
target: document.getElementById("player-host")!,
playerUrl: "https://player.percus.example/runtime",
templateUrl: "https://cdn.example.com/estado-de-cuenta.json",
manifestUrl: "https://cdn.example.com/estado-de-cuenta.manifest.json",
dataUrl: "https://api.example.com/personalizacion?id=abc123",
onReady: () => controller.play(),
onProgress: (msg: PercusProgressMessage) => {
const pct = msg.payload.duration
? (msg.payload.time / msg.payload.duration) * 100
: 0;
progressBar.style.width = `${pct}%`;
},
onError: (msg) => {
console.error(`[${msg.payload.code}] ${msg.payload.message}`);
},
});
Saltar a un punto con un botón personalizado
document.getElementById("saltar-btn").addEventListener("click", () => {
controller.seek(60); // saltar al minuto 1:00
});
Limpiar al navegar en una SPA
router.on("beforeLeave", () => {
controller.destroy();
});