BustPlay
ES

BustPlay — inicio rápido

Cinco minutos desde una página vacía hasta un reproductor con publicidad. Todo lo que sigue es la API real de los builds actuales (npm run build en la raíz del repositorio), no planes.

1. Reproductor con etiqueta script (un minuto)

<link rel="stylesheet" href="bustplay/ui.css">
<div id="player"></div>

<script src="bustplay/core.umd.cjs"></script>
<script src="bustplay/ui.umd.cjs"></script>
<script>
  const player = BustPlay.createPlayer({
    container: 'player',                       // id o el propio elemento
    sources: 'https://cdn.example/video.mp4',  // cadena o [{src, type}]
    muted: true,                               // políticas de autoplay del navegador cubiertas
  });
  BustPlayUI.createUI(player, { title: 'Título del vídeo' });
</script>

Los archivos salen de packages/*/dist/ (hasta la publicación en npm). Globales UMD: BustPlay (core), BustPlayUI, BustPlayHls, BustPlayAds, BustPlayAnalytics.

2. Lo mismo con un bundler (ESM)

import { createPlayer } from '@bustplay/core';
import { createUI } from '@bustplay/ui';
import '@bustplay/ui/ui.css';

const player = createPlayer({ container: 'player', sources: 'https://cdn.example/video.mp4' });
const ui = createUI(player, {
  title: 'Título del vídeo',
  chapters: [{ t: 0, name: 'Prólogo' }, { t: 96, name: 'Capítulo dos' }],
  skin: 'studio',            // studio | minimal | vertical
  accent: '#5B8CFF',         // marca blanca con una sola variable
});

La importación es segura para SSR: no se toca window hasta createPlayer.

3. HLS

import { attachHls, bindQualityMenu } from '@bustplay/hls';

const player = createPlayer({ container: 'player', sources: [] }); // el adaptador gestiona la fuente
const ui = createUI(player, { title: 'En directo' });
const hls = await attachHls(player, 'https://cdn.example/stream.m3u8');
bindQualityMenu(hls, ui.menu); // los niveles de calidad aparecen solos en el menú

Safari reproduce de forma nativa; el resto de navegadores carga hls.js de forma perezosa: options.lib → el global Hls de la página → import('hls.js').

4. Publicidad

import { createAdScheduler, breaksFromVmap } from '@bustplay/ads';

const scheduler = createAdScheduler(player, {
  ui,                                    // contador «Anuncio 1 de 2», búsqueda bloqueada
  breaks: [
    { offset: 'pre',              tags: [TAG_A, TAG_B] }, // si A no rellena, probamos B
    { offset: { percent: 0.5 },   tags: [TAG_MID], pod: 2 },
    { offset: 'post',             tags: [TAG_POST] },
  ],
  consent: { gdpr: '1', gdprConsent: TCF_STRING },  // → macros {gdpr}/{gdpr_consent}
});

// Pre-roll limpio sin parpadeo del contenido:
await scheduler.runPreRoll();
void player.play();

// O todo desde VMAP:
createAdScheduler(player, { ui, breaks: breaksFromVmap(vmapXml) });

5. Analítica

import { createAnalytics } from '@bustplay/analytics';

createAnalytics(player, {
  endpoint: 'https://stats.example.com/v1/events',
  video: 'video-42',       // tu id de vídeo, no PII
  sampleRate: 1,           // proporción de sesiones recogidas
});

Los eventos (view_start con TTFF, cuartiles, rebuffer, errores, session_end con QoE) se envían por lotes; el cierre de la pestaña se sobrevive con sendBeacon. El lado receptor es services/stats (README allí).

6. React y Web Component

import { BustPlayer } from '@bustplay/react';
import '@bustplay/ui/ui.css';

<BustPlayer sources="https://cdn.example/video.mp4" title="Vídeo"
  onReady={({ player, ui }) => console.log(player.getState())} />
<script src="bustplay/element.umd.cjs"></script>
<script>BustPlayElement.define()</script>
<bust-play src="https://cdn.example/video.mp4" title="Vídeo" muted></bust-play>

Cambiar sources/src recrea el reproductor; la cosmética (title, skin) se aplica al crearlo. <bust-play> no usa Shadow DOM: necesita ui.css en la página.

7. DRM

import { toHlsDrmConfig, toDashProtectionData, enableFairPlay } from '@bustplay/drm';

const drm = {
  widevine:  { licenseUrl: 'https://lic.example/wv', headers: { 'X-Auth': token } },
  playready: { licenseUrl: 'https://lic.example/pr' },
  fairplay:  { licenseUrl: 'https://lic.example/fps', certificateUrl: 'https://lic.example/cert' },
};

await attachHls(player, src, { config: toHlsDrmConfig(drm) });          // EME de hls.js
await attachDash(player, src, { protectionData: toDashProtectionData(drm) });
const off = enableFairPlay(player.media, drm.fairplay);                  // HLS nativo de Safari

Widevine/PlayReady los gestionan hls.js/dash.js; FairPlay en la ruta nativa lo lleva nuestra capa EME (SPC/CKC como bytes crudos; variaciones del servidor con los hooks licenseBody/licenseResponse). Una prueba real necesita un servidor de licencias.

¿Qué sigue?