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

veepai-video-player-pro

v1.1.0

Published

企业级 HLS 视频播放器组件,支持回放片段时间轴、精灵图预览、多片段自动切换等功能

Readme

@veepai/video-player-pro

企业级 HLS 视频播放器组件库,基于 Vue3 + TypeScript + xgplayer 开发,专为视频监控回放系统设计。

✨ 核心特性

  • 🎬 HLS 视频播放 - 支持跨浏览器兼容,自动选择最佳播放策略(Safari 原生 HLS / Chrome HLS 插件)
  • ⏱️ 回放片段时间轴 - 基于 Canvas 绘制的高性能时间轴,支持缩放、拖动、蓝色区域标记
  • 🖼️ 精灵图预览 - 鼠标悬停时间轴显示视频预览帧,支持边界检测
  • 🔄 多片段自动切换 - 自动播放连续的回放片段
  • ⏸️ 智能暂停控制 - 拖动到非回放区域自动暂停,点击播放回到上次有效位置
  • 📱 移动端支持 - 完善的触摸事件支持(拖动、双指缩放)
  • 🎨 可定制化 - 时间轴颜色、播放器配置均可自定义

📦 安装

npm install @veepai/video-player-pro
# 或
yarn add @veepai/video-player-pro
# 或
pnpm add @veepai/video-player-pro

依赖说明

组件的核心依赖和播放器库已内置,安装 NPM 包后会自动安装以下依赖:

内置依赖(自动安装):

项目中需要安装(peerDependencies):

# 必须安装的核心依赖
npm install vue@^3.0.0 dayjs@^1.11.0

注意:xgplayer 相关依赖已内置在组件库中,无需在项目中重复安装。

🚀 快速开始

全局注册

// main.ts
import { createApp } from 'vue'
import VideoPlayerPro from '@veepai/video-player-pro'
import App from './App.vue'

const app = createApp(App)
app.use(VideoPlayerPro)
app.mount('#app')

局部注册

<template>
  <div class="replay-container">
    <VideoPlayer
      ref="playerRef"
      device-id="DEVICE001"
      user-id="USER001"
      :date-picker-tiem="timeRange"
      :player-height="500"
      :timeline-height="60"
      :timeline-colors="customColors"
      :on-get-replay-data="getReplayDataAPI"
      :on-get-sprite="getSpriteAPI"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { VideoPlayer } from 'veepai-video-player-pro'
import type { ReplayItem, SpriteData, TimelineColors } from 'veepai-video-player-pro'
// 导入样式
import 'veepai-video-player-pro/dist/style.css'

const playerRef = ref()
const timeRange = ref(['2024-01-01 10:00:00', '2024-01-01 11:00:00'])

// 自定义时间轴颜色
const customColors: TimelineColors = {
  background: '#1E1E1E',
  meddleLine: '#FF0000',
  scaleText: '#00FF00',
}

// ✅ API 适配器实现(参考 视频云存储监控中心的接口调用,请注意后端接口需要同步修改,保证响应的数据格式一致,
// 后端:欧阳jw
// )
const fetchReplayData = async (params: any): Promise<ReplayItem[]> => {
  const res = await getReplayData(params)
  return res as any // requestDevice 已在响应拦截器中返回 response.data
}

const fetchSprite = async (params: any): Promise<SpriteData> => {
  const res = await getSprite(params)
  return res as any
}

// 手动刷新播放列表
const refreshPlaylist = () => {
  playerRef.value?.deviceBackPlayerList(timeRange.value)
}
</script>

📖 API 文档

VideoPlayer 组件

Props

| 参数 | 类型 | 必填 | 默认值 | 说明 | | ----------------- | ------------------ | ---- | ------------ | --------------------------------------- | | deviceId | string | ✅ | - | 设备 ID | | userId | string | ✅ | - | 用户 ID | | datePickerTiem | string[] | ❌ | 最近1小时 | 时间范围选择 ['开始时间', '结束时间'] | | m3u8BaseUrl | string | ❌ | http://... | m3u8 接口基础 URL | | playerHeight | string \| number | ❌ | '400px' | 播放器高度(支持 '500px'500) | | timelineHeight | number | ❌ | 44 | 时间轴高度(px) | | timelineColors | TimelineColors | ❌ | 见下方 | 时间轴颜色自定义配置 | | onGetReplayData | Function | ⚠️ | - | 自定义回放数据获取方法(必须提供) | | onGetSprite | Function | ⚠️ | - | 自定义精灵图获取方法(必须提供) | | mock | boolean | ❌ | false | 是否启用模拟数据 |

