Skip to content

编写插件 ​

了解播放器的属性、方法和事件后,就可以编写插件。

注册与生命周期 ​

plugins 配置接收工厂函数。工厂被调用时,唯一参数和普通函数的 this 都是播放器;箭头函数使用自己的词法 this。带配置的插件通常先调用外层函数,得到真正的工厂,如 plugins: [adsPlugin(options)]。工厂直接返回的对象会成为注册结果,不会被复制。

同步工厂在 add 返回前完成注册,art.plugins.add(factory) 返回注册管理器本身。只有与播放器同一 JavaScript realm 的 Promise 才会被等待;此时返回的 Promise 最终兑现为管理器,而不是插件结果。普通 thenable 和其他窗口的 Promise 保持原样作为同步结果。需要等待异步插件时,应在构造之后 await art.plugins.add(factory),然后读取对应名称。

构造配置中的工厂按数组顺序启动,但不会逐个等待异步工厂;播放器 ready 也不等待它们。构造时前面的工厂执行期间,art.plugins 尚未赋值,不应在工厂里依赖它访问前一个插件。同步工厂抛错会使构造或直接 add 失败;直接 add 的异步拒绝由调用者处理,构造配置中的异步拒绝会记录警告。

名称取第一个真值:结果的 name、工厂函数名、plugin 加当前注册计数。异步匿名工厂的回退名称使用完成时的计数,因此推荐显式返回稳定的字符串名称。id 在调用工厂前递增,失败也占用计数;内置插件也参与计数。art 指向宿主;next(factory, result) 是直接提交结果的底层方法,不调用工厂、不递增计数、不等待 Promise,通常无需手动调用。

结果以不可写、不可配置、不可枚举的自有属性保存,因此 Object.keys(art.plugins) 不会列出插件,重复名称会抛错。不要使用 art、id、add、next 等管理器成员名;名称与原型方法冲突可能遮蔽方法。管理器没有通用的移除、替换或自动调用插件 destroy 的机制。

插件应自行监听播放器 destroy,清理请求、定时器、Worker 和外部资源。异步工厂也要在首次等待之前注册清理逻辑,并在等待结束后检查是否已销毁。销毁后的迟到结果不会注册,也不会被自动清理;已经挂起的 add 仍返回其原 Promise,若工厂成功则兑现为管理器。销毁期间或之后的新 add 会在执行工厂前抛错。

TypeScript ​

根入口保留历史 Plugins.add 的 Promise 返回声明;它不表示同步运行时变成异步。artplayer/runtime 的 PluginRegistration 按工厂返回类型区分同步管理器与 Promise;unknown 返回值保留二者联合。跨 realm 的 Promise 无法仅靠 TS 类型判定,仍遵守上面的运行时规则。

模块扩展只声明结果类型,不负责安装插件。准确入口的 Plugins 也承接根入口已有插件的命名扩展。构造选项的 PluginFactory 使用初始化阶段的 PluginHost,避免假定工厂执行时实例已完全构造;实例化之后的 add 可以使用完整播放器。

ts
import Artplayer from 'artplayer/runtime';
import type { Plugins } from 'artplayer/runtime';

declare module 'artplayer/runtime' {
    interface Plugins {
        exampleCounter: { name: string; value: number };
    }
}

const art = new Artplayer({ container: '.artplayer-app', url: '/assets/sample/video.mp4' });
const registry: Plugins = art.plugins.add(function (host) {
    const samePlayer: boolean = this === host;
    return { name: 'exampleCounter', value: samePlayer ? 1 : 0 };
});
const value: number = registry.exampleCounter.value;
const pending: Promise<Plugins> = art.plugins.add(async () => ({ name: 'exampleAsync' }));
void pending.catch(console.error);
void value;

示例 ​

可以在实例化的时候加载插件的函数

▶ Run Code
js
function myPlugin(art) {
    console.info(art);
    return {
        name: 'myPlugin',
        something: 'something',
        doSomething: function () {
            console.info('doSomething');
        },
    };
}

var art = new Artplayer({
    container: '.artplayer-app',
    url: '/assets/sample/video.mp4',
    plugins: [myPlugin],
});

art.on('ready', () => {
    console.info(art.plugins.myPlugin);
});

可以在实例化之后再加载插件的函数

▶ Run Code
js
function myPlugin(art) {
    console.info(art);
    return {
        name: 'myPlugin',
        something: 'something',
        doSomething: function () {
            console.info('doSomething');
        },
    };
}

var art = new Artplayer({
    container: '.artplayer-app',
    url: '/assets/sample/video.mp4',
});

art.plugins.add(myPlugin);

art.on('ready', () => {
    console.info(art.plugins.myPlugin);
});

例如我想写一个在视频暂停后,显示一个图片广告的插件

▶ Run Code
js
function adsPlugin(option) {
    return (art) => {
        art.layers.add({
            name: 'ads',
            html: `<img style="width: 100px" src="${option.url}">`,
            style: {
                display: 'none',
                position: 'absolute',
                top: '20px',
                right: '20px',
            },
        });

        function show() {
            art.layers.ads.style.display = 'block';
        }

        function hide() {
            art.layers.ads.style.display = 'none';
        }

        art.controls.add({
            name: 'hide-ads',
            position: 'right',
            html: 'Hide Ads',
            tooltip: 'Hide Ads',
            click: hide,
            style: {
                marginRight: '20px'
            }
        });

        art.controls.add({
            name: 'show-ads',
            position: 'right',
            html: 'Show Ads',
            tooltip: 'Show Ads',
            click: show,
        });

        art.on('play', hide);
        art.on('pause', show);

        return {
            name: 'adsPlugin',
            show,
            hide
        };
    }
}

var art = new Artplayer({
    container: '.artplayer-app',
    url: '/assets/sample/video.mp4',
    plugins: [
        adsPlugin({
            url: '/assets/sample/layer.png'
        })
    ],
});