npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mm-player/player-ui

v0.2.1

Published

播放器 UI(Renderer / Theme / Web Component / VideoMediaTech)

Readme

@mm-player/player-ui

mm-player UI 层:Renderer / Theme / Web Component / VideoMediaTech。依赖 @mm-player/player-core,不反向依赖。

模块用途

在 Headless 核心之上提供完整浏览器 UI:9 个独立 Renderer、CSS Variable 主题、<mm-player> Web Component(Shadow DOM 隔离)、控件/封面/Overlay/插件 API。VideoMediaTech 在此包内创建 video 元素并注入 Core。

适用场景

  • 需要开箱即用的播放器界面。
  • 需要声明式 Web Component(<mm-player>)或 Shadow DOM 样式隔离。
  • 需要运行时主题切换、Overlay、插件扩展。

如需纯逻辑核心(无 DOM),使用 @mm-player/player-core

引入

pnpm add @mm-player/player-ui

player-ui 内部创建 VideoMediaTech(含 <video>)并注入 PlayerCore,调用方无需手动注入。

最小示例

Vanilla JS

import { PlayerUI } from '@mm-player/player-ui';

const player = new PlayerUI({
  container: '#player',
  url: 'https://example.com/stream.mpd',
  adapter: 'shaka',
  autoplay: false,
  ui: {
    bottomBar: { progress: true, left: ['play', 'time'], right: ['volume', 'fullscreen'] }
  }
});

player.on('error', (err) => console.error(err));
await player.play();

// 运行时增量切换主题;颜色令牌使用 "R, G, B" 通道值
player.setTheme({
  '--mm-theme-color': '255, 85, 0',
  '--mm-accent-color': '255, 85, 0',
  '--mm-border-radius': '8px'
});
console.log(player.getTheme()); // 返回合并后的完整 ThemeTokens

// 释放
await player.destroy();

Web Component

<mm-player
  src="https://example.com/stream.flv"
  protocol="flv"
  controls
  autoplay
  muted
></mm-player>
<script type="module">
  import '@mm-player/player-ui'; // 注册 <mm-player> 自定义元素
  const el = document.querySelector('mm-player');
  el.addEventListener('error', (e) => console.error(e.detail));
  el.addEventListener('timeupdate', (e) => console.log(e.detail));
</script>

关键 API

PlayerUI

| 方法 | 说明 | | --- | --- | | constructor(options: PlayerUIOptions) | container 必填(元素/ShadowRoot/选择器) | | load / reload | 加载 / 重新加载媒体 | | play / stop | 返回 Promise<void> 的播放/停止控制(经 CommandBus) | | pause | 同步暂停;低带宽模式会在后台调用 stop | | switchAdapter(config) | 运行时切换协议(whep/flv/shaka) | | setVolume / setMuted | 音量(0–1)/ 静音 | | setCover / clearCover | 封面 | | setControlsVisible | 控制栏显隐 | | setTheme / getTheme | 运行时主题(不重建 Renderer) | | showOverlay / hideOverlay / setOverlay / removeOverlay | 程序化 Overlay | | addPlugin / removePlugin | 插件(CorePlugin/UIPlugin/Legacy) | | exportSnapshot / restoreSnapshot | 快照导出/恢复 | | healthCheck | 健康检查 | | getDevTools | DevTools(惰性) | | on / off / once | 事件(代理 Core) | | getCore | 获取 PlayerCore 实例 | | moveTo(container) | 移动到新容器(保持进度) | | destroy | 异步完整释放 |

主题定制

PlayerUIOptions.themesetTheme() 都接受 ThemeUpdatePartial<ThemeTokens>)。未提供的令牌沿用当前值;getTheme() 返回副本,修改返回对象不会反向改变播放器。

| 令牌 | 默认值 | 作用 | | --- | --- | --- | | --mm-theme-color | 97, 234, 253 | 主状态色 | | --mm-accent-color | 97, 234, 253 | 进度、滑块和悬停强调色 | | --mm-font-color | 255, 255, 255 | 控件前景色 | | --mm-font-color-reverse | 0, 0, 0 | 反色区域 | | --mm-bg-color | 0, 0, 0 | 视频区域背景色 | | --mm-border-radius | 4px | 控件与进度条圆角 | | --mm-control-height | 36px | 方形控制按钮尺寸 |