onGetReplayData 参数说明

interface GetReplayDataParams {
  deviceid: string
  userid: string
  begin: number // 开始时间戳(毫秒)
  end: number // 结束时间戳(毫秒)
  channel: number
  provider: string
  bucket: string
}

interface ReplayItem {
  duration: number // 持续时间(秒)
  startTime: number // 开始时间戳(毫秒)
  endTime: number // 结束时间戳(毫秒)
  key: string // m3u8 文件名
}

// 返回值:Promise<ReplayItem[]>

onGetSprite 参数说明

interface GetSpriteParams {
  deviceid: string
  userid: string
  begin: number // 开始时间戳(毫秒)
  end: number // 结束时间戳(毫秒)
  channel: number
}

interface SpriteData {
  data: string // base64 图片数据
  meta: {
    webp_width: number // 雪碧图总宽度
    webp_height: number // 雪碧图总高度
    sprite_width: number // 单个精灵图宽度
    sprite_height: number // 单个精灵图高度
    total: number // 精灵图总数量
  }
}

// 返回值:Promise<SpriteData>

Methods

| 方法名 | 参数 | 返回值 | 说明 | | ---------------------- | ---------------- | --------------- | ------------ | | deviceBackPlayerList | date: string[] | Promise<void> | 刷新回放列表 |

使用示例

<script setup lang="ts">
import { ref } from 'vue'
import { VideoPlayer } from '@veepai/video-player-pro'

const playerRef = ref()

// 刷新播放列表
const refresh = () => {
  playerRef.value?.deviceBackPlayerList(['2024-01-01 12:00:00', '2024-01-01 13:00:00'])
}
</script>

<template>
  <VideoPlayer
    ref="playerRef"
    device-id="DEVICE001"
    user-id="USER001"
    :on-get-replay-data="getReplayDataAPI"
    :on-get-sprite="getSpriteAPI"
  />
  <button @click="refresh">刷新</button>
</template>

TimeLine 组件

可单独使用时间轴组件(不包含播放器)。

Props

| 参数 | 类型 | 必填 | 默认值 | 说明 | | -------------- | ------------------ | ---- | -------- | --------------------------- | | width | string \| number | ❌ | '100%' | 时间轴宽度 | | height | number | ❌ | 44 | 时间轴高度(px) | | setStartTime | string | ❌ | - | 初始显示时间 | | playTime | string | ❌ | - | 当前播放时间 | | timeRange | string[] | ❌ | [] | 时间范围 ['开始', '结束'] | | markTime | MarkTimeItem[] | ❌ | [] | 回放片段标记 | | isAutoPlay | boolean | ❌ | false | 是否自动播放 | | colors | TimelineColors | ❌ | 见下方 | 自定义颜色配置 | | minPxSecond | number | ❌ | 65 | 最小像素秒 |

MarkTimeItem 类型

interface MarkTimeItem {
  beginTime: string // 'YYYY-MM-DD HH:mm:ss'
  endTime: string // 'YYYY-MM-DD HH:mm:ss'
  bgColor: string // '#2AC3E4'
}

TimelineColors 默认值

{
  background: '#2D2D2D',      // 深灰色背景
  meddleLine: '#FFFFFF',      // 中心线白色
  meddleDate: '#FFFFFF',      // 中间时间白色
  moveLine: '#FFFFFF',        // 移动线白色
  moveDate: '#FFFFFF',        // 移动时间白色
  scaleLine: '#5A5A5A',       // 刻度线灰色
  scaleBar: '#1E1E1E',        // 刻度背景更深的灰色
  scaleText: '#CCCCCC',       // 刻度文字浅灰色
  smallScaleLine: '#404040'   // 小刻度线颜色
}

Events

| 事件名 | 参数 | 说明 | | --------------- | -------------- | ---------------- | | slideToMark | date: number | 拖动到某个时间点 | | change | time: string | 时间变化 | | requestSprite | data: {...} | 请求精灵图 | | clearSprite | - | 清空精灵图 |

