Compare commits

..
13 Commits
Author SHA1 Message Date
王阳 f4360c3a17 阶段6:发布收尾 v0.1.0
- LICENSE 更新为 MoePlayer+niandeng 双版权,修正 moeplayer/LICENSE 误置的 Apache 2.0
- 新增 CHANGELOG.md
- package.json 补全 author/repository/homepage/keywords
- README 更新路线图进度与插件仓库链接
2026-07-21 19:24:13 +08:00
王阳 ab73938a80 新增 npm run sync-moodle:一键同步播放器资源到 Moodle 插件仓库 2026-07-21 16:52:47 +08:00
王阳 be445f2899 阶段4:格式自动识别 + flv.js 替换为 mpegts.js + 移动端 5 项修复 + 自动化冒烟测试
- 按 URL 扩展名自动识别插件(.m3u8→hls.js,.flv/.ts/.m2ts→mpegts.js),iOS 上 m3u8 仍走原生 HLS
- flv 播放改由 mpegts.js 承担,移除 flv.js 依赖,plug:'flv.js' 保留兼容别名
- 修复 plug:'dash.js' 调用不存在函数的死代码 bug
- mpegts 能力判断由 mseLivePlayback 放宽为 isSupported()(修复部分浏览器点播被误杀)
- 移动端:iOS 原生全屏状态同步、iPad/iPod UA 识别、X5 同层播放属性、进度/音量条触摸拖动、触摸坐标滚动偏移修正
- 新增 Playwright 冒烟测试(npm test)与支持 Range 的本地服务器(npm run serve)
- 文档说明:测试禁用 python http.server(不支持 Range,mp4 无法 seek)
2026-07-21 16:43:51 +08:00
王阳 7e5fc46126 阶段3:皮肤系统现代化
- CSS 架构改为 src/css/base.css(结构+35个--moe-变量) + themes/(default/red/ixigua) 构建拼接
- 11 张 PNG 雪碧图全部删除,20+ 图标改为 SVG data-URI + CSS mask,颜色随主题变量
- 修复原 ckplayer 雪碧图 exitfull/next 共用图标 bug
- 视觉重做:渐变控制条/细轨道+圆点滑块进度条/圆角弹层/系统字体栈
- 默认皮肤深蓝 accent,ixigua 重做为浅色皮肤;播放器默认不再带品牌水印
- 修复中央播放键三角形不居中问题
- index.html 演示页重做:深色现代设计,移除原项目链接
2026-07-21 14:34:05 +08:00
王阳 639c37248d CSS 类名 ck- → moe-,版本号改为构建时从 package.json 注入
- 三个皮肤 CSS 与 src/core/moeplayerEmbed.js 共 3452 处替换,track- 等词内命中已保护
- 右键菜单 version:X3 改为 __MOE_VERSION__ 占位,@rollup/plugin-replace 构建时注入
- 版本号升级为 0.1.0,随 package.json 维护
2026-07-21 11:35:51 +08:00
王阳 159603341e 阶段2:拆分为 ES Module + Rollup 构建,重写 README
- src/ 下拆分为入口/语言包/默认配置/主函数/工具模块,主函数整体搬入零改动
- rollup 打包 UMD(moeplayer.js + moeplayer.min.js),用户使用方式不变
- 库拷贝逻辑移至 scripts/copy-libs.js
- README 重写:中文文档、快速开始、路线图、致谢原作者
- index.html 增加皮肤切换演示(默认/红色/西瓜)
2026-07-21 11:16:25 +08:00
王阳 5609532c80 阶段2改名:ckplayer → MoePlayer
- 目录 ckplayer/ → moeplayer/,核心文件/皮肤/图片同步改名
- 全局 API:new MoePlayer(...),UMD 导出 global.MoePlayer
- CSS 类名 ckplayer-ckplayer-* 折叠为 moeplayer-*,JS 与 CSS 同步
- 语言包全局变量、cookie 名、getPath() 脚本名判断同步
- 移除右键菜单默认指向旧官网的 link
- 构建与 index.html 同步更新
2026-07-21 10:33:38 +08:00
王阳 0956cfaec9 阶段1工程化改造:npm 管理依赖,构建脚本自动压缩与拷贝运行时库
- 新增 package.json/scripts/build.js,npm run build 一键完成压缩与依赖拷贝
- hls.js(1.6.16)/flv.js(1.6.2)/mpegts.js(1.8.0) 由 vendored 改为 npm 依赖
- 删除 Source File/(PSD 与 Flash 遗留 crossdomain.xml)
- 新增 AGENTS.md 项目说明与改造路线图
2026-07-21 10:03:58 +08:00
niandeng 5501689753 no commit message 2022-10-11 08:46:49 +08:00
niandeng dbec6b8ed6 no commit message 2022-07-23 16:05:28 +08:00
niandeng 11a8699724 no commit message 2022-07-23 13:36:46 +08:00
niandeng 0867c32220 no commit message 2022-07-23 13:33:01 +08:00
niandeng 966744ff13 no commit message 2022-07-13 11:09:18 +08:00
82 changed files with 23209 additions and 117761 deletions
+1
View File
@@ -0,0 +1 @@
node_modules/
+80
View File
@@ -0,0 +1,80 @@
# AGENTS.md
## 项目简介
MoePlayer —— 一款中文友好的开源网页视频播放器(MIT 协议),fork 自已停止维护的 ckplayer X3。支持 mp4 / flv / m3u8 / ts 等格式,点播、直播、直播回放,PC 与移动端 H5。
目标定位:中文文档 + 主流浏览器/手机 H5 支持 + 官方 Moodle media 插件(`media_moeplayer`,独立仓库)。
**注意**:当前处于改造阶段 2(改名 + 模块化)。改名已完成:目录 `moeplayer/`、全局变量/API 为 `MoePlayer`(语言包全局变量 `moeplayerLanguage`);单文件已拆分为 ES Module(源码在 `src/`,经 rollup 打包为 UMD 产物)。**主函数 `moeplayerEmbed` 尚未内部拆分**,仍是一个 ~6400 行的巨大函数,位于 `src/core/moeplayerEmbed.js`。
## 目录结构
```
├── index.html # 演示/测试页(含 API 调用示例)
├── package.json # npm 工程化入口,依赖与构建脚本
├── rollup.config.js # rollup 配置:src/ 打包为 UMD 产物 + terser 压缩
├── scripts/build-css.js # 构建脚本第二步:base.css + 主题拼接为三个皮肤产物
├── scripts/copy-libs.js # 构建脚本第三步:从 node_modules 拷贝运行时库
├── src/ # 播放器源码(ES Module,2026-07 由单文件拆分而来)
│ ├── index.js # 入口:export default moeplayerEmbed
│ ├── language.js # 默认中文语言包
│ ├── defaults.js # videoObjectDefault 默认配置
│ ├── core/moeplayerEmbed.js # 主函数,【整体搬自旧单文件,内部未拆分,逻辑零改动】
│ ├── utils/ # 工具函数:type/dom/format/net/cookie/path
│ └── css/ # 皮肤源码(2026-07 皮肤系统现代化:CSS 变量 + SVG mask 图标)
│ ├── base.css # 三皮肤共用结构样式 + 默认主题变量(--moe-* 定义在 .moeplayer 等作用域)
│ └── themes/ # default.css / red.css / ixigua.css,只覆盖 --moe-* 变量值
├── moeplayer/ # 播放器本体,部署时整个上传到网站
│ ├── js/moeplayer.js # UMD 产物,【由 npm run build 自动生成,勿手改】
│ ├── js/moeplayer.min.js # 压缩版,同上
│ ├── css/moeplayer.css # 默认皮肤,【由 build-css.js 拼接生成,勿手改】
│ ├── css/moeplayer.red.css # 红色皮肤,同上
│ ├── css/moeplayer.ixigua.css # 西瓜皮肤(浅色),同上
│ ├── language/ # 语言包:zh.cn.js(默认内嵌)、zh.hk.js、en.js
│ ├── hls.js/ # hls.min.js,【由构建从 npm 包拷贝,勿手改】
│ └── mpegts.js/ # mpegts.js,同上(flv 播放也基于它,flv.js 已于阶段4移除)
└── video/ # 演示用视频与封面
```
## 构建与依赖
- 安装:`npm install`
- 构建:`npm run build`(即 `rollup -c && node scripts/build-css.js && node scripts/copy-libs.js`) —— 做三件事:
1. rollup 把 `src/` 的 ES Module 打包为 UMD:`moeplayer/js/moeplayer.js`,并经 @rollup/plugin-terser 生成 `moeplayer.min.js`(保留 `软件名称|版权` 头部注释)
2. `scripts/build-css.js` 把 `src/css/base.css` 与各主题(`src/css/themes/*.css`)拼接为自包含的 `moeplayer/css/moeplayer[.red|.ixigua].css`(加版权注释头,用户只引一个文件,无 @import)
3. `scripts/copy-libs.js` 从 node_modules 拷贝 hls.js / mpegts.js 的产物及 LICENSE 到 `moeplayer/` 对应目录
- **改动 `src/` 后必须执行 `npm run build`** 重新生成产物;`moeplayer/js/` 与 `moeplayer/css/` 下的 js、css 均为生成物,勿手改
- 皮肤机制:全部主题相关值是 CSS 自定义属性(`--moe-*`),定义在 `.moeplayer, .moeplayer-menu, .moeplayer-error` 作用域(菜单/错误框挂在 body 下);按钮图标为 SVG data-URI + CSS mask,颜色跟随 `--moe-icon-color`,不再使用雪碧图 PNG;换肤=覆盖变量,新增主题=在 `src/css/themes/` 加一个变量文件并登记到 `scripts/build-css.js` 的 themes 清单
- 升级依赖:改 `package.json` 版本号后 `npm install && npm run build`
- Moodle 插件资源同步:`npm run sync-moodle` 把构建产物 `moeplayer/` 拷贝到同级目录的 `moodle-media_moeplayer` 插件仓库(播放器升级后执行,插件仓库另行提交)
- 运行时机制:播放器用 `getPath()` 定位自身所在目录,按需动态 `<script>` 加载同级 `hls.js/` 等目录里的文件,库挂全局变量(`Hls`/`mpegts`)。**这个目录结构约定不能破坏**
- 插件机制:未显式指定 `plug` 时按 URL 扩展名自动识别插件(`.m3u8`→hls.js,`.flv`/`.ts`/`.m2ts`→mpegts.js,识别不到走原生播放),只填 `vars['plug']`,canPlay 原生分流不变(iOS 上 m3u8 仍走原生 HLS);flv 播放基于 mpegts.js,`plug:'flv.js'` 保留为兼容别名;`plug:'dash.js'` 已移除
- rollup 配置要点:`treeshake: false`(完整保留所有工具函数,与旧单文件一致);UMD `name: 'MoePlayer'`、`exports: 'default'`,CJS/AMD/全局变量行为与旧 UMD 等价
## 测试方式
- 自动化冒烟测试:`npm test`(需先 `npm run build`)——Playwright 无头 Chrome 加载演示页,覆盖初始化/播放/暂停/seek/进度条点击/皮肤加载/JS 错误,脚本在 `scripts/smoke.test.js`
- 手动回归:`npm run serve` 起本地服务器(scripts/serve.js,支持 Range 请求),打开 `http://localhost:8000`。**不要用 `python3 -m http.server`——它不支持 Range 请求,会导致 mp4 无法 seek(点击/拖动进度条静默失效)**
- 移动端回归:iOS Safari 与微信内置浏览器各过一遍全屏与进度拖动(目前需真机手动)
## 代码风格
- 注释与文档使用中文(项目惯例)
- `src/` 为 ES Module 源码,但函数体保持旧单文件的 ES5 风格(`var`、函数式),阶段 2/3 重构前不要在函数体内引入 ES6+ 语法;只允许模块级的 `import`/`export` 语句
- 主函数 `moeplayerEmbed` 整体搬迁、内部逻辑零改动;语言包替换机制(`window.moeplayerLanguage`)在其内部,勿动
- `moeplayer/` 下由构建生成的文件(js 两个产物、css 三个皮肤、hls.js/mpegts.js 目录内容)不要手动修改
## Git 约定
- 主仓库在私有 Gitea:git.eryang.wang(待迁移,当前 origin 还是上游 gitee)
- 未经用户明确要求,不执行 git commit/push 等变更操作
## 改造路线图
1. ~~工程化:npm + 构建脚本 + 依赖 npm 化~~(已完成)
2. 改名 ckplayer → MoePlayer(已完成)+ 单文件拆分为 ES Module(已完成,`src/` + rollup 打包;主函数 `moeplayerEmbed` 内部尚未拆分)+ README 重写
3. 核心与 UI 分层、皮肤改用 CSS 变量 + SVG 图标
4. ~~协议插件化:按 URL 自动识别 m3u8/flv/ts 插件、flv 改用 mpegts.js、移除 flv.js~~(已完成);移动端 H5 兼容专项(审查已完成,修复待排期)
5. Moodle media 插件(独立仓库 moodle-media_moeplayer)
6. 发布:Gitea 主仓库 + 中文文档站
+39
View File
@@ -0,0 +1,39 @@
# 更新日志
本项目 fork 自 [ckplayer](http://www.ckplayer.com) X3(已停止维护),在此向原作者 niandeng 致谢。
## [0.1.0] - 2026-07-21
首个公开版本。在 ckplayer X3 基础上完成现代化改造:
### 工程化
- 引入 npm + Rollup 构建,产物为 UMD(`moeplayer.js` / `moeplayer.min.js`),`<script>` + `<link>` 引入方式不变
- 核心代码由单文件拆分为 ES Module(`src/`)
- hls.js / mpegts.js 依赖改由 npm 管理,构建时自动拷贝
- 新增 Playwright 冒烟测试(`npm test`)与支持 Range 请求的本地服务器(`npm run serve`)
- 删除 PSD 设计稿与 Flash 时代遗留文件
### 更名与品牌
- ckplayer 更名为 MoePlayer:全局 API `new MoePlayer(...)`,CSS 类名前缀 `moeplayer-*` / `moe-*`
- 移除播放器默认品牌水印与右键菜单旧官网链接;版本号随 package.json 构建时注入
### 皮肤系统
- CSS 架构重构:`src/css/base.css`(结构 + 35 个 `--moe-*` 变量)+ 主题变量文件,构建生成自包含皮肤
- 11 张 PNG 雪碧图全部替换为 SVG data-URI + CSS mask,图标颜色随主题变量
- 20+ 图标全部重绘;修复原 ckplayer 雪碧图"退出全屏/下一集"共用图标的 bug
- 视觉重做:渐变控制条、细轨道 + 圆点滑块进度条、圆角弹层;西瓜皮肤重做为浅色
- 演示页重新设计(深色现代风格,支持皮肤切换)
### 播放能力
- 按 URL 扩展名自动识别插件(`.m3u8`→hls.js,`.flv`/`.ts`/`.m2ts`→mpegts.js),无需手动指定 `plug`
- flv 播放改由 mpegts.js 承担,移除已停维护的 flv.js;`plug:'flv.js'` 保留兼容别名
- 修复 `plug:'dash.js'` 调用不存在函数的死代码 bug
- iOS 上 m3u8 保持走原生 HLS
- 新增 Moodle media 插件(独立仓库 moodle-media_moeplayer)
### 移动端 H5
- 修复 iOS 原生全屏状态不同步(双击/调用可正确退出全屏)
- 修复 iPad / iPod UA 识别(含 iPadOS 桌面 UA)
- 补充微信 X5 内核同层播放属性
- 进度条 / 音量条支持触摸按住拖动
- 修复触摸坐标未加页面滚动偏移的问题
+2 -1
View File
@@ -1,6 +1,7 @@
MIT License MIT License
Copyright (c) 2020 niandeng Copyright (c) 2026 MoePlayer 项目(wangyang)
Copyright (c) 2020 niandeng(原 ckplayer)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+63 -24
View File
@@ -1,42 +1,81 @@
#### 介绍 # MoePlayer
一款用于在网页上播放视频的软件,本软件为开源软件,遵守开源协议:MIT
#### 视频格式支持 一款中文友好的开源网页视频播放器(MIT 协议),fork 自已停止维护的 [ckplayer](http://www.ckplayer.com) X3,在其基础上持续现代化改造。
支持mp4,flv,m3u8,ts等格式
#### 播放形式支持 > 本项目处于活跃改造期,API 与 ckplayer X3 基本兼容,但全局变量已更名为 `MoePlayer`,CSS 类名前缀已更名为 `moeplayer-*`。
支持点播、直播、直播+回放
#### 平台支持 ## 特性
支持移动端,PC端。
#### 功能支持 - 支持 mp4 / flv / m3u8 / ts 等格式(m3u8 基于 hls.js,flv/ts 基于 mpegts.js)
支持使用js监听播放器的各种状态,支持使用js控制播放器的各种操作 - 无需指定插件,按 URL 扩展名自动识别 m3u8 / flv / ts(m2ts);iOS 上 m3u8 自动走原生 HLS
- 支持点播、直播、直播+回放
- 支持 PC 与移动端 H5
- 支持使用 JS 监听播放器各种状态、控制播放器各种操作
- 零框架依赖,引入一个 JS 和一个 CSS 即可使用
- 多实例友好:同一页面可创建多个播放器
#### 软件架构 ## 快速开始
本软件采用javascript编写核心代码,css文件定义播放器风格
#### 安装教程 将 `moeplayer` 文件夹上传到网站环境中,在页面引入 `moeplayer.js` 和 `moeplayer.css`:
不存在安装过程,将下载包里的ckplayer文件夹上传到网站环境中,在需要播放视频的页面上引入ckplayer.js及ckplayer.css文件,在需要插入视频的地方使用如下代码:
``` ```html
<link type="text/css" rel="stylesheet" href="ckplayer/css/ckplayer.css" /> <link type="text/css" rel="stylesheet" href="moeplayer/css/moeplayer.css" />
<script type="text/javascript" src="ckplayer/js/ckplayer.js" charset="UTF-8"></script> <script type="text/javascript" src="moeplayer/js/moeplayer.min.js" charset="UTF-8"></script>
<div class="video" style="width: 600px;height: 400px;">播放器容器</div> <div class="video" style="width: 600px;height: 400px;">播放器容器</div>
<script type="text/javascript"> <script type="text/javascript">
//定义一个变量:videoObject,用来做为视频初始化配置
var videoObject = { var videoObject = {
container: '.video', //“#”代表容器的ID,“.”或“”代表容器的class container: '.video', //“#”代表容器的ID,“.”或“”代表容器的class
video: 'http://ckplayer-video.oss-cn-shanghai.aliyuncs.com/sample-mp4/05cacb4e02f9d9e.mp4'//视频地址 video: 'https://example.com/sample.mp4' //视频地址
}; };
var player = new ckplayer(videoObject);//初始化播放器 var player = new MoePlayer(videoObject); //初始化播放器
</script> </script>
``` ```
更多配置与 API 可参考本仓库的 `index.html` 演示页;原 ckplayer 手册(http://www.ckplayer.com/manual/ )在改造期内仍有参考价值。
#### 使用说明 ### 语言
使用过程中碰到问题,请至官网查看手册:http://www.ckplayer.com/manual/
#### 参与贡献 默认中文。引入语言包可切换其它语言(需在引入 moeplayer.js 之前引入):
niandeng
```html
<script type="text/javascript" src="moeplayer/language/en.js" charset="UTF-8"></script>
```
### 皮肤
默认皮肤 `moeplayer.css`,另有红色 `moeplayer.red.css`、西瓜风格 `moeplayer.ixigua.css`,替换 CSS 文件即可换肤。
## 从源码构建
```bash
npm install
npm run build
```
源码在 `src/`(ES Module),构建产物输出到 `moeplayer/js/`(UMD,含未压缩版与 min 版),同时从 npm 依赖拷贝 hls.js / mpegts.js 运行时库到 `moeplayer/` 对应目录。
## 改造路线图
- [x] 工程化:npm + rollup 构建,依赖 npm 化管理
- [x] 改名 ckplayer → MoePlayer,单文件拆分为 ES Module
- [x] 皮肤系统:CSS 变量 + SVG 图标(核心与 UI 完全分层留待后续渐进重构)
- [x] 协议插件化:按 URL 自动识别 m3u8/flv/ts 插件,flv 改用 mpegts.js(移除已停维护的 flv.js)
- [x] 移动端 H5 兼容性专项(iOS 全屏/iPad UA/X5 同层/触摸拖动已修复,滑动手势等增强待排期)
- [x] Moodle media 插件([moodle-media_moeplayer](https://git.eryang.wang/wangyang/moodle-media_moeplayer))
- [ ] 完整中文文档
## 相关项目
- [moodle-media_moeplayer](https://git.eryang.wang/wangyang/moodle-media_moeplayer):Moodle media 插件,把 MoePlayer 接入 Moodle(播放器资源内打包,安装即用)
## 更新日志
见 [CHANGELOG.md](CHANGELOG.md)。
## 致谢
感谢 ckplayer 原作者 [niandeng](http://www.ckplayer.com) 的开源工作,本项目在其 X3 版本基础上继续演进。
## 协议
[MIT](LICENSE) © MoePlayer 项目 & ckplayer 原作者 niandeng
Binary file not shown.
Binary file not shown.
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

-202
View File
@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-104
View File
@@ -1,104 +0,0 @@
flv.js [![npm](https://img.shields.io/npm/v/flv.js.svg?style=flat)](https://www.npmjs.com/package/flv.js)
======
An HTML5 Flash Video (FLV) Player written in pure JavaScript without Flash. LONG LIVE FLV!
This project relies on [Media Source Extensions][] to work.
**For FLV live stream playback, please consider [mpegts.js][] which is under active development.**
**This project will become rarely maintained.**
[mpegts.js]: https://github.com/xqq/mpegts.js
## Overview
flv.js works by transmuxing FLV file stream into ISO BMFF (Fragmented MP4) segments, followed by feeding mp4 segments into an HTML5 `<video>` element through [Media Source Extensions][] API.
[Media Source Extensions]: https://w3c.github.io/media-source/
## Demo
[http://bilibili.github.io/flv.js/demo/](http://bilibili.github.io/flv.js/demo/)
## Features
- FLV container with H.264 + AAC / MP3 codec playback
- Multipart segmented video playback
- HTTP FLV low latency live stream playback
- FLV over WebSocket live stream playback
- Compatible with Chrome, FireFox, Safari 10, IE11 and Edge
- Extremely low overhead, and hardware accelerated by your browser!
## Installation
```bash
npm install --save flv.js
```
## Build
```bash
npm ci # install dependencies / dev-dependences
npm run build:debug # debug version flv.js will be emitted to /dist
npm run build # minimized release version flv.min.js will be emitted to /dist
```
[cnpm](https://github.com/cnpm/cnpm) mirror is recommended if you are in Mainland China.
## CORS
If you use standalone video server for FLV stream, `Access-Control-Allow-Origin` header must be configured correctly on video server for cross-origin resource fetching.
See [cors.md](docs/cors.md) for more details.
## Getting Started
```html
<script src="flv.min.js"></script>
<video id="videoElement"></video>
<script>
if (flvjs.isSupported()) {
var videoElement = document.getElementById('videoElement');
var flvPlayer = flvjs.createPlayer({
type: 'flv',
url: 'http://example.com/flv/video.flv'
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
flvPlayer.play();
}
</script>
```
## Limitations
- MP3 audio codec is currently not working on IE11 / Edge
- HTTP FLV live stream is not currently working on all browsers, see [livestream.md](docs/livestream.md)
## Multipart playback
You only have to provide a playlist for `MediaDataSource`. See [multipart.md](docs/multipart.md)
## Livestream playback
See [livestream.md](docs/livestream.md)
## API and Configuration
See [api.md](docs/api.md)
## Debug
```bash
npm ci # install dependencies / dev-dependences
npm run dev # watch file changes and build debug version on the fly
```
## Design
See [design.md](docs/design.md)
## License
```
Copyright (C) 2016 Bilibili. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
-10585
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
-10
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-442
View File
@@ -1,442 +0,0 @@
[![npm](https://img.shields.io/npm/v/hls.js.svg?style=flat)](https://npmjs.org/package/hls.js)
[![npm](https://img.shields.io/npm/v/hls.js/canary.svg?style=flat)](https://www.npmjs.com/package/hls.js/v/canary)
[![](https://data.jsdelivr.com/v1/package/npm/hls.js/badge?style=rounded)](https://www.jsdelivr.com/package/npm/hls.js)
[![Sauce Test Status](https://saucelabs.com/buildstatus/robwalch)](https://app.saucelabs.com/u/robwalch)
[comment]: <> ([![Sauce Test Status]&#40;https://saucelabs.com/browser-matrix/robwalch.svg&#41;]&#40;https://saucelabs.com/u/robwalch&#41;)
# ![HLS.js](https://cloud.githubusercontent.com/assets/616833/19739063/e10be95a-9bb9-11e6-8100-2896f8500138.png)
HLS.js is a JavaScript library that implements an [HTTP Live Streaming] client.
It relies on [HTML5 video][] and [MediaSource Extensions][] for playback.
It works by transmuxing MPEG-2 Transport Stream and AAC/MP3 streams into ISO BMFF (MP4) fragments.
Transmuxing is performed asynchronously using a [Web Worker] when available in the browser.
HLS.js also supports HLS + fmp4, as announced during [WWDC2016](https://developer.apple.com/videos/play/wwdc2016/504/).
HLS.js works directly on top of a standard HTML`<video>` element.
HLS.js is written in [ECMAScript6] (`*.js`) and [TypeScript] (`*.ts`) (strongly typed superset of ES6), and transpiled in ECMAScript5 using [Babel](https://babeljs.io/) and the [TypeScript compiler].
[Webpack] is used to build the distro bundle and serve the local development environment.
[html5 video]: https://www.html5rocks.com/en/tutorials/video/basics/
[mediasource extensions]: https://w3c.github.io/media-source/
[http live streaming]: https://en.wikipedia.org/wiki/HTTP_Live_Streaming
[web worker]: https://caniuse.com/#search=worker
[ecmascript6]: https://github.com/ericdouglas/ES6-Learning#articles--tutorials
[typescript]: https://www.typescriptlang.org/
[typescript compiler]: https://www.typescriptlang.org/docs/handbook/compiler-options.html
[webpack]: https://webpack.js.org/
## Features
- VOD & Live playlists
- DVR support on Live playlists
- Fragmented MP4 container
- MPEG-2 TS container
- ITU-T Rec. H.264 and ISO/IEC 14496-10 Elementary Stream
- ISO/IEC 13818-7 ADTS AAC Elementary Stream
- ISO/IEC 11172-3 / ISO/IEC 13818-3 (MPEG-1/2 Audio Layer III) Elementary Stream
- Packetized metadata (ID3v2.3.0) Elementary Stream
- AAC container (audio only streams)
- MPEG Audio container (MPEG-1/2 Audio Layer III audio only streams)
- Timed Metadata for HTTP Live Streaming (in ID3 format, carried in MPEG-2 TS)
- AES-128 decryption
- SAMPLE-AES decryption (only supported if using MPEG-2 TS container)
- Encrypted media extensions (EME) support for DRM (digital rights management)
- Widevine CDM (only tested with [shaka-packager](https://github.com/google/shaka-packager) test-stream on [the demo page](https://hls-js.netlify.app/demo/?src=https%3A%2F%2Fstorage.googleapis.com%2Fshaka-demo-assets%2Fangel-one-widevine-hls%2Fhls.m3u8&demoConfig=eyJlbmFibGVTdHJlYW1pbmciOnRydWUsImF1dG9SZWNvdmVyRXJyb3IiOnRydWUsInN0b3BPblN0YWxsIjpmYWxzZSwiZHVtcGZNUDQiOmZhbHNlLCJsZXZlbENhcHBpbmciOi0xLCJsaW1pdE1ldHJpY3MiOi0xfQ==))
- CEA-608/708 captions
- WebVTT subtitles
- Alternate Audio Track Rendition (Master Playlist with Alternative Audio) for VoD and Live playlists
- Adaptive streaming
- Manual & Auto Quality Switching
- 3 Quality Switching modes are available (controllable through API means)
- Instant switching (immediate quality switch at current video position)
- Smooth switching (quality switch for next loaded fragment)
- Bandwidth conservative switching (quality switch change for next loaded fragment, without flushing the buffer)
- In Auto-Quality mode, emergency switch down in case bandwidth is suddenly dropping to minimize buffering.
- Accurate Seeking on VoD & Live (not limited to fragment or keyframe boundary)
- Ability to seek in buffer and back buffer without redownloading segments
- Built-in Analytics
- All internal events can be monitored (Network Events, Video Events)
- Playback session metrics are also exposed
- Resilience to errors
- Retry mechanism embedded in the library
- Recovery actions can be triggered fix fatal media or network errors
- [Redundant/Failover Playlists](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/UsingHTTPLiveStreaming/UsingHTTPLiveStreaming.html#//apple_ref/doc/uid/TP40008332-CH102-SW22)
### Supported M3U8 tags
For details on the HLS format and these tags' meanings, see https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08
#### Manifest tags
- `#EXT-X-STREAM-INF:<attribute-list>`
`<URI>`
- `#EXT-X-MEDIA:<attribute-list>`
- `#EXT-X-SESSION-DATA:<attribute-list>`
The following properties are added to their respective variants' attribute list but are not implemented in their selection and playback.
- `VIDEO-RANGE` and `HDCP-LEVEL` (See [#2489](https://github.com/video-dev/hls.js/issues/2489))
#### Playlist tags
- `#EXTM3U`
- `#EXT-X-VERSION=<n>`
- `#EXTINF:<duration>,[<title>]`
- `#EXT-X-ENDLIST`
- `#EXT-X-MEDIA-SEQUENCE=<n>`
- `#EXT-X-TARGETDURATION=<n>`
- `#EXT-X-DISCONTINUITY`
- `#EXT-X-DISCONTINUITY-SEQUENCE=<n>`
- `#EXT-X-BYTERANGE=<n>[@<o>]`
- `#EXT-X-MAP:<attribute-list>`
- `#EXT-X-KEY:<attribute-list>` (`METHOD=SAMPLE-AES` is only supports with MPEG-2 TS segments)
- `#EXT-X-PROGRAM-DATE-TIME:<attribute-list>`
- `#EXT-X-START:TIME-OFFSET=<n>`
- `#EXT-X-SERVER-CONTROL:<attribute-list>`
- `#EXT-X-PART-INF:PART-TARGET=<n>`
- `#EXT-X-PART:<attribute-list>`
- `#EXT-X-PRELOAD-HINT:<attribute-list>`
- `#EXT-X-SKIP:<attribute-list>`
- `#EXT-X-RENDITION-REPORT:<attribute-list>`
The following tags are added to their respective fragment's attribute list but are not implemented in streaming and playback.
- `#EXT-X-DATERANGE:<attribute-list>` (Not added to metadata TextTracks. See [#2218](https://github.com/video-dev/hls.js/issues/2218))
- `#EXT-X-BITRATE` (Not used in ABR controller)
- `#EXT-X-GAP` (Not implemented. See [#2940](https://github.com/video-dev/hls.js/issues/2940))
### Not Supported
For a complete list of issues, see ["Top priorities" in the Release Planning and Backlog project tab](https://github.com/video-dev/hls.js/projects/6). Codec support is dependent on the runtime environment (for example, not all browsers on the same OS support HEVC).
- CMAF CC support [#2623](https://github.com/video-dev/hls.js/issues/2623)
- `Emsg` Inband Timed Metadata for FMP4 (ID3 within Emsgv1) in "metadata" TextTracks [#2360](https://github.com/video-dev/hls.js/issues/2360)
- `#EXT-X-DATERANGE` in "metadata" TextTracks [#2218](https://github.com/video-dev/hls.js/issues/2218)
- `#EXT-X-GAP` filling [#2940](https://github.com/video-dev/hls.js/issues/2940)
- `#EXT-X-I-FRAME-STREAM-INF` I-frame Media Playlist files
- `SAMPLE-AES` with fmp4, aac, mp3, vtt... segments (MPEG-2 TS only)
- PlayReady and FairPlay DRM ( See [#3779](https://github.com/video-dev/hls.js/issues/2360) and [issues labeled DRM](https://github.com/video-dev/hls.js/issues?q=is%3Aissue+is%3Aopen+label%3ADRM))
- Advanced variant selection based on runtime media capabilities (See issues labeled [`media-capabilities`](https://github.com/video-dev/hls.js/labels/media-capabilities))
- MP3 elementary stream audio in IE and Edge (<=18) on Windows 10 (See [#1641](https://github.com/video-dev/hls.js/issues/1641) and [Microsoft answers forum](https://answers.microsoft.com/en-us/ie/forum/all/ie11-on-windows-10-cannot-play-hls-with-mp3/2da994b5-8dec-4ae9-9201-7d138ede49d9))
### Server-side-rendering (SSR) and `require` from a Node.js runtime
You can safely require this library in Node and **absolutely nothing will happen**. A dummy object is exported so that requiring the library does not throw an error. HLS.js is not instantiable in Node.js. See [#1841](https://github.com/video-dev/hls.js/pull/1841) for more details.
## Getting started with development
First, checkout the repository and install the required dependencies
```sh
git clone https://github.com/video-dev/hls.js.git
cd hls.js
# After cloning or pulling from the repository, make sure all dependencies are up-to-date
npm install ci
# Run dev-server for demo page (recompiles on file-watch, but doesn't write to actual dist fs artifacts)
npm run dev
# After making changes run the sanity-check task to verify all checks before committing changes
npm run sanity-check
```
The dev server will host files on port 8000. Once started, the demo can be found running at http://localhost:8000/demo/.
Before submitting a PR, please see our [contribution guidelines](CONTRIBUTING.md).
Join the discussion on Slack via [video-dev.org](https://video-dev.org) in #hlsjs for updates and questions about development.
### Build tasks
Build all flavors (suitable for prod-mode/CI):
```
npm install ci
npm run build
```
Only debug-mode artifacts:
```
npm run build:debug
```
Build and watch (customized dev setups where you'll want to host through another server than webpacks' - for example in a sub-module/project)
```
npm run build:watch
```
Only specific flavor (known configs are: debug, dist, light, light-dist, demo):
```
npm run build -- --env dist # replace "dist" by other configuration name, see above ^
```
Note: The "demo" config is always built.
**NOTE:** `hls.light.*.js` dist files do not include EME, subtitles, CMCD, or alternate-audio support. In addition,
the following types are not available in the light build:
- `AudioStreamController`
- `AudioTrackController`
- `CuesInterface`
- `EMEController`
- `SubtitleStreamController`
- `SubtitleTrackController`
- `TimelineController`
- `CmcdController`
### Linter (ESlint)
Run linter:
```
npm run lint
```
Run linter with auto-fix mode:
```
npm run lint:fix
```
Run linter with errors only (no warnings)
```
npm run lint:quiet
```
### Formatting Code
Run prettier to format code
```
npm run prettier
```
### Type Check
Run type-check to verify TypeScript types
```
npm run type-check
```
### Automated tests (Mocha/Karma)
Run all tests at once:
```
npm test
```
Run unit tests:
```
npm run test:unit
```
Run unit tests in watch mode:
```
npm run test:unit:watch
```
Run functional (integration) tests:
```
npm run test:func
```
## Design
An overview of this project's design, it's modules, events, and error handling can be found [here](/docs/design.md).
## API docs and usage guide
- [API and usage docs, with code examples](./docs/API.md)
- [Auto-Generated API Docs (Latest Release)](https://hls-js.netlify.com/api-docs)
- [Auto-Generated API Docs (Development Branch)](https://hls-js-dev.netlify.com/api-docs)
_Note you can access the docs for a particular version using "[https://github.com/video-dev/hls.js/tree/deployments](https://github.com/video-dev/hls.js/tree/deployments)"_
## Demo
### Latest Release
[https://hls-js.netlify.com/demo](https://hls-js.netlify.com/demo)
### Master
[https://hls-js-dev.netlify.com/demo](https://hls-js-dev.netlify.com/demo)
### Specific Version
Find the commit on [https://github.com/video-dev/hls.js/tree/deployments](https://github.com/video-dev/hls.js/tree/deployments).
[![](https://www.netlify.com/img/global/badges/netlify-color-accent.svg)](https://www.netlify.com)
[![](https://opensource.saucelabs.com/images/opensauce/powered-by-saucelabs-badge-gray.png?sanitize=true)](https://saucelabs.com)
## Compatibility
HLS.js is only compatible with browsers supporting MediaSource extensions (MSE) API with 'video/MP4' mime-type inputs.
HLS.js is supported on:
- Chrome 39+ for Android
- Chrome 39+ for Desktop
- Firefox 41+ for Android
- Firefox 42+ for Desktop
- IE11 for Windows 8.1+
- Edge for Windows 10+
- Safari 8+ for MacOS 10.10+
- Safari for ipadOS 13+
A [Promise polyfill](https://github.com/taylorhakes/promise-polyfill) is required in browsers missing native promise support.
**Please note:** iOS Safari on iPhone does not support the MediaSource API. This includes all browsers on iOS as well as apps using UIWebView and WKWebView.
Safari browsers (iOS, iPadOS, and macOS) have built-in HLS support through the plain video "tag" source URL. See the example below (Using HLS.js) to run appropriate feature detection and choose between using HLS.js or natively built-in HLS support.
When a platform has neither MediaSource nor native HLS support, the browser cannot play HLS.
_Keep in mind that if the intention is to support HLS on multiple platforms, beyond those compatible with HLS.js, the HLS streams need to strictly follow the specifications of RFC8216, especially if apps, smart TVs, and set-top boxes are to be supported._
Find a support matrix of the MediaSource API here: https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
## Using HLS.js
### Installation
Prepackaged builds are included [with each release](https://github.com/video-dev/hls.js/releases). Or install the hls.js as a dependency
of your project:
```sh
npm install --save hls.js
```
A canary channel is also available if you prefer to work off the development branch (master):
```
npm install hls.js@canary
```
### Embedding HLS.js
Directly include dist/hls.js or dist/hls.min.js in a script tag on the page. This setup prioritizes HLS.js MSE playback over
native browser support for HLS playback in HTMLMediaElements:
```html
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<!-- Or if you want a more recent alpha version -->
<!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@alpha"></script> -->
<video id="video"></video>
<script>
var video = document.getElementById('video');
var videoSrc = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
// HLS.js is not supported on platforms that do not have Media Source
// Extensions (MSE) enabled.
//
// When the browser has built-in HLS support (check using `canPlayType`),
// we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video
// element through the `src` property. This is using the built-in support
// of the plain video element, without using HLS.js.
//
// Note: it would be more normal to wait on the 'canplay' event below however
// on Safari (where you are most likely to find built-in HLS support) the
// video.src URL must be on the user-driven white-list before a 'canplay'
// event will be emitted; the last video event that can be reliably
// listened-for when the URL is not on the white-list is 'loadedmetadata'.
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
}
</script>
```
#### Alternative setup
To check for native browser support first and then fallback to HLS.js, swap these conditionals. See [this comment](https://github.com/video-dev/hls.js/pull/2954#issuecomment-670021358) to understand some of the tradeoffs.
```html
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<!-- Or if you want a more recent alpha version -->
<!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@alpha"></script> -->
<video id="video"></video>
<script>
var video = document.getElementById('video');
var videoSrc = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
//
// First check for native browser HLS support
//
if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
//
// If no native HLS support, check if HLS.js is supported
//
} else if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
</script>
```
For more embed and API examples see [docs/API.md](./docs/API.md).
## CORS
All HLS resources must be delivered with [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) permitting `GET` requests.
## Video Control
Video is controlled through HTML `<video>` element `HTMLVideoElement` methods, events and optional UI controls (`<video controls>`).
## Player Integration
The following players integrate HLS.js for HLS playback:
- [JW Player](https://www.jwplayer.com)
- [Akamai Adaptive Media Player (AMP)](https://www.akamai.com/us/en/solutions/products/media-delivery/adaptive-media-player.jsp)
- [Clappr](https://github.com/clappr/clappr)
- [Flowplayer](https://www.flowplayer.org) through [flowplayer-hlsjs](https://github.com/flowplayer/flowplayer-hlsjs)
- [MediaElement.js](https://www.mediaelementjs.com)
- [Videojs](https://videojs.com) through [Videojs-hlsjs](https://github.com/benjipott/videojs-hlsjs)
- [Videojs](https://videojs.com) through [videojs-hls.js](https://github.com/streamroot/videojs-hls.js). hls.js is integrated as a SourceHandler -- new feature in Video.js 5.
- [Videojs](https://videojs.com) through [videojs-contrib-hls.js](https://github.com/Peer5/videojs-contrib-hls.js). Production ready plug-in with full fallback compatibility built-in.
- [Fluid Player](https://www.fluidplayer.com)
- [OpenPlayerJS](https://www.openplayerjs.com), as part of the [OpenPlayer project](https://github.com/openplayerjs)
- [CDNBye](https://github.com/cdnbye/hlsjs-p2p-engine), a p2p engine for hls.js powered by WebRTC Datachannel.
### They use HLS.js in production!
| | | | |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [<img src="https://i.cdn.turner.com/adultswim/big/img/global/adultswim.jpg" width="120">](https://www.adultswim.com/streams) | [<img src="https://avatars3.githubusercontent.com/u/5497190?s=200&v=4" width="120">](https://www.akamai.com) | [<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Canal%2B.svg/2000px-Canal%2B.svg.png" width="120">](https://www.canalplus.fr) | [<img src="https://avatars2.githubusercontent.com/u/115313" width="120">](https://www.dailymotion.com) |
| [<img src="https://user-images.githubusercontent.com/4006693/44003595-baff193c-9e8f-11e8-9848-7bb91563499f.png" width="120">](https://freshlive.tv) | [<img src="https://flowplayer.org/media/img/logo-blue.png" width="120">](https://flowplayer.com) | [<img src="https://avatars1.githubusercontent.com/u/12554082?s=240" width="120">](https://www.foxsports.com.au) | [<img src="https://cloud.githubusercontent.com/assets/244265/12556435/dfaceb48-c353-11e5-971b-2c4429725469.png" width="120">](https://www.globo.com) |
| [<img src="https://images.gunosy.com/logo/gunosy_icon_company_logo.png" width="120">](https://gunosy.com) | [<img src="https://user-images.githubusercontent.com/1480052/35802840-f8e85b8a-0a71-11e8-8eb2-eee323e3f159.png" width="120">](https://www.gl-systemhaus.de/) | [<img src="https://cloud.githubusercontent.com/assets/6525783/20801836/700490de-b7ea-11e6-82bd-e249f91c7bae.jpg" width="120">](https://nettrek.de) | [<img src="https://cloud.githubusercontent.com/assets/244265/12556385/999aa884-c353-11e5-9102-79df54384498.png" width="120">](https://www.nytimes.com/) |
| [<img src="https://cloud.githubusercontent.com/assets/1798553/20356424/ba158574-ac24-11e6-95e1-1ae591b11a0a.png" width="120">](https://www.peer5.com/) | [<img src="https://cloud.githubusercontent.com/assets/4909096/20925062/e26e6fc8-bbb4-11e6-99a5-d4762274a342.png" width="120">](https://www.qbrick.com) | [<img src="https://www.radiantmediaplayer.com/images/radiantmediaplayer-new-logo-640.jpg" width="120">](https://www.radiantmediaplayer.com/) | [<img src="https://www.rts.ch/hummingbird-static/images/logos/logo_marts.svg" width="120">](https://www.rts.ch) |
| [<img src="https://cloud.githubusercontent.com/assets/12702747/19316434/0a3601de-9067-11e6-85e2-936b1cb099a0.png" width="120">](https://www.snapstream.com/) | [<img src="https://pamediagroup.com/wp-content/uploads/2019/05/StreamAMG-Logo-RGB.png" width="120">](https://www.streamamg.com/) | [<img src="https://streamsharkio.sa.metacdn.com/wp-content/uploads/2015/10/streamshark-dark.svg" width="120">](https://streamshark.io/) | [<img src="https://camo.githubusercontent.com/9580f10e9bfa8aa7fba52c5cb447bee0757e33da/68747470733a2f2f7777772e7461626c6f74762e636f6d2f7374617469632f696d616765732f7461626c6f5f6c6f676f2e706e67" width="120">](https://my.tablotv.com/) |
| [<img src="https://user-images.githubusercontent.com/2803310/34083705-349c8fd0-e375-11e7-92a6-5c38509f4936.png" width="120">](https://www.streamroot.io/) | [<img src="https://vignette1.wikia.nocookie.net/tedtalks/images/c/c0/TED_logo.png/revision/20150915192527" width="120">](https://www.ted.com/) | [<img src="https://www.seeklogo.net/wp-content/uploads/2014/12/twitter-logo-vector-download.jpg" width="120">](https://twitter.com/) | [<img src="https://player.clevercast.com/img/clevercast.png" width="120">](https://www.clevercast.com) |
| [<img src="https://player.mtvnservices.com/edge/hosted/Viacom_logo.svg" width="120">](https://www.viacom.com/) | [<img src="https://user-images.githubusercontent.com/1181974/29248959-efabc440-802d-11e7-8050-7c1f4ca6c607.png" width="120">](https://vk.com/) | [<img src="https://avatars0.githubusercontent.com/u/5090060?s=200&v=4" width="120">](https://www.jwplayer.com) | [<img src="https://staticftv-a.akamaihd.net/arches/francetv/default/img/og-image.jpg?20161007" width="120">](https://www.france.tv) |
| [<img src="https://showmax.akamaized.net/e/logo/showmax_black.png" width="120">](https://tech.showmax.com) | [<img src="https://static3.1tv.ru/assets/web/logo-ac67852f1625b338f9d1fb96be089d03557d50bfc5790d5f48dc56799f59dec6.svg" width="120" height="120">](https://www.1tv.ru/) | [<img src="https://user-images.githubusercontent.com/1480052/40482633-c013ebce-5f55-11e8-96d5-b776415de0ce.png" width="120">](https://www.zdf.de) | [<img src="https://github.com/cdnbye/hlsjs-p2p-engine/blob/master/figs/cdnbye.png" width="120">](https://github.com/cdnbye/hlsjs-p2p-engine) |
| [cdn77](https://streaming.cdn77.com/) | [<img src="https://avatars0.githubusercontent.com/u/7442371?s=200&v=4" width="120">](https://r7.com/) | [<img src="https://raw.githubusercontent.com/Novage/p2p-media-loader/gh-pages/images/p2pml-logo.png" width="120">](https://github.com/Novage/p2p-media-loader) | [<img src="https://avatars3.githubusercontent.com/u/45617200?s=400" width="120">](https://kayosports.com.au) |
| [<img src="https://avatars1.githubusercontent.com/u/5279615?s=400&u=9771a216836c613f1edf4afe71cfc69d4c5657ed&v=4" width="120">](https://flosports.tv) | [<img src="https://www.logolynx.com/images/logolynx/c6/c67a2cb3ad33a82b5518f8ad8f124703.png" width="120">](https://global.axon.com/) | | |
## Chrome/Firefox integration
made by [gramk](https://github.com/gramk/chrome-hls), plays hls from address bar and m3u8 links
- Chrome [native-hls](https://chrome.google.com/webstore/detail/native-hls-playback/emnphkkblegpebimobpbekeedfgemhof)
- Firefox [native-hls](https://addons.mozilla.org/en-US/firefox/addon/native_hls_playback/)
## License
HLS.js is released under [Apache 2.0 License](LICENSE)
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
-27804
View File
File diff suppressed because it is too large Load Diff
-2837
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-7
View File
File diff suppressed because one or more lines are too long
-202
View File
@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+107 -28
View File
@@ -1,17 +1,112 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>ckplayer</title> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link type="text/css" rel="stylesheet" href="ckplayer/css/ckplayer.css" /> <title>MoePlayer - 中文友好的网页视频播放器</title>
<link id="skin" type="text/css" rel="stylesheet" href="moeplayer/css/moeplayer.css" />
<!-- <!--
如果需要使用其它语言,请在此处引入相应的js,比如:<script type="text/javascript" src="ckplayer/language/en.js" charset="UTF-8"></script> 如果需要使用其它语言,请在此处引入相应的js,比如:<script type="text/javascript" src="moeplayer/language/en.js" charset="UTF-8"></script>
--> -->
<script type="text/javascript" src="ckplayer/js/ckplayer.min.js" charset="UTF-8"></script> <script type="text/javascript" src="moeplayer/js/moeplayer.min.js" charset="UTF-8"></script>
<style>
/* ===== 演示页样式,与播放器无关 ===== */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
background: #0f1216;
color: #d7dde5;
min-height: 100vh;
}
.wrap { max-width: 860px; margin: 0 auto; padding: 40px 20px 60px; }
header { text-align: center; margin-bottom: 32px; }
header h1 { font-size: 32px; color: #fff; letter-spacing: 1px; }
header h1 em { color: #00a1d6; font-style: normal; }
header p { margin-top: 8px; color: #8a94a3; font-size: 14px; }
.player-card {
background: #171c23;
border: 1px solid #232a35;
border-radius: 12px;
padding: 16px;
box-shadow: 0 12px 40px rgba(0,0,0,.45);
}
.video { width: 100%; height: 460px; border-radius: 8px; overflow: hidden; }
section { margin-top: 28px; }
section h2 {
font-size: 14px; color: #8a94a3; font-weight: 600;
text-transform: uppercase; letter-spacing: 2px; margin-bottom: 12px;
}
.btns { display: flex; flex-wrap: wrap; gap: 8px; }
.btns button {
background: #1f2630; color: #d7dde5;
border: 1px solid #2c3644; border-radius: 6px;
padding: 8px 16px; font-size: 13px; cursor: pointer;
transition: all .15s;
}
.btns button:hover { background: #00a1d6; border-color: #00a1d6; color: #fff; }
.state {
margin-top: 14px; padding: 10px 14px;
background: #171c23; border: 1px solid #232a35; border-radius: 8px;
font-size: 13px; color: #00a1d6; min-height: 40px; line-height: 20px;
}
footer {
margin-top: 40px; text-align: center;
font-size: 12px; color: #5c6675; line-height: 1.9;
}
footer a { color: #8a94a3; }
@media (max-width: 600px) {
.video { height: 240px; }
header h1 { font-size: 24px; }
}
</style>
</head> </head>
<body> <body>
<div class="wrap">
<header>
<h1><em>Moe</em>Player</h1>
<p>中文友好的网页视频播放器 · mp4 / flv / m3u8 / ts · PC 与移动端 H5</p>
</header>
<div class="player-card">
<div class="video">播放容器</div>
</div>
<section>
<h2>皮肤切换</h2>
<div class="btns">
<button type="button" onclick="changeSkin('moeplayer.css')">默认</button>
<button type="button" onclick="changeSkin('moeplayer.red.css')">红色</button>
<button type="button" onclick="changeSkin('moeplayer.ixigua.css')">西瓜</button>
</div>
</section>
<section>
<h2>控制示例</h2>
<div class="btns">
<button type="button" onclick="player.play()">播放</button>
<button type="button" onclick="player.pause()">暂停</button>
<button type="button" onclick="player.seek(20)">跳转 20s</button>
<button type="button" onclick="player.volume(0.6)">音量 0.6</button>
<button type="button" onclick="player.muted()">静音</button>
<button type="button" onclick="player.exitMuted()">恢复音量</button>
<button type="button" onclick="player.full()">全屏</button>
<button type="button" onclick="player.webFull()">页面全屏</button>
<button type="button" onclick="player.theatre()">剧场模式</button>
<button type="button" onclick="player.exitTheatre()">退出剧场</button>
</div>
</section>
<section>
<h2>事件监听</h2>
<div class="state" id="state">等待播放器事件…</div>
<div class="state" id="state2" style="margin-top:8px"></div>
</section>
<footer>
<p>MoePlayer · MIT 开源协议 · fork 自 <a href="https://www.ckplayer.com" target="_blank" rel="noopener">ckplayer</a>,感谢原作者 niandeng</p>
</footer>
</div>
<div class="video" style="width: 100%; height: 500px;max-width: 800px;">播放容器</div>
<script> <script>
//调用开始 //调用开始
var videoObject = { var videoObject = {
@@ -19,11 +114,14 @@
poster:'video/poster.png',//封面图片 poster:'video/poster.png',//封面图片
video:'video/lc.mp4'//视频地址 video:'video/lc.mp4'//视频地址
}; };
var player=new ckplayer(videoObject)//调用播放器并赋值给变量player var player=new MoePlayer(videoObject)//调用播放器并赋值给变量player
//皮肤切换:替换引入的CSS文件
function changeSkin(css){
document.getElementById('skin').href='moeplayer/css/'+css;
}
/* /*
* =============================================================================================== * ===============================================================================================
* 以上代码已完成调用演示,下方的代码是演示监听动作和外部控制的部分 * 以下代码演示监听动作和外部控制
* ===============================================================================================
* =============================================================================================== * ===============================================================================================
*/ */
player.play(function(){ player.play(function(){
@@ -45,24 +143,5 @@
document.getElementById('state').innerHTML='监听到播放结束'; document.getElementById('state').innerHTML='监听到播放结束';
}); });
</script> </script>
<p>官网:<a href="https://www.ckplayer.com" target="_blank">www.ckplayer.com</a></p>
<p>手册:<a href="https://www.ckplayer.com/manual/" target="_blank">www.ckplayer.com/manual/</a></p>
<p>社区:<a href="https://bbs.ckplayer.com/" target="_blank">bbs.ckplayer.com</a></p>
<p>全功能演示:<a href="https://www.ckplayer.com/demo.html" target="_blank">www.ckplayer.com/demo.html</a></p>
<p>控制示例:</p>
<p>
<button type="button" onclick="player.play()" >播放</button>
<button type="button" onclick="player.pause()" >暂停</button>
<button type="button" onclick="player.seek(20)" >跳转</button>
<button type="button" onclick="player.volume(0.6)">修改音量</button>
<button type="button" onclick="player.muted()">静音</button>
<button type="button" onclick="player.exitMuted()">恢复音量</button>
<button type="button" onclick="player.full()">全屏</button>
<button type="button" onclick="player.webFull()">页面全屏</button>
<button type="button" onclick="player.theatre()">剧场模式</button>
<button type="button" onclick="player.exitTheatre()">退出剧场模式</button>
</p>
<p id="state"></p>
<p id="state2"></p>
</body> </body>
</html> </html>
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2026 MoePlayer 项目(wangyang)
Copyright (c) 2020 niandeng(原 ckplayer)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+13
View File
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
(function (global, factory) { (function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) : typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.ckplayerLanguage = factory()); (global = global || self, global.moeplayerLanguage = factory());
}(this, function () { 'use strict'; }(this, function () { 'use strict';
/* /*
* 功能:包含播放器用到的全部相关语言文字 * 功能:包含播放器用到的全部相关语言文字
@@ -1,7 +1,7 @@
(function (global, factory) { (function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) : typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.ckplayerLanguage = factory()); (global = global || self, global.moeplayerLanguage = factory());
}(this, function () { 'use strict'; }(this, function () { 'use strict';
/* /*
* 功能:包含播放器用到的全部相关语言文字 * 功能:包含播放器用到的全部相关语言文字
@@ -1,7 +1,7 @@
(function (global, factory) { (function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) : typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.ckplayerLanguage = factory()); (global = global || self, global.moeplayerLanguage = factory());
}(this, function () { 'use strict'; }(this, function () { 'use strict';
/* /*
*功能:包含播放機用到的全部相關語言文字 *功能:包含播放機用到的全部相關語言文字
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+788
View File
@@ -0,0 +1,788 @@
{
"name": "moeplayer",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "moeplayer",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"hls.js": "^1.6.10",
"mpegts.js": "^1.8.0"
},
"devDependencies": {
"@rollup/plugin-replace": "^6.0.3",
"@rollup/plugin-terser": "^1.0.0",
"playwright": "^1.61.1",
"rollup": "^4.62.2"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@rollup/plugin-replace": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz",
"integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"magic-string": "^0.30.3"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-terser": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz",
"integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"serialize-javascript": "^7.0.3",
"smob": "^1.0.0",
"terser": "^5.17.4"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"rollup": "^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz",
"integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
"integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz",
"integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz",
"integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz",
"integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz",
"integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz",
"integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz",
"integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==",
"cpu": [
"arm"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz",
"integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==",
"cpu": [
"arm"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz",
"integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz",
"integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz",
"integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==",
"cpu": [
"loong64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz",
"integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==",
"cpu": [
"loong64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz",
"integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==",
"cpu": [
"ppc64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz",
"integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==",
"cpu": [
"ppc64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz",
"integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==",
"cpu": [
"riscv64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz",
"integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==",
"cpu": [
"riscv64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz",
"integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==",
"cpu": [
"s390x"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",
"integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",
"integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz",
"integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz",
"integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz",
"integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz",
"integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz",
"integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz",
"integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@types/estree": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
"dev": true,
"license": "MIT"
},
"node_modules/acorn": {
"version": "8.17.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
"integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"license": "MIT"
},
"node_modules/es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
"license": "MIT"
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/hls.js": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.16.tgz",
"integrity": "sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA==",
"license": "Apache-2.0"
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/mpegts.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/mpegts.js/-/mpegts.js-1.8.0.tgz",
"integrity": "sha512-ZtujqtmTjWgcDDkoOnLvrOKUTO/MKgLHM432zGDI8oPaJ0S+ebPxg1nEpDpLw6I7KmV/GZgUIrfbWi3qqEircg==",
"license": "Apache-2.0",
"dependencies": {
"es6-promise": "^4.2.5",
"webworkify-webpack": "github:xqq/webworkify-webpack"
}
},
"node_modules/picomatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/playwright": {
"version": "1.61.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz",
"integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.61.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.61.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz",
"integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/playwright/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/rollup": {
"version": "4.62.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
"integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.9"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.62.2",
"@rollup/rollup-android-arm64": "4.62.2",
"@rollup/rollup-darwin-arm64": "4.62.2",
"@rollup/rollup-darwin-x64": "4.62.2",
"@rollup/rollup-freebsd-arm64": "4.62.2",
"@rollup/rollup-freebsd-x64": "4.62.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.62.2",
"@rollup/rollup-linux-arm-musleabihf": "4.62.2",
"@rollup/rollup-linux-arm64-gnu": "4.62.2",
"@rollup/rollup-linux-arm64-musl": "4.62.2",
"@rollup/rollup-linux-loong64-gnu": "4.62.2",
"@rollup/rollup-linux-loong64-musl": "4.62.2",
"@rollup/rollup-linux-ppc64-gnu": "4.62.2",
"@rollup/rollup-linux-ppc64-musl": "4.62.2",
"@rollup/rollup-linux-riscv64-gnu": "4.62.2",
"@rollup/rollup-linux-riscv64-musl": "4.62.2",
"@rollup/rollup-linux-s390x-gnu": "4.62.2",
"@rollup/rollup-linux-x64-gnu": "4.62.2",
"@rollup/rollup-linux-x64-musl": "4.62.2",
"@rollup/rollup-openbsd-x64": "4.62.2",
"@rollup/rollup-openharmony-arm64": "4.62.2",
"@rollup/rollup-win32-arm64-msvc": "4.62.2",
"@rollup/rollup-win32-ia32-msvc": "4.62.2",
"@rollup/rollup-win32-x64-gnu": "4.62.2",
"@rollup/rollup-win32-x64-msvc": "4.62.2",
"fsevents": "~2.3.2"
}
},
"node_modules/serialize-javascript": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.7.tgz",
"integrity": "sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/smob": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz",
"integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/terser": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.49.0.tgz",
"integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=10"
}
},
"node_modules/webworkify-webpack": {
"version": "2.1.5",
"resolved": "git+ssh://git@github.com/xqq/webworkify-webpack.git#24d1e719b4a6cac37a518b2bb10fe124527ef4ef",
"license": "MIT"
}
}
}
+37
View File
@@ -0,0 +1,37 @@
{
"name": "moeplayer",
"version": "0.1.0",
"description": "一款中文友好的网页视频播放器(fork 自 ckplayer X3),支持 mp4/flv/m3u8/ts,可搭配 Moodle media 插件使用",
"author": "wangyang (https://git.eryang.wang/wangyang)",
"homepage": "https://git.eryang.wang/wangyang/moeplayer",
"repository": {
"type": "git",
"url": "https://git.eryang.wang/wangyang/moeplayer.git"
},
"keywords": [
"video-player",
"html5-video",
"m3u8",
"flv",
"hls",
"moodle",
"播放器"
],
"license": "MIT",
"scripts": {
"build": "rollup -c && node scripts/build-css.js && node scripts/copy-libs.js",
"test": "node scripts/smoke.test.js",
"serve": "node scripts/serve.js 8000",
"sync-moodle": "node scripts/sync-moodle.js"
},
"dependencies": {
"hls.js": "^1.6.10",
"mpegts.js": "^1.8.0"
},
"devDependencies": {
"@rollup/plugin-replace": "^6.0.3",
"@rollup/plugin-terser": "^1.0.0",
"playwright": "^1.61.1",
"rollup": "^4.62.2"
}
}
+60
View File
@@ -0,0 +1,60 @@
/*
* rollup 构建配置
* 功能:把 src/ 下的 ES Module 打包成与旧版等价的 UMD 产物
* 1. moeplayer/js/moeplayer.js —— 未压缩版
* 2. moeplayer/js/moeplayer.min.js —— terser 压缩版(保留版权注释)
* 运行时依赖(hls.js/flv.js/mpegts.js)的拷贝由 scripts/copy-libs.js 完成
* 用法:npm run build
*/
import terser from '@rollup/plugin-terser';
import replace from '@rollup/plugin-replace';
import { readFileSync } from 'fs';
// 版本号从 package.json 注入,源码中用 __MOE_VERSION__ 占位
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
// 产物头部版权注释(与 src/index.js 顶部一致)
const banner = `/*
* 软件名称:MoePlayer
* 版本:X3
* 版权:MoePlayer 项目(fork 自 ckplayer, www.ckplayer.com)
* 开源协议:MIT
*/`;
const outputBase = {
format: 'umd',
name: 'MoePlayer',
// 仅导出 moeplayerEmbed 函数本身,与旧 UMD 的 module.exports = factory() 行为一致
exports: 'default',
banner
};
export default {
input: 'src/index.js',
// 关闭 tree-shaking:完整保留所有工具函数(含暂未被引用的),与旧单文件内容一致
treeshake: false,
plugins: [
replace({
preventAssignment: true,
values: { __MOE_VERSION__: JSON.stringify(pkg.version) }
})
],
output: [
{
...outputBase,
file: 'moeplayer/js/moeplayer.js'
},
{
...outputBase,
file: 'moeplayer/js/moeplayer.min.js',
plugins: [
terser({
format: {
// 保留文件头部的版权注释
comments: /^!|软件名称|版权/
}
})
]
}
]
};
+48
View File
@@ -0,0 +1,48 @@
/*
* 构建脚本(第二步)
* 功能:把 src/css/base.css 与各主题(src/css/themes/*.css)拼接为自包含的皮肤产物
* 输出到 moeplayer/css/(moeplayer.css / moeplayer.red.css / moeplayer.ixigua.css)
* 用户只需引用一个文件,无需 @import;拼接时自动加上版权注释头
* 第一步(rollup 打包压缩)见 rollup.config.js,第三步(拷贝运行时依赖)见 scripts/copy-libs.js
* 用法:npm run build(即 rollup -c && node scripts/build-css.js && node scripts/copy-libs.js)
*/
const fs = require('fs');
const path = require('path');
const root = path.resolve(__dirname, '..');
const cssSrcDir = path.join(root, 'src', 'css');
const cssOutDir = path.join(root, 'moeplayer', 'css');
// 版权注释头(与 rollup 产物头部保持一致)
const header = '/*\n' +
' * 软件名称:MoePlayer\n' +
' * 版本:X3\n' +
' * 版权:MoePlayer 项目(fork 自 ckplayer, www.ckplayer.com)\n' +
' * 开源协议:MIT\n' +
' * 本文件由 npm run build 自动生成(src/css/base.css + 主题),请勿手动修改\n' +
' */\n';
// 皮肤产物清单:[主题文件名, 输出文件名, 皮肤说明]
const themes = [
['default.css', 'moeplayer.css', '默认(深色控制条 + 蓝色 accent)'],
['red.css', 'moeplayer.red.css', '红色(深色控制条 + 红色 accent)'],
['ixigua.css', 'moeplayer.ixigua.css', '西瓜(浅色控制条 + 深色图标 + 红色 accent)']
];
function buildCss() {
const base = fs.readFileSync(path.join(cssSrcDir, 'base.css'), 'utf8');
for (const [themeFile, outFile, desc] of themes) {
const theme = fs.readFileSync(path.join(cssSrcDir, 'themes', themeFile), 'utf8');
const out = header + base + '\n' + theme;
fs.writeFileSync(path.join(cssOutDir, outFile), out);
console.log('生成皮肤: moeplayer/css/' + outFile + ' (' + desc + ')');
}
}
try {
buildCss();
console.log('皮肤构建完成');
} catch (err) {
console.error('皮肤构建失败:', err);
process.exit(1);
}
+44
View File
@@ -0,0 +1,44 @@
/*
* 构建脚本(第二步)
* 功能:从 node_modules 拷贝运行时依赖(hls.js/mpegts.js)到 moeplayer/ 对应目录
* 播放器运行时会按需动态加载这些文件,目录结构需与 getPath() 的约定保持一致
* 第一步(rollup 打包压缩)见 rollup.config.js
* 用法:npm run build(即 rollup -c && node scripts/copy-libs.js)
*/
const fs = require('fs');
const path = require('path');
const root = path.resolve(__dirname, '..');
// 运行时依赖拷贝清单:[npm包名, 产物目录, 需要拷贝的文件]
const libs = [
['hls.js', 'hls.js', ['hls.min.js', 'hls.min.js.map', 'LICENSE']],
['mpegts.js', 'mpegts.js', ['mpegts.js', 'mpegts.js.map', 'LICENSE']]
];
function copyLibs() {
for (const [pkg, dir, files] of libs) {
const pkgRoot = path.dirname(require.resolve(pkg + '/package.json'));
const targetDir = path.join(root, 'moeplayer', dir);
// 清空旧的 vendored 文件,只保留构建拷贝的产物
fs.rmSync(targetDir, { recursive: true, force: true });
fs.mkdirSync(targetDir, { recursive: true });
for (const file of files) {
const from = path.join(pkgRoot, 'dist', file);
// LICENSE 在包根目录而非 dist 目录
const fromAlt = path.join(pkgRoot, file);
const src = fs.existsSync(from) ? from : fromAlt;
fs.copyFileSync(src, path.join(targetDir, file));
}
const version = require(pkg + '/package.json').version;
console.log('拷贝完成: moeplayer/' + dir + '/ (' + pkg + '@' + version + ')');
}
}
try {
copyLibs();
console.log('依赖拷贝完成');
} catch (err) {
console.error('拷贝失败:', err);
process.exit(1);
}
+43
View File
@@ -0,0 +1,43 @@
/*
* 支持 Range 请求的静态文件服务器(测试用)
* python3 -m http.server 不支持 Range,会导致 mp4 无法 seek,故用此替代
* 用法:node scripts/serve.js [端口]
*/
const http = require('http');
const fs = require('fs');
const path = require('path');
const root = path.resolve(__dirname, '..');
const port = parseInt(process.argv[2] || '8931', 10);
const mime = {
'.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css',
'.png': 'image/png', '.mp4': 'video/mp4', '.json': 'application/json',
'.map': 'application/json', '.ico': 'image/x-icon'
};
http.createServer((req, res) => {
const urlPath = decodeURIComponent(req.url.split('?')[0]);
let file = path.join(root, urlPath === '/' ? 'index.html' : urlPath);
if (!file.startsWith(root)) { res.writeHead(403); res.end(); return; }
fs.stat(file, (err, stat) => {
if (err || !stat.isFile()) { res.writeHead(404); res.end('404'); return; }
const type = mime[path.extname(file).toLowerCase()] || 'application/octet-stream';
const range = req.headers.range;
if (range) {
const m = /bytes=(\d*)-(\d*)/.exec(range);
let start = m && m[1] ? parseInt(m[1], 10) : 0;
let end = m && m[2] ? parseInt(m[2], 10) : stat.size - 1;
end = Math.min(end, stat.size - 1);
res.writeHead(206, {
'Content-Type': type,
'Content-Range': `bytes ${start}-${end}/${stat.size}`,
'Accept-Ranges': 'bytes',
'Content-Length': end - start + 1
});
fs.createReadStream(file, { start, end }).pipe(res);
} else {
res.writeHead(200, { 'Content-Type': type, 'Accept-Ranges': 'bytes', 'Content-Length': stat.size });
fs.createReadStream(file).pipe(res);
}
});
}).listen(port, () => console.log(`静态服务器(支持 Range): http://localhost:${port}`));
+81
View File
@@ -0,0 +1,81 @@
/*
* 冒烟测试:真实浏览器加载演示页,验证核心功能
* 覆盖:播放器初始化、播放/暂停、seek 跳转、点击进度条、皮肤文件加载
* 用法:npm test(需要先 npm run build)
*/
const { chromium } = require('playwright');
const { spawn } = require('child_process');
const path = require('path');
const PORT = 8931;
let failed = 0;
function check(name, cond, detail) {
if (cond) {
console.log(' ✓ ' + name);
} else {
failed++;
console.error(' ✗ ' + name + (detail ? ' —— ' + detail : ''));
}
}
(async () => {
const server = spawn('node', [path.join(__dirname, 'serve.js'), String(PORT)], { stdio: 'ignore' });
await new Promise(r => setTimeout(r, 800));
const browser = await chromium.launch();
const page = await browser.newPage();
const errors = [];
page.on('pageerror', e => errors.push(e.message));
await page.goto(`http://localhost:${PORT}/index.html`);
await page.waitForTimeout(2500);
const video = () => page.evaluate(() => {
const v = document.querySelector('video');
return v ? { duration: v.duration, currentTime: v.currentTime, paused: v.paused } : null;
});
console.log('初始化');
check('全局 MoePlayer 存在', await page.evaluate(() => typeof window.MoePlayer === 'function'));
const v0 = await video();
check('video 元素已创建', !!v0);
check('元数据已加载(duration>0)', v0 && v0.duration > 0, JSON.stringify(v0));
console.log('播放控制');
await page.evaluate(() => player.play());
await page.waitForTimeout(1500);
const v1 = await video();
check('播放中', v1 && !v1.paused && v1.currentTime > 0.5, JSON.stringify(v1));
await page.evaluate(() => player.pause());
await page.waitForTimeout(300);
check('暂停', (await video()).paused);
console.log('seek');
await page.evaluate(() => player.seek(20));
await page.waitForTimeout(800);
const v2 = await video();
check('seek(20) 生效', v2.currentTime >= 19 && v2.currentTime <= 22, 'currentTime=' + v2.currentTime);
console.log('进度条点击');
const rect = await page.evaluate(() => {
const r = document.querySelector('.moe-bar-progress-bg').getBoundingClientRect();
return { x: r.x, y: r.y, w: r.width, h: r.height };
});
await page.mouse.click(rect.x + rect.w * 0.7, rect.y + rect.h / 2);
await page.waitForTimeout(800);
const v3 = await video();
const expect3 = v0.duration * 0.7;
check('点击 70% 位置跳转', Math.abs(v3.currentTime - expect3) < 2.5, `currentTime=${v3.currentTime},期望≈${expect3}`);
console.log('皮肤');
for (const css of ['moeplayer.css', 'moeplayer.red.css', 'moeplayer.ixigua.css']) {
const resp = await page.request.get(`http://localhost:${PORT}/moeplayer/css/${css}`);
check(css + ' 可加载', resp.status() === 200);
}
check('无页面 JS 错误', errors.length === 0, errors.join(' | '));
await browser.close();
server.kill();
console.log(failed === 0 ? '\n全部通过' : `\n${failed} 项失败`);
process.exit(failed === 0 ? 0 : 1);
})().catch(e => { console.error(e); process.exit(1); });
+21
View File
@@ -0,0 +1,21 @@
/*
* 同步播放器资源到 Moodle 插件仓库
* 把构建产物 moeplayer/ 整个拷贝到 ../moodle-media_moeplayer/moeplayer/
* 用法:npm run build 之后执行 node scripts/sync-moodle.js
*/
const fs = require('fs');
const path = require('path');
const src = path.resolve(__dirname, '..', 'moeplayer');
const dest = path.resolve(__dirname, '..', '..', 'moodle-media_moeplayer', 'moeplayer');
if (!fs.existsSync(path.dirname(dest))) {
console.error('未找到插件仓库目录: ' + path.dirname(dest));
console.error('请确认 moodle-media_moeplayer 与本仓库同级。');
process.exit(1);
}
fs.rmSync(dest, { recursive: true, force: true });
fs.cpSync(src, dest, { recursive: true });
console.log('已同步: ' + src + ' -> ' + dest);
console.log('提示:别忘了在插件仓库提交并视情况递增 version.php 的 $plugin->version');
File diff suppressed because it is too large Load Diff
+1561
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
/*
* MoePlayer 主题:默认(default)
* 深色渐变控制条 + 蓝色 accent(#00a1d6)
* 只覆盖 .moeplayer 作用域上的 --moe-* 变量(结构样式见 base.css)
* (右键菜单 .moeplayer-menu、错误框 .moeplayer-error 挂载在 body 下,需一并覆盖)
*/
.moeplayer, .moeplayer-menu, .moeplayer-error{
--moe-accent: #00a1d6;
--moe-icon-color: rgba(255,255,255,.92);
--moe-icon-hover: #00a1d6;
--moe-text-color: #FFF;
--moe-text-hover: #00a1d6;
--moe-bar-bg: linear-gradient(to top, rgba(0,0,0,.7), rgba(0,0,0,0));
--moe-top-bar-bg: linear-gradient(to bottom, rgba(0,0,0,.5), rgba(0,0,0,0));
--moe-top-bar-text: #FFF;
--moe-right-bar-bg: rgba(0,0,0,.35);
--moe-progress-bg: rgba(255,255,255,.25);
--moe-progress-buffer: rgba(255,255,255,.45);
--moe-volume-bg: rgba(255,255,255,.25);
--moe-volume-panel-bg: rgba(0,0,0,.85);
--moe-popup-bg: rgba(0,0,0,.85);
--moe-popup-text: #FFF;
--moe-menu-bg: rgba(0,0,0,.85);
--moe-menu-text: rgba(255,255,255,.75);
--moe-menu-link: #FFF;
--moe-menu-border: rgba(255,255,255,.08);
}
+102
View File
@@ -0,0 +1,102 @@
/*
* MoePlayer 主题:西瓜(ixigua)
* 浅色/白色控制条 + 深色图标 + 红色 accent(#e80101,沿用旧西瓜皮肤配色)
* 只覆盖 .moeplayer 作用域上的 --moe-* 变量(结构样式见 base.css)
* (右键菜单 .moeplayer-menu、错误框 .moeplayer-error 挂载在 body 下,需一并覆盖)
*/
.moeplayer, .moeplayer-menu, .moeplayer-error{
--moe-accent: #e80101;
--moe-icon-color: rgba(0,0,0,.72);
--moe-icon-hover: #e80101;
--moe-text-color: rgba(0,0,0,.85);
--moe-text-hover: #e80101;
--moe-bar-bg: linear-gradient(to top, rgba(255,255,255,.95), rgba(255,255,255,.55) 55%, rgba(255,255,255,0));
--moe-top-bar-bg: linear-gradient(to bottom, rgba(0,0,0,.35), rgba(0,0,0,0));
--moe-top-bar-text: #FFF;
--moe-right-bar-bg: rgba(255,255,255,.9);
--moe-progress-bg: rgba(0,0,0,.15);
--moe-progress-buffer: rgba(0,0,0,.3);
--moe-volume-bg: rgba(0,0,0,.15);
--moe-volume-panel-bg: rgba(255,255,255,.96);
--moe-popup-bg: rgba(255,255,255,.96);
--moe-popup-text: rgba(0,0,0,.85);
--moe-popup-shadow: 0 4px 16px rgba(0,0,0,.18);
--moe-menu-bg: rgba(255,255,255,.96);
--moe-menu-text: rgba(0,0,0,.6);
--moe-menu-link: rgba(0,0,0,.85);
--moe-menu-border: rgba(0,0,0,.08);
}
/*西瓜皮肤特色:全屏/网页全屏/剧场按钮 hover 脉冲动画(沿用旧西瓜皮肤的动效)*/
.moeplayer .moe-main .moe-bar .moe-bar-fullandexit .moe-bar-btn:hover,
.moeplayer .moe-main .moe-bar .moe-bar-webfullandexit .moe-bar-btn:hover{
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
-webkit-animation-duration: .5s;
animation-duration: .5s;
-webkit-animation-name: moe-button-hover;
animation-name: moe-button-hover;
}
.moeplayer .moe-main .moe-bar .moe-bar-theatreandexit .moe-bar-btn:hover{
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
-webkit-animation-duration: .5s;
animation-duration: .5s;
-webkit-animation-name: moe-button-hover-x;
animation-name: moe-button-hover-x;
}
@-webkit-keyframes moe-button-hover {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(1.15);
transform: scale(1.15);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
@keyframes moe-button-hover {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(1.15);
transform: scale(1.15);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
@-webkit-keyframes moe-button-hover-x {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(1.15,1);
transform: scale(1.15,1);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
@keyframes moe-button-hover-x {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(1.15,1);
transform: scale(1.15,1);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
+27
View File
@@ -0,0 +1,27 @@
/*
* MoePlayer 主题:红色(red)
* 深色渐变控制条 + 红色 accent(#e80101)
* 只覆盖 .moeplayer 作用域上的 --moe-* 变量(结构样式见 base.css)
* (右键菜单 .moeplayer-menu、错误框 .moeplayer-error 挂载在 body 下,需一并覆盖)
*/
.moeplayer, .moeplayer-menu, .moeplayer-error{
--moe-accent: #e80101;
--moe-icon-color: rgba(255,255,255,.92);
--moe-icon-hover: #e80101;
--moe-text-color: #FFF;
--moe-text-hover: #e80101;
--moe-bar-bg: linear-gradient(to top, rgba(0,0,0,.7), rgba(0,0,0,0));
--moe-top-bar-bg: linear-gradient(to bottom, rgba(0,0,0,.5), rgba(0,0,0,0));
--moe-top-bar-text: #FFF;
--moe-right-bar-bg: rgba(0,0,0,.35);
--moe-progress-bg: rgba(255,255,255,.25);
--moe-progress-buffer: rgba(255,255,255,.45);
--moe-volume-bg: rgba(255,255,255,.25);
--moe-volume-panel-bg: rgba(0,0,0,.85);
--moe-popup-bg: rgba(0,0,0,.85);
--moe-popup-text: #FFF;
--moe-menu-bg: rgba(0,0,0,.85);
--moe-menu-text: rgba(255,255,255,.75);
--moe-menu-link: #FFF;
--moe-menu-border: rgba(255,255,255,.08);
}
+62
View File
@@ -0,0 +1,62 @@
/*
* videoObjectDefault
* 功能:静态变量,默认播放配置,当外部传递过来的配置有未包含在varsDefault里的,则使用varsDefault里的配置
*/
var videoObjectDefault= {
container: '',//视频容器的ID
volume: 0.8,//默认音量,范围0-1
poster: '',//封面图片地址
autoplay: false,//是否自动播放
loop: false,//是否需要循环播放
live: false,//是否是直播
rotate:0,//视频旋转角度
zoom:0,//默认缩放比例
ad:null,//广告
backLive:false,//显示返回直播按钮
seek: 0,//默认需要跳转的秒数
next: null,//下一集按钮动作
loaded: '',//加载播放器后调用的函数
plug: '',//使用插件
duration:0,//视频总时间
preview: null,//预览图片对象
prompt: null,//提示点功能
crossOrigin:'',//跨域请求字符
video: null,//视频地址
type:'',//视频类型
playbackrate: 1,//默认倍速
ended:null,//结束显示的内容
webFull:false,//页面全屏按钮事件
theatre:null,//剧场模式
controls:false,//是否显示自带控制栏
rightBar:null,//是否开启右边控制栏
smallWindows:null,//是否启用小窗口模式
smallWindowsDrag:true,//当处于小窗口模式时是否可拖动播放器
screenshot:false,//截图功能是否开启
timeScheduleAdjust:1,//是否可调节播放进度,0不启用,1是启用,2是只能前进(向右拖动),3是只能后退,4是只能前进但能回到第一次拖动时的位置,5是看过的地方可以随意拖动
logo:'',//logo
menu:null,//右键菜单
information:{//关于
'Load:':'{loadTime} second',
'Duration:':'{duration} second',
'Size:':'{videoWidth}x{videoHeight}',
'Volume:':'{volume}%',
'Fps:':'{fps}fps',
'Sudio decoded:':'{audioDecodedByteCount} Byte',
'Video decoded:':'{videoDecodedByteCount} Byte'
},
track:null,//字幕
title:'',//视频标题
language:'',//语言包文件
barHideTime:1500,//控制栏隐藏时间
playbackrateOpen:true,//是否开启控制栏倍速选项
playbackrateList:[0.75,1,1.25,1.5,2,4],//倍速配置值
cookie:null,//开启cookie功能
domain:null,//指定cookie保存的域
cookiePath:'/',//指定cookie保存路径
documentFocusPause:false,//窗口失去焦点后暂停播放
mouseWheelVolume:2,//是否启用鼠标滚轮调节音量功能,0=不启用,1=启用,2=全屏时才启用
keyVolume:2,//是否启用键盘控制音量调节,0=不启用,1=启用,2=全屏时才启用
errorShow:true//是否显示错误信息
};
export { videoObjectDefault };
+9
View File
@@ -0,0 +1,9 @@
/*
* 软件名称:MoePlayer
* 版本:X3
* 版权:MoePlayer 项目(fork 自 ckplayer, www.ckplayer.com)
* 开源协议:MIT
*/
import moeplayerEmbed from './core/moeplayerEmbed.js';
export default moeplayerEmbed;
+69
View File
@@ -0,0 +1,69 @@
/*
* language
* 功能:静态变量,包含播放器用到的默认语言包
* 引入其它语言js后该变量将被替换成新引入的语言包
*/
var language={
play:'%E6%92%AD%E6%94%BE',
pause:'%E6%9A%82%E5%81%9C',
refresh:'%E9%87%8D%E6%92%AD',
full:'%E5%85%A8%E5%B1%8F',
exitFull:'%E9%80%80%E5%87%BA%E5%85%A8%E5%B1%8F',
webFull:'%E9%A1%B5%E9%9D%A2%E5%85%A8%E5%B1%8F',
exitWebFull:'%E9%80%80%E5%87%BA%E9%A1%B5%E9%9D%A2%E5%85%A8%E5%B1%8F',
theatre:'%E5%89%A7%E5%9C%BA%E6%A8%A1%E5%BC%8F',
exitTheatre:'%E9%80%80%E5%87%BA%E5%89%A7%E5%9C%BA%E6%A8%A1%E5%BC%8F',
volume:'%E9%9F%B3%E9%87%8F%EF%BC%9A',
muted:'%E9%9D%99%E9%9F%B3',
exitmuted:'%E6%81%A2%E5%A4%8D%E9%9F%B3%E9%87%8F',
seek:'seek%EF%BC%9A',
waiting:'%E7%BC%93%E5%86%B2',
live:'%E7%9B%B4%E6%92%AD%E4%B8%AD',
backLive:'%E8%BF%94%E5%9B%9E%E7%9B%B4%E6%92%AD',
lookBack:'%E5%9B%9E%E7%9C%8B%EF%BC%9A',
next:'%E4%B8%8B%E4%B8%80%E9%9B%86',
screenshot:'%E8%A7%86%E9%A2%91%E6%88%AA%E5%9B%BE',
smallwindows:'%E5%B0%8F%E7%AA%97%E5%8F%A3%E6%92%AD%E6%94%BE%E5%8A%9F%E8%83%BD',
playbackrate:'%E5%80%8D%E9%80%9F',
playbackrateSuffix:'%E5%80%8D',
track:'%E5%AD%97%E5%B9%95',
noTrack:'%E6%97%A0%E5%AD%97%E5%B9%95',
definition:'%E6%B8%85%E6%99%B0%E5%BA%A6',
switchTo:'%E5%88%87%E6%8D%A2%E6%88%90%EF%BC%9A',
closeTime:'%7Bseconds%7D%E7%A7%92%E5%90%8E%E5%8F%AF%E5%85%B3%E9%97%AD%E5%B9%BF%E5%91%8A',
closeAd:'%E5%85%B3%E9%97%AD%E5%B9%BF%E5%91%8A',
second:'%E7%A7%92',
details:'%E6%9F%A5%E7%9C%8B%E8%AF%A6%E6%83%85',
copy:'%E5%A4%8D%E5%88%B6',
copySucceeded:'%E5%A4%8D%E5%88%B6%E6%88%90%E5%8A%9F%EF%BC%8C%E5%8F%AF%E8%B4%B4%E7%B2%98%EF%BC%81',
smallwindowsOpen:'%E5%B0%8F%E7%AA%97%E5%8F%A3%E5%8A%9F%E8%83%BD%E5%B7%B2%E5%BC%80%E5%90%AF',
smallwindowsClose:'%E5%B0%8F%E7%AA%97%E5%8F%A3%E5%8A%9F%E8%83%BD%E5%B7%B2%E5%85%B3%E9%97%AD',
screenshotStart:'%E6%88%AA%E5%9B%BE%E4%B8%AD%EF%BC%8C%E8%AF%B7%E7%A8%8D%E5%80%99...',
screenshotClose:'%E6%88%AA%E5%9B%BE%E5%8A%9F%E8%83%BD%E5%B7%B2%E5%85%B3%E9%97%AD',
loopOpen:'%E5%BE%AA%E7%8E%AF%E6%92%AD%E6%94%BE',
loopClose:'%E5%B7%B2%E5%85%B3%E9%97%AD%E5%BE%AA%E7%8E%AF%E6%92%AD%E6%94%BE',
close:'%E5%85%B3%E9%97%AD',
down:'%E4%B8%8B%E8%BD%BD',
p50:'50%25',
p75:'75%25',
p100:'100%25',
timeScheduleAdjust:{
prohibit:'%E8%A7%86%E9%A2%91%E7%A6%81%E6%AD%A2%E6%8B%96%E5%8A%A8',
prohibitBackOff:'%E8%A7%86%E9%A2%91%E7%A6%81%E6%AD%A2%E9%87%8D%E5%A4%8D%E8%A7%82%E7%9C%8B',
prohibitForward:'%E8%A7%86%E9%A2%91%E7%A6%81%E6%AD%A2%E5%BF%AB%E8%BF%9B',
prohibitLookBack:'%E8%A7%86%E9%A2%91%E7%A6%81%E6%AD%A2%E6%92%AD%E6%94%BE%E9%83%A8%E5%88%86%E5%86%85%E5%AE%B9',
prohibitForwardNotViewed:'%E8%A7%86%E9%A2%91%E7%A6%81%E6%AD%A2%E6%92%AD%E6%94%BE%E6%9C%AA%E8%A7%82%E7%9C%8B%E7%9A%84%E9%83%A8%E5%88%86'
},
error:{
noMessage:'%E6%9C%AA%E7%9F%A5%E9%94%99%E8%AF%AF',
supportVideoError:'%E8%AF%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E7%89%88%E6%9C%AC%E5%A4%AA%E4%BD%8E%EF%BC%8C%E5%BB%BA%E8%AE%AE%E6%9B%B4%E6%8D%A2%E6%88%90%E5%85%B6%E5%AE%83%E6%B5%8F%E8%A7%88%E5%99%A8',
videoTypeError:'%E8%AF%A5%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8D%E6%94%AF%E6%8C%81%E6%92%AD%E6%94%BE%E8%AF%A5%E8%A7%86%E9%A2%91%EF%BC%8C%E5%BB%BA%E8%AE%AE%E6%9B%B4%E6%8D%A2%E6%88%90%E5%85%B6%E5%AE%83%E6%B5%8F%E8%A7%88%E5%99%A8',
loadingFailed:'%E5%8A%A0%E8%BD%BD%E5%A4%B1%E8%B4%A5',
emptied:'%E8%A7%86%E9%A2%91%E6%96%87%E4%BB%B6%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B%E4%B8%AD%E5%87%BA%E7%8E%B0%E9%94%99%E8%AF%AF',
screenshot:'%E8%A7%86%E9%A2%91%E6%88%AA%E5%9B%BE%E5%A4%B1%E8%B4%A5',
ajax:'Ajax%E6%95%B0%E6%8D%AE%E8%AF%B7%E6%B1%82%E9%94%99%E8%AF%AF',
noVideoContainer:'%E6%9C%AA%E6%89%BE%E5%88%B0%E6%94%BE%E7%BD%AE%E8%A7%86%E9%A2%91%E7%9A%84%E5%AE%B9%E5%99%A8'
}
};
export { language };
+64
View File
@@ -0,0 +1,64 @@
import { isUndefined } from './type.js';
/*
* cookie
* 功能,操作cookie
*/
function cookie(name,value,domain,path){
if(isUndefined(domain)){
domain='';
}
if(isUndefined(path)){
path='/';
}
var ckStr=';domain='+domain+';path='+path;
if(location.protocol=='https'){
ckStr+=';SameSite=None;Secure=true';
}
var set=function(name,value){
var time = 360*24*60*60*1000;
var exp = new Date();
exp.setTime(exp.getTime() + time);
try{
document.cookie = name + '='+ escape (value) + ';expires=' + exp.toGMTString()+ckStr;
}
catch(event){console.error(event)}
},
get=function(name){
var arr,reg=new RegExp('(^| )'+name+'=([^;]*)(;|$)');
if(arr=document.cookie.match(reg)){
return unescape(arr[2]);
}
else{
return null;
}
},
del=function(name){
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval=get(name);
if(cval!=null){
document.cookie= name + '='+cval+';expires='+exp.toGMTString()+ckStr;
}
};
if(!isUndefined(name) && !isUndefined(value)){
if(value=='delete'){
del(name);
return null;
}
else{
set(name,value);
return get(name);
}
}
else if(!isUndefined(name) && isUndefined(value)){
return get(name);
}
else if(!isUndefined(name)){
}
}
export { cookie };
+220
View File
@@ -0,0 +1,220 @@
import { valType, isUndefined } from './type.js';
/*
* documentReady
* 功能:判断页面加载完成
* @fn:页面加载完成后执行的函数
*/
function documentReady(fn) {
var isReady = false;
var timer = null;
var doReady = function() {
if(timer) clearInterval(timer);
if(isReady) return;
isReady = true;
if(valType(fn) == 'function') {
fn();
}
};
var bodyLoad = function() {
timer = setInterval(function() {
try {
if(!isUndefined(document.readyState) && document.readyState == 'complete') {
doReady();
}
} catch(event) {};
}, 10);
};
var w3c = function() {
if(valType(fn) == 'function') {
fn();
}
removeListener(window, 'load', w3c);
};
if(!isUndefined(document.body)){
if(valType(fn) == 'function') {
fn();
}
}
else{
try {
addListener(window, 'load', w3c);
} catch(event) {
bodyLoad();
}
}
}
/*
* checkJs
* 功能:判断js是否已加载
* @file:js文件路径
*/
function checkJs(file) {
var scriptList = document.scripts;
for (var i = 0; i < scriptList.length; i++) {
if(scriptList[i].src==file){
return scriptList[i];
}
}
return false;
}
/*
* getMaxZIndex
* 功能:获取当前页面最大深度
*/
function getMaxZIndex() {
var arr = document.all || document.querySelectorAll('*');
var maxZ = -1;
for (var i = 0; i < arr.length; i++) {
var temp = null;
try {
temp = window.getComputedStyle(arr[i], null).zIndex;
} catch (event) {
if (arr[i].style) {
temp = arr[i].style.zIndex;
}
}
if (temp != 'auto' && parseInt(temp) > maxZ) {
maxZ = parseInt(temp);
}
}
return maxZ;
}
/*
* getWindowSize
* 功能:获取window的宽和高
*/
function getWindowSize() {
return {
width: window.innerWidth,
height: window.innerHeight
};
}
/*
* prev
* 功能:获取指定节点前一个同胞节点
* @ele:要获取的节点
*/
function prev(ele) {
var e = ele.previousSibling;
if (e == null) { //测试同胞节点是否存在,否则返回空
return null;
}
if (e.nodeType == 3) { //如果同胞节点为文本节点
var t = prev(e);
if (t && t.nodeType == 1) {
return t;
}
} else {
if (e.nodeType == 1) { //确认节点为节点节点才返回
return e;
} else {
return false;
}
}
}
/*
* next
* 功能:获取指定节点下一个同胞节点
* @ele:要获取的节点
*/
function next(ele) {
var e = ele.nextSibling;
if (e == null) { //测试同胞节点是否存在,否则返回空
return null;
}
if (e.nodeType == 3) { //如果同胞节点为文本节点
var t = next(e);
if (t && t.nodeType == 1) {
return t;
}
} else {
if (e.nodeType == 1) { //确认节点为节点才返回
return e;
} else {
return false;
}
}
}
/*
* addListener
* 功能:事件监听
* @ele:监听对象,@e:事件名称,@f:返回事件函数
*/
function addListener(ele, e, f, t) {
if (isUndefined(t)) {
t = false;
}
if (ele.addEventListener) {
try {
ele.addEventListener(e, f, t);
} catch (event) {}
} else if (ele.attachEvent) {
try {
ele.attachEvent('on' + e, f);
} catch (event) {}
} else {
ele['on' + e] = f;
}
if(isUndefined(ele.listenerList)){
ele.listenerList=[];
}
ele.listenerList.push([e,f]);
return ele;
};
/*
* removeListener
* 功能:删除事件监听
* @ele:监听对象,@e:事件名称,@f:返回事件函数
*/
function removeListener(ele, e, f, t) {
if (isUndefined(t)) {
t = false;
}
if (ele.removeEventListener) {
try {
ele.removeEventListener(e, f, t);
} catch (e) {}
} else if (ele.detachEvent) {
try {
ele.detachEvent('on' + e, f);
} catch (e) {}
} else {
ele['on' + e] = null;
}
if(!isUndefined(ele.listenerList)){
var temp=[];
for(var i=0;i<ele.listenerList.length;i++){
if(ele.listenerList[i][0]!=e || ele.listenerList[i][1]!=f){
temp.push(ele.listenerList[i]);
}
}
ele.listenerList=temp;
}
return ele;
}
/*
* documentHidden
* 功能,监听页面切换到其它标签,执行fn函数
*/
function documentHidden(fn){
if(!isUndefined(document.visibilityState)){
fn(document.visibilityState === 'visible'?'show':'hidden');
addListener(document,'visibilitychange',function(){
fn(document.visibilityState === 'visible'?'show':'hidden');
});
}
}
export { prev, next, addListener, removeListener, getMaxZIndex, getWindowSize, documentReady, documentHidden, checkJs };
+260
View File
@@ -0,0 +1,260 @@
import { isUndefined, valType } from './type.js';
/*
* formatSeconds
* 功能:將秒转化成时分秒
*/
function formatSeconds(val) {
if(isUndefined(val)) val=0;
var result = parseInt(val);
if(isUndefined(result)) result=0;
if(result<0) result=0;
var h = Math.floor(result / 3600) < 10 ? '0' + Math.floor(result / 3600) : Math.floor(result / 3600),
m = Math.floor((result / 60 % 60)) < 10 ? '0' + Math.floor((result / 60 % 60)) : Math.floor((result / 60 % 60)),
s = Math.floor((result % 60)) < 10 ? '0' + Math.floor((result % 60)) : Math.floor((result % 60)),
res = '';
if(h !== '00') res += h+':';
res += m+':'+s;
return res;
}
/*
* date
* 功能:格式化的时间字符串
*/
function date(format, timestamp) {
if(isUndefined(timestamp)){
timestamp=new Date();
}
if(timestamp.toString().length<13) timestamp=timestamp*1000;
var a, jsdate = ((timestamp) ? new Date(timestamp) : new Date());
var pad = function(n, c) {
if((n = n + '').length < c) {
return new Array(++c - n.length).join('0') + n;
} else {
return n;
}
};
var txt_weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var txt_ordin = {
1: 'st',
2: 'nd',
3: 'rd',
21: 'st',
22: 'nd',
23: 'rd',
31: 'st'
};
var txt_months = ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
var f = {
// Day
d: function() {
return pad(f.j(), 2)
},
D: function() {
return f.l().substr(0, 3)
},
j: function() {
return jsdate.getDate()
},
l: function() {
return txt_weekdays[f.w()]
},
N: function() {
return f.w() + 1
},
S: function() {
return txt_ordin[f.j()] ? txt_ordin[f.j()] : 'th'
},
w: function() {
return jsdate.getDay()
},
z: function() {
return(jsdate - new Date(jsdate.getFullYear() + '/1/1')) / 864e5 >> 0
},
// Week
W: function() {
var a = f.z(),
b = 364 + f.L() - a;
var nd2, nd = (new Date(jsdate.getFullYear() + '/1/1').getDay() || 7) - 1;
if(b <= 2 && ((jsdate.getDay() || 7) - 1) <= 2 - b) {
return 1;
} else {
if(a <= 2 && nd >= 4 && a >= (6 - nd)) {
nd2 = new Date(jsdate.getFullYear() - 1 + '/12/31');
return date('W', Math.round(nd2.getTime() / 1000));
} else {
return(1 + (nd <= 3 ? ((a + nd) / 7) : (a - (7 - nd)) / 7) >> 0);
}
}
},
// Month
F: function() {
return txt_months[f.n()]
},
m: function() {
return pad(f.n(), 2)
},
M: function() {
return f.F().substr(0, 3)
},
n: function() {
return jsdate.getMonth() + 1
},
t: function() {
var n;
if((n = jsdate.getMonth() + 1) == 2) {
return 28 + f.L();
} else {
if(n & 1 && n < 8 || !(n & 1) && n > 7) {
return 31;
} else {
return 30;
}
}
},
// Year
L: function() {
var y = f.Y();
return(!(y & 3) && (y % 1e2 || !(y % 4e2))) ? 1 : 0
},
//o not supported yet
Y: function() {
return jsdate.getFullYear()
},
y: function() {
return(jsdate.getFullYear() + '').slice(2)
},
// Time
a: function() {
return jsdate.getHours() > 11 ? 'pm' : 'am'
},
A: function() {
return f.a().toUpperCase()
},
B: function() {
// peter paul koch:
var off = (jsdate.getTimezoneOffset() + 60) * 60;
var theSeconds = (jsdate.getHours() * 3600) + (jsdate.getMinutes() * 60) + jsdate.getSeconds() + off;
var beat = Math.floor(theSeconds / 86.4);
if(beat > 1000) beat -= 1000;
if(beat < 0) beat += 1000;
if((String(beat)).length == 1) beat = '00' + beat;
if((String(beat)).length == 2) beat = '0' + beat;
return beat;
},
g: function() {
return jsdate.getHours() % 12 || 12
},
G: function() {
return jsdate.getHours()
},
h: function() {
return pad(f.g(), 2)
},
H: function() {
return pad(jsdate.getHours(), 2)
},
i: function() {
return pad(jsdate.getMinutes(), 2)
},
s: function() {
return pad(jsdate.getSeconds(), 2)
},
//u not supported yet
// Timezone
//e not supported yet
//I not supported yet
O: function() {
var t = pad(Math.abs(jsdate.getTimezoneOffset() / 60 * 100), 4);
if(jsdate.getTimezoneOffset() > 0) t = '-' + t;
else t = '+' + t;
return t;
},
P: function() {
var O = f.O();
return(O.substr(0, 3) + ':' + O.substr(3, 2))
},
//T not supported yet
//Z not supported yet
// Full Date/Time
c: function() {
return f.Y() + '-' + f.m() + '-' + f.d() + 'T' + f.h() + ':' + f.i() + ':' + f.s() + f.P()
},
//r not supported yet
U: function() {
return Math.round(jsdate.getTime() / 1000)
}
};
return format.replace(/([a-zA-Z])/g,function(t, s) {
var ret='';
if(t != s) {
// escaped
ret = s;
} else if(f[s]) {
// a date function exists
ret = f[s]();
} else {
// nothing special
ret = s;
}
return ret;
});
}
/*
* arrayToString
* 功能,二维数组传化成字符串
*/
function arrayToString(arr) {
var str='';
if(!isUndefined(arr)){
for(var i=0;i<arr.length;i++){
var temp=arr[i];
if(i>0){
str+=',';
}
for(var y=0;y<temp.length;y++){
if(y>0){
str+=';';
}
if(valType(temp[y])=='number'){
str+=parseInt(temp[y]*100);
}
else{
str+=temp[y];
}
}
}
}
return str;
}
/*
* stringToArray
* 功能,字符串转化成二维数组
*/
function stringToArray(str) {
var newArr=[];
if(!isUndefined(str)){
var arr=str.split(',');
for(var i=0;i<arr.length;i++){
var temp=arr[i].split(';');
temp[1]=parseInt(temp[1])*0.01;
temp[2]=parseInt(temp[2])*0.01;
newArr.push(temp);
}
}
return newArr;
}
export { formatSeconds, date, arrayToString, stringToArray };
+190
View File
@@ -0,0 +1,190 @@
import { standardization, isUndefined, valType } from './type.js';
import { addListener } from './dom.js';
/*
* ajax
* 功能:ajax功能
* @cObj:传递的参数,包含请求地址,请求类型,编码等
*/
function ajax(cObj) {
var callback = null;
var obj = {
method: 'get',//请求类型
dataType: 'json',//请求的数据类型
charset: 'utf-8',
async: true,//true表示异步,false表示同步
url: '',
data: null,
success: null,
error:null
};
obj = standardization(obj, cObj);
if (isUndefined(obj['url'])) {
return;
}
/*
* errorFun
*功能:执行error
*/
var errorFun=function(info){
if(!isUndefined(obj['error']) && valType(obj['error'])=='function'){
obj['error'](info);
}
else{
obj['success'](null);
}
};
/*
* successFun
*功能:执行success
*/
var successFun=function(success){
if(!isUndefined(obj['success']) && valType(obj['success'])=='function'){
obj['success'](success);
}
};
/*
* createXHR
*功能:用来为ajax函数提供支持
*/
var createXHR=function() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch(event) {
try {
return new ActiveXObject('Msxml2.XMLHTTP');
} catch(event) {}
}
}
};
/*
* formatParams
* 功能:将对象转成地址字符串,为ajax函数提供支持
* @obj:传递一个对象
*/
var formatParams=function(obj) {
var arr = [];
for (var key in obj) {
arr.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
}
return arr.join('&');
};
if (valType(cObj) != 'object') {
if(!isUndefined(obj['error'])){
obj.error(event);
}
return;
}
if (obj.dataType === 'json' || obj.dataType === 'text' || obj.dataType === 'html' || obj.dataType === 'xml') {
var xhr = createXHR();
callback = function() {
//判断http的交互是否成功
if (xhr.status === 200) {
if (obj.dataType === 'json') {
try {
successFun(eval('(' + xhr.responseText + ')')); //回调传递参数
}
catch(event) {
successFun(null);
}
} else {
successFun(xhr.responseText); //回调传递参数
}
}
else {
errorFun({code:xhr.status,message:xhr.statusText});
}
};
obj.data = formatParams(obj.data); //通过params()将名值对转换成字符串
if (obj.method === 'get' && !isUndefined(obj.data)) {
if (obj.data != '') {
if (obj.url.indexOf('?') == -1) {
obj.url += '?' + obj.data
} else {
obj.url += '&' + obj.data;
}
}
}
if (obj.async === true) { //true表示异步,false表示同步
addListener(xhr,'readystatechange',function(event){
if (this.readyState === 4 && callback != null) { //判断对象的状态是否交互完成
callback(); //回调
}
});
}
xhr.open(obj.method, obj.url, obj.async);
if (obj.method === 'post') {
try{
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('charset', obj['charset']);
xhr.send(obj.data);
}
catch(event){
callback();
}
}
else {
try{
xhr.send(null); //get方式则填null
}
catch(event){
callback();
}
}
if (obj.async === false) { //同步
callback();
}
}
else if (obj.dataType === 'jsonp') {
var oHead = document.getElementsByTagName('head')[0];
var oScript = document.createElement('script');
var callbackName = 'callback' + new Date().getTime();
var params = formatParams(obj.data) + '&callback=' + callbackName; //按时间戳拼接字符串
//拼接好src
oScript.src = obj.url.split('?') + '?' + params;
//插入script标签
oHead.insertBefore(oScript, oHead.firstChild);
//jsonp的回调函数
window[callbackName] = function(json) {
if(!isUndefined(obj['success']) && valType(obj['success'])==='function'){
obj['success'](json);
}
oHead.removeChild(oScript);
};
}
}
/*
* dataURLtoBlob
* 功能,base64转blob
*/
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
/*
* getApp
* 功能,获取平台类型是否是iphone(含ipod/ipad,统一返回'iphone'以保持现有分支兼容)
*/
function getApp(){
var u = navigator.userAgent.toLowerCase();
if(u.indexOf('iphone')>-1 || u.indexOf('ipod')>-1 || u.indexOf('ipad')>-1){
return 'iphone';
}
//iPadOS 13+默认使用桌面UA(Macintosh),通过MacIntel平台+触屏点数判断
if(u.indexOf('macintosh')>-1 && navigator.platform=='MacIntel' && navigator.maxTouchPoints>1){
return 'iphone';
}
}
export { ajax, dataURLtoBlob, getApp };
+26
View File
@@ -0,0 +1,26 @@
import { isUndefined } from './type.js';
/*
* getPath
* 功能:获取该js文件所在路径
*/
function getPath(siz) {
var scriptList = document.scripts,
thisPath = scriptList[scriptList.length - 1].src;
for (var i = 0; i < scriptList.length; i++) {
var scriptName = scriptList[i].getAttribute('name') || scriptList[i].getAttribute('data-name');
var src = scriptList[i].src.slice(scriptList[i].src.lastIndexOf('/') + 1, scriptList[i].src.lastIndexOf('.'));
if ((scriptName && (scriptName == 'moeplayer' || scriptName == 'moeplayer.min')) || (scriptList[i].src && (src == 'moeplayer' || src == 'moeplayer.min'))) {
thisPath = scriptList[i].src;
break;
}
}
var path=thisPath.substring(0, thisPath.lastIndexOf('/js/') + 1);
if(!isUndefined(siz)){
path+=siz+'/';
}
return path;
}
export { getPath };
+99
View File
@@ -0,0 +1,99 @@
/*
* standardization
* 功能:将对象Object标准化,将n对象替换进o对象
* @o:标准化对象,@n:外部传递对象
*/
function standardization(o, n) { //n替换进o
var h = {};
var k;
for (k in o) {
h[k] = o[k];
}
for (k in n) {
if(k in h){
h[k] = n[k];
}
}
return h;
}
/*
* mergeObj
* 功能:将新对象合并到原对象中,需要确保原对像里有对应的值并且类型一样
* @o:原对象,@n:新对象
*/
function mergeObj(o,n){
var h = {};
var k;
for (k in o) {
h[k] = o[k];
}
for (k in n) {
if(k in h){
switch(valType(h[k])){
case 'object':
if(valType(n[k])=='object'){
h[k] = mergeObj(h[k],n[k]);
}
break;
default:
if(valType(h[k])==valType(n[k])){
h[k] = n[k];
}
break;
}
}
}
return h;
}
/*
* valType
* 功能:判断变量类型
*/
function valType(val) {
if(typeof val==='undefined') return 'undefined';
var str=Object.prototype.toString.call(val).split(' ')[1].replace(']','').toLowerCase();
if(str.substr(0,4)=='html' && (str.substr(-7)=='element' || str.substr(-8)=='document' || str=='window')){
str='htmlobject';
}
if(str=='htmlcollection' || str=='nodelist'){
str='htmlarray';
}
return str;
}
/*
* isUndefined
* 功能:判断变量是否存在或值是否为undefined
*/
function isUndefined(val) {
try {
return valType(val)==='undefined' || val === undefined || val === null || (valType(val)==='number' && isNaN(val));
} catch (event) {
return true;
}
return false;
}
/*
* decodeURIString
* 功能,解码中文
*/
function decodeURIString(obj){
for(var k in obj){
if(valType(obj[k])=='string' && obj[k]){
try{
obj[k]=decodeURI(obj[k]);
}
catch(event){}
}
if(valType(obj[k])=='object'){
obj[k]=decodeURIString(obj[k]);
}
}
return obj;
}
export { standardization, mergeObj, valType, isUndefined, decodeURIString };