Compare commits

...

158 Commits
v3.x ... v2.5.6

Author SHA1 Message Date
hunlongyu
90ec848c11 v2.5.6 2020-10-28 21:45:48 +08:00
hunlongyu
82270a702f v2.5.5 2020-10-28 21:39:29 +08:00
Hunlongyu
61dcb1ec49 💍 优化视频源分组功能 2020-10-28 18:22:18 +08:00
Hunlongyu
40b853361e 💄 新增或编辑源的时候, 校验key值的唯一性 2020-10-28 18:00:59 +08:00
Hunlongyu
57ff3325f0 💋 优化图标显示逻辑 2020-10-28 17:38:02 +08:00
Hunlongyu
c4283e2da0 🎓 优化播放页图标, 优化视频停止播放后不显示历史记录提示. 修复报错 2020-10-28 17:30:46 +08:00
Hunlongyu
7052bd7e05 🎩 新增播放视频时换源功能. 2020-10-28 17:04:44 +08:00
Hunlongyu
64a12a06c4 👒 移除百度统计, 主要是记录缺失, 并且不准确 2020-10-28 15:38:02 +08:00
Hunlongyu
3df3b385ce ⛑ 新增分类过滤器, 主要针对豆瓣资源网分类出错问题 2020-10-28 15:34:33 +08:00
Hunlongyu
e77db4711e 🧢 优化视频源检测功能 2020-10-28 15:33:30 +08:00
Hunlongyu
fb3fd26870 Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player into master 2020-10-28 12:00:14 +08:00
Hunlongyu
72790f5f3e 👑 添加了视频源检测功能. 2020-10-28 11:59:55 +08:00
haiyangcui
ea2815969d 把IPTV的初始数据放入单独的Json文件中 2020-10-27 23:12:30 +01:00
Hunlongyu
e330cff7d4 🧦 electron 优化, 忽略证书错误, 关闭沙盒. 2020-10-27 11:43:11 +08:00
haiyangcui
da5b91535e 搜索后获取详细信息并展示 2020-10-26 23:53:12 +01:00
haiyangcui
00c8f0c1e2 v2.5.4 2020-10-26 17:26:49 +01:00
haiyangcui
924fd439d9 更新导入收藏,支持一次导入多个文件 2020-10-26 16:37:44 +01:00
haiyangcui
ed039894c8 导入时无需调用upgradeFavorites 2020-10-26 16:20:08 +01:00
haiyangcui
005bdf7ea6 监听并更新收藏及源列表 2020-10-26 16:19:48 +01:00
haiyangcui
19071faf70 更新福利片关键词 2020-10-26 15:13:18 +01:00
hunlongyu
176d9f6b5a Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player 2020-10-26 20:57:31 +08:00
hunlongyu
31a8dd0f32 🧤 升级 xgplayer 依赖 2020-10-26 20:57:25 +08:00
haiyangcui
336ea08c69 Film页面只获取active的源站 2020-10-26 13:47:47 +01:00
haiyangcui
8e4a0870a1 播放电视频道时,更新播放页面的左上角标题 2020-10-26 13:42:44 +01:00
hunlongyu
86130e6c53 🧣 修复第一次加载资源失败的bug 2020-10-26 20:24:35 +08:00
Hunlongyu
e984d0a349 🩳 修复修改数据库,导致无法播放的bug 2020-10-26 18:18:07 +08:00
Hunlongyu
506991f3b1 👖 优化 history 和 star 数据库的搜索.后台不再提示warning.需要重置数据库. 2020-10-26 18:06:01 +08:00
Hunlongyu
04c35ba7a9 👔 开发所有系统的更新功能 2020-10-26 16:46:41 +08:00
Hunlongyu
275b6757bf 👕 实现手动更新功能 2020-10-26 16:33:00 +08:00
Hunlongyu
612930cc8b 🥼 修复停止播放后,再次播放有2个声音的问题 2020-10-26 11:19:15 +08:00
haiyangcui
80e36fa99c 源站排序总是有问题, 先移除掉 2020-10-25 15:40:18 +01:00
haiyangcui
f069044f28 简单重构 2020-10-25 16:23:31 +01:00
haiyangcui
c77ac7ea7f 重构一下EditSites页面的代码 2020-10-25 15:33:39 +01:00
haiyangcui
803181b4f7 修复源页面编辑后Film页面不更新的bug 2020-10-25 13:49:58 +01:00
haiyangcui
ac2474903d 恢复滚动条 2020-10-24 23:38:02 +02:00
haiyangcui
04b7e139da 更新内置电视频道 2020-10-24 23:00:58 +02:00
haiyangcui
b49e1b82bf 支持自选源列排序 2020-10-24 23:00:40 +02:00
haiyangcui
dc00c690e7 重新排序后更新数据库 2020-10-24 22:58:15 +02:00
haiyangcui
44ba552435 改进IPTV页面逻辑 2020-10-24 16:20:49 +02:00
haiyangcui
8d84d1bf22 统一字体大小 2020-10-24 14:29:30 +02:00
haiyangcui
c78368dac9 新增源时,支持输入源标识 2020-10-24 14:24:18 +02:00
haiyangcui
11af5a7ecd 导入收藏时,更新收藏项的源站信息 2020-10-24 14:14:44 +02:00
haiyangcui
ecfeecf45f 为表头按钮添加图标 2020-10-24 14:06:25 +02:00
haiyangcui
455f5dae84 统一star数据格式的定义 2020-10-24 13:19:47 +02:00
Hunlongyu
ffbffde74f Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player into master 2020-10-24 13:52:29 +08:00
Hunlongyu
0be0433355 🥼 移除master 分支下的更新功能 2020-10-24 13:52:20 +08:00
haiyangcui
cc2dc19e39 Revert "统一star数据格式的定义"
This reverts commit aa4583898a.
2020-10-23 23:31:44 +02:00
haiyangcui
aa4583898a 统一star数据格式的定义 2020-10-23 23:31:01 +02:00
haiyangcui
2e96812a1f 最后一列的表头向右对齐 2020-10-23 23:23:25 +02:00
haiyangcui
6f52d73d52 鼠标划过的行添加少许放大效果 2020-10-23 22:45:04 +02:00
haiyangcui
a3ebba641f 源站编辑,支持编辑分组 2020-10-23 22:29:52 +02:00
haiyangcui
8ceffab2fe 搜索结果里添加“源站”列 2020-10-23 16:43:33 +02:00
haiyangcui
454d192141 更好支持中文排序 2020-10-23 15:53:21 +02:00
haiyangcui
a1423993c8 固定表头 2020-10-23 15:14:12 +02:00
haiyangcui
1c9a84dbc6 开关批处理,无需返回页面顶部 2020-10-23 13:37:41 +02:00
haiyangcui
304b0d10b4 Fix typo 2020-10-23 13:32:46 +02:00
cuiocean
aff97f6d46 Merge pull request #307 from buvta/patch-1
IPTV小调整
2020-10-23 12:27:23 +02:00
haiyangcui
650c882f58 移除自选源列的排序 2020-10-23 12:20:17 +02:00
Hunlongyu
32fa465cf2 🦺 关闭自动更新, 手动更新软件. (未完成) 2020-10-23 14:40:40 +08:00
Hunlongyu
1fdfada14f Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player into master 2020-10-23 11:21:52 +08:00
Hunlongyu
5ed8b6e49a 👓 补上忘记写的自动更新功能 😂 2020-10-23 11:20:36 +08:00
haiyangcui
c2e2f1c490 支持自选源列排序 2020-10-22 18:34:00 +02:00
haiyangcui
7ad40ba375 历史记录的导入导出 2020-10-22 18:22:41 +02:00
buvta
c708292051 IPTV变更分租状态开关时表格回到顶部 2020-10-23 00:02:18 +08:00
buvta
79932a74bc 固定IPTV和源编辑表头 2020-10-22 23:48:19 +08:00
buvta
3f79f31550 IPTV转移总频道数到表头
这PR也太好混了吧
2020-10-22 23:40:28 +08:00
haiyangcui
1d33db0143 IPTV支持从JSON文件导入 2020-10-22 17:07:05 +02:00
haiyangcui
1dc7b38160 批处理开启关闭源是否为自选源 2020-10-22 16:53:57 +02:00
haiyangcui
db7cdab787 批处理视频源的分组 2020-10-22 16:37:54 +02:00
haiyangcui
d1e6ac1ae6 支持源分组 2020-10-22 16:18:24 +02:00
haiyangcui
d63710afe6 统一字体大小 2020-10-22 14:07:14 +02:00
haiyangcui
8abe4b7ef8 IPTV批处理分组 2020-10-22 14:04:45 +02:00
haiyangcui
98b4f5bc1d 改进star页面的排序 2020-10-22 12:27:45 +02:00
haiyangcui
248c0994c9 统一页面字体大小,更新操作栏使其自适应宽度 2020-10-22 12:22:19 +02:00
Hunlongyu
2eba0523bc 🛒 优化分享图片 2020-10-22 14:23:38 +08:00
Hunlongyu
49589102d9 🧶 添加百度统计 2020-10-22 14:22:59 +08:00
Hunlongyu
79b02df628 🧵 优化初次加载请求多次的问题 2020-10-22 13:41:47 +08:00
Hunlongyu
0f456fe7a8 🧵 优化样式 2020-10-22 11:33:30 +08:00
Hunlongyu
850c8d5423 🎨 视频源异常状态处理, 自动重置源 2020-10-22 10:59:29 +08:00
haiyangcui
63cf367f52 自适应列宽 2020-10-21 18:42:50 +02:00
haiyangcui
bd43f06e7d 可以打开或关闭源是否为自选源 2020-10-21 17:34:11 +02:00
haiyangcui
2ed47e64c3 解决Film页面的warning 2020-10-21 17:11:54 +02:00
haiyangcui
8817d39d49 IPTV搜索移到表头位置 2020-10-21 16:25:15 +02:00
haiyangcui
b68525213a 引入需要的element-ui的组件 2020-10-21 16:07:37 +02:00
haiyangcui
0efbc38137 Merge branch 'release_2.5.3' 2020-10-21 13:51:16 +02:00
haiyangcui
cd2cb684a5 v2.5.3 2020-10-21 12:35:07 +02:00
Hunlongyu
500dbfa1ee 🖼 测试代码 2020-10-21 18:23:45 +08:00
Hunlongyu
e970074266 Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player into master 2020-10-21 15:33:58 +08:00
Hunlongyu
9dc7ab4d1e 🎪 升级最新依赖 2020-10-21 09:13:53 +08:00
haiyangcui
ffe821d107 IPTV函数名更新 2020-10-20 21:57:52 +02:00
Hunlongyu
0f015b26e2 🎢 优化 IPTV 的搜索框样式 2020-10-20 09:27:13 +08:00
haiyangcui
f031f9e7fd 修复确定分组的逻辑 2020-10-19 23:40:50 +02:00
haiyangcui
b38de7f393 IPTV分组 2020-10-19 22:57:53 +02:00
haiyangcui
8b5e8fd072 IPTV添加group 2020-10-19 22:15:50 +02:00
haiyangcui
58e556554e IPTV搜索功能 2020-10-19 21:21:59 +02:00
haiyangcui
cceed30d35 IPTV拖曳功能 2020-10-19 13:46:59 +02:00
haiyangcui
980b0a6e50 el-table实现IPTV页面 2020-10-19 13:46:59 +02:00
Hunlongyu
c98236222d 🎡 修复 Film table 模式下, 不加载的 BUG 2020-10-19 18:37:48 +08:00
Hunlongyu
6a7c2afb2d 🎠 代码优化 2020-10-19 18:31:46 +08:00
Hunlongyu
af482a450a 🎟 新增右键菜单 2020-10-19 18:14:42 +08:00
Hunlongyu
42b81b98cf 🎞 修复 Film 视图报错 BUG 2020-10-19 17:42:00 +08:00
Hunlongyu
da7dfed5ba 🎁 修复视频停止播放后, 下次点击按钮失效的问题 2020-10-19 17:28:17 +08:00
Hunlongyu
7d6c5482af 🎀 新增窗口最小化, 暂停视频. 从最小化恢复窗口, 视频播放 2020-10-19 16:57:32 +08:00
Hunlongyu
11e7ffc554 🎑 新增视频停止播放按钮 2020-10-19 16:31:02 +08:00
Hunlongyu
4974d57fbf 🎑 mini 模式修复 2020-10-19 13:41:50 +08:00
Hunlongyu
037bb1a2ff 🎐 升级electron依赖到最新版 2020-10-19 13:40:12 +08:00
Hunlongyu
b19870d228 🎏 优化 table 滚动条 2020-10-19 11:52:28 +08:00
Hunlongyu
de0161c560 🎎 升级依赖, 修复element 2020-10-19 11:28:52 +08:00
Hunlongyu
623a10bd13 🎎 按需引入 element button 2020-10-19 11:09:14 +08:00
Hunlongyu
c69eadbe3e 🎍 新增清理视频缓存功能 2020-10-19 11:00:11 +08:00
Hunlongyu
5f48f46cbc 🎋 table style 2020-10-19 09:35:50 +08:00
haiyangcui
694986d6c5 用el-button实现header部分的按钮 2020-10-18 22:54:21 +02:00
haiyangcui
d4ad68e030 监听EditSites页面sites数据变化 2020-10-18 21:57:39 +02:00
haiyangcui
72089bf53f Code cleanup 2020-10-18 18:05:48 +02:00
haiyangcui
0401ce6d7e Revert "记录窗口大小及位置"
This reverts commit 381b0a8735.
2020-10-18 10:11:26 +02:00
haiyangcui
09e27d4f40 添加overflow-y: auto;到el-table格式定义中 2020-10-18 09:38:06 +02:00
haiyangcui
45de6650bf 统一EditSites页面格式 2020-10-17 23:59:58 +02:00
haiyangcui
4e0fa4d980 支持sites页面拖曳排序 2020-10-17 23:35:01 +02:00
haiyangcui
7a0f8f9644 支持star页面的拖曳排序 2020-10-17 21:19:05 +02:00
haiyangcui
e43ef98ce7 支持历史页面的拖曳排序 2020-10-17 21:11:50 +02:00
haiyangcui
2abbf41ed1 header上的按钮,鼠标划过更改指针模式 2020-10-17 18:37:49 +02:00
haiyangcui
02139d3d24 header部分加入padding 2020-10-17 18:34:27 +02:00
haiyangcui
d86a10d753 el-table实现EditSites页面 2020-10-17 18:09:17 +02:00
haiyangcui
dc8bdb29dc 解决有更新行高亮的问题 2020-10-17 14:12:24 +02:00
haiyangcui
98b019be5f 更新listpage样式 2020-10-17 13:55:04 +02:00
haiyangcui
e6d1698d62 删除star页面无用格式定义 2020-10-17 13:43:37 +02:00
haiyangcui
6c1e6c511f 删除无用的样式 2020-10-17 12:13:52 +02:00
haiyangcui
1a1393615c 套用listpage样式 2020-10-17 12:08:30 +02:00
cuiocean
5930690144 Merge pull request #292 from buvta/patch-3
el重写Star.vue,用排序替代拖拽
2020-10-17 12:07:09 +02:00
haiyangcui
091493fa77 定义listpage样式 2020-10-17 11:23:53 +02:00
buvta
cd1c4eaffe el重写Star.vue,用排序替代拖拽 2020-10-17 13:03:34 +08:00
cuiocean
1bb217d84f Merge pull request #289 from buvta/patch-4
去掉表格底部横线
2020-10-16 18:02:26 +02:00
buvta
92eac6d2dd 去掉表格底部横线 2020-10-16 23:49:12 +08:00
haiyangcui
b067cfa143 解决History页面自适应问题 2020-10-16 16:16:21 +02:00
haiyangcui
99d21fd7fe 设置不显示table的border,无需响应header-click打开关闭border 2020-10-16 14:46:24 +02:00
Hunlongyu
b167f711d0 🎄 修改历史界面样式 2020-10-16 18:03:20 +08:00
Hunlongyu
7d4ffeed87 🎃 移除 element 全局导入, 修改 star 样式 2020-10-16 18:00:13 +08:00
haiyangcui
7e94ef8025 更新el-table背景色 2020-10-15 22:30:54 +02:00
haiyangcui
1a36bc85b9 更新el-table背景色 2020-10-15 22:21:54 +02:00
haiyangcui
f7ed0e1c29 更新el-button的样式 2020-10-15 21:25:14 +02:00
cuiocean
970a359aba Merge pull request #280 from buvta/forPR1
改写History.vue
2020-10-15 21:15:31 +02:00
haiyangcui
be0441d042 设置不同主题下el-table的css样式 2020-10-15 21:12:42 +02:00
buvta
3fc350e6d6 el改写History.vue 2020-10-16 01:07:08 +08:00
buvta
b26a4d4f27 全局引入elementUI 2020-10-16 01:00:28 +08:00
haiyangcui
454ea48891 格式化Detail.vue 2020-10-14 23:02:02 +02:00
haiyangcui
d564d0928b 播放直播源时VideoTitle显示直播频道的名字 2020-10-13 22:08:40 +02:00
haiyangcui
2562119bad 支持导入源时multiSelections 2020-10-13 21:47:22 +02:00
haiyangcui
1a7aaa8dff 导入源时不删除原有源,但会检查源是否有重复,不添加重复的源 2020-10-13 21:44:24 +02:00
haiyangcui
583a768068 改进函数名 2020-10-12 18:26:27 +02:00
haiyangcui
6216ad96d6 源编辑页面添加清空功能 2020-10-12 18:25:24 +02:00
haiyangcui
766b1d458f IPTV添加置顶功能 2020-10-11 14:46:52 +02:00
haiyangcui
6a0699ec20 添加源置顶功能 2020-10-11 14:46:13 +02:00
haiyangcui
a6fd748e09 统一按钮名称和对话框名称 2020-10-11 13:47:18 +02:00
cuiocean
064ff38650 Merge pull request #271 from buvta/master
使用对话框新增或修改源
2020-10-11 13:39:25 +02:00
buvta
1c74174a3a 使用对话框新增或编辑源
bug已解决
2020-10-11 14:11:30 +08:00
haiyangcui
0625c4945f 支持'极品''喜欢看' 2020-10-11 00:05:49 +02:00
haiyangcui
d485a5733e 支持'喜欢看' 2020-10-10 10:15:09 +02:00
haiyangcui
5ab5cee6dc 支持'极品' 2020-10-09 21:06:11 +02:00
33 changed files with 3835 additions and 2517 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "zy",
"version": "2.5.2-1",
"version": "2.5.6",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -17,30 +17,32 @@
},
"main": "background.js",
"dependencies": {
"axios": "^0.19.2",
"axios": "^0.20.0",
"cheerio": "^1.0.0-rc.3",
"child_process": "^1.0.2",
"core-js": "^3.6.5",
"cors": "^2.8.5",
"dexie": "^3.0.1",
"dexie": "^3.0.2",
"electron-localshortcut": "^3.2.1",
"electron-updater": "^4.3.5",
"element-ui": "^2.13.2",
"express": "^4.17.1",
"fast-xml-parser": "^3.17.4",
"html2canvas": "^1.0.0-rc.5",
"html2canvas": "^1.0.0-rc.7",
"iptv-playlist-parser": "^0.5.0",
"m3u": "0.0.2",
"modern-normalize": "^0.6.0",
"modern-normalize": "^1.0.0",
"mousetrap": "^1.6.5",
"qrcode.vue": "^1.7.0",
"randomstring": "^1.1.5",
"vue": "^2.6.11",
"v-fit-columns": "^0.2.0",
"vue": "^2.6.12",
"vue-infinite-loading": "^2.4.5",
"vue-waterfall-plugin": "^1.0.7",
"vuedraggable": "^2.24.1",
"vuex": "^3.4.0",
"xgplayer": "^2.9.10",
"xgplayer-hls.js": "^2.2.3"
"vue-waterfall-plugin": "^1.1.0",
"vuedraggable": "^2.24.2",
"vuex": "^3.5.1",
"xgplayer": "^2.13.0",
"xgplayer-hls.js": "^2.2.5"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.4.0",
@@ -50,7 +52,7 @@
"@vue/eslint-config-standard": "^5.1.2",
"babel-eslint": "^10.1.0",
"babel-plugin-component": "^1.1.1",
"electron": "^9.3.1",
"electron": "^10.1.4",
"electron-devtools-installer": "^3.1.0",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
@@ -61,6 +63,6 @@
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"vue-cli-plugin-electron-builder": "2.0.0-rc.4",
"vue-template-compiler": "^2.6.11"
"vue-template-compiler": "^2.6.12"
}
}