Methods

| 方法名 | 参数 | 说明 | | ---------------- | ---------------------------- | ---------- | | zoomIn | - | 放大时间轴 | | zoomOut | - | 缩小时间轴 | | draw | - | 重新绘制 | | setSpriteImage | {url, x, y, width, height} | 设置精灵图 |

单独使用示例

<template>
  <TimeLine
    ref="timelineRef"
    :mark-time="markTimeList"
    :play-time="currentPlayTime"
    :time-range="['2024-01-01 00:00:00', '2024-01-01 23:59:59']"
    :colors="customColors"
    @slide-to-mark="handleSlideToMark"
    @request-sprite="handleRequestSprite"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { TimeLine } from '@veepai/video-player-pro'
import type { MarkTimeItem } from '@veepai/video-player-pro'

const timelineRef = ref()
const currentPlayTime = ref('2024-01-01 10:30:00')

const markTimeList = ref<MarkTimeItem[]>([
  {
    beginTime: '2024-01-01 10:00:00',
    endTime: '2024-01-01 10:30:00',
    bgColor: '#2AC3E4',
  },
  {
    beginTime: '2024-01-01 11:00:00',
    endTime: '2024-01-01 11:15:00',
    bgColor: '#2AC3E4',
  },
])

const customColors = {
  background: '#1E1E1E',
  meddleLine: '#FF0000',
  scaleText: '#00FF00',
}

const handleSlideToMark = (timestamp: number) => {
  console.log('滑动到:', new Date(timestamp))
}

const handleRequestSprite = (data: any) => {
  console.log('请求精灵图:', data)
}
</script>

🎯 高级用法

1. 自定义播放器和时间轴高度

<script setup lang="ts">
import { VideoPlayer } from '@veepai/video-player-pro'

// 可以使用字符串或数字
const playerHeight = 500 // 数字会自动转为 px
// const playerHeight = '500px' // 也可以使用字符串
// const playerHeight = '50vh' // 支持其他 CSS 单位
</script>

<template>
  <VideoPlayer
    device-id="DEVICE001"
    user-id="USER001"
    :player-height="playerHeight"
    :timeline-height="60"
    :on-get-replay-data="getReplayDataAPI"
    :on-get-sprite="getSpriteAPI"
  />
</template>

2. 自定义时间轴颜色

<script setup lang="ts">
import { VideoPlayer } from '@veepai/video-player-pro'
import type { TimelineColors } from '@veepai/video-player-pro'

const customColors: TimelineColors = {
  background: '#1a1a1a', // 背景色
  meddleLine: '#ff0000', // 中心线颜色
  meddleDate: '#ffffff', // 中间时间颜色
  scaleLine: '#666666', // 刻度线颜色
  scaleText: '#00ff00', // 刻度文字颜色
}
</script>

<template>
  <VideoPlayer
    device-id="DEVICE001"
    user-id="USER001"
    :timeline-colors="customColors"
    :on-get-replay-data="getReplayDataAPI"
    :on-get-sprite="getSpriteAPI"
  />
</template>

3. 自定义 M3U8 URL 生成

<script setup lang="ts">
import { VideoPlayer } from '@veepai/video-player-pro'

const customM3u8BaseUrl = 'https://your-cdn.com/m3u8'

// 组件会自动拼接参数
</script>

<template>
  <VideoPlayer
    device-id="DEVICE001"
    user-id="USER001"
    :m3u8-base-url="customM3u8BaseUrl"
    :on-get-replay-data="getReplayDataAPI"
    :on-get-sprite="getSpriteAPI"
  />
</template>

4. 禁用埋点日志

<template>
  <VideoPlayer
    device-id="DEVICE001"
    user-id="USER001"
    :enable-logger="false"
    :on-get-replay-data="getReplayDataAPI"
    :on-get-sprite="getSpriteAPI"
  />
</template>

5. 动态切换时间范围

<script setup lang="ts">
import { ref } from 'vue'
import { VideoPlayer } from '@veepai/video-player-pro'

const playerRef = ref()
const timeRange = ref(['2024-01-01 10:00:00', '2024-01-01 11:00:00'])

