Este documento está dirigido a desarrolladores frontend y full-stack, y explica cómo integrar el Web SDK de HappyOyster de manera confiable en producción en sus modos adventure, directing y acting.
Este documento está dirigido a desarrolladores frontend y full-stack. Explica cómo integrar el SDK de manera confiable en producción. Para consultar APIs individuales, consulte la Referencia de API.
0. Ejemplo mínimo de integración
Si su backend ya ha devuelto una API key temporal de Model Studio token y un ticket de Travel, el flujo del SDK en el frontend es el siguiente:
import { HappyOysterEngine, isSdkError } from "@happy-oyster/js-sdk";
const engine = new HappyOysterEngine({
APIHost: "open-platform.example.com",
model: "happyoyster-1.0-adventure", // Required; Adventure model shown as an example
logLevel: "warn",
});
engine.updateToken(token);
const videoElement = document.querySelector("video") as HTMLVideoElement;
const travel = engine.createTravel({
ticket,
videoElement,
maxExperienceTimeSec: 90, // Optional, Adventure only: 60 / 90 / 120
});
const offStatus = travel.on("statusChanged", (status) => {
// Use status to drive loading states, disabled buttons, and player UI.
});
const offError = travel.onError((error) => {
// Usually log, show a toast, and refresh the token if it has expired.
if (isSdkError(error)) console.error(error.code, error.message);
});
try {
await travel.start();
// Enable controls or Directing input with travel.can(action) after the session is running.
} finally {
offStatus();
offError();
await travel.end();
}
Este fragmento cubre únicamente el lado del SDK. La creación del mundo, la espera hasta que esté listo, la solicitud del ticket y la solicitud de la API key temporal de Model Studio token deben realizarse en, o a través de, su backend de aplicación.
1. Descripción general del flujo de extremo a extremo
Una experiencia completa involucra principalmente a los siguientes participantes.
1.1 Participantes
Participante | Responsabilidad |
|---|---|
Frontend de la aplicación | UI, |
Backend de la aplicación | Almacena la API Key principal, invoca las API del mundo de Open Platform en nombre del cliente y emite credenciales temporales de Model Studio |
OpenAPI de HappyOyster | Expone las API requeridas por el modelo de mundo HappyOyster. |
SDK de HappyOyster | El SDK utilizado por el frontend de la aplicación. Gestiona principalmente las sesiones de Travel y el acceso a la transmisión de video en el cliente. |

1.2 Flujo de experiencia
Etapa | Actor | Acción | Descripción |
|---|---|---|---|
① Crear un mundo | Backend de la aplicación → OpenAPI de HappyOyster |
| Devuelve |
② Esperar hasta que esté listo | Backend de la aplicación → OpenAPI de HappyOyster |
| Sondee hasta |
③ Obtener una credencial de Travel | Backend de la aplicación → OpenAPI de HappyOyster |
| Devuelve un |
④ Solicitar un token de clave de API temporal | Backend de la aplicación → OpenAPI de HappyOyster |
| Devuelve una API key temporal |
⑤ Entregar el token de clave de API temporal y la credencial de Travel | Frontend de la aplicación → Backend de la aplicación → Frontend de la aplicación | Solicitar una clave de API temporal y una credencial de Travel | Después de que el frontend recibe |
⑥ Ingresar a la experiencia | Frontend de la aplicación → SDK de HappyOyster | Consulte "Subflujo de inicio del SDK" a continuación |
|
1.3 Subflujo de interacción del SDK (Etapa ⑥ ampliada)
1.3.1 Iniciar un Travel

Paso | API | Descripción |
|---|---|---|
1 |
| Reutilizar un Engine para el mismo |
2 |
| Complete esto antes de |
3 |
|
|
4 |
| Registre los listeners antes de |
5 |
| Entra en |
1.3.2 Reproducir en modo Aventura

1.3.3 Reproducir en modo Dirección / Actuación