仓库完整示例的“主题定制”面板提供颜色选择器、圆角/控件高度滑块和实时 JSON,可直接作为可视化配置入口。

创建与使用插件

新插件应通过 type 显式声明接收的上下文,避免依赖旧版启发式识别:

import type { CorePlugin } from '@mm-player/player-core';
import type { UIPlugin, Plugin } from '@mm-player/player-ui';

let offState = () => {};
let watermarkNode: HTMLElement | null = null;
let onLegacyPlay = () => {};

const corePlugin: CorePlugin = {
  type: 'core',
  name: 'state-observer',
  install({ stateManager }) {
    offState = stateManager.subscribe((snapshot) => console.log(snapshot.status));
  },
  destroy() { offState(); }
};

const uiPlugin: UIPlugin = {
  type: 'ui',
  name: 'watermark',
  install({ playerUI }) {
    watermarkNode = document.createElement('span');
    watermarkNode.textContent = 'LIVE';
    playerUI.showOverlay(watermarkNode);
  },
  destroy() { watermarkNode?.remove(); }
};

const legacyPlugin: Plugin = {
  type: 'legacy',
  name: 'legacy-events',
  install(playerUI) {
    onLegacyPlay = () => console.log('play');
    playerUI.on('play', onLegacyPlay);
  },
  uninstall(playerUI) { playerUI.off('play', onLegacyPlay); }
};

await player.addPlugin(corePlugin);
await player.addPlugin(uiPlugin);
await player.addPlugin(legacyPlugin);
await player.removePlugin('watermark');

Core 插件接收 Headless 状态/命令上下文,UI 插件接收 DOM/UI 上下文,Legacy 插件直接接收 PlayerUIremovePlugin() 会执行对应清理并注销;插件必须自行对称释放订阅、DOM、计时器和外部资源。

Web Component <mm-player>

| 属性 / Attribute | 说明 | | --- | --- | | src / protocol / poster | 媒体源 / 协议 / 封面 | | autoplay / loop / playsinline / muted / controls / volume | 布尔/数值属性,反射 |

方法:play / pause / stop / destroy / load / reload / switchAdapter / setVolume / setMuted / setTheme / setControlsVisible + Overlay/Slot API(appendOverlay/removeOverlay/replaceOverlay/clearOverlay/appendPoster/removePoster/appendLeftControl/appendRightControl/removeControl)+ getCore/getPlayerUI/getShadowRoot

事件:ready / loaded / destroy / play / pause / ended / error / timeupdate / buffering / progress / statechange / themechange / overlaychange / controlchange / slotchange / resize / reconnecting / reconnected / reconnectfailed / unsupported

生命周期与资源释放

  • PlayerUI 构造时渲染 UI、创建 VideoMediaTech、注入 Core、订阅 StateManager
  • destroy 顺序:取消 StateManager 订阅 → 销毁 Renderer → 卸载插件 → 清理 IconManager → await core.destroy() → 清空 DOM → 释放引用。必须 await
  • destroy 幂等(destroyed 标志守卫)。
  • ended 状态自动触发 stop(由 state-change 驱动,仅转入 ended 时触发一次)。
  • Web Component 同一任务内移除再挂载视为 DOM 移动,不销毁 UI;若清理已经开始,重新连接会等待旧实例销毁后再初始化。

线程与回调约束

  • UI 全部派生自 PlayerSnapshot(state-change 驱动),不直读 adapter/video/tech。
  • 控件激活态由 state-change 驱动,不乐观置位(避免适配器早返回时 UI 假激活)。
  • Renderer 互不引用,仅消费 Snapshot + dispatch。
  • 主题/控件显隐运行时切换不重建 Renderer、不重新 render/mount。

常见错误与注意事项

  • container 必填且必须存在;选择器找不到会抛错。
  • volume 范围 0–1(默认 1)。
  • 低带宽模式 pause 实际执行 stop(断开节省资源);高带宽模式仅暂停。
  • ShadowRoot 宿主下样式经 adoptedStyleSheets 隔离;HTMLElement 宿主用 <style>
  • destroy 后不得再调用实例方法。

相关文档