View File

@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>

View File

@@ -9,6 +9,7 @@
<Star v-show="view === 'Star'" />
<History v-show="view === 'History'" />
<Setting v-show="view === 'Setting'" />
<EditSites v-if="view === 'EditSites'"/>
</div>
<transition name="slide">
<Detail v-if="detail.show"/>
@@ -16,68 +17,15 @@
<transition name="slide">
<Share v-if="share.show"/>
</transition>
<transition name="slide">
<EditSites v-if="editSites.show"/>
</transition>
</div>
</template>
<script>
import { setting } from './lib/dexie'
const { remote } = require('electron')
export default {
name: 'App',
data () {
return {
appTheme: 'theme-light',
winSizePosition: {
x: 0,
y: 0,
width: 0,
height: 0
}
}
},
created () {
// 窗口创建口,检查是否有窗口大小位置的记录,如果有的话,更新窗口位置及大小
setting.find().then(res => {
if (res.windowSizePosition) {
var win = remote.getCurrentWindow()
win.setBounds({
x: res.windowSizePosition.x,
y: res.windowSizePosition.y,
width: res.windowSizePosition.width,
height: res.windowSizePosition.height
})
}
})
var win = remote.getCurrentWindow()
this.winSizePosition = {
x: win.getPosition()[0],
y: win.getPosition()[1],
width: win.getSize()[0],
height: win.getSize()[1]
}
},
updated () {
// 本来想hook up到beforedestroy 但不工作
// 每当窗口更新时检查窗口大小及位置记录到setting数据库中
const win = remote.getCurrentWindow()
var newWinSizePosition = {
x: win.getPosition()[0],
y: win.getPosition()[1],
width: win.getSize()[0],
height: win.getSize()[1]
}
if (newWinSizePosition.x !== this.winSizePosition.x ||
newWinSizePosition.y !== this.winSizePosition.y ||
newWinSizePosition.width !== this.winSizePosition.width ||
newWinSizePosition.height !== this.winSizePosition.height) {
this.winSizePosition = newWinSizePosition
setting.find().then(res => {
res.windowSizePosition = newWinSizePosition
setting.update(res)
})
appTheme: 'theme-light'
}
},
computed: {

View File

@@ -124,6 +124,12 @@
flex: 1;
border-bottom: 1px solid;
overflow: auto;
.el-table__row td{
border: none;
}
.el-table::before{
height: 0px;
}
ul{
list-style: none;
padding: 0;
@@ -164,6 +170,9 @@
&.note{
width: 10%;
}
&.info{
width: 10%;
}
&.operate{
.btn{
width: 40px;
@@ -177,7 +186,7 @@
// scroll
.zy-scroll{
&::-webkit-scrollbar{
width: 5px;
width: 10px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
@@ -189,6 +198,87 @@
position: absolute;
}
}
// Page of list using el-table
.listpage{
position: absolute;
left: 80px;
right: 20px;
top: 40px;
bottom: 20px;
width: calc(100% - 100px);
height: calc(100% - 60px);
border-radius: 5px;
.listpage-content{
height: 100%;
position: relative;
font-size: 1rem;
.listpage-header{
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 50px;
padding-right: 50px;
.el-button{
font-size: 1rem;
border: none;
&:hover{
cursor: pointer;
}
}
.is-loading:before {
background-color: none !important;
}
.el-input{
width: 200px;
}
}
.listpage-body{
height: calc(100% - 40px);
overflow-y: auto;
.el-table::before{
height: 0px;
}
.el-table{
height: 100%;
width: 100%;
overflow-y: auto;
font-size: 1rem;
}
.el-table__body-wrapper{
&::-webkit-scrollbar{
width: 10px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
position: absolute;
}
&::-webkit-scrollbar-track {
border-radius: 10px;
position: absolute;
}
}
.el-input{
width: 200px;
}
.el-table__body td,.el-table__body th{
border-bottom: 1px solid;
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
transform: scale(1.02);
}
.el-table .highlight{
color: var(--highlight-color) !important;
}
.el-button{
font-size: 1rem;
}
}
}
}
// loading
.zy-loading{
width: 100%;

View File

@@ -65,7 +65,7 @@
--p-c-9: #f4f7f799;
--p-fc-1: #ffffff;
--p-fc-2: #FFFFF3;
--p-fc-3: #f15c5c;
--p-fc-3: #177ea7;
--p-bgc-1: #ff8499;
--p-bgc-2: #fea1b2;
--p-bsc: 0 1px 3px #ef528533, 0 1px 2px #ef528544;

View File

@@ -62,7 +62,9 @@
border-bottom-color: var(--d-c-2);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
&:hover{
box-shadow: var(--d-bsc-hover);
box-shadow: var(--d-bsc);
background-color: var(--d-bgc-2);
color: #fff;
}
span{
&.btn:hover{
@@ -353,4 +355,72 @@
background-color: var(--d-bgc-1);
box-shadow: var(--d-bsc);
}
// Page of list using el-table
.listpage{
color: var(--d-fc-2);
.listpage-content{
.listpage-header{
border-bottom-color: var(--d-c-3);
.btn{
&:hover{
color: var(--d-fc-3)
}
}
.el-button{
background-color: var(--d-bgc-2);
color: var(--d-fc-2);
&:hover{
color: var(--d-fc-3)
}
}
.el-input{
input{
background-color: var(--d-bgc-1);
border: 1px solid var(--d-bgc-1);
color: var(--d-fc-2);
}
}
}
.listpage-body{
/* 设置el-table的样式*/
.el-table{
color: var(--d-fc-1);
background-color: var(--d-bgc-1);
}
.el-input{
input{
background-color: var(--d-bgc-2);
border: 1px solid var(--d-bgc-2);
color: var(--d-fc-1);
}
}
.el-table__header th, .el-table__header tr, .el-table__body td,.el-table__body th{
color: var(--d-fc-1);
background-color: var(--d-bgc-1);
border-bottom-color: var(--d-c-2);
}
.el-table__body-wrapper{
&:hover{
&::-webkit-scrollbar-thumb {
box-shadow: var(--d-bsc-scroll);
background: var(--d-c-5);
}
&::-webkit-scrollbar-track {
box-shadow: var(--d-bsc-scroll);
background: var(--d-bgc-1);
}
}
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
background-color: var(--d-bgc-2);
}
.el-button{
color: var(--d-fc-1);
&:hover{
color: var(--d-fc-3)
}
}
}
}
}
}

View File

@@ -250,7 +250,7 @@
color: var(--g-fc-3);
}
&:hover{
background-color: var(--d-c-3);
background-color: var(--g-c-3);
}
}
}
@@ -351,4 +351,72 @@
background-color: var(--g-bgc-1);
box-shadow: var(--g-bsc);
}
// Page of list using el-table
.listpage{
color: var(--g-fc-2);
.listpage-content{
.listpage-header{
border-bottom-color: var(--g-c-3);
.btn{
&:hover{
color: var(--g-fc-3)
}
}
.el-button{
background-color: var(--g-bgc-2);
color: var(--g-fc-2);
&:hover{
color: var(--g-fc-3)
}
}
.el-input{
input{
background-color: var(--g-bgc-1);
border: 1px solid var(--g-bgc-1);
color: var(--g-fc-2);
}
}
}
.listpage-body{
/* 设置el-table的样式*/
.el-table{
color: var(--g-fc-1);
background-color: var(--g-bgc-1);
}
.el-input{
input{
background-color: var(--g-bgc-2);
border: 1px solid var(--g-bgc-2);
color: var(--g-fc-1);
}
}
.el-table__header th, .el-table__header tr, .el-table__body td,.el-table__body th{
color: var(--g-fc-1);
background-color: var(--g-bgc-1);
border-bottom-color: var(--g-c-2);
}
.el-table__body-wrapper{
&:hover{
&::-webkit-scrollbar-thumb {
box-shadow: var(--g-bsc-scroll);
background: var(--g-c-5);
}
&::-webkit-scrollbar-track {
box-shadow: var(--g-bsc-scroll);
background: var(--g-bgc-1);
}
}
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
background-color: var(--g-bgc-2);
}
.el-button{
color: var(--g-fc-1);
&:hover{
color: var(--g-fc-3)
}
}
}
}
}
}

View File

@@ -250,7 +250,7 @@
color: var(--l-fc-3);
}
&:hover{
background-color: var(--d-c-3);
background-color: var(--l-c-3);
}
}
}
@@ -351,4 +351,72 @@
background-color: var(--l-bgc-1);
box-shadow: var(--l-bsc);
}
// Page of list using el-table
.listpage{
color: var(--l-fc-2);
.listpage-content{
.listpage-header{
border-bottom-color: var(--l-c-3);
.btn{
&:hover{
color: var(--l-fc-3)
}
}
.el-button{
background-color: var(--l-bgc-2);
color: var(--l-fc-2);
&:hover{
color: var(--l-fc-3)
}
}
.el-input{
input{
background-color: var(--l-bgc-1);
border: 1px solid var(--l-bgc-1);
color: var(--l-fc-2);
}
}
}
.listpage-body{
/* 设置el-table的样式*/
.el-table{
color: var(--l-fc-1);
background-color: var(--l-bgc-1);
}
.el-input{
input{
background-color: var(--l-bgc-2);
border: 1px solid var(--l-bgc-2);
color: var(--l-fc-1);
}
}
.el-table__header th, .el-table__header tr, .el-table__body td,.el-table__body th{
color: var(--l-fc-1);
background-color: var(--l-bgc-1);
border-bottom-color: var(--l-c-2);
}
.el-table__body-wrapper{
&:hover{
&::-webkit-scrollbar-thumb {
box-shadow: var(--l-bsc-scroll);
background: var(--l-c-5);
}
&::-webkit-scrollbar-track {
box-shadow: var(--l-bsc-scroll);
background: var(--l-bgc-1);
}
}
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
background-color: var(--l-bgc-2);
}
.el-button{
color: var(--l-fc-1);
&:hover{
color: var(--l-fc-3)
}
}
}
}
}
}

View File

@@ -249,7 +249,7 @@
color: var(--p-fc-3);
}
&:hover{
background-color: var(--d-c-3);
background-color: var(--p-c-3);
}
}
}
@@ -350,4 +350,72 @@
background-color: var(--p-bgc-1);
box-shadow: var(--p-bsc);
}
// Page of list using el-table
.listpage{
color: var(--p-fc-2);
.listpage-content{
.listpage-header{
border-bottom-color: var(--p-c-3);
.btn{
&:hover{
color: var(--p-fc-3)
}
}
.el-button{
background-color: var(--p-bgc-2);
color: var(--p-fc-2);
&:hover{
color: var(--p-fc-3)
}
}
.el-input{
input{
background-color: var(--p-bgc-1);
border: 1px solid var(--p-bgc-1);
color: var(--p-fc-2);
}
}
}
.listpage-body{
/* 设置el-table的样式*/
.el-table{
color: var(--p-fc-1);
background-color: var(--p-bgc-1);
}
.el-input{
input{
background-color: var(--p-bgc-2);
border: 1px solid var(--p-bgc-2);
color: var(--p-fc-1);
}
}
.el-table__header th, .el-table__header tr, .el-table__body td,.el-table__body th{
color: var(--p-fc-1);
background-color: var(--p-bgc-1);
border-bottom-color: var(--p-c-2);
}
.el-table__body-wrapper{
&:hover{
&::-webkit-scrollbar-thumb {
box-shadow: var(--p-bsc-scroll);
background: var(--p-c-5);
}
&::-webkit-scrollbar-track {
box-shadow: var(--p-bsc-scroll);
background: var(--p-bgc-1);
}
}
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
background-color: var(--p-bgc-2);
}
.el-button{
color: var(--p-fc-1);
&:hover{
color: var(--p-fc-3)
}
}
}
}
}
}

View File

@@ -4,10 +4,11 @@ import './lib/site/server'
import { app, protocol, BrowserWindow, globalShortcut, ipcMain } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
import { initUpdater } from './lib/update/update'
const isDevelopment = process.env.NODE_ENV !== 'production'
// 允许跨域
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors')
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors') // 允许跨域
app.commandLine.appendSwitch('--ignore-certificate-errors', 'true') // 忽略证书相关错误
let win
let mini
@@ -22,6 +23,7 @@ function createWindow () {
resizable: true,
webPreferences: {
webSecurity: false,
enableRemoteModule: true,
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
}
})
@@ -34,6 +36,8 @@ function createWindow () {
win.loadURL('app://./index.html')
}
initUpdater(win)
win.on('closed', () => {
win = null
})
@@ -48,7 +52,9 @@ function createMini () {
frame: false,
resizable: true,
webPreferences: {
sandbox: false,
webSecurity: false,
enableRemoteModule: true,
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
}
})
@@ -71,6 +77,7 @@ if (process.platform === 'darwin') {
}
if (process.platform === 'Linux') {
app.disableHardwareAcceleration()
app.commandLine.appendSwitch('--no-sandbox') // linux 关闭沙盒模式
}
app.allowRendererProcessReuse = true

View File

@@ -69,29 +69,15 @@ export default {
set (val) {
this.SET_DETAIL(val)
}
},
editSites: {
get () {
return this.$store.getters.getEditSites
},
set (val) {
this.SET_EDITSITES(val)
}
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_EDITSITES']),
...mapMutations(['SET_VIEW', 'SET_DETAIL']),
changeView (e) {
this.view = e
// ChangeView 的时候关闭Detail和EditSites页面
this.detail = {
show: false
}
if (this.editSites.show === true) {
this.editSites = {
show: false
}
}
}
}
}

View File