Utilice esta tabla como modelo mental rápido para Aventura, Dirección y Actuación:
Modo | APIs principales | Uso típico | Precauciones |
|---|---|---|---|
Aventura ( |
| Control en tiempo real mediante teclado, joystick y botones | Enviar solo después de |
Dirección ( |
| Dirección en lenguaje natural y control de reproducción | Pausar, reanudar y rebobinar dependen de la capacidad de la sesión; verifique primero |
Actuación ( |
| Experiencias orientadas al rendimiento y configuración de la relación de aspecto | La pausa y la reanudación dependen de la capacidad de la sesión; |
1.4 Prácticas del backend de la aplicación (Resumen)
En el lado del backend, mantenga las siguientes reglas simples y estrictas:
- Mantenga la API Key principal exclusivamente en el backend de la aplicación. El navegador debe recibir únicamente un
tokende corta duración (Demo:POST /server-api/temp-api-key). - Solicite las credenciales de Travel a través del backend. Autorice el mundo y al usuario antes de emitir
ticket; no lo codifique de forma rígida ni lo almacene en caché a largo plazo en el frontend. - No emita una credencial hasta que el mundo esté
ready. De lo contrario, la entrada a Travel puede fallar o proporcionar una experiencia incompleta. - Dirija el backend de la aplicación y el SDK al mismo entorno de Open Platform. El backend puede utilizar una URL completa de OpenAPI; el
APIHostdel SDK debe ser únicamente el host base, comoopen-platform.example.com.
2. Conceptos básicos del SDK
En el flujo descrito en §1, el SDK es responsable únicamente de "entrar a Travel → reproducir → controlar → finalizar". Existen dos objetos principales:
Object | Propósito |
|---|---|
| Configura |
| Representa una sesión: |
Estados de la sesión (statusChanged):
idle ──start()──► prepare ──video playable──► running ⇄ paused ──end()──► idle
Tres restricciones (explicadas más adelante):
- Cada Engine puede tener solo un Travel activo a la vez.
- Se debe proporcionar
videoElementacreateTravel. - El token de clave de API temporal de Model Studio y el ticket de Travel tienen propósitos diferentes (consulte las etapas ④–⑤ en §1.2 y §3.2).
3. Inicialización y gestión de tokens
3.1 Instanciación del Engine
Obligatorio- Configure
APIHostcomo el host base de Open Platform, sinhttps://, esquema ni ruta, por ejemploopen-platform.example.com. - Reutilice un
HappyOysterEnginepara el mismoAPIHost + model. Antes de cambiar el host o el modelo, finalice el Travel activo y utilice un Engine configurado para el nuevo destino.
- Use
logLevel: 'debug'en desarrollo y'warn'o'none'en producción. - Cree el Engine en la capa de orquestación de sesiones en lugar de construir uno nuevo para cada inicio.
Utilice esta tabla de referencia rápida para los campos de configuración comunes:
Campo | Obligatorio | Orientación |
|---|---|---|
| Sí | Proporcione solo el host base, como |
| Sí | Obligatorio, sin valor predeterminado; manténgalo alineado con el modelo del backend utilizado para crear el mundo y emitir su ticket. Es fijo para el Engine y no se infiere de |
| No | Puede pasarlo en el constructor o configurarlo posteriormente con |
| No | Use |
| No | Auméntelo si la red o la configuración de renderizado son lentos; proporcione una ruta de reintento tras el tiempo de espera |
- Llamar repetidamente a
new HappyOysterEngine()durante el renderizado o en una ruta crítica. - Pasar una URL completa, como una con
https://, o llamar a las API del backend del SDK tras omitirAPIHost.
3.2 Modelo de credenciales duales
Correspondiente a las etapas ③–⑤ en §1.2:
Credencial | Origen | Cómo configurarlo | Propósito |
|---|---|---|---|
Token de clave de API temporal de Model Studio | Clave de corta duración emitida por el backend de la aplicación |
| Autentica las solicitudes del SDK ante Open Platform |
Ticket de Travel | Credencial de Travel solicitada por el backend de la aplicación |
| Credencial única para una sesión de Travel |
- Prepare ambas credenciales antes de
travel.start(). - El SDK no conserva ni actualiza automáticamente el token.
- Si el token caduca o se recibe un error de latido a través de
onError, solicite un nuevo token mediante el backend de la aplicación e invoqueupdateToken.
- Mezclar las dos credenciales.
- Exponer la clave de API principal al frontend de la aplicación.
4. Ciclo de vida de Travel
4.1 Crear e iniciar
Obligatorio- Invoque
createTravel({ ticket, videoElement });ticketes la credencial de Travel de un solo uso, yvideoElementse proporciona en este punto. - Registre
statusChangedyonErrorantes deawait travel.start()para que siempre se observen los errores y los cambios de estado.
- Muestre una interfaz de conexión durante
preparepara evitar clics duplicados. UtilicetravelInfoReadypara leer los metadatos de la sesión anticipadamente y usefirstFrameGeneratedcomo marcador de posición del primer fotograma cuando esté disponible.
- Crear un Travel antes de que el elemento DOM esté listo.
4.2 Durante la ejecución
- Controle la UI desde
statusChanged:preparesignifica que se está esperando el video, mientras querunningsignifica que la interacción está disponible. - Observe los casos de uso y las condiciones previas para
pause,resumeyrewind(dependen de la capacidad de la sesión;rewindtambién requiere que la sesión estépausedy que la transmisión se haya detenido).
4.3 Finalizar y recrear
Obligatorio- Finalice la sesión con
await travel.end(). - Asegúrese de que el Travel anterior haya finalizado antes de crear uno nuevo.
- Llame a
travel.end()durante la limpieza cuando la página se descargue o cambie la ruta.
- Después de un fallo, utilice "finalizar → eliminar listeners → reintentar desde la etapa ③ o ⑤ en §1.2".
- Solicite una nueva credencial de Travel al reintentar si la credencial es de un solo uso.
- Llamar a
createTraveluna segunda vez sin finalizar el primer Travel; esto lanza una excepción síncrona. - Abandonar la página sin realizar la limpieza, lo que provoca fugas de recursos RTC.
5. Elemento de video y políticas del navegador
Obligatorio- Establezca
playsInlineen<video>(obligatorio en iOS). - Comprenda cómo las políticas de reproducción automática del navegador afectan a
autoPlayymuted.
- Durante
prepare, muestre la imagen del primer fotograma con una superposición; ocúltela después de que la sesión entre enrunning. - Configure
streamReadyTimeoutsegún sea necesario (valor predeterminado: 15,000 ms) y proporcione una acción de reintento tras un tiempo de espera. - Reserve una relación de aspecto fija para el contenedor del reproductor.
- Pasar una referencia de elemento de video antes de que el elemento se adjunte al DOM.
- Dejar la UI interactiva mientras la sesión está
pausedo la transmisión está desconectada.
6. Suscripciones a eventos y manejo de errores
6.1 Suscripciones a eventos
Obligatorio- Los eventos pertenecen a
Travel, no aHappyOysterEngine. - Guarde las funciones de cancelación de suscripción devueltas por
travel.on(...)ytravel.onError(...).
- Registre y limpie los listeners en un solo lugar. Elimine los listeners antiguos antes de cambiar de sesión de Travel.
Evento | Propósito |
|---|---|
| Mostrar estado, detectar disponibilidad del video ( |
| Lea |
| Cuando existe un primer fotograma no vacío, este sigue a |
| Registro de logs, notificaciones emergentes y activación de la renovación del token |
6.2 Capas de error
- Fallos de inicio:
travel.start()rechaza la promesa (entrada al Travel / RTC / tiempo de espera). - Errores en tiempo de ejecución: reportados a través de
travel.onError(errores RTC, fallos de autenticación de latidos, etc.). - Los errores del backend de la aplicación y de Open Platform en las etapas ①–④ de §1.2 son gestionados por la capa de aplicación y no ingresan al SDK.
- Capturar solo
start()sin escucharonError. - Registrar listeners repetidamente sin cancelar la suscripción.
7. Envío de instrucciones: Command e Instruct
7.1 sendCommand (Control en tiempo real)
sendCommand() proporciona control en tiempo real en el modo Adventure (1). Envía comandos a través del DataChannel de RTC y solo está disponible mientras la sesión está running.
El SDK envía como máximo un comando aproximadamente cada 42 ms (alrededor de 24 FPS). Si ha transcurrido un ciclo desde el envío anterior, envía el nuevo comando inmediatamente. Si se invoca sendCommand() varias veces dentro del mismo ciclo, solo se envía el comando completo de la última llamada. translation, rotation y interaction son dimensiones de control independientes. Los campos omitidos se envían como None; el SDK no combina campos entre llamadas, no repite periódicamente el comando anterior ni envía automáticamente None.
Llame a sendCommand() una vez para acciones puntuales como saltar o atacar:
await travel.sendCommand({ interaction: "Jump" });
Acciones continuas
Continúe invocando sendCommand() mientras se mantenga presionado un control de movimiento o de rotación continua de la vista. La aplicación frontend no necesita limitar las llamadas a 42 ms; el SDK consolida las llamadas de alta frecuencia en una tasa de salida de aproximadamente 24 FPS. Al soltar, envíe explícitamente None para restablecer el control:
// Keep calling while the control is pressed
void travel.sendCommand({ translation: "Front" });
// Stop moving on release
void travel.sendCommand({ translation: "None" });
Evitar
- Enviar muchos comandos durante
prepare. - Usar
sendCommandpara instrucciones de dirección en lenguaje natural.
7.2 sendInstruct (Instrucciones de dirección)
Obligatorio- una sesión de Dirección (
2) o una sesión de Actuación (3) que no seascriptlist; llame aawait travel.sendInstruct({ content }).
- Verifique que
contentno esté vacío antes de enviar y evite envíos duplicados. - Proporcione retroalimentación al usuario basada en los campos devueltos, como
accepted.
8. Solución de problemas
Síntoma | Causa común | Qué hacer |
|---|---|---|
La inicialización del SDK falla con un error de argumento |
| Pase solo el host base, como |
|
| Solicite un nuevo |
| El | Solicite un nuevo token a su backend y luego llame a |
El reproductor sigue cargando o | La transmisión remota no está lista, la red es lenta o | Proporcione una acción de reintento; aumente |
Una segunda llamada a | El Travel anterior no finalizó | Llame a |
Los controles o instrucciones no tienen efecto | La sesión no está | Determine la disponibilidad de los botones desde |
Al depurar, registre tres elementos conjuntamente: el ErrorCode del SDK, el TravelStatus actual y los registros de solicitudes del backend. Esto generalmente indica si el problema está relacionado con credenciales, estado, red o renderizado.