const changeTimeRange = () => {
  const newRange = ['2024-01-01 14:00:00', '2024-01-01 15:00:00']
  playerRef.value?.deviceBackPlayerList(newRange)
}
</script>

<template>
  <div>
    <VideoPlayer
      ref="playerRef"
      device-id="DEVICE001"
      user-id="USER001"
      :date-picker-tiem="timeRange"
      :on-get-replay-data="getReplayDataAPI"
      :on-get-sprite="getSpriteAPI"
    />
    <button @click="changeTimeRange">切换时间段</button>
  </div>
</template>

6. 完整的 API 集成示例

// api.ts
import type { ReplayItem, SpriteData } from '@veepai/video-player-pro'

export async function getReplayData(params: {
  deviceid: string
  userid: string
  begin: number
  end: number
  channel: number
  provider: string
  bucket: string
}): Promise<ReplayItem[]> {
  const response = await fetch('https://api.example.com/replay/data', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(params),
  })

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`)
  }

  const data = await response.json()
  return data.items || []
}

export async function getSprite(params: {
  deviceid: string
  userid: string
  begin: number
  end: number
  channel: number
}): Promise<SpriteData> {
  const response = await fetch('https://api.example.com/replay/sprite', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(params),
  })

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`)
  }

  return response.json()
}
<!-- App.vue -->
<script setup lang="ts">
import { VideoPlayer } from '@veepai/video-player-pro'
import { getReplayData, getSprite } from './api'

const timeRange = ['2024-01-01 10:00:00', '2024-01-01 11:00:00']
</script>

<template>
  <VideoPlayer
    device-id="DEVICE001"
    user-id="USER001"
    :date-picker-tiem="timeRange"
    :on-get-replay-data="getReplayData"
    :on-get-sprite="getSprite"
    m3u8-base-url="https://your-cdn.com/m3u8"
    logger-env="prod"
  />
</template>

🔧 浏览器兼容性

| 浏览器 | 版本 | 播放策略 | 说明 | | -------------- | ---- | -------- | -------- | | Chrome | 90+ | HLS 插件 | 推荐使用 | | Safari | 14+ | 原生 HLS | 最佳体验 | | Firefox | 88+ | HLS 插件 | 支持 | | Edge | 90+ | HLS 插件 | 支持 | | iOS Safari | 14+ | 原生 HLS | 完美支持 | | Android Chrome | 90+ | HLS 插件 | 支持 |

注意事项:

  • macOS Chrome 在 ARM64 架构下可能存在解码问题,建议使用 Safari
  • 组件会自动检测环境并选择最佳播放策略

📝 核心功能说明

1. 时间轴拖动逻辑

  • 拖动到蓝色区域(回放片段):自动跳转并播放
  • 拖动到非蓝色区域:暂停播放,指针停留在拖动位置
  • 点击播放按钮:自动回到上次有效播放位置(蓝色区域)

2. 精灵图预览

  • 鼠标悬停在蓝色区域时自动请求并显示视频预览帧
  • 支持边界检测,防止精灵图超出容器被裁剪
  • 离开区域自动清空缓存

3. 多片段播放

  • 自动识别连续的回放片段
  • 片段结束自动切换到下一片段
  • 支持同一 m3u8 文件的多个片段连续播放

4. 埋点日志

记录以下事件:

  • pageIn/pageOut: 页面进入/离开
  • click: 用户点击操作
  • network: 网络请求
  • error: 错误信息
  • live: 播放器状态

⚠️ 注意事项

  1. 必须提供 API 方法:组件不包含内置的数据获取逻辑,必须通过 onGetReplayDataonGetSprite props 提供自定义 API 方法

  2. xgplayer 版本:支持 v2.x 和 v3.x,组件会根据环境自动选择版本

  3. 时间格式:所有时间参数统一使用 'YYYY-MM-DD HH:mm:ss' 格式

  4. 样式隔离:组件使用 scoped 样式,不会影响全局样式

  5. 性能优化

    • 精灵图请求有1秒节流限制
    • Canvas 绘制优化,支持高 DPI 屏幕
    • 自动释放 Blob URL 防止内存泄漏

🤝 贡献

欢迎提交 Issue 和 Pull Request!

📄 许可证

MIT License

🔗 相关链接