@@ -4,7 +4,14 @@
<div class="detail-header">
<span class="detail-title">详情</span>
<span class="detail-close zy-svg" @click="close">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="closeIconTitle">
<svg
role="img"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
aria-labelledby="closeIconTitle"
>
<title id="closeIconTitle">关闭</title>
<path d="M6.34314575 6.34314575L17.6568542 17.6568542M6.34314575 17.6568542L17.6568542 6.34314575"></path>
</svg>
@@ -44,7 +51,8 @@
</select>
</span>
</div>
<div class="desc" v-show="info.des">{{info.des}}</div>
<div
class="desc" v-show="info.des">{{info.des}}</div>
<div class="m3u8">
<div class="box">
<span v-for="(i, j) in m3u8List" :key="j" @click="playEvent(j)">{{i | ftName}}</span>
@@ -72,7 +80,7 @@ export default {
info: {},
playOnline: false,
selectedOnlineSite: '哔嘀',
onlineSites: ['哔嘀', '素白白', '简影', '1080影视']
onlineSites: ['哔嘀', '素白白', '简影', '极品', '喜欢看', '1080影视']
}
},
filters: {
@@ -133,59 +141,56 @@ export default {
this.m3u8List = dd._t.split('#')
}
},
playEvent (n) {
async playEvent (n) {
if (!this.playOnline) {
history.find({ site: this.detail.key, ids: this.detail.info.id }).then(res => {
if (res) {
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: n, site: this.detail.site } }
} else {
this.video = { key: this.detail.key, info: { id: this.detail.info.id, name: this.detail.info.name, index: n, site: this.detail.site } }
}
})
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
if (db) {
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: n, site: this.detail.site } }
} else {
this.video = { key: this.detail.key, info: { id: this.detail.info.id, name: this.detail.info.name, index: n, site: this.detail.site } }
}
this.view = 'Play'
this.detail.show = false
} else {
history.find({ site: this.detail.key, ids: this.detail.info.id }).then(res => {
if (res) {
res.index = n
history.update(res.id, res)
} else {
const doc = {
site: this.detail.key,
ids: this.detail.info.id,
name: this.detail.info.name,
type: this.detail.info.type,
year: this.detail.info.year,
index: n,
time: ''
}
history.add(doc)
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
if (db) {
db.index = n
history.update(db.id, db)
} else {
const doc = {
site: this.detail.key,
ids: this.detail.info.id,
name: this.detail.info.name,
type: this.detail.info.type,
year: this.detail.info.year,
index: n,
time: ''
}
})
this.playVideoOnline(this.detail.info.name, n)
history.add(doc)
}
onlineVideo.playVideoOnline(this.selectedOnlineSite, this.detail.info.name, n)
}
},
starEvent () {
star.find({ key: this.detail.key, ids: this.info.id }).then(res => {
if (res) {
this.$message.info('该影片已被收藏')
} else {
const docs = {
key: this.detail.key,
ids: this.info.id,
name: this.info.name,
type: this.info.type,
year: this.info.year,
last: this.info.last,
note: this.info.note
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
})
async starEvent () {
const db = await star.find({ key: this.detail.key, ids: this.info.id })
if (db) {
this.$message.info('该影片已被收藏')
} else {
const docs = {
key: this.detail.key,
ids: this.info.id,
site: this.detail.site,
name: this.info.name,
type: this.info.type,
year: this.info.year,
note: this.info.note,
last: this.info.last
}
}).catch(() => {
this.$message.warning('收藏失败')
})
star.add(docs).then(res => {
this.$message.success('收藏成功')
})
}
},
togglePlayOnlineEvent () {
this.playOnline = !this.playOnline
@@ -210,6 +215,9 @@ export default {
case '简影':
onlineVideo.playVideoOnSyrme(videoName, videoIndex)
break
case '极品':
onlineVideo.playVideoOnJpysvip(videoName, videoIndex)
break
default:
this.$message.console.error(`不支持该网站:${this.selectedOnlineSite}`)
}
@@ -326,7 +334,7 @@ export default {
}
</script>
<style lang="scss" scoped>
.detail{
.detail {
position: absolute;
left: 80px;
right: 20px;
@@ -334,28 +342,28 @@ export default {
width: calc(100% - 100px);
height: calc(100% - 40px);
z-index: 888;
.detail-content{
.detail-content {
height: calc(100% - 10px);
padding: 0 60px;
position: relative;
.detail-header{
.detail-header {
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
.detail-title{
.detail-title {
font-size: 16px;
}
.detail-close{
.detail-close {
cursor: pointer;
}
}
}
.detail-body{
.detail-body {
height: calc(100% - 50px);
overflow-y: auto;
.info{
.info {
width: 100%;
padding: 10px;
display: flex;
@@ -366,47 +374,54 @@ export default {
border-radius: 2px;
margin-bottom: 10px;
height: auto;
.info-left{
.info-left {
width: 200px;
height: 100%;
img{
img {
width: 100%;
height: auto;
}
}
.info-right{
.info-right {
flex: 1;
margin-left: 20px;
.name{
.name {
font-size: 20px;
margin-bottom: 10px;
font-weight: bold;
}
.director, .actor, .type, .area, .lang, .year, .last, .note{
.director,
.actor,
.type,
.area,
.lang,
.year,
.last,
.note {
font-size: 14px;
line-height: 26px;
}
.rate{
.rate {
font-size: 16px;
line-height: 26px;
font-weight: bolder;
}
}
}
.operate{
.operate {
border: 1px solid;
padding: 10px;
width: 100%;
margin-bottom: 10px;
border-radius: 2px;
span{
span {
margin-right: 20px;
font-size: 14px;
cursor: pointer;
user-select: none;
}
}
.desc{
.desc {
border: 1px solid;
padding: 10px;
width: 100%;
@@ -415,15 +430,15 @@ export default {
font-size: 14px;
line-height: 20px;
}
.m3u8{
.m3u8 {
border: 1px solid;
padding: 10px 0 10px 10px;
width: 100%;
margin-bottom: 10px;
border-radius: 2px;
.box{
.box {
width: 100%;
span{
span {
display: inline-block;
font-size: 12px;
border: 1px solid;
@@ -435,7 +450,7 @@ export default {
}
}
}
.detail-mask{
.detail-mask {
position: absolute;
top: 50px;
left: 0;
@@ -457,28 +472,37 @@ export default {
@keyframes load4 {
0%,
100% {
box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0;
box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em,
0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0;
}
12.5% {
box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em;
box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em,
0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em;
}
25% {
box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em;
box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0,
0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em;
}
37.5% {
box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;
box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em,
0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;
}
50% {
box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;
box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em,
0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;
}
62.5% {
box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;
box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em,
0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;
}
75% {
box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;
box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em,
2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em,
-2em -2em 0 0;
}
87.5% {
box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;
box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em,
0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;
}
}
}

View File

@@ -1,100 +1,157 @@
<template>
<div class="detail">
<div class="detail-content">
<div class="detail-header">
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="openAddSite">添加新源</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="exportSites">导出</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="importSites">导入</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="resetSites">重置</div>
</div>
<span class="detail-close zy-svg" @click="close">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="closeIconTitle">
<title id="closeIconTitle">关闭</title>
<path d="M6.34314575 6.34314575L17.6568542 17.6568542M6.34314575 17.6568542L17.6568542 6.34314575"></path>
</svg>
</span>
<div class="listpage" id="editSites">
<div class="listpage-content">
<div class="listpage-header" v-show="!enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理分组">></el-switch>
<el-button @click="addSite" icon="el-icon-document-add">新增</el-button>
<el-button @click="exportSites" icon="el-icon-upload2" >导出</el-button>
<el-button @click="importSites" icon="el-icon-download">导入</el-button>
<el-button @click="checkAllSite" icon="el-icon-refresh" :loading="checkAllSiteLoading">检测</el-button>
<el-button @click="removeAllSites" icon="el-icon-delete-solid">清空</el-button>
<el-button @click="resetSitesEvent" icon="el-icon-refresh-left">重置</el-button>
</div>
<div class="detail-body zy-scroll">
<div class="zy-table">
<div class="tBody zy-scroll">
<div class="addSites-box zy-scroll" v-show="showAddSite">
<ul>
<li >
<span class="name">源名称</span>
<span class="name">API接口</span>
<span class="name">DOWNLOAD接口</span>
<span class="operate">
<span class="btn"></span>
<span class="btn"></span>
</span>
</li>
<li>
<span class="name" style="display:inline-block;vertical-align:middle">
<input style="height: 30px" v-model="newSite.name">
</span>
<span class="name" style="display:inline-block;vertical-align:middle">
<input style="height: 30px" v-model="newSite.api">
</span>
<span class="name" style="display:inline-block;vertical-align:middle">
<input style="height: 30px" v-model="newSite.download" placeholder="可以为空">
</span>
<span class="operate">
<span class="btn" @click="addNewSite">添加</span>
<span class="btn" @click="closeAddSite">关闭</span>
</span>
</li>
<li ></li>
</ul>
</div>
<ul>
<draggable v-model="sites" @change="listUpdatedEvent">
<transition-group>
<li v-for="(i, j) in sites" :key="j">
<span class="name">{{i.name}}</span>
<span class="operate">
<span class="btn" @click.stop="removeEvent(i)">删除</span>
</span>
</li>
</transition-group>
</draggable>
</ul>
</div>
</div>
<div class="listpage-header" v-show="enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理分组"></el-switch>
<el-input placeholder="新组名" v-model="batchGroupName"></el-input>
<el-switch v-model="batchIsActive" :active-value="1" :inactive-value="0" active-text="自选源"></el-switch>
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit">保存</el-button>
</div>
<div class="listpage-body" id="sites-table">
<el-table
ref="editSitesTable"
size="mini" fit height="100%" row-key="id"
:data="sites"
:key="tableKey"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange">
<el-table-column
type="selection"
v-if="enableBatchEdit">
</el-table-column>
<el-table-column
prop="name"
label="资源名">
</el-table-column>
<el-table-column
prop="isActive"
label="自选源">
<template slot-scope="scope">
<el-switch
v-model="scope.row.isActive"
:active-value="1"
:inactive-value="0"
@change='isActiveChangeEvent'>
</el-switch>
</template>
</el-table-column>
<el-table-column
prop="group"
label="分组"
:filters="getFilters"
:filter-method="filterHandle"
filter-placement="bottom-end">
<template slot-scope="scope">
<el-button type="text">{{scope.row.group}}</el-button>
</template>
</el-table-column>
<el-table-column label="状态" width="120">
<template slot-scope="scope">
<span v-show="scope.row.status === ''">
<i class="el-icon-loading"></i>
检测中...
</span>
<span v-show="scope.row.status !== ''">{{scope.row.status}}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
header-align="right"
align="right">
<template slot-scope="scope">
<el-button size="mini" @click.stop="moveToTopEvent(scope.row)" type="text">置顶</el-button>
<el-button size="mini" @click.stop="editSite(scope.row)" type="text">编辑</el-button>
<el-button size="mini" @click.stop="removeEvent(scope.row)" type="text">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 编辑页面 -->
<div>
<el-dialog :visible.sync="dialogVisible" v-if='dialogVisible' :title="dialogType==='edit'?'编辑源':'新增源'" :append-to-body="true" @close="closeDialog">
<el-form :model="siteInfo" ref='siteInfo' label-width="75px" label-position="left" :rules="rules">
<el-form-item label="源站名" prop='name'>
<el-input v-model="siteInfo.name" placeholder="请输入源站名" />
</el-form-item>
<el-form-item label="API接口" prop='api'>
<el-input v-model="siteInfo.api" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入API接口地址"/>
</el-form-item>
<el-form-item label="下载接口" prop='download'>
<el-input v-model="siteInfo.download" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入Download接口地址可以空着"/>
</el-form-item>
<el-form-item label="分组" prop='group'>
<el-select v-model="siteInfo.group" allow-create filterable default-first-option placeholder="请输入分组">
<el-option v-for="item in siteGroup" :key="item" :label="item" :value="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="源站标识" prop='key'>
<el-input v-model="siteInfo.key" placeholder="请输入源站标识,如果为空,系统则自动生成" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取消</el-button>
<el-button type="primary" @click="addOrEditSite">保存</el-button>
</span>
</el-dialog>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { sites, setting } from '../lib/dexie'
import draggable from 'vuedraggable'
import { sites } from '../lib/dexie'
import zy from '../lib/site/tools'
import { remote } from 'electron'
import { sites as defaultSites } from '../lib/dexie/initData'
import fs from 'fs'
import Sortable from 'sortablejs'
export default {
name: 'editSites',
data () {
return {
show: false,
sites: [],
showAddSite: false,
newSite: {
dialogType: 'new',
dialogVisible: false,
siteInfo: {
key: '',
name: '',
api: '',
download: ''
}
download: '',
group: '',
isActive: 1
},
siteGroup: [],
rules: {
name: [
{ required: true, message: '源站名不能为空', trigger: 'blur' }
],
api: [
{ required: true, message: 'API地址不能为空', trigger: 'blur' }
],
download: [
{ required: false, trigger: 'blur' }
]
},
enableBatchEdit: false,
batchGroupName: '',
batchIsActive: 1,
multipleSelection: [],
tableKey: 1,
checkAllSiteLoading: false,
editeOldkey: ''
}
},
components: {
draggable
},
computed: {
setting: {
get () {
@@ -111,19 +168,93 @@ export default {
set (val) {
this.SET_EDITSITES(val)
}
},
getFilters () {
const groups = [...new Set(this.sites.map(site => site.group))]
var filters = []
groups.forEach(g => {
var doc = {
text: g,
value: g
}
filters.push(doc)
})
return filters
}
},
methods: {
...mapMutations(['SET_SETTING', 'SET_EDITSITES']),
close () {
this.editSites.show = false
filterHandle (value, row) {
return row.group === value
},
handleSelectionChange (rows) {
this.multipleSelection = rows
},
handleSortChange (column, prop, order) {
this.updateDatabase(this.sites)
},
saveBatchEdit () {
this.multipleSelection.forEach(ele => {
if (this.batchGroupName) {
ele.group = this.batchGroupName
}
ele.isActive = this.batchIsActive
})
this.updateDatabase()
},
getSites () {
sites.all().then(res => {
this.sites = res
this.editSites = {
sites: res
}
})
for (const i of this.sites) {
delete i.status
}
},
getSitesGroup () {
const arr = []
for (const i of this.sites) {
if (arr.indexOf(i.group) < 0) {
arr.push(i.group)
}
}
this.siteGroup = arr
},
addSite () {
this.getSitesGroup()
this.dialogType = 'new'
this.dialogVisible = true
this.siteInfo = {
key: '',
name: '',
api: '',
download: '',
group: '',
isActive: 1
}
},
editSite (siteInfo) {
this.getSitesGroup()
if (this.checkAllSiteLoading) {
this.$message.info('正在检测, 请勿操作.')
return false
}
this.dialogType = 'edit'
this.dialogVisible = true
this.siteInfo = siteInfo
this.editeOldkey = siteInfo.key
},
closeDialog () {
this.dialogVisible = false
this.getSites()
},
removeEvent (e) {
if (this.checkAllSiteLoading) {
this.$message.info('正在检测, 请勿操作.')
return false
}
sites.remove(e.id).then(res => {
this.getSites()
}).catch(err => {
@@ -141,39 +272,56 @@ export default {
})
})
},
openAddSite () {
this.showAddSite = true
checkSiteKey (e) {
if (this.dialogType === 'edit' && this.editeOldkey === this.siteInfo.key) {
return true
} else {
for (const i of this.sites) {
if (i.key === this.siteInfo.key) {
this.$message.warning(`源站标识: ${i.key} 已存在, 请勿重复填写.`)
return false
}
}
return true
}
},
closeAddSite () {
this.showAddSite = false
},
addNewSite () {
if (!this.newSite.name || !this.newSite.api) {
addOrEditSite () {
if (!this.siteInfo.name || !this.siteInfo.api) {
this.$message.error('名称和API接口不能为空。')
return
return false
}
if (!this.checkSiteKey()) {
return false
}
var randomstring = require('randomstring')
var doc = {
key: randomstring.generate(6),
id: this.sites[this.sites.length - 1].id + 1,
name: this.newSite.name,
api: this.newSite.api,
download: this.newSite.download
key: this.dialogType === 'edit' ? this.siteInfo.key : this.siteInfo.key ? this.siteInfo.key : randomstring.generate(6),
id: this.dialogType === 'edit' ? this.siteInfo.id : this.sites[this.sites.length - 1].id + 1,
name: this.siteInfo.name,
api: this.siteInfo.api,
download: this.siteInfo.download,
group: this.siteInfo.group,
isActive: this.siteInfo.isActive
}
if (this.dialogType === 'edit') sites.remove(this.siteInfo.id)
sites.add(doc).then(res => {
this.newSite = {
this.siteInfo = {
key: '',
name: '',
api: '',
download: ''
download: '',
group: ''
}
this.$message.success('添加新源成功!')
this.dialogType === 'edit' ? this.$message.success('修改成功!') : this.$message.success('新增源成功!')
this.dialogVisible = false
this.getSites()
})
this.editeOldkey = ''
},
exportSites () {
this.getSites()
const arr = [...this.sites]
const str = JSON.stringify(arr, null, 4)
const str = JSON.stringify(arr, null, 2)
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
@@ -197,72 +345,109 @@ export default {
{ name: 'Normal text file', extensions: ['txt'] },
{ name: 'All types', extensions: ['*'] }
],
properties: ['openFile']
properties: ['openFile', 'multiSelections']
}
remote.dialog.showOpenDialog(options).then(result => {
if (!result.canceled) {
sites.clear()
result.filePaths.forEach(file => {
var str = fs.readFileSync(file)
const json = JSON.parse(str)
sites.bulkAdd(json).then(e => {
this.getSites()
this.d.site = json[0].key
setting.update(this.d).then(res => {
this.setting = this.d
})
json.forEach(ele => {
if (ele.api && this.sites.filter(x => x.key === ele.key).length === 0 && this.sites.filter(x => x.name === ele.name && x.api === ele.api).length === 0) {
// 不含该key 同时也不含名字和url一样的
if (ele.isActive === undefined) {
ele.isActive = 1
}
if (ele.group === undefined) {
ele.group = '导入'
}
this.sites.push(ele)
}
})
this.resetId(this.sites)
sites.clear().then(sites.bulkAdd(this.sites))
this.$message.success('导入成功')
}).catch(err => {
this.$message.error(err)
this.getSites()
})
}
})
},
resetSites () {
sites.clear()
sites.bulkAdd(defaultSites).then(e => {
this.getSites()
this.$message.success('重置源成功')
resetSitesEvent () {
sites.clear().then(sites.bulkAdd(defaultSites).then(this.getSites()))
this.$message.success('重置源成功')
},
moveToTopEvent (i) {
if (this.checkAllSiteLoading) {
this.$message.info('正在检测, 请勿操作.')
return false
}
this.sites.sort(function (x, y) { return x.key === i.key ? -1 : y.key === i.key ? 1 : 0 })
this.updateDatabase()
},
syncTableData () {
if (this.$refs.editSitesTable.tableData && this.$refs.editSitesTable.tableData.length === this.sites.length) {
this.sites = this.$refs.editSitesTable.tableData
}
},
isActiveChangeEvent (row) {
this.updateDatabase()
},
resetId (inArray) {
var id = 1
inArray.forEach(ele => {
ele.id = id
id += 1
})
},
updateDatabase () {
// 因为el-table的数据是单向绑定,我们先同步el-table里的数据和其绑定的数据
this.syncTableData()
sites.clear().then(res => {
var id = 1
this.sites.forEach(ele => {
ele.id = id
id += 1
})
sites.bulkAdd(this.sites).then(this.getSites())
})
},
removeAllSites () {
sites.clear().then(this.getSites())
},
rowDrop () {
const tbody = document.getElementById('sites-table').querySelector('.el-table__body-wrapper tbody')
var _this = this
Sortable.create(tbody, {
onEnd ({ newIndex, oldIndex }) {
const currRow = _this.sites.splice(oldIndex, 1)[0]
_this.sites.splice(newIndex, 0, currRow)
_this.updateDatabase()
}
})
},
async checkAllSite () {
this.checkAllSiteLoading = true
for (const i of this.sites) {
i.status = ''
this.tableKey = Math.random()
const flag = await zy.check(i.key)
if (flag) {
i.status = '可用'
} else {
i.status = '失效'
i.isActive = 0
}
this.tableKey = Math.random()
}
this.checkAllSiteLoading = false
}
},
mounted () {
this.rowDrop()
this.checkAllSiteLoading = false
},
created () {
this.getSites()
}
}
</script>
<style lang="scss" scoped>
.detail{
position: absolute;
left: 80px;
right: 20px;
bottom: 0;
width: calc(100% - 100px);
height: calc(100% - 40px);
z-index: 888;
.detail-content{
height: calc(100% - 10px);
padding: 0 60px;
position: relative;
.detail-header{
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
.detail-title{
font-size: 16px;
}
.detail-close{
cursor: pointer;
}
}
}
.detail-body{
height: calc(100% - 50px);
overflow-y: auto;
}
}
</style>

View File

@@ -13,7 +13,7 @@
<div class="vs-placeholder" @click="show.classList = true">{{type.name}}</div>
<div class="vs-options" v-show="show.classList">
<ul class="zy-scroll" style="max-height: 600px;">
<li :class="type.tid === i.tid ? 'active' : ''" v-for="i in classList" :key="i.tid" @click="classClick(i)">{{ i.name }}</li>
<li :class="type.tid === i.tid ? 'active' : ''" v-for="i in classList" :key="i.tid" @click="classClick(i)">{{ i.name | classNameFilter }}</li>
</ul>
</div>
</div>
@@ -65,7 +65,7 @@
<span class="name">{{i.name}}</span>
<span class="type">{{i.type}}</span>
<span class="time">{{i.year}}</span>
<span class="time">{{i.note}}</span>
<span class="note">{{i.note}}</span>
<span class="last">{{i.last}}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(site, i)">播放</span>
@@ -75,7 +75,7 @@
</span>
</li>
</ul>
<infinite-loading force-use-infinite-wrapper="tBody" :identifier="infiniteId" @infinite="infiniteHandler"></infinite-loading>
<infinite-loading force-use-infinite-wrapper :identifier="infiniteId" @infinite="infiniteHandler"></infinite-loading>
</div>
</div>
</div>
@@ -87,10 +87,13 @@
<ul>
<li v-for="(i, j) in searchContents" :key="j" @click="detailEvent(i.site, i)">
<span class="name">{{i.name}}</span>
<span class="type">{{i.type}}</span>
<span class="last">{{i.last}}</span>
<span class="site">{{i.site.name}}</span>
<span class="note">{{i.note}}</span>
<span class="info">{{i.site.name}}</span>
<span class="info">{{i.director}}</span>
<span class="info">{{i.type}}</span>
<span class="info">{{i.area}}</span>
<span class="info">{{i.lang}}</span>
<span class="info">{{i.year}}</span>
<span class="info">{{i.note}}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(i.site, i)">播放</span>
<span class="btn" @click.stop="starEvent(i.site, i)">收藏</span>
@@ -123,8 +126,6 @@ export default {
class: false,
classList: false,
search: false,
img: true,
table: false,
find: false
},
sites: [],
@@ -138,7 +139,7 @@ export default {
searchTxt: '',
searchContents: [],
// 福利片关键词
r18KeyWords: ['伦理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番']
r18KeyWords: ['伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番']
}
},
components: {
@@ -180,6 +181,14 @@ export default {
},
setting () {
return this.$store.getters.getSetting
},
sitesList () {
return this.$store.getters.getEditSites.sites // 需要监听的数据
}
},
filters: {
classNameFilter: (name) => {
return name.replace(/[^\u4e00-\u9fa5]/gi, '')
}
},
watch: {
@@ -189,7 +198,7 @@ export default {
searchTxt () {
this.searchChangeEvent()
},
'$store.state.editSites.sites': function () {
sitesList () {
this.getAllsites()
}
},
@@ -308,37 +317,34 @@ export default {
info: e
}
},
playEvent (site, e) {
history.find({ site: site.key, ids: e.id }).then(res => {
if (res) {
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: res.index, site: site } }
} else {
this.video = { key: site.key, info: { id: e.id, name: e.name, index: 0, site: site } }
}
})
async playEvent (site, e) {
const db = await history.find({ site: site.key, ids: e.id })
if (db) {
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: db.index, site: site } }
} else {
this.video = { key: site.key, info: { id: e.id, name: e.name, index: 0, site: site } }
}
this.view = 'Play'
},
starEvent (site, e) {
star.find({ key: site.key, ids: e.id }).then(res => {
if (res) {
this.$message.info('已存在')
} else {
const docs = {
key: site.key,
ids: e.id,
name: e.name,
type: e.type,
year: e.year,
last: e.last,
note: e.note
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
})
async starEvent (site, e) {
const db = await star.find({ key: site.key, ids: e.id })
if (db) {
this.$message.info('已存在')
} else {
const docs = {
key: site.key,
ids: e.id,
site: site,
name: e.name,
type: e.type,
year: e.year,
last: e.last,
note: e.note
}
}).catch(() => {
this.$message.warning('收藏失败')
})
star.add(docs).then(res => {
this.$message.success('收藏成功')
})
}
},
shareEvent (site, e) {
this.share = {
@@ -388,13 +394,11 @@ export default {
},
changeView () {
if (this.view === 'Film') {
if (this.show.img) {
if (this.setting.view === 'picture') {
this.$refs.waterfall.refresh()
}
this.getPage().then(() => {
this.infiniteId += 1
if (this.show.img || this.show.table) {
}
})
}
},
@@ -403,6 +407,13 @@ export default {
this.searchList = res.reverse()
})
},
searchEvent (wd) {
if (this.setting.searchAllSites) {
this.searchAllSitesEvent(this.sites, wd)
} else {
this.searchSingleSiteEvent(this.site, wd)
}
},
searchAllSitesEvent (sites, wd) {
this.searchTxt = wd
this.searchContents = []
@@ -421,13 +432,17 @@ export default {
const type = Object.prototype.toString.call(res)
if (type === '[object Array]') {
res.forEach(element => {
element.site = site
this.searchContents.push(element)
zy.detail(site.key, element.id).then(detailRes => {
detailRes.site = site
this.searchContents.push(detailRes)
})
})
}
if (type === '[object Object]') {
res.site = site
this.searchContents.push(res)
zy.detail(site.key, res.id).then(detailRes => {
detailRes.site = site
this.searchContents.push(detailRes)
})
}
})
})
@@ -440,13 +455,6 @@ export default {
})
}
},
searchEvent (wd) {
if (this.setting.searchAllSites) {
this.searchAllSitesEvent(this.sites, wd)
} else {
this.searchSingleSiteEvent(this.site, wd)
}
},
searchSingleSiteEvent (site, wd) {
var sites = []
sites.push(this.site)
@@ -464,21 +472,28 @@ export default {
this.show.class = true
this.searchContents = []
this.show.find = false
if (this.show.img) {
if (this.setting.view === 'picture') {
this.$refs.waterfall.refresh()
}
}
},
getAllsites () {
sites.all().then(res => {
this.sites = res
this.site = this.sites[0]
this.siteClick(this.site)
if (res.length <= 0) {
this.site = {}
this.type = {}
this.list = []
} else {
this.sites = res.filter((item, index, self) => {
return self.indexOf(item) >= 0 && item.isActive
})
this.site = this.sites[0]
this.siteClick(this.site)
}
})
}
},
created () {
this.getAllsites()
this.getAllSearch()
}
}

View File

@@ -1,38 +1,45 @@
<template>
<div class="history">
<div class="body zy-scroll">
<div class="zy-table">
<div class="tHeader">
<span class="btn"></span>
<span class="btn" @click="clearAllHistory">清空</span>
</div>
<div class="tBody zy-scroll">
<ul>
<li v-show="this.history.length === 0">无数据</li>
<li v-show="this.history.length > 0">
<span class="name">名字</span>
<span class="site">片源</span>
<span class="note">观看至</span>
<span class="operate">
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
</span>
</li>
<li v-for="(i, j) in history" :key="j" @click="historyItemEvent(i)">
<span class="name" @click.stop="detailEvent(i)">{{i.name}}</span>
<span class="site">{{getSiteName(i.site)}}</span>
<span class="note">{{i.index+1}}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(i)">播放</span>
<span class="btn" @click.stop="shareEvent(i)">分享</span>
<span class="btn" @click.stop="downloadEvent(i)">下载</span>
<span class="btn" @click.stop="removeHistoryItem(i)">删除</span>
</span>
</li>
</ul>
</div>
<div class="listpage" id="history">
<div class="listpage-content">
<div class="listpage-header">
<el-button @click.stop="exportHistory" icon="el-icon-upload2">导出</el-button>
<el-button @click.stop="importHistory" icon="el-icon-download">导入</el-button>
<el-button @click.stop="clearAllHistory" icon="el-icon-delete-solid">清空</el-button>
</div>
<div class="listpage-body" id="history-table">
<el-table size="mini" fit height="100%" :data="history" row-key="id" @row-click="detailEvent">
<el-table-column
prop="name"
label="片名">
</el-table-column>
<el-table-column
prop="site"
width="120"
label="片源">
<template slot-scope="scope">
<span>{{ getSiteName(scope.row.site) }}</span>
</template>
</el-table-column>
<el-table-column
prop="index"
width="120"
label="观看至">
<template slot-scope="scope">
<span>{{ scope.row.index + 1 }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
header-align="right"
align="right">
<template slot-scope="scope">
<el-button @click.stop="playEvent(scope.row)" type="text">播放</el-button>
<el-button @click.stop="shareEvent(scope.row)" type="text">分享</el-button>
<el-button @click.stop="downloadEvent(scope.row)" type="text">下载</el-button>
<el-button @click.stop="removeHistoryItem(scope.row)" type="text">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
@@ -41,12 +48,16 @@
import { mapMutations } from 'vuex'
import { history, sites } from '../lib/dexie'
import zy from '../lib/site/tools'
import Sortable from 'sortablejs'
import { remote } from 'electron'
import fs from 'fs'
const { clipboard } = require('electron')
export default {
name: 'history',
data () {
return {
history: history,
history: [],
sites: []
}
},
@@ -102,14 +113,13 @@ export default {
}
}
},
playEvent (e) {
history.find({ site: e.site, ids: e.ids }).then(res => {
if (res) {
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: res.index } }
} else {
this.video = { key: e.site, info: { id: e.ids, name: e.name, index: 0 } }
}
})
async playEvent (e) {
const db = await history.find({ site: e.site, ids: e.ids })
if (db) {
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: db.index } }
} else {
this.video = { key: e.site, info: { id: e.ids, name: e.name, index: 0 } }
}
this.view = 'Play'
},
shareEvent (e) {
@@ -161,6 +171,44 @@ export default {
}
})
},
exportHistory () {
this.getAllhistory()
const arr = [...this.history]
const str = JSON.stringify(arr, null, 2)
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] }
]
}
remote.dialog.showSaveDialog(options).then(result => {
if (!result.canceled) {
fs.writeFileSync(result.filePath, str)
this.$message.success('已保存成功')
}
}).catch(err => {
this.$message.error(err)
})
},
importHistory () {
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] }
],
properties: ['openFile', 'multiSelections']
}
remote.dialog.showOpenDialog(options).then(result => {
if (!result.canceled) {
result.filePaths.forEach(file => {
var str = fs.readFileSync(file)
const json = JSON.parse(str)
history.bulkAdd(json).then(res => {
this.$message.success('导入成功')
this.getAllhistory()
})
})
}
})
},
clearAllHistory () {
history.clear().then(res => {
this.history = []
@@ -182,44 +230,40 @@ export default {
return site.name
}
},
historyItemEvent (e) {
this.video = {
key: e.site,
info: {
id: e.ids,
name: e.name,
type: e.type,
year: e.year,
index: e.index,
time: e.time
}
}
},
removeHistoryItem (e) {
history.remove(e.id).then(res => {
this.getAllhistory()
}).catch(err => {
this.$message.warning('删除历史记录失败, 错误信息: ' + err)
})
},
updateDatabase (data) {
history.clear().then(res => {
var id = length
data.forEach(ele => {
ele.id = id
id -= 1
history.add(ele)
})
})
},
rowDrop () {
const tbody = document.getElementById('history-table').querySelector('.el-table__body-wrapper tbody')
const _this = this
Sortable.create(tbody, {
onEnd ({ newIndex, oldIndex }) {
const currRow = _this.history.splice(oldIndex, 1)[0]
_this.history.splice(newIndex, 0, currRow)
_this.updateDatabase(_this.history)
}
})
}
},
mounted () {
this.rowDrop()
},
created () {
this.getAllhistory()
}
}
</script>
<style lang="scss" scoped>
.history{
position: relative;
height: calc(100% - 40px);
width: 100%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 5px;
.body{
width: 100%;
height: 100%;
}
}
</style>

View File

@@ -1,54 +1,69 @@
<template>
<div class="detail">
<div class="detail-content">
<div class="detail-header">
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="exportSites">导出</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="importSites">导入</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="removeAllSites">清空</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="resetSites">重置</div>
</div>
<div style="width: 200px; height: 30px;">
</div>
<div class="listpage" id="IPTV">
<div class="listpage-content">
<div class="listpage-header" v-show="!enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理分组"></el-switch>
<el-button @click.stop="exportChannels" icon="el-icon-upload2" >导出</el-button>
<el-button @click.stop="importChannels" icon="el-icon-download">导入</el-button>
<el-button @click.stop="removeAllChannels" icon="el-icon-delete-solid">清空</el-button>
<el-button @click.stop="resetChannelsEvent" icon="el-icon-refresh-left">重置</el-button>
</div>
<div class="detail-header">
<div>
<div class="vs-placeholder vs-noAfter" @click="exportSites">总频道数:{{iptvList.length}}</div>
</div>
<div class="zy-select" @mouseleave="show.search = false">
<div class="vs-input" @click="show.search = true"><input v-model.trim="searchTxt" type="text" placeholder="搜索" @keyup.enter="searchEvent(searchTxt)"></div>
<div class="vs-options" v-show="show.search">
<ul class="zy-scroll" style="max-height: 600px">
<li v-for="(i, j) in searchList" :key="j" @click="searchEvent(i.keywords)">{{i.keywords}}</li>
<li v-show="searchList.length >= 1" @click="clearSearch">清空历史记录</li>
</ul>
</div>
</div>
<div class="listpage-header" v-show="enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理分组"></el-switch>
<el-input placeholder="新组名" v-model="batchGroupName"></el-input>
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit">保存</el-button>
</div>
<div class="detail-body zy-scroll">
<div class="zy-table">
<div class="tBody zy-scroll">
<ul>
<draggable v-model="iptvList" @change="listUpdatedEvent">
<transition-group>
<li v-for="(i, j) in iptvList" :key="j" @click.stop="playEvent(i)" v-show="containsearchTxt(i)">
<span class="name">{{i.name}}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(i)">播放</span>
<span class="btn" @click.stop="removeEvent(i)">删除</span>
</span>
</li>
</transition-group>
</draggable>
</ul>
</div>
</div>
<div class="listpage-body" id="iptv-table">
<el-table
ref="iptvTable"
size="mini" fit height="100%" row-key="id"
:data="filteredTableData"
@row-click="playEvent"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange">>
<el-table-column
type="selection"
v-if="enableBatchEdit">
</el-table-column>
<el-table-column
default-sort="ascending"
prop="name"
label="频道名">
<template #header>
<el-input
placeholder="搜索"
size="mini"
v-model.trim="searchTxt">
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
</template>
</el-table-column>
<el-table-column
sort-by="['group', 'name']"
sortable
:sort-method="sortByGroup"
prop="group"
label="分组"
:filters="getFilters"
:filter-method="filterHandle"
filter-placement="bottom-end">
<template slot-scope="scope">
<el-button type="text">{{scope.row.group}}</el-button>
</template>
</el-table-column>
<el-table-column
label="操作"
header-align="right"
align="right">
<template #header>
<span>总频道数:{{ iptvList.length }}</span>
</template>
<template slot-scope="scope">
<el-button @click.stop="moveToTopEvent(scope.row)" type="text">置顶</el-button>
<el-button @click.stop="removeEvent(scope.row)" type="text">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
@@ -56,25 +71,25 @@
<script>
import { mapMutations } from 'vuex'
import { iptv, iptvSearch } from '../lib/dexie'
import draggable from 'vuedraggable'
import { iptv as defaultSites } from '../lib/dexie/initData'
import { remote } from 'electron'
import fs from 'fs'
import Sortable from 'sortablejs'
export default {
name: 'iptv',
data () {
return {
iptvList: [],
searchTxt: '',
searchList: [],
searchRecordList: [],
enableBatchEdit: false,
batchGroupName: '',
multipleSelection: [],
show: {
search: false
}
}
},
components: {
draggable
},
computed: {
view: {
get () {
@@ -94,21 +109,60 @@ export default {
set (val) {
this.SET_VIDEO(val)
}
},
filteredTableData () {
if (this.searchTxt) {
return this.iptvList.filter(x => x.name.toLowerCase().includes(this.searchTxt.toLowerCase()))
} else {
return this.iptvList
}
},
getFilters () {
const groups = [...new Set(this.iptvList.map(iptv => iptv.group))]
var filters = []
groups.forEach(g => {
var doc = {
text: g,
value: g
}
filters.push(doc)
})
return filters
}
},
watch: {
view () {
this.getAllSites()
this.getChannels()
},
searchTxt () {
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
sortByGroup (a, b) {
return a.group.localeCompare(b.group, 'zh')
},
handleSelectionChange (rows) {
this.multipleSelection = rows
},
handleSortChange (column, prop, order) {
this.updateDatabase()
},
saveBatchEdit () {
if (this.multipleSelection && this.batchGroupName) {
this.multipleSelection.forEach(ele => {
ele.group = this.batchGroupName
})
}
this.updateDatabase()
},
playEvent (e) {
this.video = { iptv: { name: e.name, url: e.url } }
this.view = 'Play'
},
filterHandle (value, row) {
return row.group === value
},
containsearchTxt (i) {
if (this.searchTxt) {
return i.name.toLowerCase().includes(this.searchTxt.toLowerCase())
@@ -118,7 +172,7 @@ export default {
},
removeEvent (e) {
iptv.remove(e.id).then(res => {
this.getAllSites()
this.getChannels()
}).catch(err => {
this.$message.warning('删除频道失败, 错误信息: ' + err)
})
@@ -134,7 +188,7 @@ export default {
})
})
},
exportSites () {
exportChannels () {
const options = {
filters: [
{ name: 'm3u file', extensions: ['m3u'] },
@@ -152,7 +206,7 @@ export default {
this.$message.success('已保存成功')
} else {
const arr = [...this.iptvList]
const str = JSON.stringify(arr, null, 4)
const str = JSON.stringify(arr, null, 2)
fs.writeFileSync(result.filePath, str)
this.$message.success('已保存成功')
}
@@ -161,65 +215,104 @@ export default {
this.$message.error(err)
})
},
importSites () {
importChannels () {
const options = {
filters: [
{ name: 'm3u file', extensions: ['m3u', 'm3u8'] }
{ name: 'm3u file', extensions: ['m3u', 'm3u8'] },
{ name: 'JSON file', extensions: ['json'] }
],
properties: ['openFile', 'multiSelections']
}
remote.dialog.showOpenDialog(options).then(result => {
if (!result.canceled) {
var docs = this.iptvList
var id = docs.length
result.filePaths.forEach(file => {
const parser = require('iptv-playlist-parser')
const playlist = fs.readFileSync(file, { encoding: 'utf-8' })
const result = parser.parse(playlist)
result.items.forEach(ele => {
if (ele.name && ele.url && ele.url.includes('.m3u8')) {
var doc = {
name: ele.name,
url: ele.url
if (file.endsWith('m3u') || file.endsWith('m3u8')) {
const parser = require('iptv-playlist-parser')
const playlist = fs.readFileSync(file, { encoding: 'utf-8' })
const result = parser.parse(playlist)
result.items.forEach(ele => {
if (ele.name && ele.url && ele.url.endsWith('.m3u8')) {
var doc = {
id: id,
name: ele.name,
url: ele.url,
group: this.determineGroup(ele.name)
}
id += 1
docs.push(doc)
}
docs.push(doc)
}
})
})
} else {
// Import Json file
var str = fs.readFileSync(file)
const json = JSON.parse(str)
json.forEach(ele => {
if (ele.name && ele.url && ele.url.endsWith('.m3u8')) {
var doc = {
id: id,
name: ele.name,
url: ele.url,
group: this.determineGroup(ele.name)
}
id += 1
docs.push(doc)
}
})
}
})
// 获取url不重复的列表
const uniqueList = [...new Map(docs.map(item => [item.url, item])).values()]
// 获取name不重复的列表
const uniqueList = [...new Map(docs.map(item => [item.name, item])).values()]
iptv.clear().then(res => {
iptv.bulkAdd(uniqueList).then(e => {
this.getAllSites()
this.getChannels()
this.$message.success('导入成功')
})
})
}
})
},
resetSites () {
iptv.clear()
iptv.bulkAdd(defaultSites).then(e => {
this.getAllSites()
})
determineGroup (name) {
if (name.toLowerCase().includes('cctv') && (name.includes('蓝光') || name.includes('高清'))) {
return '央视高清'
} else if (name.toLowerCase().includes('cctv')) {
return '央视'
} else if (name.includes('卫视')) {
return '卫视'
} else if (name.includes('香港') || name.includes('澳门') || name.includes('台湾') || name.includes('凤凰')) {
return '港澳台'
} else if (name.includes('高清') || name.includes('蓝光') || name.includes('1080P')) {
return '高清'
} else {
return '其他'
}
},
removeAllSites () {
resetChannelsEvent () {
this.resetChannels(defaultSites)
},
resetChannels (newSites) {
this.resetId(newSites)
iptv.clear().then(iptv.bulkAdd(newSites).then(this.getChannels()))
},
removeAllChannels () {
iptv.clear().then(res => {
this.getAllSites()
this.getChannels()
})
},
getAllSites () {
getChannels () {
iptv.all().then(res => {
this.iptvList = res
})
},
getAllSearch () {
getSearchRecordList () {
iptvSearch.all().then(res => {
this.searchList = res.reverse()
this.searchRecordList = res.reverse()
})
},
clearSearch () {
iptvSearch.clear().then(res => {
this.getAllSearch()
this.getSearchRecordList()
})
},
searchEvent (wd) {
@@ -230,47 +323,51 @@ export default {
if (!res) {
iptvSearch.add({ keywords: wd })
}
this.getAllSearch()
this.getSearchRecordList()
})
}
},
moveToTopEvent (i) {
this.iptvList.sort(function (x, y) { return (x.name === i.name && x.url === i.url) ? -1 : (y.name === i.name && y.url === i.url) ? 1 : 0 })
this.updateDatabase()
},
syncTableData () {
if (this.$refs.iptvTable.tableData && this.$refs.iptvTable.tableData.length === this.iptvList.length) {
this.iptvList = this.$refs.iptvTable.tableData
}
},
updateDatabase () {
this.syncTableData()
iptv.clear().then(res => {
this.resetId(this.iptvList)
iptv.bulkAdd(this.iptvList)
})
},
resetId (inArray) {
var id = 1
inArray.forEach(ele => {
ele.id = id
id += 1
})
},
rowDrop () {
const tbody = document.getElementById('iptv-table').querySelector('.el-table__body-wrapper tbody')
const _this = this
Sortable.create(tbody, {
onEnd ({ newIndex, oldIndex }) {
const currRow = _this.iptvList.splice(oldIndex, 1)[0]
_this.iptvList.splice(newIndex, 0, currRow)
_this.updateDatabase()
}
})
}
},
mounted () {
this.rowDrop()
},
created () {
this.getAllSites()
this.getAllSearch()
this.getChannels()
this.getSearchRecordList()
}
}
</script>
<style lang="scss" scoped>
.detail{
position: absolute;
left: 80px;
right: 20px;
bottom: 0;
width: calc(100% - 100px);
height: calc(100% - 40px);
z-index: 888;
.detail-content{
height: calc(100% - 10px);
padding: 0 60px;
position: relative;
.detail-header{
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
.detail-title{
font-size: 16px;
}
.detail-close{
cursor: pointer;
}
}
}
.detail-body{
height: calc(100% - 100px);
overflow-y: auto;
}
}
</style>

View File

@@ -3,23 +3,20 @@
<div class="box">
<div class="title">
<span v-if="this.right.list.length > 1"> {{(video.info.index + 1)}} </span>{{name}}
<span v-if="video.key" class="right" @click="playWithExternalPalyerEvent" title="使用第三方播放器">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<polygon points="20 8 20 20 4 20 4 8"></polygon>
<polyline stroke-linejoin="round" points="8 4 12 7.917 16 4"></polyline>
</svg>
</span>
<span v-if="video.key" class="right" @click="issueEvent" title="复制调试信息">
<svg t="1596338860607" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3127" width="24" height="24">
<path d="M503.803829 63.578014c-247.050676 0-447.328072 200.277396-447.328072 447.327048 0 247.054769 200.277396 447.333188 447.328072 447.333188 247.054769 0 447.332165-200.278419 447.332165-447.333188C951.13497 263.85541 750.858598 63.578014 503.803829 63.578014L503.803829 63.578014zM503.803829 894.313336c-211.749682 0-383.408273-171.659615-383.408273-383.408273 0-211.749682 171.659615-383.40725 383.408273-383.40725 211.753775 0 383.412366 171.658591 383.412366 383.40725C887.216195 722.653721 715.557604 894.313336 503.803829 894.313336L503.803829 894.313336zM447.745069 255.897158l127.914298 0L575.659367 383.576095 447.745069 383.576095 447.745069 255.897158 447.745069 255.897158zM447.745069 425.470251l127.914298 0 0 342.058516L447.745069 767.528767 447.745069 425.470251 447.745069 425.470251zM447.745069 425.470251" p-id="3128"></path>
</svg>
</span>
</div>
<div class="player">
<div id="xgplayer"></div>
</div>
<div class="more">
<span class="zy-svg" @click="nextEvent" v-show="showNext">
<span class="zy-svg" @click="otherEvent" v-show="name !== ''">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="coloursIconTitle">
<title id="coloursIconTitle">换源</title>
<circle cx="12" cy="9" r="5"></circle>
<circle cx="9" cy="14" r="5"></circle>
<circle cx="15" cy="14" r="5"></circle>
</svg>
</span>
<span class="zy-svg" @click="nextEvent" v-show="right.list.length > 1">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="forwardIconTitle">
<title id="forwardIconTitle">下一集</title>
<path d="M10 14.74L3 19V5l7 4.26V5l12 7-12 7v-4.26z"></path>
@@ -58,8 +55,14 @@
</svg>
</span>
<span class="zy-svg" @click="miniEvent" v-show="right.list.length > 0">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="diamondIconTitle">
<title id="diamondIconTitle">精简模式</title>
<path d="M12 20L3 11M12 20L21 11M12 20L8 11M12 20L16 11M3 11L7 5M3 11H8M7 5L8 11M7 5H12M17 5L21 11M17 5L16 11M17 5H12M21 11H16M8 11H16M8 11L12 5M16 11L12 5"></path>
</svg>
</span>
<span class="zy-svg" @click="playWithExternalPalyerEvent" v-show="right.list.length > 0">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="tvIconTitle">
<title id="tvIconTitle">精简模式</title>
<title id="tvIconTitle" >使用第三方播放器</title>
<polygon points="20 8 20 20 4 20 4 8"></polygon>
<polyline stroke-linejoin="round" points="8 4 12 7.917 16 4"></polyline>
</svg>
@@ -81,13 +84,21 @@
<rect x="17" y="6" width="1" height="1"></rect>
</svg>
</span>
<span class="zy-svg" @click="issueEvent" v-show="right.list.length > 0">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="infoIconTitle">
<title id="infoIconTitle">复制调试信息</title>
<path d="M12,12 L12,15"></path>
<line x1="12" y1="9" x2="12" y2="9"></line>
<circle cx="12" cy="12" r="10"></circle>
</svg>
</span>
<span class="last-tip" v-if="!video.key && right.history.length > 0" @click="historyItemEvent(right.history[0])">上次播放到{{right.history[0].site}}{{right.history[0].name}} {{right.history[0].index+1}}</span>
</div>
</div>
<transition name="slideX">
<div v-if="right.show" class="list">
<div class="list-top">
<span class="list-top-title">{{ right.type === 'list' ? '播放列表' : '历史记录' }}</span>
<span class="list-top-title">{{ right.type === 'list' ? '播放列表' : right.type === 'history' ? '历史记录' : '其他相同资源' }}</span>
<span class="list-top-close zy-svg" @click="closeListEvent">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="closeIconTitle">
<title id="closeIconTitle">关闭</title>
@@ -106,6 +117,10 @@
<li v-show="right.history.length === 0">无数据</li>
<li @click="historyItemEvent(m)" :class="video.info.id === m.ids ? 'active' : ''" v-for="(m, n) in right.history" :key="n"><span class="title" :title="'【' + m.site + '】' + m.name + ' 第' + (m.index+1) + '集'">【{{m.site}}】{{m.name}} 第{{m.index+1}}集</span><span @click.stop="removeHistoryItem(m)" class="detail-delete">删除</span></li>
</ul>
<ul v-show="right.type === 'other'" class="list-other">
<li v-show="right.other.length === 0">无数据</li>
<li @click="otherItemEvent(m)" v-for="(m, n) in right.other" :key="n"><span class="title">{{m.name}} - [{{m.site}}]</span></li>
</ul>
</div>
</div>
</transition>
@@ -113,7 +128,7 @@
</template>
<script>
import { mapMutations } from 'vuex'
import { star, history, setting, shortcut, mini, iptv } from '../lib/dexie'
import { star, history, setting, shortcut, mini, iptv, sites } from '../lib/dexie'
import zy from '../lib/site/tools'
import Player from 'xgplayer'
import Hls from 'xgplayer-hls.js'
@@ -170,6 +185,7 @@ export default {
right: {
show: false,
type: '',
other: [],
list: [],
history: []
},
@@ -189,6 +205,7 @@ export default {
playbackRate: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 3, 4, 5],
playPrev: true,
playNextOne: true,
videoStop: true,
showList: true,
showHistory: true,
videoTitle: true
@@ -201,7 +218,6 @@ export default {
length: 0,
timer: null,
scroll: false,
showNext: false,
isStar: false,
isTop: false,
mini: {},
@@ -286,32 +302,34 @@ export default {
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
getUrls () {
async getUrls () {
if (this.video.key === '') {
return false
}
this.name = ''
if (this.timer !== null) {
clearInterval(this.timer)
this.timer = null
}
if (this.xg) {
if (this.xg.hasStart) {
this.xg.pause()
}
if (this.xg && this.xg.hasStart) {
this.xg.pause()
}
if (this.video.iptv) {
// 是直播源,直接播放
this.playUrl(this.video.iptv.url)
this.name = this.video.iptv.name
this.getIptvList()
} else {
const index = this.video.info.index | 0
let time = 0
history.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
if (res.index === index) {
time = res.time
}
const db = await history.find({ site: this.video.key, ids: this.video.info.id })
if (db) {
if (db.index === index) {
time = db.time
}
this.playVideo(index, time)
})
}
this.playVideo(index, time)
}
},
playUrl (url) {
@@ -321,7 +339,6 @@ export default {
playVideo (index = 0, time = 0) {
this.fetchM3u8List().then(m3u8Arr => {
this.xg.src = m3u8Arr[index]
this.showNext = m3u8Arr.length > 1
if (time !== 0) {
this.xg.play()
@@ -348,7 +365,6 @@ export default {
if (VIDEO_DETAIL_CACHE[cacheKey]) {
this.name = VIDEO_DETAIL_CACHE[cacheKey].name
resolve(VIDEO_DETAIL_CACHE[cacheKey].list)
return
}
zy.detail(this.video.key, this.video.info.id).then(res => {
this.name = res.name
@@ -388,34 +404,33 @@ export default {
})
})
},
videoPlaying () {
async videoPlaying () {
this.changeVideo()
history.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
const doc = {
site: res.site,
ids: res.ids,
name: res.name,
type: res.type,
year: res.year,
index: this.video.info.index,
time: res.time
}
history.remove(res.id)
history.add(doc)
} else {
const doc = {
site: this.video.key,
ids: this.video.info.id,
name: this.video.info.name,
type: this.video.info.type,
year: this.video.info.year,
index: this.video.info.index,
time: ''
}
history.add(doc)
const db = await history.find({ site: this.video.key, ids: this.video.info.id })
if (db) {
const doc = {
site: db.site,
ids: db.ids,
name: db.name,
type: db.type,
year: db.year,
index: this.video.info.index,
time: db.time
}
})
history.remove(db.id)
history.add(doc)
} else {
const doc = {
site: this.video.key,
ids: this.video.info.id,
name: this.video.info.name,
type: this.video.info.type,
year: this.video.info.year,
index: this.video.info.index,
time: ''
}
history.add(doc)
}
this.updateStar()
this.timerEvent()
},
@@ -424,15 +439,14 @@ export default {
this.checkTop()
},
timerEvent () {
this.timer = setInterval(() => {
history.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
const doc = { ...res }
doc.time = this.xg.currentTime
delete doc.id
history.update(res.id, doc)
}
})
this.timer = setInterval(async () => {
const db = await history.find({ site: this.video.key, ids: this.video.info.id })
if (db) {
const doc = { ...db }
doc.time = this.xg.currentTime
delete doc.id
history.update(db.id, doc)
}
}, 10000)
},
prevEvent () {
@@ -497,21 +511,28 @@ export default {
this.right.history = res.reverse()
})
},
updateStar () {
async updateStar () {
const info = this.video.info
star.find({ key: this.video.key, ids: info.id }).then(res => {
if (res) {
res.index = info.index
star.update(res.id, res)
}
}).catch(() => {
this.$message.warning('检查收藏失败')
})
const db = await star.find({ key: this.video.key, ids: info.id })
if (db) {
db.index = info.index
star.update(db.id, db)
}
},
starEvent () {
async starEvent () {
const info = this.video.info
star.find({ key: this.video.key, ids: info.id }).then(res => {
const doc = {
const db = await star.find({ key: this.video.key, ids: info.id })
if (db) {
star.remove(db.id).then(res => {
if (res) {
this.$message.warning('取消收藏失败')
} else {
this.$message.success('取消收藏成功')
this.isStar = false
}
})
} else {
const docs = {
key: this.video.key,
ids: info.id,
name: info.name,
@@ -521,17 +542,11 @@ export default {
note: info.note,
index: info.index
}
if (res) {
star.update(res.id, doc)
} else {
star.add(doc).then(starRes => {
this.$message.success('收藏成功')
this.isStar = true
})
}
}).catch(() => {
this.$message.warning('检查收藏失败')
})
star.add(docs).then(res => {
this.$message.success('收藏成功')
this.isStar = true
})
}
},
detailEvent () {
this.detail = {
@@ -623,14 +638,13 @@ export default {
fs.writeFileSync(filePath, str)
return filePath
},
checkStar () {
star.find({ key: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
this.isStar = true
} else {
this.isStar = false
}
})
async checkStar () {
const db = await star.find({ key: this.video.key, ids: this.video.info.id })
if (db) {
this.isStar = true
} else {
this.isStar = false
}
},
checkTop () {
const win = remote.getCurrentWindow()
@@ -685,6 +699,7 @@ export default {
if (this.video.iptv) {
var channel = this.iptvList[n]
this.video.iptv = channel
this.name = this.video.iptv.name
// 是直播源,直接播放
this.playUrl(channel.url)
} else {
@@ -717,6 +732,70 @@ export default {
this.$message.warning('删除历史记录失败, 错误信息: ' + err)
})
},
getAllsitestest () {
this.name = '喜宝'
sites.all().then(res => {
const sites = res
const arr = []
for (const i of sites) {
zy.search(i.key, this.name).then(res => {
const type = Object.prototype.toString.call(res)
if (type === '[object Array]') {
res.forEach(element => {
zy.detail(i.key, element.id).then(detailRes => {
arr.push(detailRes)
})
})
}
if (type === '[object Object]') {
zy.detail(i.key, res.id).then(detailRes => {
arr.push(detailRes)
})
}
})
}
console.log(arr, 'arr')
})
},
async getAllsites () {
const all = await sites.all()
this.right.other = []
for (const i of all) {
if (i.isActive) {
const searchRes = await zy.search(i.key, this.name)
const type = Object.prototype.toString.call(searchRes)
if (type === '[object Array]') {
searchRes.forEach(async element => {
const detailRes = await zy.detail(i.key, element.id)
detailRes.key = i.key
detailRes.site = i.name
this.right.other.push(detailRes)
})
}
if (type === '[object Object]') {
const detailRes = await zy.detail(i.key, searchRes.id)
detailRes.key = i.key
detailRes.site = i.name
this.right.other.push(detailRes)
}
}
}
},
otherEvent (m) {
this.right.type = 'other'
this.getAllsites()
this.right.show = true
},
async otherItemEvent (e) {
const db = await history.find({ site: e.key, ids: e.id })
if (db) {
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: db.index, site: e.key } }
} else {
this.video = { key: e.key, info: { id: e.id, name: e.name, index: 0, site: e.key } }
}
this.right.show = false
this.right.type = ''
},
mtEvent () {
setting.find().then(res => {
if (res.shortcut) {
@@ -989,28 +1068,90 @@ export default {
this.toggleHistory()
})
this.xg.on('videoStop', () => {
this.videoStop()
})
const ev = ['click', 'touchend', 'mousemove']
let timerID
ev.forEach(item => {
this.xg.root.addEventListener(item, () => {
if (!this.xg.fullscreen) {
return
if (this.xg && this.xg.fullscreen) {
const videoTitle = document.querySelector('.xg-view-videoTitle')
videoTitle.style.display = 'block'
clearTimeout(timerID)
timerID = setTimeout(() => {
// 播放中自动消失
if (this.xg && !this.xg.paused) {
videoTitle.style.display = 'none'
}
}, 3000)
}
const videoTitle = document.querySelector('.xg-view-videoTitle')
videoTitle.style.display = 'block'
clearTimeout(timerID)
timerID = setTimeout(() => {
// 播放中自动消失
if (this.xg && !this.xg.paused) {
videoTitle.style.display = 'none'
}
}, 3000)
})
})
this.xg.on('exitFullscreen', () => {
document.querySelector('.xg-view-videoTitle').style.display = 'none'
})
},
videoStop () {
if (this.xg.fullscreen) {
this.xg.exitFullscreen()
}
clearInterval(this.timer)
this.video.key = ''
this.xg.src = ''
this.config.src = ''
this.xg.destroy(false)
this.xg = null
this.name = ''
this.right.list = []
this.getAllhistory()
setTimeout(() => {
this.xg = new Hls(this.config)
this.playerInstall()
this.bindEvent()
}, 1000)
},
minMaxEvent () {
const win = remote.getCurrentWindow()
win.on('minimize', () => {
if (this.xg && this.xg.hasStart) {
this.xg.pause()
}
})
win.on('restore', () => {
if (this.xg && this.config.src) {
this.xg.play()
}
})
},
playerInstall () {
Player.install('playPrev', function () {
addPlayerBtn.bind(this, 'playPrev', '<svg t="1595866093990" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3657" style="width: 20px;height: 20px;margin-top: 11px;margin-left: 9px;" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M98.583851 3.180124h190.807453a31.801242 31.801242 0 0 1 31.801243 31.801242v387.021118L902.201242 10.176398l11.130435-7.632299A31.801242 31.801242 0 0 1 957.217391 31.801242v960.397516a31.801242 31.801242 0 0 1-43.885714 29.257143l-11.130435-7.632299L321.192547 601.997516V989.018634a31.801242 31.801242 0 0 1-31.801243 31.801242H98.583851a31.801242 31.801242 0 0 1-31.801242-31.801242v-954.037268a31.801242 31.801242 0 0 1 31.801242-31.801242z" p-id="3658" fill="#ffffff"></path></svg>', { title: '上一集' })()
})
Player.install('playNextOne', function () {
addPlayerBtn.bind(this, 'playNextOne', '<svg t="1595866110378" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3946" style="width: 20px;height: 20px;margin-top: 11px;margin-left: 0px;" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M925.416149 3.180124h-190.807453a31.801242 31.801242 0 0 0-31.801243 31.801242v387.021118L121.798758 10.176398 110.668323 2.544099A31.801242 31.801242 0 0 0 98.583851 0a31.801242 31.801242 0 0 0-31.801242 31.801242v960.397516a31.801242 31.801242 0 0 0 31.801242 31.801242 31.801242 31.801242 0 0 0 12.084472-2.544099l11.130435-7.632299L702.807453 601.997516V989.018634a31.801242 31.801242 0 0 0 31.801243 31.801242h190.807453a31.801242 31.801242 0 0 0 31.801242-31.801242v-954.037268a31.801242 31.801242 0 0 0-31.801242-31.801242z" p-id="3947" fill="#ffffff"></path></svg>', { title: '下一集' })()
})
Player.install('videoStop', function () {
addPlayerBtn.bind(this, 'videoStop', '<svg t="1603093629102" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3621" style="width: 25px;height: 25px;margin-top: 8px;margin-left: 0px;"><path d="M768 768H256V256h512v512z" p-id="3622" fill="#ffffff"></path></svg>', { title: '停止播放' })()
})
Player.install('showList', function () {
addPlayerBtn.bind(this, 'showList', '<svg t="1595866128681" class="icon" viewBox="0 0 1316 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4187" style="width: 22px;height: 22px;margin-top: 9px;margin-left: 6px;" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M0 0h1316.571429v146.285714H0zM0 438.857143h1316.571429v146.285714H0zM0 877.714286h1316.571429v146.285714H0z" p-id="4188" fill="#ffffff"></path></svg>', { title: '播放列表' })()
})
Player.install('showHistory', function () {
addPlayerBtn.bind(this, 'showHistory', '<svg t="1595866015473" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3282" style="width: 22px;height: 22px;margin-top: 9px;margin-left: 6px;" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m0 910.222222a398.222222 398.222222 0 1 1 398.222222-398.222222 398.222222 398.222222 0 0 1-398.222222 398.222222z" p-id="3283" fill="#ffffff"></path><path d="M568.888889 227.555556h-113.777778v341.333333h227.555556v-113.777778h-113.777778V227.555556z" p-id="3284" fill="#ffffff"></path></svg>', { title: '播放历史' })()
})
const that = this
Player.install('videoTitle', function () {
let title
if (that.right.list.length > 1) {
title = `『第 ${that.video.info.index + 1} 集』${that.name}`
} else {
title = `${that.name}`
}
addPlayerView.bind(this, 'videoTitle', `<span>${title}</span>`, {})()
})
}
},
created () {
@@ -1018,42 +1159,20 @@ export default {
this.mtEvent()
},
mounted () {
Player.install('playPrev', function () {
addPlayerBtn.bind(this, 'playPrev', '<svg t="1595866093990" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3657" style="width: 20px;height: 20px;margin-top: 11px;margin-left: 9px;" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M98.583851 3.180124h190.807453a31.801242 31.801242 0 0 1 31.801243 31.801242v387.021118L902.201242 10.176398l11.130435-7.632299A31.801242 31.801242 0 0 1 957.217391 31.801242v960.397516a31.801242 31.801242 0 0 1-43.885714 29.257143l-11.130435-7.632299L321.192547 601.997516V989.018634a31.801242 31.801242 0 0 1-31.801243 31.801242H98.583851a31.801242 31.801242 0 0 1-31.801242-31.801242v-954.037268a31.801242 31.801242 0 0 1 31.801242-31.801242z" p-id="3658" fill="#ffffff"></path></svg>', { title: '上一集' })()
})
Player.install('playNextOne', function () {
addPlayerBtn.bind(this, 'playNextOne', '<svg t="1595866110378" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3946" style="width: 20px;height: 20px;margin-top: 11px;margin-left: 0px;" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M925.416149 3.180124h-190.807453a31.801242 31.801242 0 0 0-31.801243 31.801242v387.021118L121.798758 10.176398 110.668323 2.544099A31.801242 31.801242 0 0 0 98.583851 0a31.801242 31.801242 0 0 0-31.801242 31.801242v960.397516a31.801242 31.801242 0 0 0 31.801242 31.801242 31.801242 31.801242 0 0 0 12.084472-2.544099l11.130435-7.632299L702.807453 601.997516V989.018634a31.801242 31.801242 0 0 0 31.801243 31.801242h190.807453a31.801242 31.801242 0 0 0 31.801242-31.801242v-954.037268a31.801242 31.801242 0 0 0-31.801242-31.801242z" p-id="3947" fill="#ffffff"></path></svg>', { title: '下一集' })()
})
Player.install('showList', function () {
addPlayerBtn.bind(this, 'showList', '<svg t="1595866128681" class="icon" viewBox="0 0 1316 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4187" style="width: 22px;height: 22px;margin-top: 9px;margin-left: 6px;" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M0 0h1316.571429v146.285714H0zM0 438.857143h1316.571429v146.285714H0zM0 877.714286h1316.571429v146.285714H0z" p-id="4188" fill="#ffffff"></path></svg>', { title: '播放列表' })()
})
Player.install('showHistory', function () {
addPlayerBtn.bind(this, 'showHistory', '<svg t="1595866015473" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3282" style="width: 22px;height: 22px;margin-top: 9px;margin-left: 6px;" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m0 910.222222a398.222222 398.222222 0 1 1 398.222222-398.222222 398.222222 398.222222 0 0 1-398.222222 398.222222z" p-id="3283" fill="#ffffff"></path><path d="M568.888889 227.555556h-113.777778v341.333333h227.555556v-113.777778h-113.777778V227.555556z" p-id="3284" fill="#ffffff"></path></svg>', { title: '播放历史' })()
})
const that = this
Player.install('videoTitle', function () {
let title
if (that.right.list.length > 1) {
title = `『第 ${that.video.info.index + 1} 集』${that.name}`
} else {
title = `${that.name}`
}
addPlayerView.bind(this, 'videoTitle', `<span>${title}</span>`, {})()
})
this.playerInstall()
this.xg = new Hls(this.config)
ipcRenderer.on('miniClosed', () => {
history.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
if (this.video.info.index !== res.index) {
this.video.info.index = res.index
} else {
this.getUrls()
}
ipcRenderer.on('miniClosed', async () => {
const db = await history.find({ site: this.video.key, ids: this.video.info.id })
if (db) {
if (this.video.info.index !== db.index) {
this.video.info.index = db.index
} else {
this.getUrls()
}
})
}
})
this.bindEvent()
this.minMaxEvent()
},
beforeDestroy () {
clearInterval(this.timer)
@@ -1061,7 +1180,11 @@ export default {
}
</script>
<style>
.xgplayer-skin-default .xg-btn-playPrev {
.xgplayer-skin-default .xg-btn-playPrev,
.xgplayer-skin-default .xg-btn-playNextOne,
.xgplayer-skin-default .xg-btn-showList,
.xgplayer-skin-default .xg-btn-showHistory,
.xgplayer-skin-default .xg-btn-videoStop {
width: 32px;
position: relative;
-webkit-order: 0;
@@ -1071,79 +1194,54 @@ export default {
cursor: pointer;
margin-left: 3px;
}
.xgplayer-skin-default .xg-btn-playPrev:hover {
.xgplayer-skin-default .xg-btn-playPrev:hover,
.xgplayer-skin-default .xg-btn-playNextOne:hover,
.xgplayer-skin-default .xg-btn-showList:hover,
.xgplayer-skin-default .xg-btn-showHistory:hover,
.xgplayer-skin-default .xg-btn-videoStop:hover {
opacity: 0.8;
}
.xgplayer-skin-default .xg-btn-playNextOne {
width: 32px;
position: relative;
-webkit-order: 2;
-moz-box-ordinal-group: 1;
order: 2;
display: block;
cursor: pointer;
margin-left: 3px;
}
.xgplayer-skin-default .xg-btn-playNextOne:hover {
opacity: 0.8;
}
.xgplayer-skin-default .xgplayer-play, .xgplayer-skin-default .xgplayer-play-img {
order: 1 !important;
}
.xgplayer-skin-default .xg-btn-showList {
width: 32px;
position: relative;
-webkit-order: 4;
-moz-box-ordinal-group: 1;
order: 4;
display: block;
cursor: pointer;
margin-right: 3px;
.xgplayer-skin-default .xg-btn-videoStop {
order: 2;
}
.xgplayer-skin-default .xg-btn-showList:hover {
opacity: 0.8;
.xgplayer-skin-default .xg-btn-showList {
order: 4;
}
.xgplayer-skin-default .xg-btn-showHistory {
width: 32px;
position: relative;
-webkit-order: 4;
-moz-box-ordinal-group: 1;
order: 4;
display: block;
cursor: pointer;
margin-right: 3px;
}
.xgplayer-skin-default .xg-btn-showHistory:hover {
opacity: 0.8;
}
.xgplayer-skin-default .xg-btn-showList ul, .xgplayer-skin-default .xg-btn-showHistory ul {
display: none;
list-style: none;
min-width: 85px;
max-width: 300px;
max-height: 60vh;
overflow-y: scroll;
background: rgba(0,0,0,.54);
border-radius: 1px;
position: absolute;
bottom: 45px;
left: 50%;
-webkit-transform: translateX(-50%);
-ms-transform: translateX(-50%);
transform: translateX(-50%);
text-align: left;
white-space: nowrap;
z-index: 26;
cursor: pointer;
display: none;
list-style: none;
min-width: 85px;
max-width: 300px;
max-height: 60vh;
overflow-y: scroll;
background: rgba(0,0,0,.54);
border-radius: 1px;
position: absolute;
bottom: 45px;
left: 50%;
transform: translateX(-50%);
text-align: left;
white-space: nowrap;
z-index: 26;
cursor: pointer;
}
.xgplayer-skin-default .xg-btn-showList ul li, .xgplayer-skin-default .xg-btn-showHistory ul li {
opacity: .7;
font-family: PingFangSC-Regular;
font-size: 13px;
color: hsla(0,0%,100%,.8);
position: relative;
padding: 5px;
text-align: center;
opacity: .7;
font-family: PingFangSC-Regular;
font-size: 13px;
color: hsla(0,0%,100%,.8);
position: relative;
padding: 5px;
text-align: center;
}
.xgplayer-skin-default .xg-btn-showList ul li:first-child, .xgplayer-skin-default .xg-btn-showHistory ul li:first-child {
position: relative;
@@ -1153,26 +1251,26 @@ export default {
margin-bottom: 12px;
}
.xgplayer-skin-default .xg-btn-showList ul li.selected, .xgplayer-skin-default .xg-btn-showHistory ul li.selected, .xgplayer-skin-default .xg-btn-showList ul li:hover, .xgplayer-skin-default .xg-btn-showHistory ul li:hover {
color: #fff;
opacity: 1;
color: #fff;
opacity: 1;
}
.xgplayer-skin-default .xgplayer-volume {
width: 32px !important;
width: 32px !important;
}
.xgplayer-skin-default .xgplayer-playbackrate {
width: 40px !important;
width: 40px !important;
}
.xgplayer-skin-default .xgplayer-playbackrate .name {
top: 10px !important;
top: 10px !important;
}
.xgplayer-skin-default .xgplayer-playbackrate ul {
bottom: 25px;
}
.xgplayer-skin-default .xgplayer-playbackrate ul li {
font-size: 13px !important;
font-size: 13px !important;
}
.xgplayer-skin-default .xgplayer-screenshot .name span {
width: 40px !important;
width: 40px !important;
}
.xgplayer-skin-default .xg-view-videoTitle {
display: none;

View File

@@ -6,7 +6,7 @@
<a @click="linkOpen('http://zyplayer.fun/')">官网</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player')">Github</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/issues')">当前版本v{{pkg.version}} 反馈</a>
<a style="color:#38dd77" @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/releases/tag/v' + latestVersion)" v-show="latestVersion !== pkg.version" >最新版本v{{latestVersion}}</a>
<a style="color:#38dd77" @click="quitAndInstall()" v-show="latestVersion !== pkg.version" >最新版本v{{latestVersion}}</a>
</div>
<div class="view">
<div class="title">视图</div>
@@ -42,6 +42,14 @@
</div>
</div>
</div>
<div class="shortcut">
<div class="title">缓存</div>
<div class="shortcut-box">
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="clearCache">清理视频缓存</div>
</div>
</div>
</div>
<div class="site">
<div class="title">定位时间设置</div>
<div class="zy-input">
@@ -133,8 +141,9 @@
<script>
import { mapMutations } from 'vuex'
import pkg from '../../package.json'
import { setting, sites, shortcut, star } from '../lib/dexie'
import { shell, clipboard, remote } from 'electron'
import { setting, sites, shortcut } from '../lib/dexie'
import { sites as defaultSites } from '../lib/dexie/initData'
import { shell, clipboard, remote, ipcRenderer } from 'electron'
import db from '../lib/dexie/dexie'
export default {
name: 'setting',
@@ -143,7 +152,6 @@ export default {
pkg: pkg,
sitesList: [],
shortcutList: [],
favoritesList: [],
show: {
site: false,
shortcut: false,
@@ -170,6 +178,14 @@ export default {
}
},
computed: {
view: {
get () {
return this.$store.getters.getView
},
set (val) {
this.SET_VIEW(val)
}
},
setting: {
get () {
return this.$store.getters.getSetting
@@ -188,7 +204,7 @@ export default {
}
},
methods: {
...mapMutations(['SET_SETTING', 'SET_EDITSITES']),
...mapMutations(['SET_SETTING', 'SET_VIEW', 'SET_EDITSITES']),
linkOpen (e) {
shell.openExternal(e)
},
@@ -210,7 +226,15 @@ export default {
},
getSites () {
sites.all().then(res => {
this.sitesList = res
if (res.length <= 0) {
this.$message.warning('检测到视频源未能正常加载, 即将重置源.')
sites.clear().then(sites.bulkAdd(defaultSites).then(this.getSites()))
} else {
this.sitesList = res
this.editSites = {
sites: res
}
}
})
},
getShortcut () {
@@ -218,16 +242,19 @@ export default {
this.shortcutList = res
})
},
getFavorites () {
star.all().then(res => {
this.favoritesList = res
})
},
changeView (e) {
this.d.view = e
this.updateSettingEvent()
this.show.view = false
},
async clearCache () {
const win = remote.getCurrentWindow()
const ses = win.webContents.session
const size = await ses.getCacheSize() / 1024 / 1024
const mb = size.toFixed(2)
await ses.clearCache()
this.$message.success(`清除缓存成功, 共清理 ${mb} MB`)
},
updateSettingEvent () {
this.editPlayerPath = false
this.setting = this.d
@@ -277,10 +304,7 @@ export default {
this.updateSettingEvent()
},
editSitesEvent () {
this.editSites = {
show: true,
sites: this.sitesList
}
this.view = 'EditSites'
},
changeTheme (e) {
this.d.theme = e
@@ -293,7 +317,7 @@ export default {
},
expShortcut () {
const arr = [...this.shortcutList]
const str = JSON.stringify(arr, null, 4)
const str = JSON.stringify(arr, null, 2)
clipboard.writeText(str)
this.$message.success('已复制到剪贴板')
},
@@ -326,14 +350,29 @@ export default {
}
},
getLatestVersion () {
const cheerio = require('cheerio')
const axios = require('axios')
var url = 'https://github.com/Hunlongyu/ZY-Player/releases'
axios.get(url).then(res => {
const $ = cheerio.load(res.data)
var e = $('div.release-header')[0]
var firstResult = $(e).find('div>div>a')
this.latestVersion = firstResult.text()
ipcRenderer.send('checkForUpdate')
ipcRenderer.on('update-available', (e, info) => {
this.latestVersion = info.version
})
ipcRenderer.on('update-error', () => {
this.$message.warning = '更新出错.'
})
ipcRenderer.on('update-downloaded', () => {
this.$message.info = '下载完毕, 退出安装'
})
},
quitAndInstall () {
ipcRenderer.send('quitAndInstall')
},
createContextMenu () {
const { Menu, MenuItem } = remote
const menu = new Menu()
menu.append(new MenuItem({ label: '快速复制', role: 'copy' }))
menu.append(new MenuItem({ label: '快速粘贴', role: 'paste' }))
menu.append(new MenuItem({ label: '编辑', role: 'editMenu' }))
window.addEventListener('contextmenu', e => {
e.preventDefault()
menu.popup(remote.getCurrentWindow())
})
}
},
@@ -341,8 +380,8 @@ export default {
this.getSites()
this.getSetting()
this.getShortcut()
this.getFavorites()
this.getLatestVersion()
this.createContextMenu()
}
}
</script>

View File

@@ -89,7 +89,7 @@ export default {
})
},
picLoadEvent () {
const dom = document.getElementById('right')
const dom = document.getElementById('share')
html2canvas(dom, { useCORS: true, allowTaint: true }).then(res => {
const png = res.toDataURL('image/png')
const p = nativeImage.createFromDataURL(png)

View File

@@ -1,76 +1,77 @@
<template>
<div class="detail">
<div class="detail-content">
<div class="detail-header">
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="exportFavoritesEvent">
导出
</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="importFavoritesEvent">
导入
</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="clearFavoritesEvent">
清空
</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="updateAllEvent">
同步所有收藏
</div>
</div>
<div class="listpage" id="star">
<div class="listpage-content">
<div class="listpage-header">
<el-button @click.stop="exportFavoritesEvent" icon="el-icon-upload2">导出</el-button>
<el-button @click.stop="importFavoritesEvent" icon="el-icon-download">导入</el-button>
<el-button @click.stop="clearFavoritesEvent" icon="el-icon-delete-solid">清空</el-button>
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">同步所有收藏</el-button>
</div>
<div class="detail-body zy-scroll">
<div class="zy-table">
<div class="tBody zy-scroll">
<ul>
<li v-show="this.list.length > 0">
<span class="name">名字</span>
<span class="type">类型</span>
<span class="time">上映</span>
<span class="site">片源</span>
<span class="note">备注</span>
<span class="note">观看至</span>
<span class="operate">
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
</span>
</li>
<draggable v-model="list" @change="listUpdatedEvent">
<transition-group>
<li
v-for="(i, j) in list"
:key="j"
@click="detailEvent(i)"
:class="[i.hasUpdate ? 'zy-highlighted' : '']"
>
<span class="name">{{ i.name }}</span>
<span class="type">{{ i.type }}</span>
<span class="time">{{ i.year }}</span>
<span class="site">{{ getSiteName(i.key) }}</span>
<span class="note">{{ i.note }}</span>
<span class="note">{{ getHistoryNote(i.index) }}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(i)">播放</span>
<span class="btn" @click.stop="shareEvent(i)">分享</span>
<span class="btn" @click.stop="updateEvent(i)">同步</span>
<span class="btn" @click.stop="downloadEvent(i)"
>下载</span
>
<span class="btn" @click.stop="deleteEvent(i)">删除</span>
</span>
</li>
</transition-group>
</draggable>
</ul>
</div>
</div>
<div class="listpage-body" id="star-table">
<el-table size="mini" fit height="100%" row-key="id"
ref="starTable"
:data="list"
:cell-class-name="checkUpdate"
@row-click="detailEvent"
@sort-change="handleSortChange">
<el-table-column
sortable
:sort-method="sortByName"
prop="name"
label="片名">
</el-table-column>
<el-table-column
:sort-by="['type', 'name']"
sortable
:sort-method="sortByType"
prop="type"
label="类型"
width="100">
</el-table-column>
<el-table-column
sortable
:sort-by="['year', 'name']"
prop="year"
label="上映"
width="100"
align="center">
</el-table-column>
<el-table-column
:sort-by="['site', 'name']"
sortable
:sort-method="sortBySite"
prop="site"
width="120"
label="片源">
<template slot-scope="scope">
<span>{{ getSiteName(scope.row.key) }}</span>
</template>
</el-table-column>
<el-table-column v-if="list.some(e => e.note)"
prop="note"
width="120"
label="备注">
</el-table-column>
<el-table-column v-if="list.some(e => e.index >= 0)"
prop="index"
width="120"
label="观看至">
<template slot-scope="scope">
<span>{{ getHistoryNote(scope.row.index) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
header-align="right"
align="right">
<template slot-scope="scope">
<el-button @click.stop="playEvent(scope.row)" type="text">播放</el-button>
<el-button @click.stop="shareEvent(scope.row)" type="text">分享</el-button>
<el-button @click.stop="downloadEvent(scope.row)" type="text">下载</el-button>
<el-button @click.stop="deleteEvent(scope.row)" type="text">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
@@ -79,10 +80,9 @@
import { mapMutations } from 'vuex'
import { star, history, sites } from '../lib/dexie'
import zy from '../lib/site/tools'
import draggable from 'vuedraggable'
import { remote } from 'electron'
import fs from 'fs'
import Sortable from 'sortablejs'
const { clipboard } = require('electron')
export default {
name: 'star',
@@ -92,9 +92,6 @@ export default {
sites: []
}
},
components: {
draggable
},
computed: {
view: {
get () {
@@ -131,12 +128,29 @@ export default {
},
watch: {
view () {
this.getFavorites()
this.getAllsites()
this.getFavorites()
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
handleSortChange (column, prop, order) {
this.updateDatabase()
},
sortByName (a, b) {
return a.name.localeCompare(b.name, 'zh')
},
sortByType (a, b) {
return a.type.localeCompare(b.type)
},
sortBySite (a, b) {
const siteA = this.getSiteName(a.key)
if (!siteA) {
return -1
} else {
return siteA.localeCompare(this.getSiteName(b.key))
}
},
detailEvent (e) {
this.detail = {
show: true,
@@ -150,14 +164,13 @@ export default {
this.clearHasUpdateFlag(e)
}
},
playEvent (e) {
history.find({ site: e.key, ids: e.ids }).then(res => {
if (res) {
this.video = { key: e.key, info: { id: res.ids, name: res.name, index: res.index } }
} else {
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 } }
}
})
async playEvent (e) {
const db = await history.find({ site: e.key, ids: e.ids })
if (db) {
this.video = { key: e.key, info: { id: db.ids, name: db.name, index: db.index } }
} else {
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 } }
}
if (e.hasUpdate) {
this.clearHasUpdateFlag(e)
}
@@ -180,35 +193,33 @@ export default {
info: e
}
},
clearHasUpdateFlag (e) {
star.find({ id: e.id }).then(res => {
res.hasUpdate = false
star.update(e.id, res)
this.getFavorites()
})
checkUpdate ({ row, rowIndex }) {
if (this.list[rowIndex].hasUpdate) {
return 'highlight'
}
},
listUpdatedEvent () {
star.clear().then(res1 => {
// 重新排序
var id = this.list.length
this.list.forEach(element => {
element.id = id
star.add(element)
id -= 1
})
})
async clearHasUpdateFlag (e) {
const db = await star.find({ id: e.id })
if (db) {
db.hasUpdate = false
star.update(e.id, db)
this.getFavorites()
}
},
updateEvent (e) {
zy.detail(e.key, e.ids).then(res => {
var doc = {
key: e.key,
id: e.id,
key: e.key,
ids: res.id,
last: res.last,
site: res.site,
name: res.name,
type: res.type,
year: res.year,
note: res.note
note: res.note,
index: res.index,
last: res.last,
hasUpdate: res.hasUpdate
}
star.get(e.id).then(resStar => {
doc.hasUpdate = resStar.hasUpdate
@@ -301,7 +312,7 @@ export default {
},
exportFavoritesEvent () {
const arr = [...this.list]
const str = JSON.stringify(arr, null, 4)
const str = JSON.stringify(arr, null, 2)
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
@@ -329,84 +340,76 @@ export default {
}
remote.dialog.showOpenDialog(options).then(result => {
if (!result.canceled) {
var starList = this.list
result.filePaths.forEach(file => {
var str = fs.readFileSync(file)
const json = JSON.parse(str)
star.bulkAdd(json).then(e => {
this.getFavorites()
json.forEach(ele => {
const starExists = starList.includes(x => x.key === ele.key && x.ids === ele.ids)
if (!starExists) {
var doc = {
key: ele.key,
ids: ele.ids,
site: ele.site === undefined ? ele.site = this.sites.find(x => x.key === ele.key) : ele.site,
name: ele.name,
type: ele.type,
year: ele.year,
note: ele.note,
index: ele.index,
last: ele.last,
hasUpdate: ele.hasUpdate
}
starList.push(doc)
}
})
this.upgradeFavorites()
})
this.$message.success('导入收藏成功')
star.clear().then(star.bulkAdd(starList).then(res => {
this.getFavorites()
this.$message.success('导入收藏成功')
}))
}
}).catch(err => {
this.$message.error(err)
})
},
upgradeFavorites () {
star.all().then(res => {
res.forEach(element => {
const docs = {
key: element.key,
ids: element.ids,
name: element.name,
type: element.type,
year: element.year,
last: element.last,
note: element.note
}
star.find({ key: element.key, ids: element.ids }).then(res => {
if (!res) {
star.add(docs)
}
})
})
this.getFavorites()
})
},
clearFavoritesEvent () {
star.clear().then(e => {
this.getFavorites()
this.$message.success('清空所有收藏成功')
})
},
syncTableData () {
if (this.$refs.starTable.tableData && this.$refs.starTable.tableData.length === this.list.length) {
this.list = this.$refs.starTable.tableData
}
},
updateDatabase () {
this.syncTableData()
star.clear().then(res => {
var id = this.list.length
this.list.forEach(ele => {
ele.id = id
id -= 1
})
star.bulkAdd(this.list)
})
},
rowDrop () {
const tbody = document.getElementById('star-table').querySelector('.el-table__body-wrapper tbody')
const _this = this
Sortable.create(tbody, {
onEnd ({ newIndex, oldIndex }) {
const currRow = _this.list.splice(oldIndex, 1)[0]
_this.list.splice(newIndex, 0, currRow)
_this.updateDatabase()
}
})
}
},
mounted () {
this.rowDrop()
},
created () {
this.getFavorites()
window.Sortable = require('sortablejs').Sortable
}
}
</script>
<style lang="scss" scoped>
.detail{
position: absolute;
left: 80px;
right: 20px;
bottom: 0;
width: calc(100% - 100px);
height: calc(100% - 40px);
z-index: 888;
.detail-content{
height: calc(100% - 10px);
padding: 0 60px;
position: relative;
.detail-header{
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
.detail-title{
font-size: 16px;
}
.detail-close{
cursor: pointer;
}
}
}
.detail-body{
height: calc(100% - 50px);
overflow-y: auto;
}
}
</style>

View File

@@ -3,16 +3,16 @@ import { setting, sites, localKey, iptv } from './initData'
const db = new Dexie('zy')
db.version(3).stores({
db.version(4).stores({
search: '++id, keywords',
iptvSearch: '++id, keywords',
setting: 'id, theme, site, shortcut, view, externalPlayer, searchAllSites, excludeRootClasses, excludeR18Films, forwardTimeInSec',
shortcut: 'name, key, desc',
star: '++id, site, ids, name, type, year, index',
sites: '++id, key, name, json, xml, down, level',
history: '++id, site, ids, name, type, year, index, time',
star: '++id, [key+ids], site, name, type, year, note, index, last, hasUpdate',
sites: '++id, key, name, api, download, isActive, group',
history: '++id, [site+ids], name, type, year, index, time',
mini: 'id, site, ids, name, index, time',
iptv: '++id, name, url'
iptv: '++id, name, url, group'
})
db.on('populate', () => {

View File

@@ -4,8 +4,11 @@ export default {
async add (doc) {
return await history.add(doc)
},
async bulkAdd (doc) {
return await history.bulkAdd(doc)
},
async find (doc) {
return await history.get(doc)
return await history.where(doc).first()
},
async update (id, docs) {
return await history.update(id, docs)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ export default {
return await star.bulkAdd(doc)
},
async find (doc) {
return await star.get(doc)
return await star.where(doc).first()
},
async update (id, docs) {
return await star.update(id, docs)

View File

@@ -1,3 +1,16 @@
import Vue from 'vue'
import { Message } from 'element-ui'
import { Message, Button, Table, TableColumn, Tag, Input, Dialog, Form, FormItem, Switch, Select, Option } from 'element-ui'
import Plugin from 'v-fit-columns'
Vue.use(Button)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(Tag)
Vue.use(Input)
Vue.use(Dialog)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Switch)
Vue.use(Plugin)
Vue.use(Select)
Vue.use(Option)
Vue.prototype.$message = Message

View File

@@ -3,6 +3,36 @@ import axios from 'axios'
import cheerio from 'cheerio'
const onlineVideo = {
playVideoOnline (selectedOnlineSite, videoName, videoIndex) {
switch (selectedOnlineSite) {
case '哔嘀':
onlineVideo.playVideoOnBde4(videoName, videoIndex)
break
case '1080影视':
onlineVideo.playVideoOnK1080(videoName, videoIndex)
break
case '素白白':
onlineVideo.playVideoOnSubaibai(videoName, videoIndex)
break
case '哆咪动漫':
onlineVideo.playVideoOndmdm2020(videoName, videoIndex)
break
case '樱花动漫':
onlineVideo.playVideoOnYhdm(videoName, videoIndex)
break
case '简影':
onlineVideo.playVideoOnSyrme(videoName, videoIndex)
break
case '极品':
onlineVideo.playVideoOnJpysvip(videoName, videoIndex)
break
case '喜欢看':
onlineVideo.playVideoOnXhkan(videoName, videoIndex)
break
default:
this.$message.console.error(`不支持该网站:${this.selectedOnlineSite}`)
}
},
playVideoOnBde4 (videoName, videoIndex) {
videoName = videoName.replace(/\s/g, '')
var url = `https://bde4.com/search/${videoName}`
@@ -214,6 +244,74 @@ const onlineVideo = {
})
}
})
},
playVideoOnJpysvip (videoName, videoIndex) {
videoName = videoName.replace(/\s/g, '')
var url = `https://www.jpysvip.net/vodsearch/-------------.html?wd=${videoName}&submit=`
axios.get(url).then(res => {
const $ = cheerio.load(res.data)
var e = $('#searchList')
var searchResult = $(e).find('ul>li>div>a').toArray()
// 获取第一个搜索结果的视频链接
var detailPageLink = $(searchResult[0]).attr('href')
// 获取第一个搜索结果的title
var title = $(searchResult[0]).attr('title')
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
// 如果第一个搜索结果不符合,打开搜索页面
open(url)
} else {
// 解析详情页面
var detailPageFullLink = 'https://www.jpysvip.net' + detailPageLink
axios.get(detailPageFullLink).then(res2 => {
const $ = cheerio.load(res2.data)
// 获取playlist1
var e = $('#playlist1')
// 获取所有视频链接
var videoList = $(e).find('div>ul>li>a').toArray()
// 获取index视频链接
var videoFullLink = detailPageFullLink
if (videoIndex < videoList.length) {
var indexVideoLink = $(videoList[videoIndex]).attr('href')
videoFullLink = 'https://www.jpysvip.net/' + indexVideoLink
}
open(videoFullLink)
})
}
})
},
playVideoOnXhkan (videoName, videoIndex) {
videoName = videoName.replace(/\s/g, '')
var url = `https://www.xhkan.com/vodsearch.html?wd=${videoName}&submit=`
axios.get(url).then(res => {
const $ = cheerio.load(res.data)
var e = $('#searchList')
var searchResult = $(e).find('ul>li>div>a').toArray()
// 获取第一个搜索结果的视频链接
var detailPageLink = $(searchResult[0]).attr('href')
// 获取第一个搜索结果的title
var title = $(searchResult[0]).attr('title')
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
// 如果第一个搜索结果不符合,打开搜索页面
open(url)
} else {
// 解析详情页面
var detailPageFullLink = detailPageLink
axios.get(detailPageFullLink).then(res2 => {
const $ = cheerio.load(res2.data)
// 获取playlist1
var e = $('#playlist1')
// 获取所有视频链接
var videoList = $(e).find('div>ul>li>a').toArray()
// 获取index视频链接
var videoFullLink = detailPageFullLink
if (videoIndex < videoList.length) {
var indexVideoLink = $(videoList[videoIndex]).attr('href')
videoFullLink = indexVideoLink
}
open(videoFullLink)
})
}
})
}
}
export default onlineVideo

View File

@@ -193,6 +193,23 @@ const zy = {
}
})
})
},
/**
* 检查资源
* @param {*} key 资源网 key
* @returns boolean
*/
async check (key, id) {
try {
const cls = await this.class(key)
if (cls) {
return true
} else {
return false
}
} catch (e) {
return false
}
}
}

48
src/lib/update/update.js Normal file
View File

@@ -0,0 +1,48 @@
import { BrowserWindow, ipcMain } from 'electron'
import { autoUpdater } from 'electron-updater'
export function initUpdater (win = BrowserWindow) {
autoUpdater.autoDownload = false
autoUpdater.autoInstallOnAppQuit = false
// 主进程监听检查更新事件
ipcMain.on('checkForUpdate', () => {
autoUpdater.checkForUpdates()
})
// 主进程监听退出并安装事件
ipcMain.on('quitAndInstall', () => {
autoUpdater.downloadUpdate()
})
// 开始检测是否有更新
autoUpdater.on('checking-for-update', () => {
win.webContents.send('checking-for-update')
})
// 检测到有可用的更新
autoUpdater.on('update-available', (info) => {
win.webContents.send('update-available', info)
})
// 没有检测到有可用的更新
autoUpdater.on('update-not-available', () => {
win.webContents.send('update-not-available')
})
// 更新出错
autoUpdater.on('update-error', err => {
win.webContents.send('update-error', err)
})
// 下载更新进度
autoUpdater.on('download-progress', (progressObj) => {
win.webContents.send('download-progress', progressObj)
})
// 下载完成并退出安装
autoUpdater.on('update-downloaded', () => {
win.webContents.send('update-downloaded')
autoUpdater.quitAndInstall()
})
}

View File

@@ -4,6 +4,7 @@ import store from './store'
import 'modern-normalize'
import Register from './components/register'
import './lib/element/index'
Register.registerComponents()
Vue.config.productionTip = false
new Vue({

View File

@@ -175,49 +175,48 @@ export default {
})
})
},
videoPlaying () {
history.find({ site: this.video.site, ids: this.video.ids }).then(res => {
if (res) {
res.index = this.video.index
history.update(res.id, res)
} else {
const doc = {
site: this.video.site,
ids: this.video.ids,
name: this.video.name,
index: this.video.index,
time: 0
}
history.add(doc)
async videoPlaying () {
const db = await history.find({ site: this.video.site, ids: this.video.ids })
if (db) {
db.index = this.video.index
history.update(db.id, db)
} else {
const doc = {
site: this.video.site,
ids: this.video.ids,
name: this.video.name,
index: this.video.index,
time: 0
}
})
history.add(doc)
}
this.timerEvent()
},
timerEvent () {
this.timer = setInterval(() => {
this.timer = setInterval(async () => {
const endTime = this.xg.duration
const currentTime = this.xg.currentTime
const progress = (currentTime / endTime) * 100
this.progress = progress.toFixed(2)
history.find({ site: this.video.site, ids: this.video.ids }).then(res => {
if (res) {
const v = res
v.time = this.xg.currentTime
v.index = this.video.index
const id = v.id
delete v.id
history.update(id, v)
}
})
const db = await history.find({ site: this.video.site, ids: this.video.ids })
if (db) {
const v = db
v.time = this.xg.currentTime
v.index = this.video.index
const id = v.id
delete v.id
history.update(id, v)
}
}, 10000)
},
prevEvent () {
async prevEvent () {
if (this.video.index === 0) {
this.$message.info('已是第一集.')
return false
}
history.find({ site: this.video.site, ids: this.video.ids }).then(res => {
const v = res
const db = await history.find({ site: this.video.site, ids: this.video.ids })
if (db) {
const v = db
const id = v.id
v.index--
delete v.id
@@ -225,15 +224,16 @@ export default {
this.xg.src = this.m3u8Arr[v.index]
this.video.index--
})
})
}
},
nextEvent () {
async nextEvent () {
if (this.video.index >= this.m3u8Arr.length - 1) {
this.$message.info('已是最后一集.')
return false
}
history.find({ site: this.video.site, ids: this.video.ids }).then(res => {
const v = res
const db = await history.find({ site: this.video.site, ids: this.video.ids })
if (db) {
const v = db
v.index++
const id = v.id
delete v.id
@@ -241,7 +241,7 @@ export default {
this.xg.src = this.m3u8Arr[v.index]
this.video.index++
})
})
}
},
playbackRateEvent (e) {
let rate = this.xg.playbackRate

View File

@@ -27,7 +27,6 @@ export default new Vuex.Store({
info: {}
},
editSites: {
show: false,
sites: []
}
},

167
yarn.lock
View File

@@ -1023,6 +1023,11 @@
resolved "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
"@types/semver@^7.3.1":
version "7.3.4"
resolved "https://registry.npm.taobao.org/@types/semver/download/@types/semver-7.3.4.tgz#43d7168fec6fa0988bb1a513a697b29296721afb"
integrity sha1-Q9cWj+xvoJiLsaUTppeykpZyGvs=
"@types/yargs-parser@*":
version "15.0.0"
resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
@@ -1809,12 +1814,12 @@ aws4@^1.8.0:
resolved "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==
axios@^0.19.2:
version "0.19.2"
resolved "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
axios@^0.20.0:
version "0.20.0"
resolved "https://registry.npm.taobao.org/axios/download/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd"
integrity sha1-BXujDwSIRpSZOozQf6OUz/EcUL0=
dependencies:
follow-redirects "1.5.10"
follow-redirects "^1.10.0"
babel-eslint@^10.1.0:
version "10.1.0"
@@ -2166,6 +2171,14 @@ builder-util-runtime@8.7.1:
debug "^4.2.0"
sax "^1.2.4"
builder-util-runtime@8.7.2:
version "8.7.2"
resolved "https://registry.npm.taobao.org/builder-util-runtime/download/builder-util-runtime-8.7.2.tgz#d93afc71428a12789b437e13850e1fa7da956d72"
integrity sha1-2Tr8cUKKEnibQ34ThQ4fp9qVbXI=
dependencies:
debug "^4.1.1"
sax "^1.2.4"
builder-util@22.7.0:
version "22.7.0"
resolved "https://registry.npmjs.org/builder-util/-/builder-util-22.7.0.tgz#0776a66e6d6e408a78bed7f17a7ad22516d9e7f0"
@@ -3175,10 +3188,10 @@ d@1, d@^1.0.1:
es5-ext "^0.10.50"
type "^1.0.1"
danmu.js@^0.2.17:
version "0.2.18"
resolved "https://registry.npmjs.org/danmu.js/-/danmu.js-0.2.18.tgz#fd688e7df50367a8a68630d1a189a0132c08dd9c"
integrity sha512-ALfB4o0eQoK5pceEQCwyHmmg2wcxOOGqOflxodVtuc9Prq9Fyb2MAiyrfA+q/4WV1E4CnjoCep/8YHKy1X9IQg==
danmu.js@0.2.23:
version "0.2.23"
resolved "https://registry.npm.taobao.org/danmu.js/download/danmu.js-0.2.23.tgz#339b512152ed5a8253829be007c5be5bd3821856"
integrity sha1-M5tRIVLtWoJTgpvgB8W+W9OCGFY=
dependencies:
event-emitter "^0.3.5"
@@ -3201,13 +3214,6 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
dependencies:
ms "2.0.0"
debug@=3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
ms "2.0.0"
debug@^3.1.1, debug@^3.2.5:
version "3.2.6"
resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -3382,10 +3388,10 @@ detect-node@^2.0.4:
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
dexie@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/dexie/-/dexie-3.0.1.tgz#faafeb94be0d5e18b25d700546a2c05725511cfc"
integrity sha512-/s4KzlaerQnCad/uY1ZNdFckTrbdMVhLlziYQzz62Ff9Ick1lHGomvTXNfwh4ApEZATyXRyVk5F6/y8UU84B0w==
dexie@^3.0.2:
version "3.0.2"
resolved "https://registry.npm.taobao.org/dexie/download/dexie-3.0.2.tgz#4b979904d739e0530b68352005f175a82633a075"
integrity sha1-S5eZBNc54FMLaDUgBfF1qCYzoHU=
diffie-hellman@^5.0.0:
version "5.0.3"
@@ -3664,10 +3670,23 @@ electron-to-chromium@^1.3.488:
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.496.tgz#3f43d32930481d82ad3663d79658e7c59a58af0b"
integrity sha512-TXY4mwoyowwi4Lsrq9vcTUYBThyc1b2hXaTZI13p8/FRhY2CTaq5lK+DVjhYkKiTLsKt569Xes+0J5JsVXFurQ==
electron@^9.3.1:
version "9.3.1"
resolved "https://registry.yarnpkg.com/electron/-/electron-9.3.1.tgz#e301932c5c0537d8c9a8850d216d3ba454dbf55c"
integrity sha512-DScrhqBT4a54KfdF0EoipALpHmdQTn3m7SSCtbpTcEcG+UDUiXad2cOfW6DHeVH7N+CVDKDG12q2PhVJjXkFAA==
electron-updater@^4.3.5:
version "4.3.5"
resolved "https://registry.npm.taobao.org/electron-updater/download/electron-updater-4.3.5.tgz?cache=0&sync_timestamp=1600328924432&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felectron-updater%2Fdownload%2Felectron-updater-4.3.5.tgz#4fb36f593a031c87ea07ee141c9f064d5deffb15"
integrity sha1-T7NvWToDHIfqB+4UHJ8GTV3v+xU=
dependencies:
"@types/semver" "^7.3.1"
builder-util-runtime "8.7.2"
fs-extra "^9.0.1"
js-yaml "^3.14.0"
lazy-val "^1.0.4"
lodash.isequal "^4.5.0"
semver "^7.3.2"
electron@^10.1.4:
version "10.1.4"
resolved "https://registry.npm.taobao.org/electron/download/electron-10.1.4.tgz?cache=0&sync_timestamp=1603157068707&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felectron%2Fdownload%2Felectron-10.1.4.tgz#5462c5fac5b4728691042d0f62133ea2c133e6fd"
integrity sha1-VGLF+sW0coaRBC0PYhM+osEz5v0=
dependencies:
"@electron/get" "^1.0.1"
"@types/node" "^12.0.12"
@@ -4121,6 +4140,11 @@ eventemitter3@^4.0.0:
resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384"
integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==
eventemitter3@^4.0.7:
version "4.0.7"
resolved "https://registry.npm.taobao.org/eventemitter3/download/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha1-Lem2j2Uo1WRO9cWVJqG0oHMGFp8=
events@^3.0.0:
version "3.1.0"
resolved "https://registry.npmjs.org/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59"
@@ -4528,18 +4552,16 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
dependencies:
debug "=3.1.0"
follow-redirects@^1.0.0:
version "1.12.1"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz#de54a6205311b93d60398ebc01cf7015682312b6"
integrity sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==
follow-redirects@^1.10.0:
version "1.13.0"
resolved "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
integrity sha1-tC6Nk6Kn7qXtiGM2dtZZe8jjhNs=
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -5081,10 +5103,10 @@ html-webpack-plugin@^3.2.0:
toposort "^1.0.0"
util.promisify "1.0.0"
html2canvas@^1.0.0-rc.5:
version "1.0.0-rc.5"
resolved "https://registry.npmjs.org/html2canvas/-/html2canvas-1.0.0-rc.5.tgz#4ee3cac9f6e20a0fa0c2f35a6f99c960ae7ec4c1"
integrity sha512-DtNqPxJNXPoTajs+lVQzGS1SULRI4GQaROeU5R41xH8acffHukxRh/NBVcTBsfCkJSkLq91rih5TpbEwUP9yWA==
html2canvas@^1.0.0-rc.7:
version "1.0.0-rc.7"
resolved "https://registry.npm.taobao.org/html2canvas/download/html2canvas-1.0.0-rc.7.tgz#70c159ce0e63954a91169531894d08ad5627ac98"
integrity sha1-cMFZzg5jlUqRFpUxiU0IrVYnrJg=
dependencies:
css-line-break "1.1.1"
@@ -6070,6 +6092,11 @@ lodash.defaultsdeep@^4.6.1:
resolved "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6"
integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.npm.taobao.org/lodash.isequal/download/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.kebabcase@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
@@ -6440,10 +6467,10 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
modern-normalize@^0.6.0:
version "0.6.0"
resolved "https://registry.npmjs.org/modern-normalize/-/modern-normalize-0.6.0.tgz#21a469988edfc7c9020348e7a3b5b3583a1c9406"
integrity sha512-gzvL1uFLV4EErHhaKoqTcrx52/sea6AIcMFWz/GBCuFrDBp485wSgyX5M+MZsj+grfcuqYLQGfHjDRrCvvwZLw==
modern-normalize@^1.0.0:
version "1.0.0"
resolved "https://registry.npm.taobao.org/modern-normalize/download/modern-normalize-1.0.0.tgz#539d84a1e141338b01b346f3e27396d0ed17601e"
integrity sha1-U52EoeFBM4sBs0bz4nOW0O0XYB4=
mousetrap@^1.6.5:
version "1.6.5"
@@ -9584,6 +9611,11 @@ uuid@^3.3.2, uuid@^3.4.0:
resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
v-fit-columns@^0.2.0:
version "0.2.0"
resolved "https://registry.npm.taobao.org/v-fit-columns/download/v-fit-columns-0.2.0.tgz#4d75b0eb66fcd701025044f356cc00e8d6fec7a2"
integrity sha1-TXWw62b81wECUETzVswA6Nb+x6I=
v8-compile-cache@^2.0.3:
version "2.1.1"
resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745"
@@ -9687,10 +9719,10 @@ vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
vue-template-compiler@^2.6.11:
version "2.6.11"
resolved "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz#c04704ef8f498b153130018993e56309d4698080"
integrity sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==
vue-template-compiler@^2.6.12:
version "2.6.12"
resolved "https://registry.npm.taobao.org/vue-template-compiler/download/vue-template-compiler-2.6.12.tgz?cache=0&sync_timestamp=1602239010008&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-template-compiler%2Fdownload%2Fvue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e"
integrity sha1-lH7XGWdEyKUoXr4SM/6WBDf8xX4=
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
@@ -9700,27 +9732,27 @@ vue-template-es2015-compiler@^1.9.0:
resolved "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue-waterfall-plugin@^1.0.7:
version "1.0.7"
resolved "https://registry.npmjs.org/vue-waterfall-plugin/-/vue-waterfall-plugin-1.0.7.tgz#bd351e8d3015b9ce71320e00a88443929d63465f"
integrity sha512-60JlBvrQbIOapGwc9AW9cXV064uzIEV1bjzc1ApNGt+MI4PI0Xs9aDvFgRMsGdynV9UlsLNtplX0bRwJ1syT9A==
vue-waterfall-plugin@^1.1.0:
version "1.1.0"
resolved "https://registry.npm.taobao.org/vue-waterfall-plugin/download/vue-waterfall-plugin-1.1.0.tgz#a4d87c74b72f1e36de5bbbadf1d645eb549ed592"
integrity sha1-pNh8dLcvHjbeW7ut8dZF61Se1ZI=
vue@^2.6.11:
version "2.6.11"
resolved "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
vue@^2.6.12:
version "2.6.12"
resolved "https://registry.npm.taobao.org/vue/download/vue-2.6.12.tgz?cache=0&sync_timestamp=1602778254831&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue%2Fdownload%2Fvue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
integrity sha1-9evU+mvShpQD4pqJau1JBEVskSM=
vuedraggable@^2.24.1:
version "2.24.1"
resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.24.1.tgz#304abd7644dde05c1f199a227bf9e9107f56197a"
integrity sha512-G1fxO1oshx+WLdieSGl6jSJdlHOQFga1FpjuUpgXldbpKNzxpjsGn4xYNnRHVrOAqm8aG5FfpdQlh5LHesxCeA==
vuedraggable@^2.24.2:
version "2.24.2"
resolved "https://registry.npm.taobao.org/vuedraggable/download/vuedraggable-2.24.2.tgz#cd98faec99905238469e9b4d235fba5a59fb7da2"
integrity sha1-zZj67JmQUjhGnptNI1+6Wln7faI=
dependencies:
sortablejs "^1.10.1"
vuex@^3.4.0:
vuex@^3.5.1:
version "3.5.1"
resolved "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz#f1b8dcea649bc25254cf4f4358081dbf5da18b3d"
integrity sha512-w7oJzmHQs0FM9LXodfskhw9wgKBiaB+totOdb8sNzbTB2KDCEEwEs29NzBZFh/lmEK1t5tDmM1vtsO7ubG1DFw==
resolved "https://registry.npm.taobao.org/vuex/download/vuex-3.5.1.tgz#f1b8dcea649bc25254cf4f4358081dbf5da18b3d"
integrity sha1-8bjc6mSbwlJUz09DWAgdv12hiz0=
watchpack-chokidar2@^2.0.0:
version "2.0.0"
@@ -9994,22 +10026,23 @@ xdg-basedir@^4.0.0:
resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
xgplayer-hls.js@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/xgplayer-hls.js/-/xgplayer-hls.js-2.2.3.tgz#bf538911145346c447528d609fc95dd267a24d81"
integrity sha512-CFsZanBHHRbqTmqYCFFONk76oWwJwbn+8LK/XkwBBrUrqGPQPmrXfjelGjKDDGfZ+w3xReC1nGom3IpN+lLoaQ==
xgplayer-hls.js@^2.2.5:
version "2.2.5"
resolved "https://registry.npm.taobao.org/xgplayer-hls.js/download/xgplayer-hls.js-2.2.5.tgz?cache=0&sync_timestamp=1600935842683&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fxgplayer-hls.js%2Fdownload%2Fxgplayer-hls.js-2.2.5.tgz#8f90980cf2b6b4610b5449a3a55a4e7d87335cc8"
integrity sha1-j5CYDPK2tGELVEmjpVpOfYczXMg=
dependencies:
deepmerge "2.0.1"
event-emitter "^0.3.5"
eventemitter3 "^4.0.7"
xgplayer@^2.9.10:
version "2.9.10"
resolved "https://registry.yarnpkg.com/xgplayer/-/xgplayer-2.9.10.tgz#4e79b889621f829b773c118ab93da55f829d4490"
integrity sha512-5a7Xm0AY8qPRXZFxrPRErjzCERsENaskQ8Fq5Sy4ht13TMzYp0VIRREU7XF8efpJGcPsOOuFlCiMrqSpYEIAUA==
xgplayer@^2.13.0:
version "2.13.0"
resolved "https://registry.npmjs.org/xgplayer/-/xgplayer-2.13.0.tgz#24df20fd527a87cccc5cee3db70937408eab86ed"
integrity sha512-QMjwnpO6o8bd2ZywHtANaOqvey476UAmPS+hXC0im9wiESMKqxPsCmeqhIxFFVao/pUtIyopHADcor9ipai5Rg==
dependencies:
chalk "^2.3.2"
commander "^2.15.1"
danmu.js "^0.2.17"
danmu.js "0.2.23"
deepmerge "^1.5.0"
downloadjs "1.4.7"
draggabilly "^2.2.0"