Si eres usuario de Video.js desde hace tiempo, probablemente estés familiarizado con el concepto de plugins: funciones que se convierten en métodos de cualquier reproductor que crees. Si no estás familiarizado con los plugins de Video.js, disponemos de una completa guía sobre plugins.
Estos plugins -que llamaremos plugins básicos- sonligeros y ofrecen un control total del reproductor. Eso es muy útil y no está cambiando.
Pero, ¿y si desea un conjunto más amplio de funciones? ¿O más orientación sobre cómo estructurar tu plugin? ¿O más herramientas listas para usar que le ayuden a gestionar los complejos reproductores ricos en plugins?
Bueno, hasta Video.js 6.0, tenías que arreglártelas por tu cuenta.
Plugins avanzados
Uno de los puntos fuertes de Video.js es su rico ecosistema de plugins. Por eso, en los últimos meses hemos querido centrar nuestros esfuerzos en mejorar la experiencia del autor de plugins.
Aunque proyectos como el generador de plugins hacen que convertirse en autor de plugins sea más fácil que nunca, el equipo de Video.js pensó que era importante proporcionar una API básica y un conjunto de convenciones sobre las que se pudiera construir el futuro de los plugins de Video.js.
Nuestra solución son los plugins avanzados.
Los plugins avanzados son similares a los componentes
Uno de los objetivos de diseño de los plugins avanzados era ofrecer una API que recordara al sistema de componentes existente. Lo hemos conseguido de varias maneras.
En el nivel más bajo, esto incluía un cambio de nombre para la función de registro de plugins de videojs.plugin
a videojs.registerPlugin
(siguiendo el ejemplo de videojs.registerComponent
y videojs.registerTech
).
Más allá de un simple cambio de nombre del método de registro, los plugins avanzados se basan en clases. Un ejemplo trivial de un plugin avanzado podría ser algo como esto:
var Plugin = videojs.getPlugin('plugin');
var HelloWorld = videojs.extend(Plugin, {
constructor(player) {
Plugin.call(this, player);
this.player.addClass('hello-world');
}
});
videojs.registerPlugin('helloWorld', HelloWorld);
Si utiliza un transpilador ES6, puede utilizar clases ES6 de forma similar.
Este plugin se puede inicializar de la misma forma que un plugin básico: mediante un método player cuyo nombre coincida con el nombre registrado del plugin.
En el caso de los plugins avanzados, este método es una función de fábrica, que instancia la clase del plugin y devuelve una instancia.
Es útil saber que el método del reproductor que se cree será siempre una función. Si un reproductor ya tiene una instancia de un plugin avanzado, su método asociado simplemente devolverá la instancia preexistente en lugar de reiniciarla:
var player = videojs('my-player');
var instance = player.helloWorld();
// Logs: 'true'
videojs.log(instance === player.helloWorld());
En helloWorld
devolverá este objeto plugin hasta que sea desechado - después de lo cual creará una nueva instancia plugin de nuevo.
Eventos
Al igual que los componentes, los plugins avanzados pueden escuchar y activar eventos a través de la función on
, one
, off
y trigger
métodos.
Esto proporciona un canal de comunicación poco acoplado para que los plugins y otros objetos (componentes, reproductores, etc.) gestionen su propio estado y respondan a los cambios de estado de los demás.
Datos adicionales del evento
El sistema de eventos de Video.js permite pasar datos adicionales a los oyentes como segundo argumento al activar eventos (el primer argumento es el propio objeto del evento).
Los eventos de plugin pasan un conjunto coherente de propiedades en este objeto (incluyendo cualquier propiedad personalizada pasada a trigger
):
instance
: La instancia del plugin que desencadenó el evento.name
: El nombre del plugin en forma de cadena (por ejemplo.'helloWorld'
).plugin
: La clase/función constructora del plugin (por ejemploHelloWorld
).
Por ejemplo, un listener para un evento en un plugin puede esperar algo como esto:
var player = videojs('my-player');
var instance = player.helloWorld();
instance.on('some-custom-event', function(e, data) {
videojs.log(data.instance === instance); // true
videojs.log(data.name === 'helloWorld'); // true
videojs.log(data.plugin === videojs.getPlugin('helloWorld')); // true
videojs.log(data.foo); // "bar"
});
instance.trigger('some-custom-event', {foo: 'bar'});
Ciclo de vida
Otra similitud entre los plugins y los componentes es el concepto de ciclo de vida, más concretamente, los procesos de instalación y desinstalación.
Obtenemos la función de configuración como un efecto secundario de la creación normal de objetos en JavaScript, pero se nos deja a nuestros propios dispositivos cuando se trata de la destrucción de objetos y asegurar que las referencias entre objetos se limpian para evitar fugas de memoria.
Hace tiempo que los componentes de Video.js dispose
que se encargan de eliminar un componente del DOM y de la memoria. Los plugins avanzados tienen la misma función:
var player = videojs('my-player');
var firstInstance = player.helloWorld();
// Logs: 'true'
videojs.log(firstInstance === player.helloWorld());
firstInstance.on('dispose', function() {
videojs.log('disposing a helloWorld instance');
});
// Logs: 'disposing a helloWorld instance'
firstInstance.dispose();
var secondInstance = player.helloWorld();
// Logs: 'false'
videojs.log(firstInstance === secondInstance);
En pluginsetup
Evento
Los plugins tienen una característica del ciclo de vida que no tienen los componentes: la función pluginsetup
evento.
Este evento se activa en un reproductor cuando se inicializa en él un plugin:
var player = videojs('my-player');
player.on('pluginsetup', function(e, hash) {
if (hash.name === 'helloWorld') {
videojs.log('A helloWorld instance was created!');
}
});
// Logs: 'A helloWorld instance was created!'
player.helloWorld();
Statefulness inspirado en React
Una de las novedades más interesantes de Video.js, tanto para plugins avanzados como para componentes, es la statefulness inspirada en React. Básicamente, esto significa que todos los objetos del plugin y los objetos del componente tienen una función state
que es un objeto plano que se puede utilizar para almacenar el estado variable de ese objeto. Además, existe una propiedad setState
que actualiza este objeto y activa un método statechanged
evento.
Este sistema permite a los plugins y componentes utilizar su naturaleza de eventos para comunicar cambios de estado en memoria a través de una API coherente:
// A static property of the constructor can be used to pre-populate state
// for all instances.
HelloWorld.defaultState = {color: 'red'};
var player = videojs('my-player');
var instance = player.helloWorld();
instance.on('statechanged', function(e) {
var color = e.changes.color;
if (color) {
videojs.log('The helloWorld color changed from "' + color.from + '" to "' + color.from + '"!');
}
});
// Logs: 'The helloWorld color changed from "red" to "blue"!'
instance.setState({color: 'blue'});
Conocimiento de los plugins del reproductor
Por último, no podíamos añadir una nueva infraestructura de plugins sin trabajar en uno de los problemas más perniciosos de la gestión de combinaciones complejas de plugins: el reproductor no puede informar de qué plugins ha inicializado, o no. Para ello, el reproductor dispone de dos nuevos métodos: hasPlugin
y usingPlugin
. Estos métodos funcionan para ambos tipos de plugins.
En hasPlugin
Método
Este método informa de si un complemento que coincide con un nombre dado está disponible en el reproductor:
var player = videojs('my-player');
// Logs: 'true'
videojs.log(player.hasPlugin('helloWorld'));
// Logs: 'false'
videojs.log(player.hasPlugin('fooBar'));
Este método ignora si el plugin se ha inicializado o no y se limita a informar de si se ha registrado o no.
En usingPlugin
Método
Este método informa no sólo de si un complemento está disponible en un reproductor, sino también de si está actualmente activo en el reproductor:
var player = videojs('my-player');
// Logs: 'false'
videojs.log(player.usingPlugin('helloWorld'));
player.helloWorld();
// Logs: 'true'
videojs.log(player.usingPlugin('helloWorld'));
Una advertencia a tener en cuenta: aunque esto funciona para ambos tipos de plugins, sólo los plugins avanzados pueden cambiar este valor más de una vez. Un plugin básico no tiene incorporado un ciclo de vida o eventos, por lo que no es posible determinar si uno ha sido "desechado".
Avanza y codifica
Esperamos que estas adiciones y mejoras a la arquitectura de plugins hagan que escribir plugins Video.js sea más placentero y eliminen parte del trabajo de bajo nivel involucrado en asegurar que los plugins no estén creando fugas de memoria y otros problemas.
El diseño de los plugins avanzados es tal que podemos añadir funciones a medida que la versión 6.0 madura y recibimos más comentarios de la comunidad. Como siempre, animamos encarecidamente a nuestros usuarios a devolver al proyecto Video.js lo que puedan.
Este artículo se publicó originalmente en el blog Video.js.