阶段2改名:ckplayer → MoePlayer
- 目录 ckplayer/ → moeplayer/,核心文件/皮肤/图片同步改名 - 全局 API:new MoePlayer(...),UMD 导出 global.MoePlayer - CSS 类名 ckplayer-ckplayer-* 折叠为 moeplayer-*,JS 与 CSS 同步 - 语言包全局变量、cookie 名、getPath() 脚本名判断同步 - 移除右键菜单默认指向旧官网的 link - 构建与 index.html 同步更新
@@ -6,7 +6,7 @@ MoePlayer —— 一款中文友好的开源网页视频播放器(MIT 协议
|
||||
|
||||
目标定位:中文文档 + 主流浏览器/手机 H5 支持 + 官方 Moodle media 插件(`media_moeplayer`,独立仓库)。
|
||||
|
||||
**注意**:当前处于改造阶段 1(工程化),代码本体仍是 ckplayer,全局变量/API 仍为 `ckplayer`,改名在阶段 2 进行。
|
||||
**注意**:当前处于改造阶段 2(改名 + 模块化)。改名已完成:目录 `moeplayer/`、全局变量/API 为 `MoePlayer`(语言包全局变量 `moeplayerLanguage`);单文件拆分为 ES Module 待做。
|
||||
|
||||
## 目录结构
|
||||
|
||||
@@ -14,10 +14,10 @@ MoePlayer —— 一款中文友好的开源网页视频播放器(MIT 协议
|
||||
├── index.html # 演示/测试页(含 API 调用示例)
|
||||
├── package.json # npm 工程化入口,依赖与构建脚本
|
||||
├── scripts/build.js # 构建脚本:压缩核心 + 从 node_modules 拷贝运行时库
|
||||
├── ckplayer/ # 播放器本体,部署时整个上传到网站(阶段2将改名)
|
||||
│ ├── js/ckplayer.js # 核心源码(UMD 模块,~7400 行,ES5)
|
||||
│ ├── js/ckplayer.min.js # 压缩版,【由 npm run build 自动生成,勿手改】
|
||||
│ ├── css/ckplayer.css # 默认皮肤;另有 ckplayer.red.css、ckplayer.ixigua.css
|
||||
├── moeplayer/ # 播放器本体,部署时整个上传到网站
|
||||
│ ├── js/moeplayer.js # 核心源码(UMD 模块,~7400 行,ES5)
|
||||
│ ├── js/moeplayer.min.js # 压缩版,【由 npm run build 自动生成,勿手改】
|
||||
│ ├── css/moeplayer.css # 默认皮肤;另有 moeplayer.red.css、moeplayer.ixigua.css
|
||||
│ ├── css/images/ # 皮肤图片资源
|
||||
│ ├── language/ # 语言包:zh.cn.js(默认内嵌)、zh.hk.js、en.js
|
||||
│ ├── hls.js/ # hls.min.js,【由构建从 npm 包拷贝,勿手改】
|
||||
@@ -30,9 +30,9 @@ MoePlayer —— 一款中文友好的开源网页视频播放器(MIT 协议
|
||||
|
||||
- 安装:`npm install`
|
||||
- 构建:`npm run build` —— 做两件事:
|
||||
1. terser 压缩 `ckplayer/js/ckplayer.js` → `ckplayer.min.js`
|
||||
2. 从 node_modules 拷贝 hls.js / flv.js / mpegts.js 的产物及 LICENSE 到 `ckplayer/` 对应目录
|
||||
- **改动 `ckplayer.js` 后必须执行 `npm run build`** 重新生成 min 版
|
||||
1. terser 压缩 `moeplayer/js/moeplayer.js` → `moeplayer.min.js`
|
||||
2. 从 node_modules 拷贝 hls.js / flv.js / mpegts.js 的产物及 LICENSE 到 `moeplayer/` 对应目录
|
||||
- **改动 `moeplayer.js` 后必须执行 `npm run build`** 重新生成 min 版
|
||||
- 升级依赖:改 `package.json` 版本号后 `npm install && npm run build`
|
||||
- 运行时机制:播放器用 `getPath()` 定位自身所在目录,按需动态 `<script>` 加载同级 `hls.js/` 等目录里的文件,库挂全局变量(`Hls`/`flvjs`/`mpegts`)。**这个目录结构约定不能破坏**
|
||||
|
||||
@@ -44,7 +44,7 @@ MoePlayer —— 一款中文友好的开源网页视频播放器(MIT 协议
|
||||
|
||||
- 注释与文档使用中文(项目惯例)
|
||||
- 核心源码为单文件 ES5 风格(`var`、函数式),阶段 2/3 重构前保持一致,不要引入 ES6+ 语法
|
||||
- `ckplayer/` 下由构建生成的文件(min.js、hls.js/flv.js/mpegts.js 目录内容)不要手动修改
|
||||
- `moeplayer/` 下由构建生成的文件(min.js、hls.js/flv.js/mpegts.js 目录内容)不要手动修改
|
||||
|
||||
## Git 约定
|
||||
|
||||
@@ -54,7 +54,7 @@ MoePlayer —— 一款中文友好的开源网页视频播放器(MIT 协议
|
||||
## 改造路线图
|
||||
|
||||
1. ~~工程化:npm + 构建脚本 + 依赖 npm 化~~(已完成)
|
||||
2. 改名 ckplayer → MoePlayer + 单文件拆分为 ES Module + README 重写
|
||||
2. 改名 ckplayer → MoePlayer(已完成)+ 单文件拆分为 ES Module(待做)+ README 重写
|
||||
3. 核心与 UI 分层、皮肤改用 CSS 变量 + SVG 图标
|
||||
4. 协议插件化(hls/mpegts 按需加载,移除 flv.js)、移动端 H5 兼容专项
|
||||
5. Moodle media 插件(独立仓库 moodle-media_moeplayer)
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>ckplayer</title>
|
||||
<link type="text/css" rel="stylesheet" href="ckplayer/css/ckplayer.css" />
|
||||
<title>MoePlayer</title>
|
||||
<link 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>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
poster:'video/poster.png',//封面图片
|
||||
video:'video/lc.mp4'//视频地址
|
||||
};
|
||||
var player=new ckplayer(videoObject)//调用播放器并赋值给变量player
|
||||
var player=new MoePlayer(videoObject)//调用播放器并赋值给变量player
|
||||
/*
|
||||
* ===============================================================================================
|
||||
* 以上代码已完成调用演示,下方的代码是演示监听动作和外部控制的部分
|
||||
@@ -45,6 +45,7 @@
|
||||
document.getElementById('state').innerHTML='监听到播放结束';
|
||||
});
|
||||
</script>
|
||||
<p>以下链接指向原 ckplayer 项目:</p>
|
||||
<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>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* 软件名称:ckplayer
|
||||
* 软件名称:MoePlayer
|
||||
* 版本:X3
|
||||
* 版权:www.ckplayer.com
|
||||
* 版权:MoePlayer 项目(fork 自 ckplayer, www.ckplayer.com)
|
||||
* 开源协议:MIT
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global = global || self, global.ckplayer = factory());
|
||||
(global = global || self, global.MoePlayer = factory());
|
||||
}(this, function () { 'use strict';
|
||||
/*
|
||||
* language
|
||||
@@ -136,7 +136,7 @@
|
||||
keyVolume:2,//是否启用键盘控制音量调节,0=不启用,1=启用,2=全屏时才启用
|
||||
errorShow:true//是否显示错误信息
|
||||
};
|
||||
function ckplayerEmbed(videoObj){
|
||||
function moeplayerEmbed(videoObj){
|
||||
/*
|
||||
* rightMenu
|
||||
* 功能:全局变量,右键内容
|
||||
@@ -144,8 +144,7 @@
|
||||
*/
|
||||
var rightMenu=[
|
||||
{
|
||||
title:'ckplayer',
|
||||
link:'http://www.ckplayer.com'
|
||||
title:'MoePlayer'
|
||||
},
|
||||
{
|
||||
title:'version:X3',
|
||||
@@ -193,7 +192,7 @@
|
||||
var nowZoom=100;//当前缩放比例
|
||||
var smallWindowsState=false;//当前是否处理小窗口状态
|
||||
var isDrag=false;//是否在播放器上按下并且拖动
|
||||
var ckplayerCookie='ckplayer-player-cookie';
|
||||
var moeplayerCookie='moeplayer-player-cookie';
|
||||
var cookieName='';//cookie名称
|
||||
var cookieTime=0;//cookie保存时间,单位:秒
|
||||
var cookieArray=[];//保存当前所有记录
|
||||
@@ -219,8 +218,8 @@
|
||||
/*
|
||||
* 检查是否在顶部引入了外部语言包文件,如果引入了,则使用外部语言包替换进language变量
|
||||
*/
|
||||
if(!isUndefined(window.ckplayerLanguage)){
|
||||
language=mergeObj(language,window.ckplayerLanguage);
|
||||
if(!isUndefined(window.moeplayerLanguage)){
|
||||
language=mergeObj(language,window.moeplayerLanguage);
|
||||
}
|
||||
/*
|
||||
* 初始化注册事件函数,该函数的作用是将所有相关的事件都注册进一个变量newEvent
|
||||
@@ -351,8 +350,8 @@
|
||||
if(vars['language']){
|
||||
var path=getPath('language')+vars['language']+'.js';
|
||||
loadJs(path,function(){
|
||||
if(!isUndefined(window['ckplayerLanguage'])){
|
||||
language=mergeObj(language,window['ckplayerLanguage']);
|
||||
if(!isUndefined(window['moeplayerLanguage'])){
|
||||
language=mergeObj(language,window['moeplayerLanguage']);
|
||||
}
|
||||
return embed(obj);
|
||||
});
|
||||
@@ -391,7 +390,7 @@
|
||||
* 清空容器
|
||||
*/
|
||||
if(existenceObj){
|
||||
CK=CT.find('.ckplayer-ckplayer')?CT.find('.ckplayer-ckplayer').eq(0):null;
|
||||
CK=CT.find('.moeplayer')?CT.find('.moeplayer').eq(0):null;
|
||||
if(CK){
|
||||
CM=CK.find('.ck-main')?CK.find('.ck-main').eq(0):null;
|
||||
}
|
||||
@@ -404,7 +403,7 @@
|
||||
/*
|
||||
* 在播放容器里新建一个总的容器
|
||||
*/
|
||||
CK=createlDiv('ckplayer-ckplayer');
|
||||
CK=createlDiv('moeplayer');
|
||||
CT.append(CK);
|
||||
/*
|
||||
* 在总容器里再新建一个容器
|
||||
@@ -1197,7 +1196,7 @@
|
||||
C['tip'].append(C['tip']['content']);
|
||||
C['tip']['triangle']=createlDiv('ck-triangle ck-triangle-auto');
|
||||
C['tip'].append(C['tip']['triangle']);
|
||||
tip(video,'ckplayer');//初始化显示,为了获取高度
|
||||
tip(video,'MoePlayer');//初始化显示,为了获取高度
|
||||
C['tip'].minHeight=C['tip'].getHeight();
|
||||
tip();
|
||||
//贴片广告层
|
||||
@@ -1417,7 +1416,7 @@
|
||||
*/
|
||||
changeCookie=function(time){
|
||||
var arr=[];
|
||||
var cStr=cookie(ckplayerCookie);
|
||||
var cStr=cookie(moeplayerCookie);
|
||||
var i=0,y=0;
|
||||
var cTime=Math.floor(Date.now()*0.001);
|
||||
time=Math.floor(time*100);
|
||||
@@ -1441,8 +1440,8 @@
|
||||
arr=tempArr;
|
||||
}
|
||||
if(arr.length>0){
|
||||
cookie(ckplayerCookie,'delete',vars['domain'],vars['cookiePath']);
|
||||
cookie(ckplayerCookie,arrayToString(arr),vars['domain'],vars['cookiePath']);
|
||||
cookie(moeplayerCookie,'delete',vars['domain'],vars['cookiePath']);
|
||||
cookie(moeplayerCookie,arrayToString(arr),vars['domain'],vars['cookiePath']);
|
||||
}
|
||||
cookieArray=arr;
|
||||
},
|
||||
@@ -3462,13 +3461,13 @@
|
||||
*/
|
||||
cookie:function(name){
|
||||
if(name=='delete'){
|
||||
cookie(ckplayerCookie,'delete',vars['domain']);
|
||||
cookie(moeplayerCookie,'delete',vars['domain']);
|
||||
return null;
|
||||
}
|
||||
if(!isUndefined(name) && name){
|
||||
name=name.replace(/[ ]*,[ ]*|[ ]+/g, '').replace(/[ ]*;[ ]*|[ ]+/g, '');
|
||||
}
|
||||
var cStr=cookie(ckplayerCookie);
|
||||
var cStr=cookie(moeplayerCookie);
|
||||
if(cStr){
|
||||
var arr=stringToArray(cStr);
|
||||
var tempArr=[];
|
||||
@@ -3480,8 +3479,8 @@
|
||||
}
|
||||
arr=tempArr;
|
||||
if(arr.length>0){
|
||||
cookie(ckplayerCookie,'delete',vars['domain'],vars['cookiePath']);
|
||||
cookie(ckplayerCookie,arrayToString(arr),vars['domain'],vars['cookiePath']);
|
||||
cookie(moeplayerCookie,'delete',vars['domain'],vars['cookiePath']);
|
||||
cookie(moeplayerCookie,arrayToString(arr),vars['domain'],vars['cookiePath']);
|
||||
for(var i=0;i<arr.length;i++){
|
||||
arr[i]={
|
||||
name:arr[i][0],
|
||||
@@ -3496,7 +3495,7 @@
|
||||
return arr;
|
||||
}
|
||||
else{
|
||||
cookie(ckplayerCookie,'delete',vars['domain'],vars['cookiePath']);
|
||||
cookie(moeplayerCookie,'delete',vars['domain'],vars['cookiePath']);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -3690,7 +3689,7 @@
|
||||
if(!isUndefined(vars['menu']) && vars['menu']){
|
||||
rightMenu=vars['menu'];
|
||||
}
|
||||
C['menu']=createlDiv('ckplayer-ckplayer-menu');
|
||||
C['menu']=createlDiv('moeplayer-menu');
|
||||
for(var i=0;i<rightMenu.length;i++){
|
||||
var m=rightMenu[i];
|
||||
var li=createlDiv('ck-li');
|
||||
@@ -4412,7 +4411,7 @@
|
||||
* 功能:显示错误信息
|
||||
*/
|
||||
showWindowsError=function(error){
|
||||
var ele=createlDiv('ckplayer-error',error);
|
||||
var ele=createlDiv('moeplayer-error',error);
|
||||
$('body').append(ele);
|
||||
},
|
||||
/*
|
||||
@@ -4479,8 +4478,8 @@
|
||||
}
|
||||
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
if(scrollTop>CT.offset()['top']+CT.getHeight()){
|
||||
if(!CK.hasClass('ckplayer-ckplayer-smallwindow')){
|
||||
CK.addClass('ckplayer-ckplayer-smallwindow');
|
||||
if(!CK.hasClass('moeplayer-smallwindow')){
|
||||
CK.addClass('moeplayer-smallwindow');
|
||||
eventTarget('smallWindows',true);
|
||||
smallWindowsState=true;
|
||||
if(vars['smallWindowsDrag']){
|
||||
@@ -4499,8 +4498,8 @@
|
||||
* 退出小窗口模式
|
||||
*/
|
||||
exitSmallWindows=function(){
|
||||
if(CK.hasClass('ckplayer-ckplayer-smallwindow')){
|
||||
CK.removeClass('ckplayer-ckplayer-smallwindow');
|
||||
if(CK.hasClass('moeplayer-smallwindow')){
|
||||
CK.removeClass('moeplayer-smallwindow');
|
||||
if(vars['smallWindowsDrag']){
|
||||
drag();
|
||||
}
|
||||
@@ -5364,7 +5363,7 @@
|
||||
if (obj) {
|
||||
if (valType(obj) == 'htmlobject' || valType(obj)=='object') {
|
||||
res = obj;
|
||||
if (!isUndefined(res.ckplayer)) {
|
||||
if (!isUndefined(res.moeplayer)) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -5441,7 +5440,7 @@
|
||||
|
||||
if (res) {
|
||||
if (valType(res) == 'htmlobject' || valType(res) == 'object' || valType(res) == 'document') {
|
||||
res.ckplayer = 'Thanks for using';
|
||||
res.moeplayer = 'Thanks for using';
|
||||
/*
|
||||
* find
|
||||
* 功能:在当前节点中查找指定节点
|
||||
@@ -6821,7 +6820,7 @@
|
||||
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 == 'ckplayer' || scriptName == 'ckplayer.min')) || (scriptList[i].src && (src == 'ckplayer' || src == 'ckplayer.min'))) {
|
||||
if ((scriptName && (scriptName == 'moeplayer' || scriptName == 'moeplayer.min')) || (scriptList[i].src && (src == 'moeplayer' || src == 'moeplayer.min'))) {
|
||||
thisPath = scriptList[i].src;
|
||||
break;
|
||||
}
|
||||
@@ -7417,5 +7416,5 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
return ckplayerEmbed;
|
||||
return moeplayerEmbed;
|
||||
}));
|
||||
@@ -1,7 +1,7 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global = global || self, global.ckplayerLanguage = factory());
|
||||
(global = global || self, global.moeplayerLanguage = factory());
|
||||
}(this, function () { 'use strict';
|
||||
/*
|
||||
* 功能:包含播放器用到的全部相关语言文字
|
||||
@@ -1,7 +1,7 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global = global || self, global.ckplayerLanguage = factory());
|
||||
(global = global || self, global.moeplayerLanguage = factory());
|
||||
}(this, function () { 'use strict';
|
||||
/*
|
||||
* 功能:包含播放器用到的全部相关语言文字
|
||||
@@ -1,7 +1,7 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global = global || self, global.ckplayerLanguage = factory());
|
||||
(global = global || self, global.moeplayerLanguage = factory());
|
||||
}(this, function () { 'use strict';
|
||||
/*
|
||||
*功能:包含播放機用到的全部相關語言文字
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* 构建脚本
|
||||
* 功能:
|
||||
* 1. 使用 terser 将 ckplayer/js/ckplayer.js 压缩为 ckplayer.min.js
|
||||
* 2. 从 node_modules 拷贝运行时依赖(hls.js/flv.js/mpegts.js)到 ckplayer/ 对应目录
|
||||
* 1. 使用 terser 将 moeplayer/js/moeplayer.js 压缩为 moeplayer.min.js
|
||||
* 2. 从 node_modules 拷贝运行时依赖(hls.js/flv.js/mpegts.js)到 moeplayer/ 对应目录
|
||||
* 播放器运行时会按需动态加载这些文件,目录结构需与 getPath() 的约定保持一致
|
||||
* 用法:npm run build
|
||||
*/
|
||||
@@ -20,8 +20,8 @@ const libs = [
|
||||
];
|
||||
|
||||
async function minifyCore() {
|
||||
const src = path.join(root, 'ckplayer/js/ckplayer.js');
|
||||
const dist = path.join(root, 'ckplayer/js/ckplayer.min.js');
|
||||
const src = path.join(root, 'moeplayer/js/moeplayer.js');
|
||||
const dist = path.join(root, 'moeplayer/js/moeplayer.min.js');
|
||||
const code = fs.readFileSync(src, 'utf8');
|
||||
const result = await terser.minify(code, {
|
||||
output: {
|
||||
@@ -33,13 +33,13 @@ async function minifyCore() {
|
||||
throw result.error;
|
||||
}
|
||||
fs.writeFileSync(dist, result.code, 'utf8');
|
||||
console.log('压缩完成: ckplayer/js/ckplayer.min.js (' + result.code.length + ' bytes)');
|
||||
console.log('压缩完成: moeplayer/js/moeplayer.min.js (' + result.code.length + ' bytes)');
|
||||
}
|
||||
|
||||
function copyLibs() {
|
||||
for (const [pkg, dir, files] of libs) {
|
||||
const pkgRoot = path.dirname(require.resolve(pkg + '/package.json'));
|
||||
const targetDir = path.join(root, 'ckplayer', dir);
|
||||
const targetDir = path.join(root, 'moeplayer', dir);
|
||||
// 清空旧的 vendored 文件,只保留构建拷贝的产物
|
||||
fs.rmSync(targetDir, { recursive: true, force: true });
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
@@ -51,7 +51,7 @@ function copyLibs() {
|
||||
fs.copyFileSync(src, path.join(targetDir, file));
|
||||
}
|
||||
const version = require(pkg + '/package.json').version;
|
||||
console.log('拷贝完成: ckplayer/' + dir + '/ (' + pkg + '@' + version + ')');
|
||||
console.log('拷贝完成: moeplayer/' + dir + '/ (' + pkg + '@' + version + ')');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||