Compare commits

...

231 Commits

Author SHA1 Message Date
buvta
fea076318d v2.7.1 2020-12-31 23:30:00 +08:00
buvta
df867fa919 修复播放和历史页面无法分享的bug 2020-12-31 23:27:25 +08:00
buvta
a7f0030462 疑似需要授权,先移除阿里云源 2020-12-31 22:53:05 +08:00
buvta
b5274c79b0 修复获取下载链接时的bug 2020-12-31 22:32:59 +08:00
buvta
a2de1984e3 改为原来的更新组件,避免因为超时而中断卡住 2020-12-31 22:12:30 +08:00
buvta
81b5391f64 详情页根据历史记录更新videoFlag 2020-12-31 20:20:34 +08:00
buvta
2c6ad44974 根据videoFlag获取下载链接 2020-12-31 20:11:06 +08:00
buvta
e1c942dc7b 过滤掉无效链接 2020-12-31 19:16:35 +08:00
buvta
4420e6961e 修复因为删除m3u8List数据库更新bug 2020-12-31 18:35:25 +08:00
buvta
2deffab3ba 解决detail丢失问题 2020-12-31 17:08:55 +08:00
buvta
05a07bee62 移除m3u8List 2020-12-31 16:34:18 +08:00
buvta
025d1606f7 保存videoFlag 2020-12-31 16:34:18 +08:00
buvta
b72c88dfb9 将ZY支持的播放列表前置到首位 2020-12-31 16:34:05 +08:00
buvta
db240cc163 优化getPlayer 2020-12-31 13:38:44 +08:00
haiyangcui
0ef55f0139 在线解析的视频,不显示"手动跳略时长" 2020-12-30 17:14:46 +01:00
haiyangcui
0ad9b49224 解决有多个视频列表时的播放错误 2020-12-30 17:08:21 +01:00
haiyangcui
ccbf4fb5d7 移除不必要的log 2020-12-30 17:03:47 +01:00
haiyangcui
46e376a351 翻转需要解析的视频网站的视频顺序,保证最近更新的视频在最上面 2020-12-30 16:58:33 +01:00
buvta
23e693a314 多个播放视频列表时优先选择应用支持的 2020-12-30 23:52:38 +08:00
buvta
3a3e37f4bd 修复直接手动设置调略设置时片头片尾一起变动 2020-12-30 23:08:46 +08:00
buvta
e568053b95 修复改动导致快捷键设置片头片尾标记失效 2020-12-30 23:02:22 +08:00
haiyangcui
fe9cd9f5bc 无需记录mp4List 2020-12-30 15:44:19 +01:00
buvta
8cf3ec94d9 清理Play多余代码 2020-12-30 20:23:57 +08:00
buvta
112f92d3e0 在线解析时添加进历史记录 2020-12-30 20:10:11 +08:00
buvta
88db766337 添加提示,避免调用解析接口响应慢时尴尬空白 2020-12-30 19:32:55 +08:00
buvta
205b9d7979 改写zy.download以优化代码 2020-12-30 17:13:12 +08:00
buvta
d5a7771a67 vuex引入DetailCache,避免重复获取详情页 2020-12-30 15:57:08 +08:00
buvta
655ed7fb16 解决改动新引入的bug 2020-12-30 12:41:34 +08:00
haiyangcui
808f3d2012 修复播放完在线解析的视频后无法播放m3u8的问题 2020-12-29 23:38:02 +01:00
haiyangcui
ee4b944abf 修改被选中的video flag背景色 2020-12-29 23:03:47 +01:00
haiyangcui
b4abd9369f 将视频flag列表和剧集列表分开 2020-12-29 22:56:08 +01:00
haiyangcui
079da637ec 只有当视频有多个源时,显示视频源flag列表 2020-12-29 22:53:30 +01:00
haiyangcui
0c8a100d1c 视频分类被选后时改变背景颜色 2020-12-29 22:29:39 +01:00
haiyangcui
0cc330463f 获取全部视频提供的视频列表,为兼容目的,zy.detail函数仍返回m3u8List和mp4List, 会在后续工作中陆续删除 2020-12-29 22:04:52 +01:00
buvta
f50d669a5f 修正下载功能并调整通知描述 2020-12-30 00:16:22 +08:00
buvta
57fd1d325e 修复Play点击播放器下方按钮时弹窗反复弹出及按钮active状态 2020-12-29 22:59:48 +08:00
buvta
e31b921486 修复Play点播m3u8播放列表导出功能 2020-12-29 21:29:10 +08:00
buvta
08b39a5bb1 调整Play历史记录的播放时间保存 2020-12-29 18:30:05 +08:00
buvta
1fefc792fc 修改请求referer 2020-12-29 17:03:13 +08:00
buvta
b9a4784de2 bug依旧,xgplayer-mp4似乎没啥作用先移除 2020-12-29 15:59:43 +08:00
buvta
855472fe68 修复mp4停止后点击播放按钮无法播放,并引入xgplayer-flv.js 2020-12-29 00:50:59 +08:00
buvta
4834d4423c Revert "eslint自动修复var定义变量带来的警告"
This reverts commit 4de2177cc7.
2020-12-28 23:05:12 +08:00
buvta
4de2177cc7 eslint自动修复var定义变量带来的警告 2020-12-28 22:29:11 +08:00
buvta
ca9f9c9160 修复点击播放按钮时产生重复历史记录 2020-12-28 21:37:29 +08:00
buvta
7c0f688f9d 修正Play页面最近历史记录 2020-12-28 20:15:09 +08:00
buvta
4bfae63201 点播支持mp4格式视频源 2020-12-28 19:45:18 +08:00
haiyangcui
419a56518f 解决上次的commit错误 2020-12-27 21:55:30 +01:00
haiyangcui
36efdcd869 sites数据引入jiexiUrl数据段,播放时需要解析时,不同源可以定义不同的解析网站 2020-12-27 21:53:24 +01:00
haiyangcui
a297aca812 直接在内嵌iframe中播放阿里云资源 2020-12-27 17:36:49 +01:00
buvta
0b4dd2e859 阿里云资源站临时可用 2020-12-28 00:00:24 +08:00
buvta
3142306a0c 优化tools使某些源站api可用 2020-12-27 21:20:28 +08:00
haiyangcui
9d4765c1ea 修改拼写错误 2020-12-26 22:59:25 +01:00
buvta
547dffb922 动态配置axios超时 2020-12-26 22:35:30 +08:00
buvta
84e61acde7 Update bug.md 2020-12-26 14:18:10 +08:00
haiyangcui
11c756466c 播放的一开始就先清空onlineUrl,否则播放iptv会有问题 2020-12-24 16:22:51 +01:00
haiyangcui
06291ffc4b 解决播放解析视频后再打开,因为没有时间记录而报错的问题 2020-12-24 16:06:56 +01:00
haiyangcui
1f65e5ba0a 在播放页面利用iframe嵌入需要在线解析的内容 2020-12-24 15:53:02 +01:00
haiyangcui
392f0fd326 改进豆瓣搜索,匹配名字和年代 2020-12-24 13:04:01 +01:00
haiyangcui
a6b6407679 采用7K接口 2020-12-23 14:30:59 +01:00
haiyangcui
96678bf5df 只有当xg-btn-showhistory存在时,才设置它的样式 2020-12-23 14:30:05 +01:00
haiyangcui
3c37b8286f 解决运行时因为star history table还没实例化而出现的错误 2020-12-23 14:27:53 +01:00
haiyangcui
38862f6ad8 升级依赖,并解决升级后出现的错误 2020-12-23 14:24:43 +01:00
buvta
35213238f4 使用el内置clickoutside替代vue-clickaway,修复点击菜单按钮时被捕捉失效 2020-12-21 21:01:51 +08:00
buvta
2a03388d6b v2.7.0 2020-12-19 13:29:31 +08:00
buvta
ef704a9d45 禁用allowRunningInsecureContent 2020-12-19 13:27:07 +08:00
buvta
946978fa86 修复深色主题下el-switch标签文本不可见 2020-12-19 13:10:30 +08:00
haiyangcui
70b03e67fb 在右侧显示分隔栏内容,1.在中间的时候永远无法和中间的下拉菜单对齐, 2. 不常用的按钮放在边侧,以降低对用户注意力的影响 2020-12-18 22:25:55 +01:00
haiyangcui
1ee7c6f032 设置不同主题下的工具栏样式 2020-12-18 22:11:18 +01:00
buvta
f2f58fb888 调整版本号到v2.6.10 2020-12-18 18:03:35 +08:00
buvta
5286568801 允许证书错误v2.7.0rc 2020-12-18 17:16:23 +08:00
buvta
2a9f8ed0dc 避免改动数据库时重置软件 2020-12-18 16:25:04 +08:00
buvta
eb3d064cb5 改动shortcut数据库,避免快捷键指南太过杂乱 2020-12-17 20:33:56 +08:00
buvta
a0ec282cc2 fix:改动快捷键设置时未及时生效 2020-12-17 20:15:16 +08:00
buvta
ea61dca27b film下el-select的visible-change事件改为change 2020-12-17 16:54:21 +08:00
buvta
fd0be9e96b film搜索调整隐藏列 2020-12-17 16:49:49 +08:00
buvta
775247e28b 设置快捷键添加重置功能 2020-12-17 16:47:57 +08:00
buvta
ded5c39790 Play频道列表添加clickaway事件 2020-12-17 16:36:40 +08:00
buvta
16e44d71bd 海报模式下不提醒支持shift 2020-12-17 16:22:20 +08:00
buvta
ae0858319f 完善导入导出的title提示 2020-12-15 20:35:50 +08:00
buvta
3c6733648b 再次调整resize事件 2020-12-15 20:22:01 +08:00
buvta
06bd915964 调大el-select下拉框高度 2020-12-15 17:15:25 +08:00
buvta
dfcd786f53 fix:收藏批量删除 2020-12-15 16:24:12 +08:00
buvta
d0f6282b81 批量检测完成时提示 2020-12-15 15:42:13 +08:00
buvta
ab3a6e1fd2 各页面完善按钮提示 2020-12-15 15:20:55 +08:00
buvta
0cb540d3e5 历史收藏添加支持shift提示 2020-12-15 14:34:14 +08:00
buvta
1d0123d69f fix:历史收藏列表模式拖拽调序 2020-12-15 14:23:31 +08:00
buvta
6dbcb8e621 历史与收藏添加批量删除 2020-12-15 14:17:13 +08:00
buvta
b1a6d58974 清除iptv的watch view 2020-12-15 14:12:02 +08:00
haiyangcui
bb45006ff7 Revert "删除跳过片头片尾里的无用代码"
This reverts commit e9f81faf70.
2020-12-14 17:11:47 +01:00
haiyangcui
e9f81faf70 删除跳过片头片尾里的无用代码 2020-12-14 16:26:47 +01:00
cuiocean
b864c6ca15 Merge pull request #383 from Hunlongyu/dependabot/npm_and_yarn/ini-1.3.8
Bump ini from 1.3.5 to 1.3.8
2020-12-14 16:12:49 +01:00
buvta
9f98231eed 添加提示次数限制 2020-12-14 22:27:21 +08:00
buvta
3bcf5b1618 添加多选支持shift提示 2020-12-14 21:21:29 +08:00
buvta
d7a6245f8a 导出为json格式时自动添加扩展名 2020-12-14 21:19:32 +08:00
buvta
2abf2aeda7 设置页添加更新日志与常见问题链接 2020-12-14 17:18:19 +08:00
buvta
c3129c7cee 调整Play页面某些变量的命名 2020-12-14 17:08:38 +08:00
buvta
3b0ec94d2b 直播停止时亦可弹出频道列表 2020-12-14 17:03:24 +08:00
buvta
9902d98342 调整海报模式延时间隔 2020-12-13 23:17:28 +08:00
buvta
b5f5baeb8f 点击播放历史记录按钮时隐藏频道列表 2020-12-13 22:59:17 +08:00
buvta
f396d5ea05 停止时隐藏跳过片头片尾 2020-12-13 22:51:46 +08:00
buvta
e616aa5e2a 播放器初始状态点播放,历史记录为空时直接停止重置 2020-12-13 22:25:58 +08:00
buvta
f041093fe9 清除播放窗口还原时hasStart变量 2020-12-13 22:25:58 +08:00
buvta
e66c056283 稍微调整跳过片头片尾,并添加注释 2020-12-13 22:25:52 +08:00
haiyangcui
d33bbcff06 设置片头片尾按钮的文字提示改为tooltips提示 2020-12-13 14:49:11 +01:00
buvta
992c2f152f 推荐页切换页面时没必要重新读取数据库,并修正换行符 2020-12-13 18:36:31 +08:00
buvta
118bc5058c 调整历史播放页面watch监测view的事件 2020-12-13 18:21:38 +08:00
buvta
19c11f2694 fix:海报模式film页面加载图片时来回切换页面偶尔有卡片被部分遮掩,其它页面是重新加载数据故而不用 2020-12-13 18:12:47 +08:00
buvta
0a812ff6c4 el-select多选时显示全部标签 2020-12-13 16:30:12 +08:00
buvta
1e6a98df88 海报模式重新加上resize事件 2020-12-13 16:08:57 +08:00
buvta
b1b4e61244 fix:改造时调整rowDrop触发时机 2020-12-13 15:38:36 +08:00
buvta
2a05b2abdb 推荐、历史、收藏页面进行类film改造 2020-12-13 15:37:10 +08:00
buvta
74beb47ad5 fix: film搜索切换回海报模式时部分卡片被遮掩显示不全的bug 2020-12-13 15:08:32 +08:00
buvta
325a7f3b0d 调小播放器重置延时 2020-12-13 13:10:52 +08:00
buvta
9b5b5a1334 修复播放器初始状态时点击播放时的bug,并给直播加上对应功能 2020-12-13 13:09:36 +08:00
buvta
74dee7a892 调整film窗口resize事件触发机制 2020-12-13 11:53:35 +08:00
dependabot[bot]
d07436d876 Bump ini from 1.3.5 to 1.3.8
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-13 01:04:59 +00:00
buvta
e283152a89 去除多余代码,Waterfall在数据及窗口变动时会自动刷新 2020-12-12 23:02:20 +08:00
buvta
bdeeccedb9 film列表模式窗口宽度足够时显示“最近更新” 2020-12-12 23:01:31 +08:00
haiyangcui
5931266770 播放页面,显示历史时间信息 2020-12-12 13:51:58 +01:00
haiyangcui
bbeb75631d 打开ZY,直接播放的话,或者是视频停止后再点击播放的话,播放最近的历史记录 2020-12-12 13:39:44 +01:00
haiyangcui
c0f4940289 设置quitMiniMode按钮默认不显示,这样每次播放视频的时候就无需设置'xg-btn-quitMiniMode'的样式 2020-12-12 13:01:27 +01:00
buvta
db1b81243a 修复“站内”时切换站点可搜索 2020-12-12 12:56:55 +08:00
buvta
97b6a8259f 继续调整 2020-12-12 12:52:27 +08:00
buvta
4c92ff9e70 fix:优化film停止搜索时的自动切换searchRunning问题 2020-12-12 12:25:04 +08:00
buvta
b8dc7f4526 film工具栏分割线添加"回到顶部" 2020-12-11 23:42:28 +08:00
buvta
0d6e0d6e9f film停止搜索优化 2020-12-11 23:07:13 +08:00
buvta
1a5bac68ad 调整film搜索表格过滤器设置 2020-12-11 16:11:12 +08:00
buvta
83e302aeb7 film搜索使用工具栏实现过滤(功能实现) 2020-12-11 15:45:36 +08:00
buvta
2196eaa68c 视图模式切换转移到工具栏分割线,搜索亦可用工具栏(功能待实现) 2020-12-11 15:41:28 +08:00
haiyangcui
40aae02af6 修复收藏页的共享功能 2020-12-10 17:17:28 +01:00
haiyangcui
f078df6f8e 修复推荐页的分享功能 2020-12-10 17:14:49 +01:00
haiyangcui
ee44bfe0a8 支持停止搜索,虽然实际上是停止把搜索结果加入searchContents,但对于用户来说应该是足够了. 2020-12-10 16:59:07 +01:00
hunlongyu
9d9a808226 修复分享二维码地址为空的bug 2020-12-10 23:17:55 +08:00
buvta
d78784c0d9 fix:精简模式切换内容时quitMini图标消失 2020-12-10 16:10:51 +08:00
buvta
1ead8a5594 开启过滤切换分类数据过少时加大请求间隔 2020-12-10 16:10:51 +08:00
Hunlongyu
b63c8f4daf 优化自动更新功能, 增加进度条 2020-12-10 14:59:49 +08:00
Hunlongyu
011400f714 删除 server.js 文件 2020-12-10 10:00:20 +08:00
Hunlongyu
1d725579de Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player into master 2020-12-10 09:59:07 +08:00
Hunlongyu
93749c3841 移除 node 代理 2020-12-10 09:59:01 +08:00
haiyangcui
4b0c67b8fa 添加"跳略设置"按钮,避免一直显示片头片尾设置 2020-12-08 18:08:03 +01:00
buvta
165040872c fix:工具栏收起时重置selectedArea 2020-12-08 22:06:09 +08:00
buvta
27773be1c7 v2.6.9 2020-12-08 18:26:05 +08:00
buvta
fadb50a28b film调整areas刷新策略 2020-12-08 18:14:21 +08:00
buvta
518a9c3492 播放视频开始时重置片头片尾输入框数据 2020-12-08 17:36:33 +08:00
buvta
d5eb4dc49f film搜索不加载工具栏开关 2020-12-08 16:48:27 +08:00
buvta
e76bee574f film避免列表模式下加载图片 2020-12-08 16:46:02 +08:00
buvta
b7779a3e6f 疑似el-select弹窗bug,临时性修补 2020-12-08 15:45:25 +08:00
buvta
4cb5b48985 收起工具栏时重置列表 2020-12-08 15:37:21 +08:00
buvta
b2128113ba 调整工具栏refreshFilteredList触发方式 2020-12-08 15:24:36 +08:00
buvta
58d0ae6da1 工具栏"语言"替换为"排序" 2020-12-08 13:03:58 +08:00
buvta
2bd821f59d 调整工具栏打开方式 2020-12-08 12:20:12 +08:00
haiyangcui
8d002225fd 添加'过滤器'按钮,控制过滤选项按钮组的显示 2020-12-07 16:47:45 +01:00
haiyangcui
0243c2f0fe Fix: 切换类型时,总是调用refreshFilteredList 2020-12-07 16:28:30 +01:00
buvta
b8edd7f440 Revert "fix:海报模式切换分类时偶尔空白页"
This reverts commit 1df6726a3b.
2020-12-07 20:22:22 +08:00
buvta
4683aecf7b fix:上层弹窗被过滤工具栏遮蔽 2020-12-07 20:22:22 +08:00
buvta
16d38ba2b4 调整相应样式 2020-12-07 19:43:36 +08:00
buvta
fe7fe06d48 film添加过滤工具栏 2020-12-07 19:43:36 +08:00
buvta
c1220ef752 手动设置跳过片头片尾时间长度 2020-12-07 07:01:22 +08:00
buvta
f0c221a863 修复福利屏蔽 2020-12-06 18:40:18 +08:00
buvta
1df6726a3b fix:海报模式切换分类时偶尔空白页 2020-12-06 18:29:45 +08:00
buvta
1a9e939f9c 更新直播源 2020-12-06 15:53:55 +08:00
buvta
39cb188604 调整iptv分组命名规则 2020-12-06 15:53:13 +08:00
buvta
aba4d12302 确保不会意外引入代理 2020-12-05 23:54:29 +08:00
buvta
17c77fd48a 禁止xgplayer显示重播 2020-12-05 23:48:24 +08:00
buvta
1cfc12ec19 fix:确保不会跳过多集 2020-12-05 23:46:03 +08:00
buvta
b0aa1dc28a 视频多集时可通过快捷键设置跳过片头片尾,保存在相应的历史记录中 2020-12-05 19:10:05 +08:00
haiyangcui
0c12b394a7 视频列表页,支持按地区过滤数据 2020-12-04 14:06:30 +01:00
haiyangcui
9e468bc82e 在类型框内显示视频数信息 2020-12-04 13:48:31 +01:00
haiyangcui
973a15b593 统一downloadEvent函数 2020-12-02 15:19:50 +01:00
Hunlongyu
253c1e7723 分享的网址改为 gitee, 国内用户打开更快 2020-12-02 10:21:54 +08:00
Hunlongyu
7b44051190 更新官网截图,, 修改部分描述 2020-12-02 10:19:12 +08:00
Hunlongyu
a25ac77ebc 更新 README 2020-12-02 10:02:42 +08:00
haiyangcui
d187167fbe 解决换源时视频类型不正常更新的问题 2020-12-01 16:01:47 +01:00
haiyangcui
b34347cf43 添加视频数信息 2020-12-01 13:20:50 +01:00
haiyangcui
829d9447a4 删除重复和不可用的源站 2020-12-01 13:07:11 +01:00
haiyangcui
39067c6a35 缓存视频列表信息 2020-12-01 00:24:38 +01:00
haiyangcui
6dbb64a4f2 重构 2020-11-30 23:58:12 +01:00
haiyangcui
f8041290d2 无需定义getPage函数 2020-11-30 23:49:56 +01:00
haiyangcui
f772ac2e9d 从搜索界面返回时,无需再重新加载数据 2020-11-30 17:13:10 +01:00
haiyangcui
ef485ef64a 删除无用代码 2020-11-30 16:48:43 +01:00
haiyangcui
b164d5e83e Revert "降级xgplayer回2.13.1,新版居然不支持缓冲了."
This reverts commit 8cd2b920c8.
2020-11-30 15:48:13 +01:00
haiyangcui
8a5800df93 v2.6.8 2020-11-30 12:37:40 +01:00
haiyangcui
8cd2b920c8 降级xgplayer回2.13.1,新版居然不支持缓冲了. 2020-11-30 11:37:13 +01:00
haiyangcui
5b4cb43aa5 如果源站返回多个视频列表的话, 仅获取m3u8列表 2020-11-30 11:21:55 +01:00
haiyangcui
de1472e668 升级依赖 2020-11-30 10:56:24 +01:00
haiyangcui
0f846b996b 设置播放器默认预加载300秒 2020-11-29 20:11:23 +01:00
cuiocean
2bd5e31a28 Merge pull request #362 from Hunlongyu/dependabot/npm_and_yarn/highlight.js-9.18.5
Bump highlight.js from 9.18.1 to 9.18.5
2020-11-29 20:04:32 +01:00
haiyangcui
9d8dc9ecb1 删除不工作的cctv蓝光源 2020-11-29 16:53:24 +01:00
haiyangcui
2a073e4092 v2.6.7 2020-11-29 16:26:37 +01:00
haiyangcui
b1daa73afe 更新推荐 2020-11-29 16:23:15 +01:00
haiyangcui
18940ff9f7 Revert "更新视频源,删除无效源,添加需要解析的腾讯,搜狐视频等源"
This reverts commit 76d4d68401.
2020-11-29 13:27:20 +01:00
haiyangcui
76d4d68401 更新视频源,删除无效源,添加需要解析的腾讯,搜狐视频等源 2020-11-28 13:00:29 +01:00
haiyangcui
f91200e609 非m3u8视频,使用在线解析网站播放 2020-11-28 12:46:10 +01:00
haiyangcui
b64f500710 Download函数内解析m3u8List,改进downloadEvent函数 2020-11-28 12:30:43 +01:00
haiyangcui
cf1cfe9f77 支持某些源返回json数据不包含rss层 2020-11-28 11:47:48 +01:00
Hunlongyu
da4fe162f6 Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player into master 2020-11-28 09:44:37 +08:00
Hunlongyu
d949c52020 代码优化 2020-11-28 09:44:32 +08:00
buvta
1ce9e0525b Merge branch 'master' into master 2020-11-25 23:38:06 +08:00
dependabot[bot]
4ebf82eae0 Bump highlight.js from 9.18.1 to 9.18.5
Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 9.18.1 to 9.18.5.
- [Release notes](https://github.com/highlightjs/highlight.js/releases)
- [Changelog](https://github.com/highlightjs/highlight.js/blob/9.18.5/CHANGES.md)
- [Commits](https://github.com/highlightjs/highlight.js/compare/9.18.1...9.18.5)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-25 06:15:54 +00:00
buvta
e0b1a5332d iptv优化 2020-11-24 22:27:49 +08:00
buvta
3ecb4302ba iptv优化 2020-11-24 16:46:29 +08:00
buvta
5c39db8ca6 iptv恢复"播放",避免行点击时误操作 2020-11-23 16:34:53 +08:00
buvta
4f45045d18 精简模式alt+m切换进入退出 2020-11-23 16:34:53 +08:00
buvta
b7733179f2 导入直播源时允许网址带参数 2020-11-23 16:34:53 +08:00
haiyangcui
e1f7044ae0 数据更新后,刷新页面 2020-11-22 20:57:27 +01:00
buvta
93e76db444 恢复记录mini窗口状态
This reverts commit bedd1339df.
2020-11-22 23:48:50 +08:00
buvta
6eaa281e4a 直播主窗口隐藏退出mini图标 2020-11-22 23:31:30 +08:00
buvta
490d34e1fb 移除iptvSearch数据库 2020-11-22 22:39:42 +08:00
buvta
0d0d6bd90f iptv移除iptvSearch 2020-11-22 22:35:48 +08:00
buvta
ecdc96d2f3 Play样式相应调整 2020-11-22 22:35:48 +08:00
buvta
234f6dd069 Play频道搜索用el-input替代 2020-11-22 22:35:38 +08:00
haiyangcui
f80fe7ecf1 添加退出精简模式按钮 2020-11-22 14:37:30 +01:00
haiyangcui
6876721567 进入精简模式,无需设置大小位置,直接调用getCssFullscreen即可 2020-11-22 12:41:22 +01:00
haiyangcui
bedd1339df Revert "记录mini窗口大小" 2020-11-22 12:09:36 +01:00
hunlongyu
0a3a7a4d57 Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player 2020-11-22 16:38:49 +08:00
hunlongyu
786e23f5a2 删除冗余代码 2020-11-22 16:38:43 +08:00
buvta
418f14bcc1 调整Ctrl+0描述(弹窗容不下) 2020-11-22 11:20:00 +08:00
buvta
a9e4a34e4f 精简模式添加快捷键Ctrl+0用于恢复默认 2020-11-22 10:56:30 +08:00
buvta
fede7e4ef2 Revert "mini模式,大小直接设置为当前窗口大小,无需记录到数据库中"
This reverts commit f92af48a4e.
2020-11-22 10:41:19 +08:00
buvta
956ebbf8b8 调整Play页面channelList加载策略 2020-11-22 10:32:26 +08:00
haiyangcui
014dac0b64 排序源站,把可用的放上面 2020-11-21 23:37:05 +01:00
haiyangcui
5fc1020e79 升级所有依赖到最新版 2020-11-21 22:48:44 +01:00
haiyangcui
aca487f796 升级依赖 2020-11-21 22:41:09 +01:00
haiyangcui
429eb4aac0 移动getChannelList到if里,避免不必要的开销 2020-11-21 22:27:26 +01:00
haiyangcui
e032f86d3d 切换视图时,无需更新页面和改变infiniteId 2020-11-21 22:16:06 +01:00
haiyangcui
f92af48a4e mini模式,大小直接设置为当前窗口大小,无需记录到数据库中 2020-11-21 19:07:05 +01:00
haiyangcui
eff69db063 infiniteId会在classClick里更新 2020-11-21 18:52:33 +01:00
haiyangcui
a5c8d20635 更新麻花资源的api链接 2020-11-21 18:39:33 +01:00
haiyangcui
da22abd25d 避免不必要的资源访问 2020-11-21 18:38:53 +01:00
haiyangcui
2dd91f78c9 牛牛资源已失效,删除 2020-11-21 18:38:51 +01:00
buvta
a8b0e7e6a8 fix:直播时上下切换频道时的bug 2020-11-21 23:20:17 +08:00
53 changed files with 6211 additions and 3421 deletions

View File

@@ -12,6 +12,7 @@ module.exports = {
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'standard/no-callback-literal': 0
}
}

View File

@@ -1,7 +1,7 @@
---
name: 报告Bug(请先查看常见问题及搜索issue列表中有无你要提的问题)
name: 报告Bug(请先查看常见问题及搜索已关闭issue列表中有无你要提的问题)
about: 创建报告以帮助我们改进
title: '(未回答的问题请删除)'
title: '(未回答的问题请删除,减少多余信息干扰'
labels: bug
assignees: ''

View File

@@ -46,6 +46,17 @@
- 🍉 [蓝奏云 -- 快速下载](https://www.lanzoux.com/b04s6a3re) 密码:95px
- 🍒 适用于32位操作系统的x86软件,在蓝奏云网盘里, 后缀名: ZY Player * 32位.exe
### 🎠 平台
| 平台 | 链接 |
| :------------------------------------ | :---------------------------------------------------------- |
| 🖥️ 电脑端 ( Windows & Mac & Linux ) | [ZY Player](https://github.com/Hunlongyu/ZY-Player) |
| 📱 手机端 ( Android & IOS ) | [ZY Player APP](https://github.com/Hunlongyu/ZY-Player-APP) |
| 📺 电视端 ( Android & Mac ) ( 进行中 ) | [ZY Player TV](https://github.com/cuiocean/ZY-Player-TV) |
| 🌐 浏览器 ( Web ) | [ZY Player Web](https://github.com/Hunlongyu/ZY-Player-Web) |
### 🚀 快捷键
播放窗口 和 Mini窗口
@@ -81,7 +92,8 @@
### 🍭 开发者
| [Hunlongyu](https://github.com/Hunlongyu) | [cuiocean](https://github.com/cuiocean) |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
| <img width="120" src="https://avatars2.githubusercontent.com/u/15273630?s=460&u=48cf3299e2a842c0252233d8be42ef4c5d792138&v=4"/> | <img width="120" src="https://avatars0.githubusercontent.com/u/5760235?s=460&u=9d969dd8d83f069ce7ebd60516770c93ac07a330&v=4" /> |
| 💻 🎨 🐛 | 💻 🐛 |
| [Hunlongyu](https://github.com/Hunlongyu) | [cuiocean](https://github.com/cuiocean) | [buvta](https://github.com/buvta) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
| <img width="120" src="https://avatars2.githubusercontent.com/u/15273630?s=460&u=48cf3299e2a842c0252233d8be42ef4c5d792138&v=4"/> | <img width="120" src="https://avatars0.githubusercontent.com/u/5760235?s=460&u=9d969dd8d83f069ce7ebd60516770c93ac07a330&v=4" /> | <img width="120" src="https://avatars3.githubusercontent.com/u/12312540?s=400&v=4" /> |
| 💻 🎨 🐛 | 💻 🐛 | 💻 🐛 |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -89,7 +89,7 @@
<div class="section-title">
<h2>软件特色</h2>
<p>经过三个大版本更迭, 软件功能丰富, 操作简单.</p>
<p>软件功能丰富, 操作简单.</p>
</div>
<div class="row no-gutters">
@@ -99,7 +99,7 @@
<div class="col-md-6 icon-box" data-aos="fade-up">
<i class="bx bx-receipt"></i>
<h4>浏览</h4>
<p>浏览全网热门视频, 支持切换视频源. 详细的电影分类.支持搜索电影名和演员名称. </p>
<p>浏览全网热门视频, 支持切换视频源. 详细的电影分类. </p>
</div>
<div class="col-md-6 icon-box" data-aos="fade-up" data-aos-delay="100">
<i class="icofont-play-alt-3"></i>
@@ -124,7 +124,7 @@
<div class="col-md-6 icon-box" data-aos="fade-up" data-aos-delay="500">
<i class="icofont-cubes"></i>
<h4>其他</h4>
<p>多主题, 多语言, 自动更新</p>
<p>多主题, 自动更新</p>
</div>
</div>
</div>
@@ -146,14 +146,14 @@
</div>
<div class="owl-carousel gallery-carousel" data-aos="fade-up">
<a href="assets/img/gallery/001.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/001.png" alt=""></a>
<a href="assets/img/gallery/002.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/002.png" alt=""></a>
<a href="assets/img/gallery/003.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/003.png" alt=""></a>
<a href="assets/img/gallery/004.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/004.png" alt=""></a>
<a href="assets/img/gallery/005.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/005.png" alt=""></a>
<a href="assets/img/gallery/006.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/006.png" alt=""></a>
<a href="assets/img/gallery/007.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/007.png" alt=""></a>
<a href="assets/img/gallery/008.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/008.png" alt=""></a>
<a href="assets/img/gallery/01.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/01.png" alt=""></a>
<a href="assets/img/gallery/02.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/02.png" alt=""></a>
<a href="assets/img/gallery/03.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/03.png" alt=""></a>
<a href="assets/img/gallery/04.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/04.png" alt=""></a>
<a href="assets/img/gallery/05.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/05.png" alt=""></a>
<a href="assets/img/gallery/06.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/06.png" alt=""></a>
<a href="assets/img/gallery/07.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/07.png" alt=""></a>
<a href="assets/img/gallery/08.png" class="venobox" data-gall="gallery-carousel"><img src="assets/img/gallery/08.png" alt=""></a>
</div>
</div>
@@ -193,7 +193,7 @@
<i class="bx bx-help-circle icon-help"></i> <a data-toggle="collapse" href="#accordion-list-3" class="collapsed">跨平台<i class="bx bx-chevron-down icon-show"></i><i class="bx bx-chevron-up icon-close"></i></a>
<div id="accordion-list-3" class="collapse" data-parent=".accordion-list">
<p>
目前支持 Windows, Mac, Linux 桌面系统. 暂不支持手机端或者电视端. 未来会考虑实现全平台.
目前支持 Windows, Mac, Linux, Android, IOS, TV, Web 全平台.
</p>
</div>
</li>

View File

@@ -1,6 +1,6 @@
{
"name": "zy",
"version": "2.6.6",
"version": "2.7.1-HappyNewYear",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -17,58 +17,56 @@
},
"main": "background.js",
"dependencies": {
"axios": "^0.21.0",
"cheerio": "^1.0.0-rc.3",
"core-js": "^3.7.0",
"cors": "^2.8.5",
"dexie": "^3.0.2",
"axios": "^0.21.1",
"cheerio": "^1.0.0-rc.5",
"core-js": "^3.8.1",
"dexie": "^3.0.3",
"electron-localshortcut": "^3.2.1",
"electron-proxy-agent": "^1.2.0",
"electron-updater": "^4.3.5",
"element-ui": "^2.14.0",
"express": "^4.17.1",
"fast-xml-parser": "^3.17.4",
"element-ui": "^2.14.1",
"fast-xml-parser": "^3.17.5",
"html2canvas": "^1.0.0-rc.7",
"iptv-playlist-parser": "^0.5.0",
"iptv-playlist-parser": "^0.5.1",
"m3u": "0.0.2",
"m3u8-parser": "^4.5.0",
"memcached": "^2.2.2",
"modern-normalize": "^1.0.0",
"mousetrap": "^1.6.5",
"pinyin-match": "^1.1.1",
"pinyin-match": "^1.1.7",
"qrcode.vue": "^1.7.0",
"randomstring": "^1.1.5",
"session": "^0.1.0",
"sortablejs": "^1.12.0",
"v-fit-columns": "^0.2.0",
"vue": "^2.6.12",
"vue-clickaway": "^2.2.2",
"vue-infinite-loading": "^2.4.5",
"vue-waterfall-plugin": "^1.1.0",
"vuedraggable": "^2.24.3",
"vuex": "^3.5.1",
"xgplayer": "^2.13.0",
"xgplayer-hls.js": "^2.2.5"
"vuex": "^3.6.0",
"xgplayer": "^2.16.0",
"xgplayer-flv.js": "^2.1.2",
"xgplayer-hls.js": "^2.3.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.4.0",
"@vue/cli-plugin-eslint": "~4.4.0",
"@vue/cli-plugin-vuex": "~4.4.0",
"@vue/cli-service": "~4.4.0",
"@vue/eslint-config-standard": "^5.1.2",
"@vue/cli-plugin-babel": "~4.5.9",
"@vue/cli-plugin-eslint": "~4.5.9",
"@vue/cli-plugin-vuex": "~4.5.9",
"@vue/cli-service": "~4.5.9",
"@vue/eslint-config-standard": "^6.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-component": "^1.1.1",
"electron": "^10.1.5",
"electron-devtools-installer": "^3.1.0",
"eslint": "^6.7.2",
"electron": "^11.1.1",
"electron-devtools-installer": "^3.1.1",
"eslint": "^7.16.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.2",
"eslint-plugin-vue": "^6.2.2",
"sass": "^1.29.0",
"sass-loader": "^8.0.2",
"vue-cli-plugin-electron-builder": "2.0.0-rc.4",
"eslint-plugin-standard": "^4.1.0",
"eslint-plugin-vue": "^7.3.0",
"sass": "^1.30.0",
"sass-loader": "^10.1.0",
"vue-cli-plugin-electron-builder": "2.0.0-rc.5",
"vue-template-compiler": "^2.6.12"
}
}

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="referrer" content="same-origin"/>
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>

View File

@@ -111,16 +111,6 @@
}
}
.play{
.popper {
font-size: 1rem;;
border: none;
li {
font-size: 1rem;
border: none;
}
}
}
// Page of list using el-table
.listpage{
position: absolute;
@@ -133,7 +123,7 @@
border-radius: 5px;
display: flex;
flex-direction: column;
.listpage-header{
.listpage-header, .toolbar{
height: 60px;
width: 100%;
display: flex;
@@ -185,7 +175,39 @@
font-size: 1rem;
border: none;
}
.el-select-dropdown__item.selected.hover{ //是上游的bug吗临时性修补
background-color: transparent;
}
.el-select-dropdown__wrap{
max-height: 574px
}
}
> span{
.el-input-number{
width:120px;
.el-input{
width: 100px;
}
.el-input__inner{
padding-left: 5px;
padding-right: 5px;
width: 88px;
}
.el-input-number__increase, .el-input-number__decrease {
background-color: inherit;
border: none;
}
}
}
}
.el-divider{
.el-divider--horizontal{
margin: 12px 0;
}
}
.toolbar{
z-index: 5;
}
.listpage-body{
height: calc(100% - 60px);

View File

@@ -131,6 +131,12 @@
border-color: var(--d-c-8);
}
}
.selected {
background-color: var(--d-c-5);
&:hover{
background-color: var(--d-c-5);
}
}
}
}
}
@@ -139,6 +145,19 @@
.play{
background-color: var(--d-bgc-1);
box-shadow: var(--d-bsc);
.el-switch__label {
color: var(--d-fc-2)
}
.el-switch__label.is-active {
color: #409EFF
}
.el-input{
input{
background-color: var(--d-bgc-1);
border: 1px solid var(--d-bgc-1);
color: var(--d-fc-2);
}
}
.title{
color: var(--d-fc-1);
.right {
@@ -309,40 +328,35 @@
box-shadow: var(--d-bsc);
}
.play{
.el-input{
input{
background-color: var(--d-bgc-1);
border: 1px solid var(--d-bgc-1);
color: var(--d-fc-2);
}
}
.popper {
color: var(--d-fc-1);
background-color: var(--d-bgc-1);
box-shadow: var(--d-bsc);
li {
color: var(--d-fc-1);
background-color: var(--d-bgc-1);
&:hover{
background-color: var(--d-bgc-2);
}
}
.popper__arrow, .popper__arrow::after{
border-bottom-color: var(--d-bgc-1);
}
}
}
// Page of list using table and picture
.listpage{
color: var(--d-fc-2);
.listpage-header{
.listpage-header-divider{
background-color: var(--d-bgc-1);
.el-divider__text {
background-color: var(--d-bgc-2);
}
.el-button{
background-color: var(--d-bgc-2);
color: var(--d-fc-2);
&:hover{
color: var(--d-fc-3)
}
}
}
.listpage-header, .toolbar{
border-bottom-color: var(--d-c-3);
.btn{
&:hover{
color: var(--d-fc-3)
}
}
.el-switch__label {
color: var(--d-fc-2)
}
.el-switch__label.is-active {
color: #409EFF
}
.el-button{
background-color: var(--d-bgc-2);
color: var(--d-fc-2);

View File

@@ -131,6 +131,12 @@
border-color: var(--g-c-8);
}
}
.selected {
background-color: var(--g-c-5);
&:hover{
background-color: var(--g-c-5);
}
}
}
}
}
@@ -139,6 +145,13 @@
.play{
background-color: var(--g-bgc-1);
box-shadow: var(--g-bsc);
.el-input{
input{
background-color: var(--g-bgc-1);
border: 1px solid var(--g-bgc-1);
color: var(--g-fc-2);
}
}
.title{
color: var(--g-fc-1);
.right {
@@ -309,34 +322,23 @@
box-shadow: var(--g-bsc);
}
.play{
.el-input{
input{
background-color: var(--g-bgc-1);
border: 1px solid var(--g-bgc-1);
color: var(--g-fc-2);
}
}
.popper {
color: var(--g-fc-1);
background-color: var(--g-bgc-1);
box-shadow: var(--g-bsc);
li {
color: var(--g-fc-1);
background-color: var(--g-bgc-1);
&:hover{
background-color: var(--g-bgc-2);
}
}
.popper__arrow, .popper__arrow::after{
border-bottom-color: var(--g-bgc-1);
}
}
}
// Page of list using table and picture
.listpage{
color: var(--g-fc-2);
.listpage-header{
.listpage-header-divider{
background-color: var(--g-bgc-1);
.el-divider__text {
background-color: var(--g-bgc-2);
}
.el-button{
background-color: var(--g-bgc-2);
color: var(--g-fc-2);
&:hover{
color: var(--g-fc-3)
}
}
}
.listpage-header, .toolbar{
border-bottom-color: var(--g-c-3);
.btn{
&:hover{

View File

@@ -131,6 +131,12 @@
border-color: var(--l-c-8);
}
}
.selected {
background-color: var(--l-c-5);
&:hover{
background-color: var(--l-c-5);
}
}
}
}
}
@@ -139,6 +145,13 @@
.play{
background-color: var(--l-bgc-1);
box-shadow: var(--l-bsc);
.el-input{
input{
background-color: var(--l-bgc-1);
border: 1px solid var(--l-bgc-1);
color: var(--l-fc-2);
}
}
.title{
color: var(--l-fc-1);
.right {
@@ -309,34 +322,23 @@
box-shadow: var(--l-bsc);
}
.play{
.el-input{
input{
background-color: var(--l-bgc-1);
border: 1px solid var(--l-bgc-1);
color: var(--l-fc-2);
}
}
.popper {
color: var(--l-fc-1);
background-color: var(--l-bgc-1);
box-shadow: var(--l-bsc);
li {
color: var(--l-fc-1);
background-color: var(--l-bgc-1);
&:hover{
background-color: var(--l-bgc-2);
}
}
.popper__arrow, .popper__arrow::after{
border-bottom-color: var(--l-bgc-1);
}
}
}
// Page of list using table and picture
.listpage{
color: var(--l-fc-2);
.listpage-header{
.listpage-header-divider{
background-color: var(--l-bgc-1);
.el-divider__text {
background-color: var(--l-bgc-2);
}
.el-button{
background-color: var(--l-bgc-2);
color: var(--l-fc-2);
&:hover{
color: var(--l-fc-3)
}
}
}
.listpage-header, .toolbar{
border-bottom-color: var(--l-c-3);
.btn{
&:hover{

View File

@@ -131,6 +131,12 @@
border-color: var(--p-c-8);
}
}
.selected {
background-color: var(--p-c-5);
&:hover{
background-color: var(--p-c-5);
}
}
}
}
}
@@ -139,6 +145,13 @@
.play{
background-color: var(--p-bgc-1);
box-shadow: var(--p-bsc);
.el-input{
input{
background-color: var(--p-bgc-1);
border: 1px solid var(--p-bgc-1);
color: var(--p-fc-2);
}
}
.title{
color: var(--p-fc-1);
.right {
@@ -309,34 +322,23 @@
box-shadow: var(--p-bsc);
}
.play{
.el-input{
input{
background-color: var(--p-bgc-1);
border: 1px solid var(--p-bgc-1);
color: var(--p-fc-2);
}
}
.popper {
color: var(--p-fc-1);
background-color: var(--p-bgc-1);
box-shadow: var(--p-bsc);
li {
color: var(--p-fc-1);
background-color: var(--p-bgc-1);
&:hover{
background-color: var(--p-bgc-2);
}
}
.popper__arrow, .popper__arrow::after{
border-bottom-color: var(--p-bgc-1);
}
}
}
// Page of list using table and picture
.listpage{
color: var(--p-fc-2);
.listpage-header{
.listpage-header-divider{
background-color: var(--p-bgc-1);
.el-divider__text {
background-color: var(--p-bgc-2);
}
.el-button{
background-color: var(--p-bgc-2);
color: var(--p-fc-2);
&:hover{
color: var(--p-fc-3)
}
}
}
.listpage-header, .toolbar{
border-bottom-color: var(--p-c-3);
.btn{
&:hover{

View File

@@ -1,14 +1,14 @@
'use strict'
import './lib/site/server'
import { app, protocol, BrowserWindow, globalShortcut, ipcMain } from 'electron'
import { app, protocol, BrowserWindow, globalShortcut } 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'
// const log = require('electron-log') // 用于调试主程序
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors') // 允许跨域
// app.commandLine.appendSwitch('--ignore-certificate-errors', 'true') // 忽略证书相关错误
app.commandLine.appendSwitch('--ignore-certificate-errors', 'true') // 忽略证书相关错误
let win
@@ -23,7 +23,8 @@ function createWindow () {
webPreferences: {
webSecurity: false,
enableRemoteModule: true,
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
allowRunningInsecureContent: false
}
})
@@ -34,7 +35,24 @@ function createWindow () {
createProtocol('app')
win.loadURL('app://./index.html')
}
// 修改request headers
// Sec-Fetch下禁止修改浏览器自动加上请求头 https://www.cnblogs.com/fulu/p/13879080.html 暂时先用index.html的meta referer policy替代
const filter = {
urls: ['http://*/*', 'http://*/*']
}
win.webContents.session.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
const url = new URL(details.url)
details.requestHeaders.Origin = url.origin
if (!details.url.includes('//localhost') && details.requestHeaders.Referer && details.requestHeaders.Referer.includes('//localhost')) {
details.requestHeaders.Referer = url.origin
}
callback({
cancel: false,
requestHeaders: details.requestHeaders
})
})
initUpdater(win)
win.on('closed', () => {

View File

@@ -52,10 +52,16 @@
</span>
</div>
<div
class="desc" v-show="info.des">{{info.des}}</div>
class="desc" v-show="info.des">{{info.des}}
</div>
<div class="m3u8" v-if="videoFullList.length > 1">
<div class="box">
<span v-bind:class="{ selected: i.flag === videoFlag }" v-for="(i, j) in videoFullList" :key="j" @click="updateVideoList(i)">{{i.flag}}</span>
</div>
</div>
<div class="m3u8">
<div class="box">
<span v-for="(i, j) in m3u8List" :key="j" @click="playEvent(j)">{{i | ftName}}</span>
<span v-for="(i, j) in videoList" :key="j" @click="playEvent(j)">{{i | ftName}}</span>
</div>
</div>
</div>
@@ -76,7 +82,9 @@ export default {
data () {
return {
loading: true,
m3u8List: [],
videoFlag: '',
videoList: [],
videoFullList: [],
info: {},
playOnline: false,
selectedOnlineSite: '哔嘀',
@@ -121,26 +129,52 @@ export default {
set (val) {
this.SET_SHARE(val)
}
},
DetailCache: {
get () {
return this.$store.getters.getDetailCache
},
set (val) {
this.SET_DetailCache(val)
}
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_VIDEO', 'SET_DETAIL', 'SET_SHARE']),
...mapMutations(['SET_VIEW', 'SET_VIDEO', 'SET_DETAIL', 'SET_SHARE', 'SET_DetailCache']),
addClass (flag) {
if (flag === this.videoFlag) {
return 'selectedBox'
} else {
return 'box'
}
},
close () {
this.detail.show = false
},
async updateVideoList (e) {
this.videoFlag = e.flag
this.videoList = e.list
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
if (db) {
const doc = { ...db }
doc.videoFlag = e.flag
delete doc.id
history.update(db.id, doc)
}
},
async playEvent (n) {
if (!this.playOnline) {
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 } }
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: n, site: this.detail.site, videoFlag: this.videoFlag } }
} 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.video = { key: this.detail.key, info: { id: this.detail.info.id, name: this.detail.info.name, index: n, site: this.detail.site, videoFlag: this.videoFlag } }
}
this.video.detail = this.info
this.view = 'Play'
this.detail.show = false
} else {
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
const db = await history.find({ site: this.detail.key, ids: this.info.id })
if (db) {
db.index = n
db.detail = this.info
@@ -211,64 +245,54 @@ export default {
}
},
downloadEvent () {
zy.download(this.detail.key, this.info.id).then(res => {
if (res && res.dl && res.dl.dd) {
const text = res.dl.dd._t
if (text) {
const list = text.split('#')
let downloadUrl = res.name + '\n'
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
} else {
this.$message.warning('没有查询到下载链接.')
}
} else {
const list = [...this.m3u8List]
let downloadUrl = this.detail.info.name + '\n'
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
}
zy.download(this.detail.key, this.info.id, this.videoFlag).then(res => {
clipboard.writeText(res.downloadUrls)
this.$message.success(res.info)
}).catch((err) => {
this.$message.error(err.info)
})
},
shareEvent () {
this.share = {
show: true,
key: this.detail.key,
info: this.detail.info
info: this.info
}
},
doubanLinkEvent () {
const name = this.detail.info.name.trim()
zy.doubanLink(name).then(link => {
const name = this.info.name.trim()
const year = this.info.year
zy.doubanLink(name, year).then(link => {
const open = require('open')
open(link)
})
},
getDoubanRate () {
const name = this.detail.info.name.trim()
zy.doubanRate(name).then(res => {
this.info.rate = res
})
async getDoubanRate () {
const name = this.info.name.trim()
const year = this.info.year
this.info.rate = await zy.doubanRate(name, year)
},
getDetailInfo () {
async getDetailInfo () {
const id = this.detail.info.ids || this.detail.info.id
zy.detail(this.detail.key, id).then(res => {
if (res) {
this.info = res
this.$set(this.info, 'rate', '')
this.m3u8List = res.m3u8List
this.getDoubanRate()
this.loading = false
const cacheKey = this.detail.key + '@' + id
const db = await history.find({ site: this.detail.key, ids: id })
if (db) this.videoFlag = db.videoFlag
if (!this.DetailCache[cacheKey]) {
this.DetailCache[cacheKey] = await zy.detail(this.detail.key, id)
}
const res = this.DetailCache[cacheKey]
if (res) {
this.info = res
this.$set(this.info, 'rate', this.DetailCache[cacheKey].rate || '')
this.videoFlag = this.videoFlag || res.fullList[0].flag
this.videoList = res.fullList[0].list
this.videoFullList = res.fullList
this.loading = false
if (!this.info.rate) {
await this.getDoubanRate()
this.DetailCache[cacheKey].rate = this.info.rate
}
})
}
}
},
created () {
@@ -390,6 +414,15 @@ export default {
margin: 6px 10px 0px 0px;
padding: 8px 22px;
}
.selected {
display: inline-block;
font-size: 12px;
border: 1px solid;
border-radius: 2px;
cursor: pointer;
margin: 6px 10px 0px 0px;
padding: 8px 22px;
}
}
}
}

View File

@@ -4,16 +4,16 @@
<el-switch v-model="enableBatchEdit" active-text="批处理分组">></el-switch>
<el-checkbox v-model="setting.excludeR18Films" @change="excludeR18FilmsChangeEvent">屏蔽福利片</el-checkbox>
<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="checkAllSitesLoading">检测{{ this.checkAllSitesLoading ? this.checkProgress + '/' + this.sites.length : '' }}</el-button>
<el-button @click="exportSites" icon="el-icon-upload2" title="导出全部,自动添加扩展名">导出</el-button>
<el-button @click="importSites" icon="el-icon-download" title="支持同时导入多个文件">导入</el-button>
<el-button @click="checkAllSite" icon="el-icon-refresh" :loading="checkAllSitesLoading" title="可在后台运行">检测{{ this.checkAllSitesLoading ? this.checkProgress + '/' + this.sites.length : '' }}</el-button>
<el-button @click="resetSitesEvent" icon="el-icon-refresh-left">重置</el-button>
</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-text="启用"></el-switch>
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit">保存</el-button>
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit" title="输入框组名为空时仅保存开关状态">保存分组与开关状态</el-button>
<el-button @click="removeSelectedSites" icon="el-icon-delete-solid">删除</el-button>
</div>
<div class="listpage-body" id="sites-body">
@@ -94,6 +94,9 @@
<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='jiexiUrl'>
<el-input v-model="siteInfo.jiexiUrl" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入解析接口地址可以空着"/>
</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>
@@ -133,6 +136,7 @@ export default {
name: '',
api: '',
download: '',
jiexiUrl: '',
group: '',
isActive: true
},
@@ -186,6 +190,17 @@ export default {
this.$message.info('正在检测, 请勿操作.')
this.enableBatchEdit = false
}
if (this.enableBatchEdit) {
if (this.setting.shiftTooltipLimitTimes === undefined) this.setting.shiftTooltipLimitTimes = 5
if (this.setting.shiftTooltipLimitTimes) {
this.$message.info('多选时支持shift快捷键')
this.setting.shiftTooltipLimitTimes--
setting.find().then(res => {
res.shiftTooltipLimitTimes = this.setting.shiftTooltipLimitTimes
setting.update(res)
})
}
}
}
},
methods: {
@@ -260,6 +275,7 @@ export default {
name: '',
api: '',
download: '',
jiexiUrl: '',
group: '',
isActive: true
}
@@ -318,6 +334,7 @@ export default {
name: this.siteInfo.name,
api: this.siteInfo.api,
download: this.siteInfo.download,
jiexiUrl: this.siteInfo.jiexiUrl,
group: this.siteInfo.group,
isActive: this.siteInfo.isActive
}
@@ -328,6 +345,7 @@ export default {
name: '',
api: '',
download: '',
jiexiUrl: '',
group: ''
}
this.dialogType === 'edit' ? this.$message.success('修改成功!') : this.$message.success('新增源成功!')
@@ -342,13 +360,12 @@ export default {
const str = JSON.stringify(arr, null, 2)
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
{ name: 'Normal text file', extensions: ['txt'] },
{ name: 'All types', extensions: ['*'] }
{ name: 'JSON file', extensions: ['json'] }
]
}
remote.dialog.showSaveDialog(options).then(result => {
if (!result.canceled) {
if (!result.filePath.endsWith('.json')) result.filePath += '.json'
fs.writeFileSync(result.filePath, str)
this.$message.success('已保存成功')
}
@@ -363,9 +380,7 @@ export default {
}
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
{ name: 'Normal text file', extensions: ['txt'] },
{ name: 'All types', extensions: ['*'] }
{ name: 'JSON file', extensions: ['json'] }
],
properties: ['openFile', 'multiSelections']
}
@@ -462,6 +477,7 @@ export default {
})
},
async checkAllSite () {
if (this.checkAllSitesLoading) return
this.checkAllSitesLoading = true
this.stopFlag = false
this.checkProgress = 0
@@ -471,6 +487,7 @@ export default {
await Promise.all(other.map(site => this.checkSingleSite(site))).then(res => {
this.checkAllSitesLoading = false
this.getSites()
if (!this.stopFlag) this.$message.success('视频点播源站批量检测已完成!')
})
},
async checkSingleSite (row) {

View File

@@ -9,9 +9,7 @@
:value="item.name">
</el-option>
</el-select>
<el-switch v-model="searchViewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateSearchViewMode"
v-if="show.find"></el-switch>
<el-select v-model="selectedClassName" size="small" placeholder="类型" :popper-append-to-body="false" popper-class="popper" @change="classClick" v-show="show.class">
<el-select v-model="selectedClassName" size="small" placeholder="类型" :popper-append-to-body="false" popper-class="popper" @change="classClick" v-if="classList && classList.length" v-show="!showFind">
<el-option
v-for="item in classList"
:key="item.tid"
@@ -19,6 +17,14 @@
:value="item.name">
</el-option>
</el-select>
<el-select v-model="selectedSearchClassNames" size="small" multiple placeholder="类型" :popper-append-to-body="false" popper-class="popper" v-if="searchClassList && searchClassList.length" v-show="showFind && showToolbar" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
<el-option
v-for="(item, index) in searchClassList"
:key='index'
:label="item"
:value="item">
</el-option>
</el-select>
<el-autocomplete
clearable
size="small"
@@ -44,12 +50,42 @@
</el-option>
</el-select>
<!--方便触屏-->
<el-button icon="el-icon-search" @click.stop="searchEvent" slot="append" />
<el-button icon="el-icon-search" @click.stop="searchEvent" slot="append" v-if="!searchRunning"/>
<el-button icon="el-icon-loading" @click.stop="stopSearchEvent" slot="append" v-if="searchRunning" title='点击可停止搜索'/>
</el-autocomplete>
</div>
<div class="toolbar" v-show="showToolbar">
<el-select v-model="selectedAreas" size="small" multiple placeholder="地区" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
<el-option
v-for="item in areas"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-select v-model="sortKeyword" size="small" placeholder="排序" popper-class="popper" :popper-append-to-body="false" @change="refreshFilteredList">
<el-option
v-for="item in sortKeywords"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<span>
上映区间
<el-input-number size="small" v-model="selectedYears.start" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
<el-input-number size="small" v-model="selectedYears.end" :min=0 :max="new Date().getFullYear()" controls-position="right" step-strictly @change="refreshFilteredList"></el-input-number>
</span>
</div>
<el-divider class="listpage-header-divider" content-position="right">
<el-button type="text" size="mini" @click="toggleViewMode">视图切换</el-button>
<el-button type="text" size="mini" @click='() => { showToolbar = !showToolbar; if (!showToolbar) this.refreshFilteredList() }' title="收起工具栏会重置筛选排序">{{ showToolbar ? '隐藏工具栏' : '显示工具栏' }}</el-button>
<el-button type="text" size="mini" @click="backTop">回到顶部</el-button>
</el-divider>
<div class="listpage-body" id="film-body" infinite-wrapper>
<div class="show-picture" v-if="setting.view === 'picture' && !show.find">
<Waterfall ref="filmWaterfall" :list="list" :gutter="20" :width="240"
<div class="show-picture" v-if="setting.view === 'picture' && !showFind">
<Waterfall ref="filmWaterfall" :list="filteredList" :gutter="20" :width="240"
:breakpoints="{
1200: { //当屏幕宽度小于等于1200
rowPerView: 4,
@@ -64,7 +100,7 @@
animationEffect="fadeIn"
backgroundColor="rgba(0, 0, 0, 0)">
<template slot="item" slot-scope="props">
<div class="card" v-show="!setting.excludeR18Films || !containsR18Keywords(props.data.type)">
<div class="card">
<div class="img">
<img style="width: 100%" :src="props.data.pic" alt="" @load="$refs.filmWaterfall.refresh()" @click="detailEvent(site, props.data)">
<div class="operate">
@@ -87,10 +123,11 @@
</Waterfall>
<infinite-loading force-use-infinite-wrapper :identifier="infiniteId" @infinite="infiniteHandler"></infinite-loading>
</div>
<div class="show-table" v-if="setting.view === 'table' && !show.find">
<div class="show-table" v-if="setting.view === 'table' && !showFind">
<el-table
size="mini"
:data="list.filter(res => !setting.excludeR18Films || !containsR18Keywords(res.type))"
:data="filteredList"
ref="filmTable"
height="100%"
:empty-text="statusText"
@row-click="(row) => detailEvent(site, row)"
@@ -99,7 +136,7 @@
prop="name"
label="片名">
</el-table-column>
<el-table-column
<el-table-column v-if="type.name === '最新'"
prop="type"
label="类型"
width="100">
@@ -120,16 +157,16 @@
label="语言"
width="100">
</el-table-column>
<el-table-column
prop="note"
label="备注"
width="120">
</el-table-column>
<el-table-column
<el-table-column v-if="showTableLastColumn"
prop="last"
label="最近更新"
:formatter="dateFormat"
align="left">
align="left"
width="120">
</el-table-column>
<el-table-column
prop="note"
label="备注">
</el-table-column>
<el-table-column
label="操作"
@@ -152,10 +189,10 @@
</infinite-loading>
</el-table>
</div>
<div class="show-table" v-show="searchViewMode=== 'table' && show.find">
<div class="show-table" v-if="setting.searchViewMode === 'table' && showFind">
<el-table size="mini"
ref="searchResultTable"
:data="searchContents.filter(res => !setting.excludeR18Films || (res.type !== undefined && !containsR18Keywords(res.type)))"
:data="filteredSearchContents"
height="100%"
:empty-text="statusText"
@filter-change="filterChange"
@@ -181,36 +218,38 @@
</el-table-column>
<el-table-column
prop="type"
:filters="getFilters('type')"
:filter-method="(value, row, column) => { this.currentColumn = column; return value === row.type }"
label="类型"
width="90">
width="100">
</el-table-column>
<el-table-column
sortable
prop="year"
label="上映"
width="90">
width="100">
</el-table-column>
<el-table-column
prop="area"
:filters="getFilters('area')"
:filter-method="(value, row, column) => { this.currentColumn = column; return value === row.area }"
label="地区"
width="90">
width="100">
</el-table-column>
<el-table-column
:filters="getFilters('lang')"
:filter-method="(value, row, column) => { this.currentColumn = column; return value === row.lang }"
prop="lang"
label="语言"
width="70">
width="100">
</el-table-column>
<el-table-column v-if="showTableLastColumn"
prop="last"
label="最近更新"
:formatter="dateFormat"
align="left"
width="120">
</el-table-column>
<el-table-column
sortable
prop="note"
label="备注"
width="120">
label="备注">
</el-table-column>
<el-table-column
label="操作"
@@ -226,8 +265,8 @@
</el-table-column>
</el-table>
</div>
<div class="show-picture" v-show="searchViewMode === 'picture' && show.find">
<Waterfall ref="filmSearchWaterfall" :list="searchContents.filter(res => !setting.excludeR18Films || (res.type !== undefined && !containsR18Keywords(res.type)))" :gutter="20" :width="240"
<div class="show-picture" v-if="setting.searchViewMode === 'picture' && showFind">
<Waterfall ref="filmSearchWaterfall" :list="filteredSearchContents" :gutter="20" :width="240"
:breakpoints="{
1200: { //当屏幕宽度小于等于1200
rowPerView: 4,
@@ -277,24 +316,25 @@ import zy from '../lib/site/tools'
import Waterfall from 'vue-waterfall-plugin'
import InfiniteLoading from 'vue-infinite-loading'
const { clipboard } = require('electron')
const FILM_DATA_CACHE = {} // key = site.key, value = classList; key = site.key + '@' + type.tid, value = {list, pageCount}
export default {
name: 'film',
data () {
return {
show: {
body: false,
site: false,
class: false,
classList: false,
find: false
},
showFind: false,
showToolbar: false,
showTableLastColumn: false,
sites: [],
site: {},
classList: [],
searchClassList: [],
type: {},
selectedClassName: '最新',
selectedSiteName: '',
selectedClassName: '',
selectedSearchClassNames: [],
totalpagecount: 0,
pagecount: 0,
recordcount: 0,
list: [],
statusText: ' ',
infiniteId: +new Date(),
@@ -302,12 +342,21 @@ export default {
searchList: [],
searchTxt: '',
searchContents: [],
filteredSearchContents: [],
currentColumn: '',
searchGroup: '',
searchGroups: [],
// 福利片关键词
r18KeyWords: ['伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番'],
searchViewMode: 'picture'
filteredList: [],
areas: [],
selectedAreas: [],
sortKeyword: '',
sortKeywords: ['按片名', '按上映年份', '按更新时间'],
selectedYears: { start: 0, end: new Date().getFullYear() },
searchRunning: false,
siteSearchCount: 0,
infiniteHandlerCount: 0
}
},
components: {
@@ -355,8 +404,21 @@ export default {
this.SET_SETTING(val)
}
},
DetailCache: {
get () {
return this.$store.getters.getDetailCache
},
set (val) {
this.SET_DetailCache(val)
}
},
filterSettings () {
return this.$store.getters.getSetting.excludeR18Films // 需要监听的数据
},
searchSites () {
if (this.searchGroup === '站内') return [this.site]
if (this.searchGroup === '全站') return this.sites
return this.sites.filter(site => site.group === this.searchGroup)
}
},
filters: {
@@ -367,7 +429,11 @@ export default {
},
watch: {
view () {
this.changeView()
if (this.view === 'Film') {
this.getAllSites()
if (this.$refs.filmWaterfall) this.$refs.filmWaterfall.resize() // 瀑布插件resize和refresh功能相同只是延时不同
if (this.$refs.filmSearchWaterfall) this.$refs.filmSearchWaterfall.resize()
}
},
searchTxt () {
if (this.searchTxt === '清除历史记录...') {
@@ -378,13 +444,99 @@ export default {
},
filterSettings () {
this.siteClick(this.site.name)
},
list: {
handler (list) {
this.areas = [...new Set(list.map(ele => ele.area))].filter(x => x)
this.refreshFilteredList()
},
deep: true
},
siteSearchCount () {
if (this.siteSearchCount === this.searchSites.length) this.searchRunning = false
},
searchContents: {
handler (list) {
this.areas = [...new Set(list.map(ele => ele.area))].filter(x => x)
this.searchClassList = [...new Set(list.map(ele => ele.type))].filter(x => x)
this.refreshFilteredList()
},
deep: true
},
selectedAreas: {
handler () {
this.infiniteHandlerCount = 0
},
deep: true
},
selectedYears: {
handler () {
this.infiniteHandlerCount = 0
},
deep: true
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
updateSearchViewMode () {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING', 'SET_DetailCache']),
backTop () {
const viewMode = this.showFind ? this.setting.searchViewMode : this.setting.view
if (viewMode === 'picture') {
document.getElementById('film-body').scrollTop = 0
} else {
const table = this.showFind ? this.$refs.searchResultTable : this.$refs.filmTable
table.bodyWrapper.scrollTop = 0
}
},
refreshFilteredList () {
if (!this.showToolbar) {
this.sortKeyword = ''
this.selectedAreas = []
this.selectedSearchClassNames = []
this.selectedYears.start = 0
this.selectedYears.end = new Date().getFullYear()
}
let filteredData = this.showFind ? this.searchContents : this.list
if (this.showFind) filteredData = filteredData.filter(x => (this.selectedSearchClassNames.length === 0) || this.selectedSearchClassNames.includes(x.type))
filteredData = filteredData.filter(x => (this.selectedAreas.length === 0) || this.selectedAreas.includes(x.area))
filteredData = filteredData.filter(res => !this.setting.excludeR18Films || !this.containsR18Keywords(res.type))
filteredData = filteredData.filter(res => res.year >= this.selectedYears.start)
filteredData = filteredData.filter(res => res.year <= this.selectedYears.end)
if (!this.showFind) this.selectedClassName = this.type.name + ' ' + filteredData.length + '/' + this.recordcount
switch (this.sortKeyword) {
case '按上映年份':
filteredData.sort(function (a, b) {
return a.year - b.year
})
break
case '按片名':
filteredData.sort(function (a, b) {
return a.name.localeCompare(b.name, 'zh')
})
break
case '按更新时间':
filteredData.sort(function (a, b) {
return new Date(b.last) - new Date(a.last)
})
break
default:
break
}
if (this.showFind) {
this.filteredSearchContents = filteredData
} else {
this.filteredList = filteredData
}
},
toggleViewMode () {
if (this.showFind) {
this.setting.searchViewMode = this.setting.searchViewMode === 'picture' ? 'table' : 'picture'
setTimeout(() => { if (this.$refs.filmSearchWaterfall) this.$refs.filmSearchWaterfall.refresh() }, 700)
} else {
this.setting.view = this.setting.view === 'picture' ? 'table' : 'picture'
}
setting.find().then(res => {
res.searchViewMode = this.searchViewMode
res.searchViewMode = this.setting.searchViewMode
res.view = this.setting.view
setting.update(res)
})
},
@@ -399,9 +551,8 @@ export default {
return date.split(/\s/)[0]
},
getFilters (column) {
const searchContents = this.searchContents.filter(res => !this.setting.excludeR18Films || (res.type !== undefined && !this.containsR18Keywords(res.type)))
if (column === 'siteName') return [...new Set(searchContents.map(row => row.site.name))].map(e => { return { text: e, value: e } }) // 有方法合并这两行吗?
return [...new Set(searchContents.map(row => row[column]))].map(e => { return { text: e, value: e } })
if (column === 'siteName') return [...new Set(this.filteredSearchContents.map(row => row.site.name))].map(e => { return { text: e, value: e } }) // 有方法合并这两行吗?
return [...new Set(this.filteredSearchContents.map(row => row[column]))].map(e => { return { text: e, value: e } })
},
filterChange (filters) {
// 一次只能一列
@@ -409,7 +560,7 @@ export default {
const otherColumns = this.$refs.searchResultTable.columns.filter(col => col.id !== this.currentColumn.id)
otherColumns.forEach(col => { col.filterable = false })
} else {
const filterLabels = ['源站', '类型', '地区', '语言']
const filterLabels = ['源站', '语言']
const columns = this.$refs.searchResultTable.columns.filter(col => filterLabels.includes(col.label))
columns.forEach(col => { col.filterable = true })
}
@@ -417,28 +568,51 @@ export default {
siteClick (siteName) {
this.list = []
this.site = this.sites.find(x => x.name === siteName)
if (this.searchTxt.length > 0 && this.searchGroup === '站内') {
if (this.searchGroup === '站内' && this.searchTxt) {
this.searchEvent()
return
} else {
this.searchTxt = ''
this.show.find = false
this.classList = []
this.type = {}
}
this.showFind = false
this.classList = []
if (FILM_DATA_CACHE[this.site.key]) {
this.classList = FILM_DATA_CACHE[this.site.key].classList
this.classClick(this.type.name)
} else {
this.getClass().then(res => {
this.infiniteId += 1
this.classClick(this.classList[0].name)
this.classList = res
// cache classList data
FILM_DATA_CACHE[this.site.key] = {
classList: this.classList
}
this.classClick(this.type.name)
})
}
},
classClick (className) {
this.show.classList = false
this.list = []
this.type = this.classList.find(x => x.name === className)
this.getPage().then(res => {
if (res) {
this.infiniteHandlerCount = 0
if (!this.type) {
this.type = this.classList[0]
}
if (this.type.name.endsWith('剧')) this.selectedAreas = []
const cacheKey = this.site.key + '@' + this.type.tid
if (FILM_DATA_CACHE[cacheKey]) {
this.totalpagecount = FILM_DATA_CACHE[cacheKey].totalpagecount
this.pagecount = FILM_DATA_CACHE[cacheKey].pagecount
this.recordcount = FILM_DATA_CACHE[cacheKey].recordcount
this.list = FILM_DATA_CACHE[cacheKey].list
this.areas = FILM_DATA_CACHE[cacheKey].areas
} else {
zy.page(this.site.key, this.type.tid).then(res => {
this.totalpagecount = res.pagecount
this.pagecount = res.pagecount
this.recordcount = res.recordcount
this.infiniteId += 1
}
})
})
}
},
getClass () {
return new Promise((resolve, reject) => {
@@ -459,11 +633,7 @@ export default {
}
}
})
this.classList = allClass
this.show.class = true
this.pagecount = res.pagecount
this.type = this.classList[0]
resolve(true)
resolve(allClass)
}).catch(err => {
reject(err)
})
@@ -476,49 +646,61 @@ export default {
}
return this.r18KeyWords.some(v => name.includes(v))
},
getPage () {
return new Promise((resolve, reject) => {
const key = this.site.key
const type = this.type.tid
zy.page(key, type).then(res => {
this.pagecount = res.pagecount
this.show.body = true
resolve(true)
}).catch(err => {
reject(err)
})
})
toFlipPagecount () {
// 似乎需要解析的网站的视频排序和其他m3u8采集站的顺序正好相反
if (this.site.jiexiUrl) {
return true
}
return false
},
infiniteHandler ($state) {
const key = this.site.key
const type = this.type.tid
const page = this.pagecount
const typeTid = this.type.tid
var page = this.pagecount
if (this.toFlipPagecount()) {
page = this.totalpagecount - this.pagecount + 1
}
this.statusText = ' '
if (key && page < 1) { // OK资源前几类硬是去不掉
if (key === undefined || page < 1 || typeTid === undefined) {
$state.complete()
this.statusText = '暂无数据'
return false
}
zy.list(key, page, type).then(res => {
if (res) {
this.pagecount -= 1
const type = Object.prototype.toString.call(res)
if (type === '[object Undefined]') {
$state.complete()
if (this.showToolbar && this.filteredList.length && this.filteredList.length < 10) {
this.infiniteHandlerCount++
}
const interval = this.setting.view === 'picture' ? 1200 : 300
setTimeout(() => {
zy.list(key, page, typeTid).then(res => {
if (res) {
this.pagecount -= 1
const type = Object.prototype.toString.call(res)
if (type === '[object Undefined]') {
$state.complete()
}
if (type === '[object Array]') {
if (!this.toFlipPagecount()) {
// zy.list 返回的是按时间从旧到新排列, 我门需要翻转为从新到旧
this.list.push(...res.reverse())
} else {
// 如果是需要解析的视频网站zy.list已经是按从新到旧排列
this.list.push(...res)
}
}
if (type === '[object Object]') {
this.list.push(res)
}
$state.loaded()
// 更新缓存数据
const cacheKey = this.site.key + '@' + typeTid
FILM_DATA_CACHE[cacheKey] = {
pagecount: this.pagecount,
recordcount: this.recordcount,
list: this.list
}
}
if (type === '[object Array]') {
// zy.list 返回的是按时间从旧到新排列, 我门需要翻转为从新到旧
this.list.push(...res.reverse())
}
if (type === '[object Object]') {
this.list.push(res)
}
$state.loaded()
} else {
$state.complete()
this.statusText = '暂无数据'
}
})
})
}, (this.infiniteHandlerCount <= 1 ? 0 : this.infiniteHandlerCount - 1) * interval)
},
detailEvent (site, e) {
this.detail = {
@@ -535,9 +717,6 @@ export default {
} else {
this.video = { key: site.key, info: { id: e.id, name: e.name, index: 0, site: site } }
}
zy.detail(site.key, e.id).then(detailRes => {
this.video.detail = detailRes
})
this.view = 'Play'
},
async starEvent (site, e) {
@@ -545,17 +724,19 @@ export default {
if (db) {
this.$message.info('已存在')
} else {
zy.detail(site.key, e.id).then(detailRes => {
const docs = {
key: site.key,
ids: e.id,
site: site,
name: e.name,
detail: detailRes
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
})
const cacheKey = site.key + '@' + e.id
if (!this.DetailCache[cacheKey]) {
this.DetailCache[cacheKey] = await zy.detail(site.key, e.id)
}
const docs = {
key: site.key,
ids: e.id,
site: site,
name: e.name,
detail: this.DetailCache[cacheKey]
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
})
}
},
@@ -566,58 +747,17 @@ export default {
info: e
}
},
downloadEvent (site, row) {
zy.download(site.key, row.id).then(res => {
if (res && res.length > 0) {
const text = res.dl.dd._t
if (text) {
const list = text.split('#')
let downloadUrl = res.name + '\n'
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
} else {
this.$message.warning('没有查询到下载链接.')
}
} else {
let m3u8List = []
const dd = row.dl.dd
const type = Object.prototype.toString.call(dd)
if (type === '[object Array]') {
for (const i of dd) {
if (i._flag.indexOf('m3u8') >= 0) {
m3u8List = i._t.split('#')
}
}
} else {
m3u8List = dd._t.split('#')
}
let downloadUrl = row.name + '\n'
for (const i of m3u8List) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
}
async downloadEvent (site, row) {
const db = await history.find({ site: site.key, ids: row.id })
let videoFlag
if (db) videoFlag = db.videoFlag
zy.download(site.key, row.id, videoFlag).then(res => {
clipboard.writeText(res.downloadUrls)
this.$message.success(res.info)
}).catch((err) => {
this.$message.error(err.info)
})
},
changeView () {
if (this.view === 'Film') {
this.getAllSites()
if (this.setting.view === 'picture') {
if (this.$refs.filmWaterfall) {
this.$refs.filmWaterfall.refresh()
}
this.getPage().then(() => {
this.infiniteId += 1
})
}
}
},
querySearch (queryString, cb) {
var searchList = this.searchList.slice(0, -1)
var results = queryString ? searchList.filter(this.createFilter(queryString)) : this.searchList
@@ -651,6 +791,9 @@ export default {
this.searchList.push({ id: this.searchList.length + 1, keywords: '清除历史记录...' })
})
},
stopSearchEvent () {
this.searchRunning = false
},
searchEvent () {
const wd = this.searchTxt
if (this.setting.searchGroup !== this.searchGroup) {
@@ -659,71 +802,53 @@ export default {
}
if (!wd) return
this.searchID += 1
var searchSites = []
if (this.searchGroup === '站内') searchSites.push(this.site)
if (this.searchGroup === '全站') searchSites = this.sites
if (!searchSites.length) {
searchSites = this.sites.filter(site => site.group === this.searchGroup)
}
this.searchContents = []
this.pagecount = 0
this.show.find = true
this.show.class = false
this.showFind = true
this.statusText = ' '
if (wd) {
searchSites.forEach(site => {
const id = this.searchID
zy.search(site.key, wd).then(res => {
if (id !== this.searchID) return
const type = Object.prototype.toString.call(res)
if (type === '[object Array]') {
res.forEach(element => {
zy.detail(site.key, element.id).then(detailRes => {
detailRes.site = site
if (id !== this.searchID) return
this.searchContents.push(detailRes)
this.searchContents.sort(function (a, b) {
return a.site.id - b.site.id
})
this.statusText = '暂无数据'
})
})
}
if (type === '[object Object]') {
zy.detail(site.key, res.id).then(detailRes => {
this.searchRunning = true
this.siteSearchCount = 0
this.searchSites.forEach(site => {
const id = this.searchID
zy.search(site.key, wd).then(res => {
if (id !== this.searchID || !this.searchRunning) return
const type = Object.prototype.toString.call(res)
if (type === '[object Array]') {
let count = 0
res.forEach(element => {
zy.detail(site.key, element.id).then(detailRes => {
if (id !== this.searchID || !this.searchRunning) return
detailRes.site = site
if (id !== this.searchID) return
this.searchContents.push(detailRes)
this.searchContents.sort(function (a, b) {
return a.site.id - b.site.id
})
this.statusText = '暂无数据'
}).finally(() => { count++; if (count === res.length) { this.siteSearchCount++; this.statusText = '暂无数据' } })
})
} else if (type === '[object Object]') {
zy.detail(site.key, res.id).then(detailRes => {
if (id !== this.searchID || !this.searchRunning) return
detailRes.site = site
this.searchContents.push(detailRes)
this.searchContents.sort(function (a, b) {
return a.site.id - b.site.id
})
}
})
})
}
}).finally(() => { this.siteSearchCount++; this.statusText = '暂无数据' })
} else if (res === undefined) {
this.siteSearchCount++
this.statusText = '暂无数据'
if (this.searchGroup === '站内') this.$message.info('没有查询到数据!')
}
}).catch(() => { this.siteSearchCount++; if (this.searchGroup === '站内') this.$message.error('本次查询状态异常,未获取到数据!') })
})
},
searchAndRecord () {
this.addSearchRecord()
this.searchEvent()
},
searchChangeEvent () {
if (this.searchTxt.length >= 1) {
this.show.class = false
} else {
this.show.class = true
if (!this.searchTxt.length) {
this.searchContents = []
this.show.find = false
if (this.setting.view === 'picture' && this.$refs.filmWaterfall) {
this.$refs.filmWaterfall.refresh()
} else {
this.getClass().then(res => {
if (res) {
this.infiniteId += 1
}
})
}
this.showFind = false
}
},
getAllSites () {
@@ -746,28 +871,20 @@ export default {
this.searchGroup = this.setting.searchGroup
if (this.searchGroup === undefined) setting.find().then(res => { this.searchGroup = res.searchGroup })
})
},
getSearchViewMode () {
setting.find().then(res => {
this.searchViewMode = res.searchViewMode === undefined ? 'picture' : res.searchViewMode
})
}
},
created () {
this.getAllSites()
this.getSearchHistory()
this.getSearchViewMode()
},
mounted () {
window.addEventListener('resize', () => {
if (this.$refs.filmWaterfall && this.view === 'Film') {
this.$refs.filmWaterfall.resize()
this.$refs.filmWaterfall.refresh()
setTimeout(() => {
this.$refs.filmWaterfall.refresh()
}, 500)
}
}, false)
addEventListener('resize', () => {
setTimeout(() => {
this.showTableLastColumn = window.outerWidth >= 1200
if (this.$refs.filmWaterfall) this.$refs.filmWaterfall.resize()
if (this.$refs.filmSearchWaterfall) this.$refs.filmSearchWaterfall.resize()
}, 500)
})
}
}
</script>

View File

@@ -1,14 +1,23 @@
<template>
<div class="listpage" id="history">
<div class="listpage-header" id="history-header">
<el-switch v-model="viewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateViewMode"></el-switch>
<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>
<el-switch v-model="setting.historyViewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateViewMode"></el-switch>
<el-button @click.stop="exportHistory" icon="el-icon-upload2" title="导出全部,自动添加扩展名">导出</el-button>
<el-button @click.stop="importHistory" icon="el-icon-download" title="支持同时导入多个文件">导入</el-button>
<el-button @click.stop="removeSelectedItems" icon="el-icon-delete-solid">{{ multipleSelection.length === 0 ? "清空" : "删除所选" }}</el-button>
</div>
<div class="listpage-body" id="history-body">
<div class="show-table" id="history-table" v-show="viewMode === 'table'">
<el-table size="mini" fit height="100%" :data="history" row-key="id" @row-click="detailEvent">
<div class="show-table" id="history-table" v-if="setting.historyViewMode === 'table'">
<el-table size="mini" fit height="100%"
:data="history"
row-key="id"
ref="historyTable"
@select="selectionCellClick"
@selection-change="handleSelectionChange"
@row-click="detailEvent">
<el-table-column
type="selection">
</el-table-column>
<el-table-column
prop="name"
label="片名">
@@ -26,8 +35,8 @@
width="180"
label="观看至">
<template slot-scope="scope">
<span v-if="scope.row.detail && scope.row.detail.m3u8List && scope.row.detail.m3u8List.length > 1">
{{ scope.row.index + 1 }}({{scope.row.detail.m3u8List.length}})
<span v-if="scope.row.detail && scope.row.detail.fullList[0].list && scope.row.detail.fullList[0].list.length > 1">
{{ scope.row.index + 1 }}({{scope.row.detail.fullList[0].list.length}})
</span>
</template>
</el-table-column>
@@ -36,6 +45,7 @@
label="时间进度">
<template slot-scope="scope">
<span v-if="scope.row.time && scope.row.duration">{{fmtMSS(scope.row.time.toFixed(0))}}/{{fmtMSS(scope.row.duration.toFixed(0))}}</span>
<span v-if="scope.row.onlinePlay">在线解析</span>
</template>
</el-table-column>
<el-table-column
@@ -52,7 +62,7 @@
</el-table-column>
</el-table>
</div>
<div class="show-picture" id="star-picture" v-show="viewMode === 'picture'">
<div class="show-picture" id="star-picture" v-if="setting.historyViewMode === 'picture'">
<Waterfall ref="historyWaterfall" :list="history" :gutter="20" :width="240"
:breakpoints="{
1200: { //当屏幕宽度小于等于1200
@@ -85,8 +95,9 @@
<span v-if="props.data.time && props.data.duration">
{{fmtMSS(props.data.time.toFixed(0))}}/{{fmtMSS(props.data.duration.toFixed(0))}}
</span>
<span v-if="props.data.detail && props.data.detail.m3u8List !== undefined && props.data.detail.m3u8List.length > 1">
{{ props.data.index + 1 }}({{props.data.detail.m3u8List.length}})
<span v-if="props.data.onlinePlay">在线解析</span>
<span v-if="props.data.detail && props.data.detail.fullList[0].list !== undefined && props.data.detail.fullList[0].list.length > 1">
{{ props.data.index + 1 }}({{props.data.detail.fullList[0].list.length}})
</span>
</div>
</div>
@@ -112,7 +123,10 @@ export default {
return {
history: [],
sites: [],
viewMode: setting.historyViewMode
shiftDown: false,
selectionBegin: '',
selectionEnd: '',
multipleSelection: []
}
},
components: {
@@ -150,22 +164,57 @@ export default {
set (val) {
this.SET_SHARE(val)
}
},
setting: {
get () {
return this.$store.getters.getSetting
},
set (val) {
this.SET_SETTING(val)
}
}
},
watch: {
view () {
this.getAllhistory()
this.getAllsites()
if (this.$refs.historyWaterfall) {
this.$refs.historyWaterfall.refresh()
if (this.view === 'History') {
this.getAllhistory()
this.getAllsites()
if (this.setting.historyViewMode === 'table') this.showShiftPrompt()
}
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
fmtMSS (s) {
return (s - (s %= 60)) / 60 + (s > 9 ? ':' : ':0') + s
},
selectionCellClick (selection, row) { // 历史id与顺序刚好相反大的反而在前面
if (this.shiftDown && this.selectionBegin !== '' && selection.includes(row)) {
this.selectionEnd = row.id
const start = this.history.findIndex(e => e.id === Math.max(this.selectionBegin, this.selectionEnd))
const end = this.history.findIndex(e => e.id === Math.min(this.selectionBegin, this.selectionEnd))
const selections = this.history.slice(start, end + 1)
this.$nextTick(() => {
selections.forEach(e => this.$refs.historyTable.toggleRowSelection(e, true))
})
this.selectionBegin = this.selectionEnd = ''
return
}
if (selection.includes(row)) {
this.selectionBegin = row.id
} else {
this.selectionBegin = ''
}
},
handleSelectionChange (rows) {
this.multipleSelection = rows
},
removeSelectedItems () {
if (!this.multipleSelection.length) this.multipleSelection = this.history
this.multipleSelection.forEach(e => history.remove(e.id))
this.getAllhistory()
this.updateDatabase()
},
detailEvent (e) {
this.detail = {
show: true,
@@ -183,58 +232,21 @@ export default {
} else {
this.video = { key: e.site, info: { id: e.ids, name: e.name, index: 0 } }
}
zy.detail(e.site, e.ids).then(detailRes => {
this.video.detail = detailRes
})
this.view = 'Play'
},
shareEvent (e) {
this.share = {
show: true,
key: e.site,
info: e
info: e.detail
}
},
downloadEvent (e) {
zy.download(e.site, e.ids).then(res => {
if (res && res.dl && res.dl.dd) {
const text = res.dl.dd._t
if (text) {
const list = text.split('#')
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
} else {
this.$message.warning('没有查询到下载链接.')
}
} else {
var m3u8List = {}
zy.detail(e.site, e.ids).then(res => {
const dd = res.dl.dd
const type = Object.prototype.toString.call(dd)
if (type === '[object Array]') {
for (const i of dd) {
if (i._flag.indexOf('m3u8') >= 0) {
m3u8List = i._t.split('#')
}
}
} else {
m3u8List = dd._t.split('#')
}
const list = [...m3u8List]
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
})
}
zy.download(e.site, e.ids, e.videoFlag).then(res => {
clipboard.writeText(res.downloadUrls)
this.$message.success(res.info)
}).catch((err) => {
this.$message.error(err.info)
})
},
exportHistory () {
@@ -248,6 +260,7 @@ export default {
}
remote.dialog.showSaveDialog(options).then(result => {
if (!result.canceled) {
if (!result.filePath.endsWith('.json')) result.filePath += '.json'
fs.writeFileSync(result.filePath, str)
this.$message.success('已保存成功')
}
@@ -275,11 +288,6 @@ export default {
}
})
},
clearAllHistory () {
history.clear().then(res => {
this.history = []
})
},
getAllhistory () {
history.all().then(res => {
this.history = res.reverse()
@@ -303,10 +311,10 @@ export default {
this.$message.warning('删除历史记录失败, 错误信息: ' + err)
})
},
updateDatabase (data) {
updateDatabase () {
history.clear().then(res => {
var id = length
data.forEach(ele => {
this.history.forEach(ele => {
ele.id = id
id -= 1
history.add(ele)
@@ -314,43 +322,51 @@ export default {
})
},
rowDrop () {
if (!document.getElementById('history-table')) return
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)
_this.updateDatabase()
}
})
},
getViewMode () {
setting.find().then(res => {
this.viewMode = res.historyViewMode
})
},
updateViewMode () {
if (this.setting.historyViewMode === 'table') {
setTimeout(() => { this.rowDrop() }, 100)
this.showShiftPrompt()
} else {
setTimeout(() => { if (this.$refs.historyWaterfall) this.$refs.historyWaterfall.refresh() }, 700)
}
setting.find().then(res => {
res.historyViewMode = this.viewMode
res.historyViewMode = this.setting.historyViewMode
setting.update(res)
})
},
showShiftPrompt () {
if (this.setting.shiftTooltipLimitTimes === undefined) this.setting.shiftTooltipLimitTimes = 5
if (this.setting.shiftTooltipLimitTimes) {
this.$message.info('多选时支持shift快捷键')
this.setting.shiftTooltipLimitTimes--
setting.find().then(res => {
res.shiftTooltipLimitTimes = this.setting.shiftTooltipLimitTimes
setting.update(res)
})
}
}
},
mounted () {
this.rowDrop()
window.addEventListener('resize', () => {
if (this.$refs.historyWaterfall && this.view === 'History') {
this.$refs.historyWaterfall.resize()
this.$refs.historyWaterfall.refresh()
setTimeout(() => {
this.$refs.historyWaterfall.refresh()
}, 500)
}
}, false)
if (this.setting.historyViewMode === 'table') setTimeout(() => { this.rowDrop() }, 100)
addEventListener('keydown', code => { if (code.keyCode === 16) this.shiftDown = true })
addEventListener('keyup', code => { if (code.keyCode === 16) this.shiftDown = false })
addEventListener('resize', () => {
setTimeout(() => { if (this.$refs.historyWaterfall) this.$refs.historyWaterfall.resize() }, 500)
})
},
created () {
this.getAllhistory()
this.getViewMode()
}
}
</script>

View File

@@ -2,17 +2,17 @@
<div class="listpage" id="iptv">
<div class="listpage-header" id="iptv-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="checkAllChannels" icon="el-icon-refresh" :loading="checkAllChannelsLoading">检测{{ this.checkAllChannelsLoading ? this.checkProgress + '/' + this.iptvList.length : '' }}</el-button>
<el-button @click.stop="exportChannels" icon="el-icon-upload2" title="导出m3u时必须手动添加扩展名要保存频道配置信息请选择json格式">导出</el-button>
<el-button @click.stop="importChannels" icon="el-icon-download" title='支持同时导入多个文件,导入m3u时网址可带参数、含有"#"号时自动分割'>导入</el-button>
<el-button @click="checkAllChannels" icon="el-icon-refresh" :loading="checkAllChannelsLoading" title="可在后台运行">检测{{ this.checkAllChannelsLoading ? this.checkProgress + '/' + this.iptvList.length : '' }}</el-button>
<el-button @click.stop="resetChannelsEvent" icon="el-icon-refresh-left">重置</el-button>
</div>
<div class="listpage-header" id="iptv-header" v-show="enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理及频道调整"></el-switch>
<el-input placeholder="新组名/新频道名" v-model="inputContent"></el-input>
<el-switch v-model="batchIsActive" active-text="启用"></el-switch>
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit">保存分组与开关状态</el-button>
<el-button type="primary" icon="el-icon-film" @click.stop="mergeChannel">{{ this.multipleSelection.length === 1 ? '频道重命名' : '频道合并' }}</el-button>
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit" title="输入框组名为空时仅保存开关状态">保存分组与开关状态</el-button>
<el-button type="primary" icon="el-icon-film" @click.stop="mergeChannel" title="勾选单个时可重命名频道">{{ this.multipleSelection.length === 1 ? '频道重命名' : '频道合并' }}</el-button>
<el-button @click.stop="removeSelectedChannels" icon="el-icon-delete-solid">删除</el-button>
</div>
<div class="listpage-body" id="iptv-table">
@@ -25,7 +25,6 @@
:load="(row, treeNode, resolve) => resolve(row.channels)"
:tree-props="{hasChildren: 'hasChildren'}"
@expand-change="expandChange"
@row-click="playEvent"
@select="selectionCellClick"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange">
@@ -95,6 +94,7 @@
</template>
<template slot-scope="scope">
<el-button @click.stop="moveToTopEvent(scope.row)" type="text" v-if="scope.row.channels">置顶</el-button>
<el-button @click.stop="playEvent(scope.row)" type="text">播放</el-button>
<!-- 检测时先强制批量检测一遍,如果不强制直接单个检测时第一次不会显示“检测中”-->
<el-button size="mini" v-if="iptvList.every(channel => channel.status)" v-show="!checkAllChannelsLoading" @click.stop="checkChannel(scope.row)" type="text">检测</el-button>
<el-button @click.stop="removeEvent(scope.row)" type="text">删除</el-button>
@@ -107,7 +107,7 @@
</template>
<script>
import { mapMutations } from 'vuex'
import { iptv, iptvSearch, channelList } from '../lib/dexie'
import { iptv, channelList, setting } from '../lib/dexie'
import { iptv as defaultChannels } from '../lib/dexie/initData'
import zy from '../lib/site/tools'
import { remote } from 'electron'
@@ -120,7 +120,6 @@ export default {
iptvList: [],
channelList: [],
searchTxt: '',
searchRecordList: [],
enableBatchEdit: false,
inputContent: '',
batchIsActive: true,
@@ -132,10 +131,7 @@ export default {
checkAllChannelsLoading: false,
checkProgress: 0,
stopFlag: false,
sortableTable: '',
show: {
search: false
}
sortableTable: ''
}
},
computed: {
@@ -147,8 +143,13 @@ export default {
this.SET_VIEW(val)
}
},
setting () {
return this.$store.getters.getSetting
setting: {
get () {
return this.$store.getters.getSetting
},
set (val) {
this.SET_SETTING(val)
}
},
video: {
get () {
@@ -179,11 +180,6 @@ export default {
}
},
watch: {
view () {
if (this.view === 'IPTV') {
this.getChannelList()
}
},
enableBatchEdit () {
if (this.checkAllChannelsLoading) {
this.$message.info('正在检测, 请勿操作.')
@@ -191,6 +187,15 @@ export default {
return
}
if (this.enableBatchEdit) {
if (this.setting.shiftTooltipLimitTimes === undefined) this.setting.shiftTooltipLimitTimes = 5
if (this.setting.shiftTooltipLimitTimes) {
this.$message.info('多选时支持shift快捷键')
this.setting.shiftTooltipLimitTimes--
setting.find().then(res => {
res.shiftTooltipLimitTimes = this.setting.shiftTooltipLimitTimes
setting.update(res)
})
}
this.$nextTick(() => {
this.expandedRows.forEach(e => this.$refs.iptvTable.toggleRowExpansion(e, false))
})
@@ -201,7 +206,7 @@ export default {
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
sortByLocaleCompare (a, b) {
return a.localeCompare(b, 'zh')
},
@@ -293,6 +298,7 @@ export default {
ele.channels.splice(ele.channels.findIndex(e => e.id === row.id), 1)
channelList.remove(row.channelID)
if (ele.channels.length) {
if (ele.channels.length === 1) ele.hasChildren = false
channelList.add(ele)
this.$set(this.$refs.iptvTable.store.states.lazyTreeNodeMap, ele.id, ele.channels)
}
@@ -321,6 +327,7 @@ export default {
fs.writeFileSync(result.filePath, writer.toString())
this.$message.success('已保存成功')
} else {
if (!result.filePath.endsWith('.json')) result.filePath += '.json'
const arr = [...this.channelList] // 要保存channelList必须选json
const str = JSON.stringify(arr, null, 2)
fs.writeFileSync(result.filePath, str)
@@ -348,21 +355,25 @@ export default {
result.filePaths.forEach(file => {
if (file.endsWith('m3u') || file.endsWith('m3u8')) {
const docs = []
const URL = require('url')
let id = this.channelList.length ? this.channelList.slice(-1)[0].id + 1 : 1
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,
isActive: true
const urls = ele.url.split('#').filter(e => e.startsWith('http')) // 网址带#时自动分割
urls.forEach(url => {
if (ele.name && url && new URL.URL(url).pathname.endsWith('.m3u8')) { // 网址可能带参数
var doc = {
id: id,
name: ele.name,
url: url,
isActive: true
}
id += 1
docs.push(doc)
}
id += 1
docs.push(doc)
}
})
})
// 获取url不重复的列表
const uniqueList = [...new Map(docs.map(item => [item.url, item])).values()]
@@ -393,14 +404,12 @@ export default {
})
},
determineGroup (name) {
if (name.toLowerCase().includes('cctv') && (name.includes('蓝光') || name.includes('高清'))) {
return '央视高清'
} else if (name.toLowerCase().includes('cctv')) {
if (name.toLowerCase().includes('cctv') || name.toLowerCase().includes('cgtn')) {
return '央视'
} else if (name.includes('香港') || name.includes('澳门') || name.includes('台湾') || name.includes('凤凰') || name.includes('翡翠')) {
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 {
@@ -468,8 +477,8 @@ export default {
iptv.clear() // iptv默认清空状态
})
},
getChannelList () {
channelList.all().then(res => {
async getChannelList () {
await channelList.all().then(res => {
this.channelList = res
this.getIptvList()
})
@@ -477,28 +486,6 @@ export default {
getIptvList () {
this.iptvList = this.channelList.reduce((result, item) => { item.channels.forEach(e => { e.channelID = item.id }); return result.concat(item.channels) }, [])
},
getSearchRecordList () {
iptvSearch.all().then(res => {
this.searchRecordList = res.reverse()
})
},
clearSearch () {
iptvSearch.clear().then(res => {
this.getSearchRecordList()
})
},
searchEvent (wd) {
this.searchTxt = wd
this.show.search = false
if (wd) {
iptvSearch.find({ keywords: wd }).then(res => {
if (!res) {
iptvSearch.add({ keywords: wd })
}
this.getSearchRecordList()
})
}
},
moveToTopEvent (row) {
if (this.checkAllChannelsLoading) {
this.$message.info('正在检测, 请勿操作.')
@@ -537,6 +524,11 @@ export default {
ele.channels.forEach((e, index) => { e.id = embedChannelID + index }) // 为避免混杂给内置iptv重起id
if (prefer) ele.prefer = prefer.id
})
if (ele.channels.length === 1) {
ele.hasChildren = false
} else {
ele.hasChildren = true
}
})
},
rowDrop () {
@@ -562,21 +554,24 @@ export default {
channelList.remove(row.channelID)
channelList.add(ele)
} else {
if (row.channels.length === 1) row.channels[0].isActive = row.isActive
channelList.remove(row.id)
channelList.add(row)
}
},
async checkAllChannels () {
if (this.checkAllChannelsLoading) return
this.checkAllChannelsLoading = true
this.stopFlag = false
this.checkProgress = 0
this.channelList.forEach(e => { e.status = ' '; e.hasCheckedNum = 0 })
this.channelList.filter(e => e.channels.length).forEach(e => { e.status = ' '; e.hasCheckedNum = 0 })
const uncheckedList = this.iptvList.filter(e => e.status === undefined || e.status === ' ') // 未检测过的优先
const other = this.iptvList.filter(e => !uncheckedList.includes(e))
await this.checkChannelsBySite(uncheckedList)
await this.checkChannelsBySite(other).then(res => {
this.checkAllChannelsLoading = false
this.updateChannelList()
this.getChannelList()
if (!this.stopFlag) this.$message.success('直播频道批量检测已完成!')
})
},
async checkChannelsBySite (channels) {
@@ -598,6 +593,10 @@ export default {
}
},
async checkSingleChannel (channel) {
if (this.setting.allowPassWhenIptvCheck && !channel.isActive) {
this.checkProgress += 1
return
}
channel.status = ' '
const ele = this.channelList.find(e => e.id === channel.channelID)
if (this.stopFlag) {
@@ -612,12 +611,17 @@ export default {
} else {
channel.status = '失效'
channel.isActive = false
if (this.setting.autocleanWhenIptvCheck) {
ele.channels.splice(ele.channels.findIndex(e => e.id === channel.id), 1)
ele.hasCheckedNum--
}
}
if (ele.hasCheckedNum === ele.channels.length) {
ele.status = ele.channels.some(channel => channel.status === '可用') ? '可用' : '失效'
if (ele.status === '失效') ele.isActive = false
channelList.remove(channel.channelID)
channelList.add(ele)
if (ele.channels.length === 1) ele.hasChildren = false
if (ele.channels.length) channelList.add(ele)
}
return channel.status
},
@@ -635,10 +639,9 @@ export default {
addEventListener('keydown', code => { if (code.keyCode === 16) this.shiftDown = true })
addEventListener('keyup', code => { if (code.keyCode === 16) this.shiftDown = false })
},
created () {
this.getChannelList()
async created () {
await this.getChannelList()
if (!this.channelList.length) this.resetChannelsEvent()
this.getSearchRecordList()
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -1,360 +1,322 @@
<template>
<div class="listpage" id="recommendataions">
<div class="listpage-header" id="recommendataions-header">
<el-switch v-model="viewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateViewMode"></el-switch>
<el-button type="text">视频数{{ recommendations.length }}</el-button>
<el-select v-model="selectedAreas" size="small" multiple collapse-tags placeholder="地区" popper-class="popper" :popper-append-to-body="false">
<el-option
v-for="item in areas"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-select v-model="selectedTypes" size="small" multiple collapse-tags placeholder="类型" popper-class="popper" :popper-append-to-body="false">
<el-option
v-for="item in types"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-select v-model="sortKeyword" size="small" placeholder="排序" popper-class="popper" :popper-append-to-body="false">
<el-option
v-for="item in sortKeywords"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-button :loading="loading" @click.stop="updateEvent" icon="el-icon-refresh">更新推荐</el-button>
</div>
<div class="listpage-body" id="recommendataions-body" >
<div class="show-table" id="star-table" v-show="viewMode === 'table'">
<el-table size="mini" fit height="100%" row-key="id"
ref="recommendataionsTable"
:data="filteredRecommendations"
@row-click="detailEvent">
<el-table-column
prop="name"
label="片名">
</el-table-column>
<el-table-column
prop="detail.area"
label="地区"
width="100">
</el-table-column>
<el-table-column
prop="detail.type"
label="类型"
width="100">
</el-table-column>
<el-table-column
prop="detail.year"
label="上映"
width="100"
align="center">
</el-table-column>
<el-table-column v-if="filteredRecommendations.some(e => e.rate)"
prop="rate"
align="center"
width="100"
label="豆瓣评分">
</el-table-column>
<el-table-column v-if="filteredRecommendations.some(e => e.detail.note)"
prop="detail.note"
label="备注">
</el-table-column>
<el-table-column
label="操作"
header-align="center"
align="right"
width="200">
<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 class="show-picture" id="star-picture" v-show="viewMode === 'picture'">
<Waterfall ref="recommendataionsWaterfall" :list="filteredRecommendations" :gutter="20" :width="240"
:breakpoints="{
1200: { //当屏幕宽度小于等于1200
rowPerView: 4,
},
800: { //当屏幕宽度小于等于800
rowPerView: 3,
},
500: { //当屏幕宽度小于等于500
rowPerView: 2,
}
}"
animationDuration="0.5s"
backgroundColor="rgba(0, 0, 0, 0)">
<template slot="item" slot-scope="props">
<div class="card">
<div class="img">
<div class="rate" v-if="props.data.rate && props.data.rate !== '暂无评分'">
<span>{{props.data.rate}}</span>
</div>
<img style="width: 100%" :src="props.data.detail.pic" alt="" @load="$refs.recommendataionsWaterfall.refresh()" @click="detailEvent(props.data)">
<div class="operate">
<div class="operate-wrap">
<span class="o-play" @click="playEvent(props.data)">播放</span>
<span class="o-share" @click="shareEvent(props.data)">分享</span>
<span class="o-star" @click="downloadEvent(props.data)">下载</span>
<span class="o-star" @click="deleteEvent(props.data)">删除</span>
</div>
</div>
</div>
<div class="name" @click="detailEvent(props.data)">{{props.data.name}}</div>
<div class="info">
<span>{{props.data.detail.area}}</span>
<span>{{props.data.detail.year}}</span>
<span>{{props.data.detail.note}}</span>
<span>{{props.data.detail.type}}</span>
</div>
</div>
</template>
</Waterfall>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { history, recommendation, setting } from '../lib/dexie'
import zy from '../lib/site/tools'
import Waterfall from 'vue-waterfall-plugin'
const { clipboard } = require('electron')
export default {
name: 'recommendations',
data () {
return {
recommendations: [],
sites: [],
viewMode: 'picture',
loading: false,
types: [],
selectedTypes: [],
areas: [],
selectedAreas: [],
sortKeyword: '',
sortKeywords: ['上映', '评分', '默认']
}
},
components: {
Waterfall
},
computed: {
view: {
get () {
return this.$store.getters.getView
},
set (val) {
this.SET_VIEW(val)
}
},
video: {
get () {
return this.$store.getters.getVideo
},
set (val) {
this.SET_VIDEO(val)
}
},
detail: {
get () {
return this.$store.getters.getDetail
},
set (val) {
this.SET_DETAIL(val)
}
},
share: {
get () {
return this.$store.getters.getShare
},
set (val) {
this.SET_SHARE(val)
}
},
filteredRecommendations () {
var filteredData = this.recommendations.filter(x => (this.selectedAreas.length === 0) || this.selectedAreas.includes(x.detail.area))
filteredData = filteredData.filter(x => (this.selectedTypes.length === 0) || this.selectedTypes.includes(x.detail.type))
return filteredData
}
},
watch: {
view () {
if (this.view === 'Recommendation') {
this.getRecommendations()
if (this.$refs.recommendataionsWaterfall) {
this.$refs.recommendataionsWaterfall.refresh()
}
}
},
sortKeyword () {
switch (this.sortKeyword) {
case '上映':
this.recommendations = this.recommendations.sort(function (a, b) {
return b.detail.year - a.detail.year
})
break
case '评分':
this.recommendations.sort(function (a, b) {
return b.rate - a.rate
})
break
case '默认':
this.recommendations.sort(function (a, b) {
return b.id - a.id
})
break
default:
break
}
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
detailEvent (e) {
this.detail = {
show: true,
key: e.key,
info: {
id: e.ids,
name: e.name
}
}
},
updateEvent () {
const url = 'https://raw.githubusercontent.com/cuiocean/ZY-Player-Resources/main/Recommendations/Recommendations.json'
this.loading = true
const axios = require('axios')
axios.get(url).then(res => {
if (res.status === 200) {
if (res.data.length > 0) {
this.recommendations = res.data
recommendation.clear().then(recommendation.bulkAdd(this.recommendations))
this.getFilterData()
this.$message.success('更新推荐成功. 仅根据作者cuiocean个人喜好推荐,不喜请无视.')
}
}
this.loading = false
}).catch(error => {
this.loading = false
this.$message.error('更新推荐失败. ' + error)
this.$message.warning('最新的推荐数据保存在Github上,请考虑使用代理或者等待下一版本内置数据更新.')
})
},
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 }, detail: db.detail }
} else {
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 }, detail: e.detail }
}
this.view = 'Play'
},
deleteEvent (e) {
recommendation.remove(e.id).then(res => {
if (res) {
this.$message.warning('删除失败')
}
this.getRecommendations()
})
},
shareEvent (e) {
this.share = {
show: true,
key: e.key,
info: e
}
},
downloadEvent (e) {
zy.download(e.key, e.ids).then(res => {
if (res && res.dl && res.dl.dd) {
const text = res.dl.dd._t
if (text) {
const list = text.split('#')
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
} else {
this.$message.warning('没有查询到下载链接.')
}
} else {
var m3u8List = {}
zy.detail(e.key, e.ids).then(res => {
const dd = res.dl.dd
const type = Object.prototype.toString.call(dd)
if (type === '[object Array]') {
for (const i of dd) {
if (i._flag.indexOf('m3u8') >= 0) {
m3u8List = i._t.split('#')
}
}
} else {
m3u8List = dd._t.split('#')
}
const list = [...m3u8List]
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
})
}
})
},
getRecommendations () {
recommendation.all().then(res => {
this.recommendations = res.sort(function (a, b) {
return b.id - a.id
})
this.getFilterData()
})
},
getFilterData () {
this.types = [...new Set(this.recommendations.map(ele => ele.detail.type))].filter(x => x)
this.areas = [...new Set(this.recommendations.map(ele => ele.detail.area))].filter(x => x)
},
getViewMode () {
setting.find().then(res => {
this.viewMode = res.recommendationViewMode
})
},
updateViewMode () {
setting.find().then(res => {
res.recommendationViewMode = this.viewMode
setting.update(res)
})
}
},
created () {
this.getRecommendations()
this.getViewMode()
},
mounted () {
window.addEventListener('resize', () => {
if (this.$refs.recommendataionsWaterfall && this.view === 'Recommendation') {
this.$refs.recommendataionsWaterfall.resize()
this.$refs.recommendataionsWaterfall.refresh()
setTimeout(() => {
this.$refs.recommendataionsWaterfall.refresh()
}, 500)
}
}, false)
}
}
</script>
<template>
<div class="listpage" id="recommendations">
<div class="listpage-header" id="recommendations-header">
<el-switch v-model="setting.recommendationViewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateViewMode"></el-switch>
<el-button type="text">视频数{{ recommendations.length }}</el-button>
<el-select v-model="selectedAreas" size="small" multiple placeholder="地区" popper-class="popper" :popper-append-to-body="false">
<el-option
v-for="item in areas"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-select v-model="selectedTypes" size="small" multiple placeholder="类型" popper-class="popper" :popper-append-to-body="false">
<el-option
v-for="item in types"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-select v-model="sortKeyword" size="small" placeholder="排序" popper-class="popper" :popper-append-to-body="false">
<el-option
v-for="item in sortKeywords"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-button :loading="loading" @click.stop="updateEvent" icon="el-icon-refresh">更新推荐</el-button>
</div>
<div class="listpage-body" id="recommendations-body" >
<div class="show-table" id="star-table" v-if="setting.recommendationViewMode === 'table'">
<el-table size="mini" fit height="100%" row-key="id"
ref="recommendationsTable"
:data="filteredRecommendations"
@row-click="detailEvent">
<el-table-column
prop="name"
label="片名">
</el-table-column>
<el-table-column
prop="detail.area"
label="地区"
width="100">
</el-table-column>
<el-table-column
prop="detail.type"
label="类型"
width="100">
</el-table-column>
<el-table-column
prop="detail.year"
label="上映"
width="100"
align="center">
</el-table-column>
<el-table-column v-if="filteredRecommendations.some(e => e.rate)"
prop="rate"
align="center"
width="100"
label="豆瓣评分">
</el-table-column>
<el-table-column v-if="filteredRecommendations.some(e => e.detail.note)"
prop="detail.note"
label="备注">
</el-table-column>
<el-table-column
label="操作"
header-align="center"
align="right"
width="200">
<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 class="show-picture" id="star-picture" v-if="setting.recommendationViewMode === 'picture'">
<Waterfall ref="recommendationsWaterfall" :list="filteredRecommendations" :gutter="20" :width="240"
:breakpoints="{
1200: { //当屏幕宽度小于等于1200
rowPerView: 4,
},
800: { //当屏幕宽度小于等于800
rowPerView: 3,
},
500: { //当屏幕宽度小于等于500
rowPerView: 2,
}
}"
animationDuration="0.5s"
backgroundColor="rgba(0, 0, 0, 0)">
<template slot="item" slot-scope="props">
<div class="card">
<div class="img">
<div class="rate" v-if="props.data.rate && props.data.rate !== '暂无评分'">
<span>{{props.data.rate}}</span>
</div>
<img style="width: 100%" :src="props.data.detail.pic" alt="" @load="$refs.recommendationsWaterfall.refresh()" @click="detailEvent(props.data)">
<div class="operate">
<div class="operate-wrap">
<span class="o-play" @click="playEvent(props.data)">播放</span>
<span class="o-share" @click="shareEvent(props.data)">分享</span>
<span class="o-star" @click="downloadEvent(props.data)">下载</span>
<span class="o-star" @click="deleteEvent(props.data)">删除</span>
</div>
</div>
</div>
<div class="name" @click="detailEvent(props.data)">{{props.data.name}}</div>
<div class="info">
<span>{{props.data.detail.area}}</span>
<span>{{props.data.detail.year}}</span>
<span>{{props.data.detail.note}}</span>
<span>{{props.data.detail.type}}</span>
</div>
</div>
</template>
</Waterfall>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { history, recommendation, setting } from '../lib/dexie'
import zy from '../lib/site/tools'
import Waterfall from 'vue-waterfall-plugin'
const { clipboard } = require('electron')
export default {
name: 'recommendations',
data () {
return {
recommendations: [],
sites: [],
loading: false,
types: [],
selectedTypes: [],
areas: [],
selectedAreas: [],
sortKeyword: '',
sortKeywords: ['上映', '评分', '默认']
}
},
components: {
Waterfall
},
computed: {
view: {
get () {
return this.$store.getters.getView
},
set (val) {
this.SET_VIEW(val)
}
},
video: {
get () {
return this.$store.getters.getVideo
},
set (val) {
this.SET_VIDEO(val)
}
},
detail: {
get () {
return this.$store.getters.getDetail
},
set (val) {
this.SET_DETAIL(val)
}
},
share: {
get () {
return this.$store.getters.getShare
},
set (val) {
this.SET_SHARE(val)
}
},
setting: {
get () {
return this.$store.getters.getSetting
},
set (val) {
this.SET_SETTING(val)
}
},
filteredRecommendations () {
var filteredData = this.recommendations.filter(x => (this.selectedAreas.length === 0) || this.selectedAreas.includes(x.detail.area))
filteredData = filteredData.filter(x => (this.selectedTypes.length === 0) || this.selectedTypes.includes(x.detail.type))
return filteredData
}
},
watch: {
view () {
if (this.view === 'Recommendation') {
if (this.$refs.recommendationsWaterfall) this.$refs.recommendationsWaterfall.resize()
}
},
sortKeyword () {
switch (this.sortKeyword) {
case '上映':
this.recommendations = this.recommendations.sort(function (a, b) {
return b.detail.year - a.detail.year
})
break
case '评分':
this.recommendations.sort(function (a, b) {
return b.rate - a.rate
})
break
case '默认':
this.recommendations.sort(function (a, b) {
return b.id - a.id
})
break
default:
break
}
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
detailEvent (e) {
this.detail = {
show: true,
key: e.key,
info: {
id: e.ids,
name: e.name
}
}
},
updateEvent () {
const url = 'https://raw.githubusercontent.com/cuiocean/ZY-Player-Resources/main/Recommendations/Recommendations.json'
this.loading = true
const axios = require('axios')
axios.get(url).then(res => {
if (res.status === 200) {
if (res.data.length > 0) {
this.recommendations = res.data
recommendation.clear().then(recommendation.bulkAdd(this.recommendations))
this.getFilterData()
this.$message.success('更新推荐成功. 仅根据作者cuiocean个人喜好推荐,不喜请无视.')
}
}
this.loading = false
}).catch(error => {
this.loading = false
this.$message.error('更新推荐失败. ' + error)
this.$message.warning('最新的推荐数据保存在Github上,请考虑使用代理或者等待下一版本内置数据更新.')
})
},
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 }, detail: db.detail }
} else {
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 }, detail: e.detail }
}
this.view = 'Play'
},
deleteEvent (e) {
recommendation.remove(e.id).then(res => {
if (res) {
this.$message.warning('删除失败')
}
this.getRecommendations()
})
},
shareEvent (e) {
this.share = {
show: true,
key: e.key,
info: e.detail
}
},
async downloadEvent (e) {
const db = await history.find({ site: e.key, ids: e.ids })
let videoFlag
if (db) videoFlag = db.videoFlag
zy.download(e.key, e.ids, videoFlag).then(res => {
clipboard.writeText(res.downloadUrls)
this.$message.success(res.info)
}).catch((err) => {
this.$message.error(err.info)
})
},
getRecommendations () {
recommendation.all().then(res => {
this.recommendations = res.sort(function (a, b) {
return b.id - a.id
})
this.getFilterData()
})
},
getFilterData () {
this.types = [...new Set(this.recommendations.map(ele => ele.detail.type))].filter(x => x)
this.areas = [...new Set(this.recommendations.map(ele => ele.detail.area))].filter(x => x)
},
updateViewMode () {
setTimeout(() => { if (this.$refs.recommendationsWaterfall) this.$refs.recommendationsWaterfall.refresh() }, 700)
setting.find().then(res => {
res.recommendationViewMode = this.setting.recommendationViewMode
setting.update(res)
})
}
},
created () {
this.getRecommendations()
},
mounted () {
addEventListener('resize', () => {
setTimeout(() => { if (this.$refs.recommendationsWaterfall) this.$refs.recommendationsWaterfall.resize() }, 500)
})
}
}
</script>

View File

@@ -5,22 +5,10 @@
<div class="info">
<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="quitAndInstall()" v-show="latestVersion !== pkg.version" >最新版本v{{latestVersion}}</a>
</div>
<div class="view">
<div class="title">视图</div>
<div class="view-box">
<div class="zy-select" @mouseleave="show.view = false">
<div class="vs-placeholder" @click="show.view = true">默认视图</div>
<div class="vs-options" v-show="show.view">
<ul class="zy-scroll">
<li :class="d.view === 'picture' ? 'active' : ''" @click="changeView('picture')">海报</li>
<li :class="d.view === 'table' ? 'active' : ''" @click="changeView('table')">列表</li>
</ul>
</div>
</div>
</div>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/releases/tag/v' + pkg.version)">v{{pkg.version}}更新日志</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/issues/80')">常见问题</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/issues')">反馈建议</a>
<a style="color:#38dd77" @click="openUpdate()" v-show="update.find" >最新版本v{{update.version}}</a>
</div>
<div class="shortcut">
<div class="title">快捷键</div>
@@ -40,6 +28,9 @@
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="impShortcut">导入</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="resetShortcut">重置</div>
</div>
</div>
</div>
<div class="shortcut">
@@ -78,6 +69,12 @@
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="view = 'IPTV'">编辑直播源</div>
</div>
<div class="zy-input">
<input type="checkbox" v-model = "d.allowPassWhenIptvCheck" @change="updateSettingEvent"> 检测时自动跳过停用源
</div>
<div class="zy-input">
<input type="checkbox" v-model = "d.autocleanWhenIptvCheck" @change="updateSettingEvent"> 检测时自动清理无效源
</div>
</div>
</div>
<div class="site">
@@ -204,13 +201,26 @@
</span>
</el-dialog>
</div>
<div class="update" v-if="update.show">
<div class="wrapper">
<div class="body">
<div class="content" v-html="update.html"></div>
</div>
<div class="footer">
<el-button size="small" @click="closeUpdate">关闭</el-button>
<el-button size="small" v-show="update.showDownload" @click="startUpdate">更新</el-button>
<el-button size="small" v-show="!update.showDownload && !update.downloaded">正在更新...</el-button>
<el-button size="small" v-show="update.downloaded" @click="installUpdate">安装</el-button>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import pkg from '../../package.json'
import { setting, sites, shortcut } from '../lib/dexie'
import { sites as defaultSites } from '../lib/dexie/initData'
import { sites as defaultSites, localKey as defaultShortcuts } from '../lib/dexie/initData'
import { shell, clipboard, remote, ipcRenderer } from 'electron'
import db from '../lib/dexie/dexie'
import zy from '../lib/site/tools'
@@ -239,6 +249,14 @@ export default {
scheme: '',
url: '',
port: ''
},
update: {
find: false,
version: '',
show: false,
html: '',
downloaded: false,
showDownload: true
}
}
},
@@ -284,11 +302,6 @@ export default {
this.shortcutList = res
})
},
changeView (e) {
this.d.view = e
this.updateSettingEvent()
this.show.view = false
},
async clearCache () {
const win = remote.getCurrentWindow()
const ses = win.webContents.session
@@ -410,10 +423,20 @@ export default {
this.$message.info('已清空原数据')
shortcut.add(json).then(e => {
this.$message.success('已添加成功')
this.getSites()
this.getShortcut()
this.d.shortcutModified = true
this.updateSettingEvent()
})
})
},
resetShortcut () {
shortcut.clear().then(shortcut.add(defaultShortcuts)).then(res => {
this.getShortcut()
this.$message.success('快捷键已重置')
this.d.shortcutModified = true
this.updateSettingEvent()
})
},
async changeProxyType (e) {
this.d.proxy.type = e
if (e === 'manual') {
@@ -460,20 +483,28 @@ export default {
return false
}
},
getLatestVersion () {
checkUpdate () {
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 = '下载完毕, 退出安装'
this.update.find = true
this.update.version = info.version
this.update.html = info.releaseNotes
})
},
quitAndInstall () {
this.$message.success('已开始下载更新,下载完毕后,将自动退出安装。')
openUpdate () {
this.update.show = true
},
closeUpdate () {
this.update.show = false
},
startUpdate () {
this.update.showDownload = false
ipcRenderer.send('downloadUpdate')
ipcRenderer.on('update-downloaded', () => {
this.update.downloaded = true
})
},
installUpdate () {
ipcRenderer.send('quitAndInstall')
},
createContextMenu () {
@@ -492,7 +523,7 @@ export default {
this.getSites()
this.getSetting()
this.getShortcut()
this.getLatestVersion()
this.checkUpdate()
this.createContextMenu()
}
}
@@ -530,17 +561,6 @@ export default {
cursor: pointer;
}
}
.view{
width: 100%;
padding: 20px;
margin-top: 20px;
.view-box{
margin-top: 10px;
.zy-select{
margin-right: 20px;
}
}
}
.site{
width: 100%;
padding: 20px;
@@ -635,5 +655,28 @@ export default {
font-size: 12px;
color: #ff000066;
}
.update{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(7, 17, 27, 0.7);
display: flex;
align-items: center;
justify-content: center;
.wrapper{
background-color: #fff;
padding: 20px 50px 40px;
border-radius: 4px;
max-width: 500px;
max-height: 90%;
overflow: auto;
.footer{
display: flex;
justify-content: flex-end;
}
}
}
}
</style>

View File

@@ -1,11 +1,11 @@
<template>
<div class="share" id="share" @click="shareClickEvent" v-on-clickaway="shareClickEvent">
<div class="share" id="share" @click="shareClickEvent" v-clickoutside="shareClickEvent">
<div class="left">
<img :src="pic" alt="" @load="picLoadEvent">
</div>
<div class="right" id="right">
<div class="title">{{ share.info.name }}</div>
<qrcode-vue id="qr" :value="link" :size="160" level="L" />
<qrcode-vue v-if="link !== ''" id="qr" :value="link" :size="160" level="L" />
<div class="tips">
<p>长按二维码识别播放</p>
<p><img src="@/assets/image/logo.png"></p>
@@ -22,7 +22,7 @@ import { mapMutations } from 'vuex'
import QrcodeVue from 'qrcode.vue'
import html2canvas from 'html2canvas'
import zy from '../lib/site/tools'
import { directive as onClickaway } from 'vue-clickaway'
import Clickoutside from 'element-ui/src/utils/clickoutside'
const { clipboard, nativeImage } = require('electron')
export default {
name: 'share',
@@ -45,6 +45,14 @@ export default {
set (val) {
this.SET_SHARE(val)
}
},
DetailCache: {
get () {
return this.$store.getters.getDetailCache
},
set (val) {
this.SET_DetailCache(val)
}
}
},
watch: {
@@ -58,43 +66,43 @@ export default {
}
},
directives: {
onClickaway: onClickaway
Clickoutside
},
methods: {
...mapMutations(['SET_SHARE']),
...mapMutations(['SET_SHARE', 'SET_DetailCache']),
shareClickEvent () {
this.share = {
show: false,
info: {}
}
},
getDetail () {
this.loading = true
const id = this.share.info.ids || this.share.info.id
zy.detail(this.share.key, id).then(res => {
if (res) {
this.pic = res.pic
var m3u8List = {}
const dd = res.dl.dd
const type = Object.prototype.toString.call(dd)
if (type === '[object Array]') {
for (const i of dd) {
if (i._flag.indexOf('m3u8') >= 0) {
m3u8List = i._t.split('#')
}
}
} else {
m3u8List = dd._t.split('#')
}
const url = m3u8List[1]
this.link = 'http://zyplayer.fun/player/player.html?url=' + url + '&title=' + this.share.info.name
async getUrl (dl) {
const t = dl.dd._t
if (t) {
return t.split('#')[0].split('$')[1]
} else {
const id = this.share.info.ids || this.share.info.id
const cacheKey = this.share.key + '@' + id
let res = this.DetailCache[cacheKey]
if (!this.DetailCache[cacheKey]) {
res = await zy.detail(this.share.key, id)
this.DetailCache[cacheKey] = res
}
this.loading = false
})
if (res) {
return res.fullList[0].list[0]
}
}
},
async getDetail () {
this.loading = true
this.pic = this.share.info.pic
const url = await this.getUrl(this.share.info.dl)
this.link = 'http://hunlongyu.gitee.io/zy-player-web?url=' + url + '&name=' + this.share.info.name
this.loading = false
},
picLoadEvent () {
const dom = document.getElementById('share')
html2canvas(dom, { useCORS: true, allowTaint: true }).then(res => {
html2canvas(dom).then(res => {
const png = res.toDataURL('image/png')
const p = nativeImage.createFromDataURL(png)
clipboard.writeImage(p)

View File

@@ -1,20 +1,25 @@
<template>
<div class="listpage" id="star">
<div class="listpage-header" id="star-header">
<el-switch v-model="viewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateViewMode"></el-switch>
<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-switch v-model="setting.starViewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="table" @change="updateViewMode"></el-switch>
<el-button @click.stop="exportFavoritesEvent" icon="el-icon-upload2" title="导出全部,自动添加扩展名">导出</el-button>
<el-button @click.stop="importFavoritesEvent" icon="el-icon-download" title="支持同时导入多个文件">导入</el-button>
<el-button @click.stop="removeSelectedItems" icon="el-icon-delete-solid">{{ multipleSelection.length === 0 ? "清空" : "删除所选" }}</el-button>
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">同步所有收藏</el-button>
</div>
<div class="listpage-body" id="star-body">
<div class="show-table" id="star-table" v-show="viewMode === 'table'">
<div class="show-table" id="star-table" v-if="setting.starViewMode === '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">
ref="starTable"
:data="list"
:cell-class-name="checkUpdate"
@row-click="detailEvent"
@sort-change="handleSortChange"
@select="selectionCellClick"
@selection-change="handleSelectionChange">
<el-table-column
type="selection">
</el-table-column>
<el-table-column
sortable
:sort-method="(a , b) => sortByLocaleCompare(a.name, b.name)"
@@ -78,7 +83,7 @@
</el-table-column>
</el-table>
</div>
<div class="show-picture" id="star-picture" v-show="viewMode === 'picture'">
<div class="show-picture" id="star-picture" v-if="setting.starViewMode === 'picture'">
<Waterfall ref="starWaterfall" :list="list" :gutter="20" :width="240"
:breakpoints="{
1200: { //当屏幕宽度小于等于1200
@@ -102,7 +107,7 @@
<div class="update" v-if="props.data.hasUpdate">
<span>有更新</span>
</div>
<div class="progress" v-if="props.data.index && props.data.detail && props.data.detail.m3u8List !== undefined && props.data.detail.m3u8List.length > 1">
<div class="progress" v-if="props.data.index && props.data.detail && props.data.detail.fullList[0].list !== undefined && props.data.detail.fullList[0].list.length > 1">
<span>
看至第{{ props.data.index + 1 }}集
</span>
@@ -133,7 +138,7 @@
</template>
<script>
import { mapMutations } from 'vuex'
import { star, sites, setting } from '../lib/dexie'
import { history, star, sites, setting } from '../lib/dexie'
import zy from '../lib/site/tools'
import { remote } from 'electron'
import fs from 'fs'
@@ -146,8 +151,11 @@ export default {
return {
list: [],
sites: [],
viewMode: 'picture',
numNoUpdate: 0
numNoUpdate: 0,
shiftDown: false,
selectionBegin: '',
selectionEnd: '',
multipleSelection: []
}
},
components: {
@@ -185,6 +193,14 @@ export default {
set (val) {
this.SET_SHARE(val)
}
},
setting: {
get () {
return this.$store.getters.getSetting
},
set (val) {
this.SET_SETTING(val)
}
}
},
watch: {
@@ -192,9 +208,7 @@ export default {
if (this.view === 'Star') {
this.getAllsites()
this.getFavorites()
if (this.$refs.starWaterfall) {
this.$refs.starWaterfall.refresh()
}
if (this.setting.starViewMode === 'table') this.showShiftPrompt()
}
},
numNoUpdate () {
@@ -206,13 +220,40 @@ export default {
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
handleSortChange (column, prop, order) {
this.updateDatabase()
},
sortByLocaleCompare (a, b) {
return a.localeCompare(b, 'zh')
},
selectionCellClick (selection, row) { // 同history一样逆序
if (this.shiftDown && this.selectionBegin !== '' && selection.includes(row)) {
this.selectionEnd = row.id
const start = this.list.findIndex(e => e.id === Math.max(this.selectionBegin, this.selectionEnd))
const end = this.list.findIndex(e => e.id === Math.min(this.selectionBegin, this.selectionEnd))
const selections = this.list.slice(start, end + 1)
this.$nextTick(() => {
selections.forEach(e => this.$refs.starTable.toggleRowSelection(e, true))
})
this.selectionBegin = this.selectionEnd = ''
return
}
if (selection.includes(row)) {
this.selectionBegin = row.id
} else {
this.selectionBegin = ''
}
},
handleSelectionChange (rows) {
this.multipleSelection = rows
},
removeSelectedItems () {
if (!this.multipleSelection.length) this.multipleSelection = this.list
this.multipleSelection.forEach(e => star.remove(e.id))
this.getFavorites()
this.updateDatabase()
},
detailEvent (e) {
this.detail = {
show: true,
@@ -249,7 +290,7 @@ export default {
this.share = {
show: true,
key: e.key,
info: e
info: e.detail
}
},
checkUpdate ({ row, rowIndex }) {
@@ -298,46 +339,15 @@ export default {
this.updateEvent(e)
})
},
downloadEvent (e) {
zy.download(e.key, e.ids).then(res => {
if (res && res.dl && res.dl.dd) {
const text = res.dl.dd._t
if (text) {
const list = text.split('#')
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
} else {
this.$message.warning('没有查询到下载链接.')
}
} else {
var m3u8List = {}
zy.detail(e.key, e.ids).then(res => {
const dd = res.dl.dd
const type = Object.prototype.toString.call(dd)
if (type === '[object Array]') {
for (const i of dd) {
if (i._flag.indexOf('m3u8') >= 0) {
m3u8List = i._t.split('#')
}
}
} else {
m3u8List = dd._t.split('#')
}
const list = [...m3u8List]
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
})
}
async downloadEvent (e) {
const db = await history.find({ site: e.key, ids: e.ids })
let videoFlag
if (db) videoFlag = db.videoFlag
zy.download(e.key, e.ids, videoFlag).then(res => {
clipboard.writeText(res.downloadUrls)
this.$message.success(res.info)
}).catch((err) => {
this.$message.error(err.info)
})
},
getSiteName (row) {
@@ -374,13 +384,12 @@ export default {
const str = JSON.stringify(arr, null, 2)
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
{ name: 'Normal text file', extensions: ['txt'] },
{ name: 'All types', extensions: ['*'] }
{ name: 'JSON file', extensions: ['json'] }
]
}
remote.dialog.showSaveDialog(options).then(result => {
if (!result.canceled) {
if (!result.filePath.endsWith('.json')) result.filePath += '.json'
fs.writeFileSync(result.filePath, str)
this.$message.success('导出收藏成功')
}
@@ -391,9 +400,7 @@ export default {
importFavoritesEvent () {
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
{ name: 'Normal text file', extensions: ['txt'] },
{ name: 'All types', extensions: ['*'] }
{ name: 'JSON file', extensions: ['json'] }
],
properties: ['openFile', 'multiSelections']
}
@@ -407,6 +414,16 @@ export default {
json.reverse().forEach(ele => {
const starExists = starList.some(x => x.key === ele.key && x.ids === ele.ids)
if (!starExists) {
const newDetail = {
director: ele.director,
actor: ele.actor,
type: ele.type,
area: ele.area,
lang: ele.lang,
year: ele.year,
last: ele.last,
note: ele.note
}
var doc = {
id: id,
key: ele.key,
@@ -416,16 +433,7 @@ export default {
hasUpdate: ele.hasUpdate,
index: ele.index,
rate: ele.rate,
detail: ele.detail === undefined ? {
director: ele.director,
actor: ele.actor,
type: ele.type,
area: ele.area,
lang: ele.lang,
year: ele.year,
last: ele.last,
note: ele.note
} : ele.detail
detail: ele.detail === undefined ? newDetail : ele.detail
}
id += 1
starList.push(doc)
@@ -441,11 +449,6 @@ export default {
this.$message.error(err)
})
},
clearFavoritesEvent () {
star.clear().then(e => {
this.getFavorites()
})
},
syncTableData () {
if (this.$refs.starTable.tableData && this.$refs.starTable.tableData.length === this.list.length) {
this.list = this.$refs.starTable.tableData
@@ -463,6 +466,7 @@ export default {
})
},
rowDrop () {
if (!document.getElementById('star-table')) return
const tbody = document.getElementById('star-table').querySelector('.el-table__body-wrapper tbody')
const _this = this
Sortable.create(tbody, {
@@ -473,33 +477,40 @@ export default {
}
})
},
getViewMode () {
setting.find().then(res => {
this.viewMode = res.starViewMode
})
},
updateViewMode () {
if (this.setting.starViewMode === 'table') {
setTimeout(() => { this.rowDrop() }, 100)
this.showShiftPrompt()
} else {
setTimeout(() => { if (this.$refs.starWaterfall) this.$refs.starWaterfall.refresh() }, 700)
}
setting.find().then(res => {
res.starViewMode = this.viewMode
res.starViewMode = this.setting.starViewMode
setting.update(res)
})
},
showShiftPrompt () {
if (this.setting.shiftTooltipLimitTimes === undefined) this.setting.shiftTooltipLimitTimes = 5
if (this.setting.shiftTooltipLimitTimes) {
this.$message.info('多选时支持shift快捷键')
this.setting.shiftTooltipLimitTimes--
setting.find().then(res => {
res.shiftTooltipLimitTimes = this.setting.shiftTooltipLimitTimes
setting.update(res)
})
}
}
},
created () {
this.getFavorites()
this.getViewMode()
},
mounted () {
this.rowDrop()
window.addEventListener('resize', () => {
if (this.$refs.starWaterfall && this.view === 'Star') {
this.$refs.starWaterfall.resize()
this.$refs.starWaterfall.refresh()
setTimeout(() => {
this.$refs.starWaterfall.refresh()
}, 500)
}
}, false)
if (this.setting.starViewMode === 'table') setTimeout(() => { this.rowDrop() }, 100)
addEventListener('keydown', code => { if (code.keyCode === 16) this.shiftDown = true })
addEventListener('keyup', code => { if (code.keyCode === 16) this.shiftDown = false })
addEventListener('resize', () => {
setTimeout(() => { if (this.$refs.starWaterfall) this.$refs.starWaterfall.resize() }, 500)
})
}
}
</script>

View File

@@ -5,19 +5,49 @@ const db = new Dexie('zy')
db.version(4).stores({
search: '++id, keywords',
iptvSearch: '++id, keywords',
setting: 'id, theme, site, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode, searchViewMode, password, proxy',
setting: 'id, theme, site, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode, searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck',
shortcut: 'name, key, desc',
star: '++id, [key+ids], site, name, detail, index, rate, hasUpdate',
recommendation: '++id, [key+ids], site, name, detail, index, rate, hasUpdate',
sites: '++id, key, name, api, download, isActive, group',
history: '++id, [site+ids], name, type, year, index, time, duration, detail',
// mini: 'id, mode, site, ids, name, index, time, url',
mini: 'id, bounds',
iptv: '++id, name, url, isActive',
channelList: '++id, name, prefer, channels, group, isActive'
})
// 开发和稳定版同一版本号会有不同的数据库
// 参考https://github.com/dfahlander/Dexie.js/releases/tag/v3.0.0-alpha.3 upgrade可以改变主键和表名了
// https://dexie.org/docs/Version/Version.stores()
// https://dexie.org/docs/Version/Version.upgrade()
// https://ahuigo.github.io/b/ria/js-indexedDB#/ 比较旧,适当参考
db.version(5).stores({
shortcut: null
})
db.version(6).stores({
shortcut: '++id, name, key, desc'
}).upgrade(async tx => {
await tx.shortcut.bulkAdd(localKey)
})
db.version(7).stores({
sites: '++id, key, name, api, download, jiexiUrl, isActive, group',
history: '++id, [site+ids], name, type, year, index, time, duration, detail, onlinePlay'
}).upgrade(trans => {
trans.sites.toCollection().modify(site => {
site.jiexiUrl = ''
})
trans.history.toCollection().modify(record => {
record.detail.fullList = [].concat(record.detail.m3u8List)
delete record.detail.m3u8List
})
trans.star.toCollection().modify(favorite => {
favorite.detail.fullList = [].concat(favorite.detail.m3u8List)
delete favorite.detail.m3u8List
})
})
db.on('populate', () => {
db.setting.bulkAdd(setting)
db.sites.bulkAdd(sites)

View File

@@ -5,7 +5,6 @@ import shortcut from './shortcut'
import star from './star'
import sites from './sites'
import search from './search'
import iptvSearch from './iptvSearch'
import iptv from './iptv'
import channelList from './channelList'
import recommendation from './recommendation'
@@ -20,6 +19,5 @@ export {
iptv,
channelList,
search,
iptvSearch,
recommendation
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,259 @@
[
{
"key": "1886zy",
"ids": 16944,
"site": {
"id": 2,
"key": "1886zy",
"name": "1886 资源",
"api": "http://cj.1886zy.co/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
"name": "柯明斯基理论",
"detail": {
"last": "2018-11-23 18:13:07",
"id": 16944,
"tid": 15,
"name": "柯明斯基理论",
"type": "欧美剧",
"pic": "http://pic.yc370.com/upload/vod/2019-01-19/15479133688.jpg",
"lang": "",
"area": "美国",
"year": 2018,
"state": 8,
"note": "08",
"actor": "迈克尔·道格拉斯,艾伦·阿金,莎拉·贝克,南希·特拉维斯",
"director": "安迪·坦纳特,查克·罗瑞,唐纳德·佩特瑞,贝丝·麦卡锡-米勒",
"dl": {
"dd": [
{
"_t": "08$https://zuikzy.51moca.com/share/XoIqsGnIP7IED2LS#07$https://zkcdn.yiya520.com/share/kJOL42dTmm6VJkqK#06$https://zkcdn.yiya520.com/share/f8gxIqAchIPyxlAg#05$https://zkcdn.yiya520.com/share/mTG5GkXpB7yro1sQ#04$https://zkcdn.yiya520.com/share/HjQAbC0lzzHAusIw#03$https://zkcdn.yiya520.com/share/qlN7TvZUDDueiefE#02$https://zkcdn.yiya520.com/share/2P9HAA5H1KvkMgm2#01$https://zkcdn.yiya520.com/share/KUD3QMMS4UGnNA3Y",
"_flag": "zkyun"
},
{
"_t": "08$https://zuikzy.51moca.com/2018/11/23/XoIqsGnIP7IED2LS/playlist.m3u8#07$https://zkcdn.yiya520.com/2018/11/21/kJOL42dTmm6VJkqK/playlist.m3u8#06$https://zkcdn.yiya520.com/2018/11/21/f8gxIqAchIPyxlAg/playlist.m3u8#05$https://zkcdn.yiya520.com/2018/11/21/mTG5GkXpB7yro1sQ/playlist.m3u8#04$https://zkcdn.yiya520.com/2018/11/19/HjQAbC0lzzHAusIw/playlist.m3u8#03$https://zkcdn.yiya520.com/2018/11/19/qlN7TvZUDDueiefE/playlist.m3u8#02$https://zkcdn.yiya520.com/2018/11/18/2P9HAA5H1KvkMgm2/playlist.m3u8#01$https://zkcdn.yiya520.com/2018/11/18/KUD3QMMS4UGnNA3Y/playlist.m3u8",
"_flag": "zkm3u8"
}
]
},
"des": "迈克尔·道格拉斯、艾伦·阿金有望加盟Netflix喜剧剧集《柯明斯基理论》(The Kominsky Method暂译)该剧由《生活大爆炸》联合编剧查克·罗瑞担任制片。道格拉斯剧中饰演曾经红极一时的明星现下却只能靠教授表演课程为生阿金饰演他的老友。道格拉斯上次出演电视剧集还是上世纪70年代的《旧金山风物记》而阿金上一部荧屏作品则是2001年的犯罪剧集《百厦街》。",
"m3u8List": [
"08$https://zuikzy.51moca.com/2018/11/23/XoIqsGnIP7IED2LS/playlist.m3u8",
"07$https://zkcdn.yiya520.com/2018/11/21/kJOL42dTmm6VJkqK/playlist.m3u8",
"06$https://zkcdn.yiya520.com/2018/11/21/f8gxIqAchIPyxlAg/playlist.m3u8",
"05$https://zkcdn.yiya520.com/2018/11/21/mTG5GkXpB7yro1sQ/playlist.m3u8",
"04$https://zkcdn.yiya520.com/2018/11/19/HjQAbC0lzzHAusIw/playlist.m3u8",
"03$https://zkcdn.yiya520.com/2018/11/19/qlN7TvZUDDueiefE/playlist.m3u8",
"02$https://zkcdn.yiya520.com/2018/11/18/2P9HAA5H1KvkMgm2/playlist.m3u8",
"01$https://zkcdn.yiya520.com/2018/11/18/KUD3QMMS4UGnNA3Y/playlist.m3u8"
],
"rate": "9.2"
},
"rate": "9.2",
"id": 255
},
{
"key": "mahuazy",
"ids": 21869,
"site": {
"id": 1,
"key": "mahuazy",
"name": "麻花资源",
"api": "http://www.mhapi123.com/inc/ldg_api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
"name": "海上钢琴师",
"detail": {
"last": "2019-12-26 17:45:44",
"id": 21869,
"tid": 10,
"name": "海上钢琴师",
"type": "剧情片",
"pic": "https://mahuapic.com/upload/vod/2019-12-26/201912261577353503.png",
"lang": "英语",
"area": "其它",
"year": 2019,
"state": 0,
"note": "HD",
"actor": "蒂姆·罗斯",
"director": "朱塞佩·托纳多雷",
"dl": {
"dd": {
"_t": "HD$https://mhkuaibo.com/20191226/4s3AkbXZ/index.m3u8$mahua",
"_flag": "mahua"
}
},
"des": "<span style=\"color: rgb(51, 51, 51); font-family: Tahoma, Helvetica, Arial, 微软雅黑, sans-serif; font-size: 14px; line-height: 22px; background-color: rgb(248, 248, 248);\">1900年Virginian号豪华邮轮上一个孤儿被遗弃在头等舱由船上的水手抚养长大取名1900。1900慢慢长大显示出了无师自通的非凡钢琴天赋在船上的乐队表演钢琴每个听过他演奏的人都被深深打动。爵士乐鼻祖杰尼听说了1900的高超技艺专门上船和他比赛最后自叹弗如黯然离去。可惜这一切的事情都发生在海上1900从来不愿踏上陆地直到有一天他爱上了一个女孩情愫在琴键上流淌。他会不会为了爱情踏上陆地开始新的生活用他的琴声惊艳世界他将怎样谱写自己非凡的人生。</span><br />",
"m3u8List": [
"HD$https://mhkuaibo.com/20191226/4s3AkbXZ/index.m3u8$mahua"
],
"rate": "9.3"
},
"rate": "9.3",
"id": 254
},
{
"key": "mahuazy",
"ids": 22567,
"site": {
"id": 1,
"key": "mahuazy",
"name": "麻花资源",
"api": "http://www.mhapi123.com/inc/ldg_api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
"name": "王冠第一季",
"detail": {
"last": "2019-12-19 11:41:40",
"id": 22567,
"tid": 15,
"name": "王冠第一季",
"type": "欧美剧",
"pic": "https://mahuapic.com/upload/vod/2019-12-19/15767269760.jpg",
"lang": "英语",
"area": "美国",
"year": 2016,
"state": 0,
"note": "完结",
"actor": "克莱尔·芙伊,马特·史密斯,约翰·利思戈,凡妮莎·柯比",
"director": "本·卡隆,史蒂芬·戴德利,菲利普·马丁,朱里安·杰拉德",
"dl": {
"dd": {
"_t": "第01集$https://mhyunbo.com/20191218/5vUSpghQ/index.m3u8$mahua#第02集$https://mhyunbo.com/20191218/jC9O5JXb/index.m3u8$mahua#第03集$https://mhyunbo.com/20191218/yHN6zIDc/index.m3u8$mahua#第04集$https://mhyunbo.com/20191218/q47c0tVo/index.m3u8$mahua#第05集$https://mhyunbo.com/20191218/PBckT1Qt/index.m3u8$mahua#第06集$https://mhyunbo.com/20191218/BSy4h1fR/index.m3u8$mahua#第07集$https://mhyunbo.com/20191218/gn2peH5k/index.m3u8$mahua#第08集$https://mhyunbo.com/20191218/2UNcvDq5/index.m3u8$mahua#第09集$https://mhyunbo.com/20191218/ROV7hhdI/index.m3u8$mahua#第10集$https://mhyunbo.com/20191218/VnSKiBc4/index.m3u8$mahua",
"_flag": "mahua"
}
},
"des": "马特·史密斯和约翰·利斯高加盟Netflix剧集《王冠》(The Crown暂译),二人分别饰演菲利普亲王和丘吉尔。剧集剧本由《女王》编剧彼得·摩根创作,首播集由《时时刻刻》导演史蒂芬·戴德利执导,讲述伊丽莎白二世与丘吉尔在二战后,重塑英伦的故事。之前确定由克莱尔·福伊出演伊丽莎白二世。",
"m3u8List": [
"第01集$https://mhyunbo.com/20191218/5vUSpghQ/index.m3u8$mahua",
"第02集$https://mhyunbo.com/20191218/jC9O5JXb/index.m3u8$mahua",
"第03集$https://mhyunbo.com/20191218/yHN6zIDc/index.m3u8$mahua",
"第04集$https://mhyunbo.com/20191218/q47c0tVo/index.m3u8$mahua",
"第05集$https://mhyunbo.com/20191218/PBckT1Qt/index.m3u8$mahua",
"第06集$https://mhyunbo.com/20191218/BSy4h1fR/index.m3u8$mahua",
"第07集$https://mhyunbo.com/20191218/gn2peH5k/index.m3u8$mahua",
"第08集$https://mhyunbo.com/20191218/2UNcvDq5/index.m3u8$mahua",
"第09集$https://mhyunbo.com/20191218/ROV7hhdI/index.m3u8$mahua",
"第10集$https://mhyunbo.com/20191218/VnSKiBc4/index.m3u8$mahua"
],
"rate": "9.2"
},
"rate": "9.2",
"id": 253
},
{
"id": 252,
"key": "mahuazy",
"ids": 29806,
"site": {
"id": 1,
"key": "mahuazy",
"name": "麻花资源",
"api": "https://www.mhapi123.com/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
"name": "最后的棒棒",
"rate": "9.6",
"detail": {
"last": "2020-04-12 15:43:02",
"id": 29806,
"tid": 20,
"name": "最后的棒棒",
"type": "记录片",
"pic": "https://mahuapic.com/upload/vod/2020-04-12/15866774000.jpg",
"lang": "国语",
"area": "大陆",
"year": 2016,
"state": 0,
"note": "完结",
"actor": "何苦",
"director": "何苦",
"dl": {
"dd": {
"_t": "第01集$https://mahua-kb.com/20200412/vpFPIG1f/index.m3u8#第02集$https://mahua-kb.com/20200412/K0b5AvUp/index.m3u8#第03集$https://mahua-kb.com/20200412/tgpvWhFj/index.m3u8#第04集$https://mahua-kb.com/20200412/9mc4bPsO/index.m3u8#第05集$https://mahua-kb.com/20200412/gs0qmble/index.m3u8#第06集$https://mahua-kb.com/20200412/l2eDtPPY/index.m3u8#第07集$https://mahua-kb.com/20200412/7LrOTyqT/index.m3u8#第08集$https://mahua-kb.com/20200412/rnLFz9kO/index.m3u8#第09集$https://mahua-kb.com/20200412/nOTSjrba/index.m3u8#第10集$https://mahua-kb.com/20200412/qrfl8OzL/index.m3u8#第11集$https://mahua-kb.com/20200412/Y4sTsCWm/index.m3u8#第12集$https://mahua-kb.com/20200412/w5L2lXpy/index.m3u8#第13集$https://mahua-kb.com/20200412/Up2AGSWE/index.m3u8",
"_flag": "mahua"
}
},
"des": "改革开放之初,山城重庆特殊的地理环境孕育了一个特殊的行业——山城棒棒军。爬坡上坎,负重前行的三十多年,数十万棒棒大军不仅挑走了汗水浸泡的年华,也挑走了属于自己的年代。癸巳岁末,几个佝偻背影即将道别正在消逝的行业,一名退役中校扛起一根棒棒开始了自己的追寻——辉煌与尴尬,艰韧和无奈,他们的人生无须评说,他们的故事值得铭记。",
"m3u8List": [
"第01集$https://mahua-kb.com/20200412/vpFPIG1f/index.m3u8",
"第02集$https://mahua-kb.com/20200412/K0b5AvUp/index.m3u8",
"第03集$https://mahua-kb.com/20200412/tgpvWhFj/index.m3u8",
"第04集$https://mahua-kb.com/20200412/9mc4bPsO/index.m3u8",
"第05集$https://mahua-kb.com/20200412/gs0qmble/index.m3u8",
"第06集$https://mahua-kb.com/20200412/l2eDtPPY/index.m3u8",
"第07集$https://mahua-kb.com/20200412/7LrOTyqT/index.m3u8",
"第08集$https://mahua-kb.com/20200412/rnLFz9kO/index.m3u8",
"第09集$https://mahua-kb.com/20200412/nOTSjrba/index.m3u8",
"第10集$https://mahua-kb.com/20200412/qrfl8OzL/index.m3u8",
"第11集$https://mahua-kb.com/20200412/Y4sTsCWm/index.m3u8",
"第12集$https://mahua-kb.com/20200412/w5L2lXpy/index.m3u8",
"第13集$https://mahua-kb.com/20200412/Up2AGSWE/index.m3u8"
],
"rate": "9.6"
}
},
{
"id": 251,
"key": "mahuazy",
"ids": 50694,
"site": {
"id": 1,
"key": "mahuazy",
"name": "麻花资源",
"api": "https://www.mhapi123.com/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
"name": "以你的心诠释我的爱",
"index": 2,
"rate": "9.5",
"detail": {
"last": "2020-11-13 09:34:47",
"id": 50694,
"tid": 17,
"name": "以你的心诠释我的爱",
"type": "泰剧",
"pic": "https://mahuapic.com/upload/vod/2020-10-23/16034119330.jpg",
"lang": "泰语",
"area": "泰国",
"year": 2020,
"state": 0,
"note": "更新至04集",
"actor": "克里特·安努艾德奇康,普提蓬·阿萨拉塔纳功",
"director": "纳卢拜·库诺",
"dl": {
"dd": {
"_t": "第01集$https://b.mhqiyi.com/20201022/K4gdEqpu/index.m3u8#第02集$https://b.mhqiyi.com/20201029/h3maf38g/index.m3u8#第03集$https://e.mahua-kb.com/20201105/HVK7wMlM/index.m3u8#第04集$https://b.mhqiyi.com/20201112/pOl6JaA7/index.m3u8",
"_flag": "mahua"
}
},
"des": "这部电视剧是BKPP project的一部分",
"m3u8List": [
"第01集$https://b.mhqiyi.com/20201022/K4gdEqpu/index.m3u8",
"第02集$https://b.mhqiyi.com/20201029/h3maf38g/index.m3u8",
"第03集$https://e.mahua-kb.com/20201105/HVK7wMlM/index.m3u8",
"第04集$https://b.mhqiyi.com/20201112/pOl6JaA7/index.m3u8"
],
"rate": "9.5"
}
},
{
"id": 250,
"key": "niuniucj",
"ids": 20044,
"site": {
@@ -12,6 +266,7 @@
"isActive": 1
},
"name": "此画怎讲",
"rate": "8.1",
"detail": {
"last": "2020-09-23 23:23:36",
"id": 20044,
@@ -72,11 +327,10 @@
"第30集$https://s6.niuniu-baidu.com/2020/09/23/37H4vtEiTXcjSzoa/index.m3u8"
],
"rate": "8.1"
},
"rate": "8.1",
"id": 250
}
},
{
"id": 249,
"key": "niuniucj",
"ids": 4255,
"site": {
@@ -89,6 +343,7 @@
"isActive": 1
},
"name": "紧急呼救第一季",
"rate": "8.6",
"detail": {
"last": "2019-10-10 19:19:15",
"id": 4255,
@@ -129,11 +384,10 @@
"第10集$https://s1.niuniu-baidu.com/20191010/XZjWRMxLgh8nyFlT/index.m3u8"
],
"rate": "8.6"
},
"rate": "8.6",
"id": 249
}
},
{
"id": 248,
"key": "niuniucj",
"ids": 20632,
"site": {
@@ -146,6 +400,7 @@
"isActive": 1
},
"name": "小小世界",
"rate": "9.4",
"detail": {
"last": "2020-10-03 12:00:49",
"id": 20632,
@@ -182,9 +437,7 @@
"第06集$https://s6.niuniu-baidu.com/2020/10/03/U0JFALI1lHOjWCfw/index.m3u8"
],
"rate": "9.4"
},
"rate": "9.4",
"id": 248
}
},
{
"id": 247,
@@ -9238,6 +9491,7 @@
}
},
{
"id": 22,
"key": "mahuazy",
"ids": 20868,
"site": {
@@ -9250,6 +9504,7 @@
"group": "默认"
},
"name": "铁血战士(1987)",
"rate": "7.7",
"detail": {
"last": "2019-11-23 10:59:36",
"id": 20868,
@@ -9275,9 +9530,7 @@
"HD$https://mhbobo.com/20191121/vyWmCGRH/index.m3u8"
],
"rate": "7.7"
},
"rate": "7.7",
"id": 22
}
},
{
"id": 21,

View File

@@ -3,270 +3,180 @@
"id": 1,
"key": "mahuazy",
"name": "麻花资源",
"api": "https://www.mhapi123.com/inc/api.php",
"api": "http://www.mhapi123.com/inc/ldg_api.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true,
"status": "可用"
"isActive": true
},
{
"id": 2,
"key": "niuniucj",
"name": "牛牛资源",
"api": "http://v.niuniucj.com/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 3,
"key": "88zyw",
"name": "88 影视资源站",
"api": "http://www.88zyw.net/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 4,
"key": "apibdzy",
"name": "百度云资源",
"api": "https://api.apibdzy.com/api.php/provide/vod/at/xml",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 5,
"key": "mbo",
"name": "秒播资源",
"api": "http://caiji.mb77.vip/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 6,
"key": "zuidazy",
"name": "最大资源网",
"api": "http://www.zdziyuan.com/inc/api.php",
"download": "http://www.zdziyuan.com/inc/apidown.php",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 7,
"key": "123ku",
"name": "123 资源",
"api": "http://cj.123ku2.com:12315/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 8,
"key": "okzy",
"name": "OK 资源网",
"api": "http://cj.okzy.tv/inc/api.php",
"download": "http://cj.okzy.tv/inc/apidown.php",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 9,
"key": "kuyunzy",
"name": "酷云资源",
"api": "http://caiji.kuyun98.com/inc/ldg_api.php",
"download": "http://caiji.kuyun98.com/inc/apidown.php",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 10,
"key": "kubozy",
"name": "酷播资源",
"api": "http://api.kbzyapi.com/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 11,
"key": "yongjiuzy",
"name": "永久资源",
"api": "http://cj.yongjiuzyw.com/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 12,
"key": "rrzy",
"name": "人人资源",
"api": "https://www.rrzyw.cc/api.php/provide/vod/from/rrm3u8/at/xml/",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 13,
"key": "bbkdj",
"name": "步步高顶尖资源网",
"api": "http://api.bbkdj.com/api",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 14,
"key": "solezy",
"name": "搜乐资源网",
"api": "https://www.caijizy.vip/api.php/provide/vod/at/xml/",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 15,
"key": "zuixinzy",
"name": "最新资源",
"api": "http://api.zuixinapi.com/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 16,
"key": "605zy",
"name": "605资源",
"api": "http://www.605zy.net/inc/seacmsapi.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 17,
"key": "subo988",
"name": "速播资源站",
"api": "https://www.subo988.com/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 18,
"key": "1886zy",
"name": "1886 资源",
"api": "http://cj.1886zy.co/inc/api.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true,
"status": "可用"
"isActive": true
},
{
"id": 19,
"key": "doubanzy",
"name": "豆瓣电影资源",
"api": "http://v.1988cj.com/inc/api.php",
"download": "http://v.1988cj.com/inc/apidown.php",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 20,
"key": "135zy",
"name": "135 资源网",
"api": "http://cj.zycjw1.com/inc/api.php",
"download": "http://cj.zycjw1.com/inc/apidown.php",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 21,
"key": "mgtvzy",
"name": "芒果 TV 资源网",
"api": "https://api.shijiapi.com/api.php/provide/vod/at/xml/",
"id": 3,
"key": "123ku",
"name": "123 资源",
"api": "http://cj.123ku2.com:12315/inc/api.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true,
"status": "可用"
"isActive": true
},
{
"id": 22,
"key": "7K资源",
"id": 4,
"name": "7K资源",
"api": "https://zy.7kjx.com/cjapi.php",
"download": "",
"jiexiUrl": "https://jx.7kjx.com/?url=",
"group": "默认",
"isActive": true
},
{
"key": "1717云资源网",
"id": 5,
"name": "1717云资源网",
"api": "http://zy.itono.cn/inc/api.php",
"download": "",
"jiexiUrl": "https://www.1717yun.com/jiexi/?url=",
"group": "默认",
"isActive": true
},
{
"id": 6,
"key": "subo988",
"name": "速播资源站",
"api": "https://www.subo988.com/inc/api.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 7,
"key": "88zyw",
"name": "88 影视资源站",
"api": "http://www.88zyw.net/inc/api.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"key": "zuidazy",
"id": 8,
"name": "最大资源网",
"api": "http://www.zdziyuan.com/inc/ldg_sea.php",
"download": "http://www.zdziyuan.com/inc/apidown.php",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"key": "mbo",
"id": 9,
"name": "秒播资源",
"api": "http://caiji.mb77.vip/inc/seacmsapi.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 10,
"key": "apibdzy",
"name": "百度云资源",
"api": "https://api.apibdzy.com/api.php/provide/vod/at/xml",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 11,
"key": "okzy",
"name": "OK 资源网",
"api": "http://cj.okzy.tv/inc/api.php",
"download": "http://cj.okzy.tv/inc/apidown.php",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 12,
"key": "kuyunzy",
"name": "酷云资源",
"api": "http://caiji.kuyun98.com/inc/ldg_api.php",
"download": "http://caiji.kuyun98.com/inc/apidown.php",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 13,
"key": "kubozy",
"name": "酷播资源",
"api": "http://api.kbzyapi.com/inc/api.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 14,
"key": "yongjiuzy",
"name": "永久资源",
"api": "http://cj.yongjiuzyw.com/inc/api.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 15,
"key": "rrzy",
"name": "人人资源",
"api": "https://www.rrzyw.cc/api.php/provide/vod/from/rrm3u8/at/xml/",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 16,
"key": "bbkdj",
"name": "步步高顶尖资源网",
"api": "http://api.bbkdj.com/api",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 17,
"key": "zuixinzy",
"name": "最新资源",
"api": "http://api.zuixinapi.com/inc/api.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true
},
{
"id": 18,
"key": "209zy",
"name": "209 资源",
"api": "http://cj.1156zy.com/inc/api.php",
"download": "",
"jiexiUrl": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 23,
"key": "kkzy",
"name": "快快资源",
"api": "https://api.kkzy.tv/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 24,
"key": "wolongzy",
"name": "卧龙资源",
"api": "http://cj.wlzy.tv/inc/api_mac.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 25,
"key": "mokazy",
"name": "魔卡资源网",
"api": "https://cj.heiyap.com/api.php/provide/vod/at/xml/",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 26,
"key": "158zy",
"name": "壹伍捌资源网",
"api": "http://cj.158zyz.net:158/inc/api.php",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
},
{
"id": 27,
"key": "kyzy",
"name": "快影资源站",
"api": "https://www.kyzy.tv/api.php/kyyun/vod/at/xml/",
"download": "",
"group": "默认",
"isActive": true,
"status": "可用"
"isActive": true
}
]
]

View File

@@ -19,7 +19,9 @@ const setting = [
scheme: '',
url: '',
port: ''
}
},
allowPassWhenIptvCheck: true,
autocleanWhenIptvCheck: false
}
]
@@ -66,7 +68,7 @@ const localKey = [
},
{
name: 'escape',
desc: '退出全屏',
desc: '退出全屏/精简模式',
key: 'esc'
},
{
@@ -89,6 +91,21 @@ const localKey = [
desc: '跳到视频结束位置',
key: 'end'
},
{
name: 'startPosition',
desc: '标记片头',
key: 'ctrl+home'
},
{
name: 'endPosition',
desc: '标记片尾',
key: 'ctrl+end'
},
{
name: 'clearPosition',
desc: '清除标记',
key: 'ctrl+del'
},
{
name: 'opacityUp',
desc: '透明度调高',
@@ -113,6 +130,11 @@ const localKey = [
name: 'mini',
desc: '进入或退出mini模式',
key: 'alt+m'
},
{
name: 'resetMini',
desc: '重置mini窗口',
key: 'ctrl+0'
}
]

View File

@@ -1,22 +0,0 @@
import db from './dexie'
const { iptvSearch } = db
export default {
async add (doc) {
return await iptvSearch.add(doc)
},
async find (doc) {
return await iptvSearch.get(doc)
},
async update (id, docs) {
return await iptvSearch.update(id, docs)
},
async all () {
return await iptvSearch.toArray()
},
async remove (id) {
return await iptvSearch.delete(id)
},
async clear () {
return await iptvSearch.clear()
}
}

View File

@@ -1,5 +1,5 @@
import Vue from 'vue'
import { Message, Button, Table, TableColumn, Tag, Input, Dialog, Form, FormItem, Switch, Select, Option, Checkbox, Autocomplete, Col, Tree } from 'element-ui'
import { Message, Button, Table, TableColumn, Tag, Input, InputNumber, Dialog, Form, FormItem, Switch, Select, Option, Checkbox, Autocomplete, Col, Tree, Divider, Progress } from 'element-ui'
import Plugin from 'v-fit-columns'
Vue.use(Button)
Vue.use(Col)
@@ -7,6 +7,7 @@ Vue.use(Table)
Vue.use(TableColumn)
Vue.use(Tag)
Vue.use(Input)
Vue.use(InputNumber)
Vue.use(Dialog)
Vue.use(Form)
Vue.use(FormItem)
@@ -17,4 +18,6 @@ Vue.use(Option)
Vue.use(Checkbox)
Vue.use(Autocomplete)
Vue.use(Tree)
Vue.use(Divider)
Vue.use(Progress)
Vue.prototype.$message = Message

View File

@@ -1,18 +0,0 @@
import express from 'express'
import cors from 'cors'
const Axios = require('axios')
const app = express()
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.post('/api', async (req, res) => {
const result = await Axios.get(req.body.url)
res.json({
code: 1,
info: result.data
})
})
app.listen(44444)

View File

@@ -15,7 +15,8 @@ var session = win.webContents.session
var ElectronProxyAgent = require('electron-proxy-agent')
// 请求超时时限
axios.defaults.timeout = 10000 // 可能使用代理,增长超时
// axios.defaults.timeout = 10000 // 可能使用代理,增长超时
const TIMEOUT = 6000
// 重试次数共请求3次
axios.defaults.retry = 2
@@ -23,11 +24,20 @@ axios.defaults.retry = 2
// 请求的间隙
axios.defaults.retryDelay = 1000
// 使用请求拦截器动态调整超时
axios.interceptors.request.use(function (config) {
if (config.__retryCount === undefined) {
config.timeout = TIMEOUT
} else {
config.timeout = TIMEOUT * (config.__retryCount + 1)
}
return config
}, function (err) {
return Promise.reject(err)
})
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做些事
if (response.status && response.status === 200 && response.request.responseURL.includes('api.php') && !response.data.startsWith('<?xml')) {
}
return response
}, function (err) { // 请求错误时做些事
// 请求超时的之后,抛出 err.code = ECONNABORTED的错误..错误信息是 timeout of xxx ms exceeded
@@ -92,9 +102,10 @@ const zy = {
axios.post(url).then(res => {
const data = res.data
const json = parser.parse(data, this.xmlConfig)
const jsondata = json.rss === undefined ? json : json.rss
const arr = []
if (json.rss.class) {
for (const i of json.rss.class.ty) {
if (jsondata.class) {
for (const i of jsondata.class.ty) {
const j = {
tid: i._id,
name: i._t
@@ -104,10 +115,10 @@ const zy = {
}
const doc = {
class: arr,
page: json.rss.list._page,
pagecount: json.rss.list._pagecount,
pagesize: json.rss.list._pagesize,
recordcount: json.rss.list._recordcount
page: jsondata.list._page,
pagecount: jsondata.list._pagecount,
pagesize: jsondata.list._pagesize,
recordcount: jsondata.list._recordcount
}
resolve(doc)
}).catch(err => {
@@ -136,8 +147,13 @@ const zy = {
axios.post(url).then(async res => {
const data = res.data
const json = parser.parse(data, this.xmlConfig)
const videoList = json.rss.list.video
resolve(videoList)
const jsondata = json.rss === undefined ? json : json.rss
const videoList = jsondata.list.video
if (videoList && videoList.length) {
resolve(videoList)
} else {
resolve([])
}
}).catch(err => {
reject(err)
})
@@ -161,13 +177,14 @@ const zy = {
url = `${site.api}?ac=videolist`
}
axios.post(url).then(async res => {
const data = res.data
const data = res.data.match(/<list [^>]*>/)[0] + '</list>' // 某些源站不含页码时获取到的数据parser无法解析
const json = parser.parse(data, this.xmlConfig)
const jsondata = json.rss === undefined ? json : json.rss
const pg = {
page: json.rss.list._page,
pagecount: json.rss.list._pagecount,
pagesize: json.rss.list._pagesize,
recordcount: json.rss.list._recordcount
page: jsondata.list._page,
pagecount: jsondata.list._pagecount,
pagesize: jsondata.list._pagesize,
recordcount: jsondata.list._recordcount
}
resolve(pg)
}).catch(err => {
@@ -191,8 +208,9 @@ const zy = {
axios.post(url, { timeout: 3000 }).then(res => {
const data = res.data
const json = parser.parse(data, this.xmlConfig)
if (json && json.rss && json.rss.list) {
const videoList = json.rss.list.video
const jsondata = json.rss === undefined ? json : json.rss
if (json && jsondata && jsondata.list) {
const videoList = jsondata.list.video
resolve(videoList)
}
}).catch(err => {
@@ -216,21 +234,48 @@ const zy = {
axios.post(url).then(res => {
const data = res.data
const json = parser.parse(data, this.xmlConfig)
const videoList = json.rss.list.video
// Parse m3u8List
var m3u8List = []
const jsondata = json.rss === undefined ? json : json.rss
const videoList = jsondata.list.video
// Parse video lists
var fullList = []
let index = 0
const supportedFormats = ['m3u8', 'mp4']
const dd = videoList.dl.dd
const type = Object.prototype.toString.call(dd)
if (type === '[object Array]') {
for (const i of dd) {
if (i._flag.indexOf('m3u8') >= 0) {
m3u8List = i._t.split('#')
const ext = Array.from(new Set(...i._t.split('#').map(e => e.split('$')[1].match(/\.\w+?$/)))).map(e => e.slice(1))
if (ext.length && ext.length <= supportedFormats.length && ext.every(e => supportedFormats.includes(e))) {
if (ext.length === 1) {
i._flag = ext[0]
} else {
i._flag = index ? 'ZY支持-' + index : 'ZY支持'
index++
}
}
fullList.push(
{
flag: i._flag,
list: i._t.split('#').filter(e => e && e.split('$')[1] && e.split('$')[1].startsWith('http'))
}
)
}
} else {
m3u8List = dd._t.split('#')
fullList.push(
{
flag: dd._flag,
list: dd._t.split('#').filter(e => e && e.split('$')[1] && e.split('$')[1].startsWith('http'))
}
)
}
videoList.m3u8List = m3u8List
if (fullList.length > 1) { // 将ZY支持的播放列表前置
index = fullList.findIndex(e => supportedFormats.includes(e.flag) || e.flag.startsWith('ZY支持'))
if (index !== -1) {
const first = fullList.splice(index, 1)
fullList = first.concat(fullList)
}
}
videoList.fullList = fullList
resolve(videoList)
}).catch(err => {
reject(err)
@@ -246,8 +291,10 @@ const zy = {
* @param {*} id 资源唯一标识符 id
* @returns
*/
download (key, id) {
download (key, id, videoFlag) {
return new Promise((resolve, reject) => {
let info = ''
let downloadUrls = ''
this.getSite(key).then(res => {
const site = res
if (site.download) {
@@ -255,13 +302,44 @@ const zy = {
axios.post(url).then(res => {
const data = res.data
const json = parser.parse(data, this.xmlConfig)
const videoList = json.rss.list.video
resolve(videoList)
}).catch(err => {
const jsondata = json.rss === undefined ? json : json.rss
const videoList = jsondata.list.video
const dd = videoList.dl.dd
const type = Object.prototype.toString.call(dd)
if (type === '[object Array]') {
for (const i of dd) {
downloadUrls = i._t.split('#').map(e => encodeURI(e.split('$')[1])).join('\n')
}
} else {
downloadUrls = dd._t.split('#').map(e => encodeURI(e.split('$')[1])).join('\n')
}
if (downloadUrls) {
info = '调用下载接口获取到的链接已复制, 快去下载吧!'
resolve({ downloadUrls: downloadUrls, info: info })
} else {
throw new Error()
}
}).catch((err) => {
err.info = '无法获取到下载链接,请通过播放页面点击“调试”按钮获取'
reject(err)
})
} else {
resolve([])
zy.detail(key, id).then(res => {
const dl = res.fullList.find(e => e.flag === videoFlag) || res.fullList[0]
for (const i of dl.list) {
const url = encodeURI(i.split('$')[1])
downloadUrls += (url + '\n')
}
if (downloadUrls) {
info = '视频源链接已复制, 快去下载吧!'
resolve({ downloadUrls: downloadUrls, info: info })
} else {
throw new Error()
}
}).catch((err) => {
err.info = '无法获取到下载链接,请通过播放页面点击“调试”按钮获取'
reject(err)
})
}
})
})
@@ -309,28 +387,26 @@ const zy = {
/**
* 获取豆瓣页面链接
* @param {*} name 视频名称
* @param {*} year 视频年份
* @returns 豆瓣页面链接,如果没有搜到该视频,返回搜索页面链接
*/
doubanLink (name) {
doubanLink (name, year) {
return new Promise((resolve, reject) => {
// 豆瓣搜索链接
var nameToSearch = name.replace(/\s/g, '')
var doubanSearchLink = 'https://www.douban.com/search?q=' + nameToSearch
axios.get(doubanSearchLink).then(res => {
const $ = cheerio.load(res.data)
// 比较第一和第二给豆瓣搜索结果, 看名字是否相符
// 查询所有搜索结果, 看名字和年代是否相符
var link = ''
var linkInDouban = $($('div.result')[0]).find('div>div>h3>a').first()
var nameInDouban = linkInDouban.text().replace(/\s/g, '')
if (nameToSearch === nameInDouban) {
link = linkInDouban.attr('href')
} else {
linkInDouban = $($('div.result')[1]).find('div>div>h3>a').first()
nameInDouban = linkInDouban.text().replace(/\s/g, '')
if (nameToSearch === nameInDouban) {
$('div.result').each(function () {
var linkInDouban = $(this).find('div>div>h3>a').first()
var nameInDouban = linkInDouban.text().replace(/\s/g, '')
var subjectCast = $(this).find('span.subject-cast').text()
if (nameToSearch === nameInDouban && subjectCast && subjectCast.includes(year)) {
link = linkInDouban.attr('href')
}
}
})
if (link) {
resolve(link)
} else {
@@ -345,12 +421,13 @@ const zy = {
/**
* 获取豆瓣评分
* @param {*} name 视频名称
* @param {*} year 视频年份
* @returns 豆瓣评分
*/
doubanRate (name) {
doubanRate (name, year) {
return new Promise((resolve, reject) => {
var nameToSearch = name.replace(/\s/g, '')
this.doubanLink(nameToSearch).then(link => {
this.doubanLink(nameToSearch, year).then(link => {
if (link.includes('https://www.douban.com/search')) {
resolve('暂无评分')
} else {
@@ -374,17 +451,15 @@ const zy = {
async proxy () {
return new Promise((resolve, reject) => {
setting.find().then(db => {
if (db.proxy) {
if (db.proxy.type === 'none') {
session.setProxy({ proxyRules: 'direct://' })
if (db && db.proxy && db.proxy.type === 'manual') {
if (db.proxy.scheme && db.proxy.url && db.proxy.port) {
const proxyURL = db.proxy.scheme + '://' + db.proxy.url.trim() + ':' + db.proxy.port.trim()
session.setProxy({ proxyRules: proxyURL })
http.globalAgent = https.globalAgent = new ElectronProxyAgent(session)
} else if (db.proxy.type === 'manual') {
if (db.proxy.scheme && db.proxy.url && db.proxy.port) {
const proxyURL = db.proxy.scheme + '://' + db.proxy.url.trim() + ':' + db.proxy.port.trim()
session.setProxy({ proxyRules: proxyURL })
http.globalAgent = https.globalAgent = new ElectronProxyAgent(session)
}
}
} else {
session.setProxy({ proxyRules: 'direct://' })
http.globalAgent = https.globalAgent = new ElectronProxyAgent(session)
}
// 不要删了,留着测试用
// axios.get('https://api.my-ip.io/ip').then(res => console.log(res))

View File

@@ -1,18 +1,24 @@
import { BrowserWindow, ipcMain } from 'electron'
import { autoUpdater } from 'electron-updater'
const { autoUpdater } = require('electron-updater')
// electron-updater 增量更新时似乎无法显示进度
export function initUpdater (win = BrowserWindow) {
autoUpdater.autoDownload = false
autoUpdater.autoInstallOnAppQuit = false
autoUpdater.autoInstallOnAppQuit = true
// 主进程监听检查更新事件
ipcMain.on('checkForUpdate', () => {
autoUpdater.checkForUpdates()
})
// 主进程监听开始下载事件
ipcMain.on('downloadUpdate', () => {
autoUpdater.downloadUpdate()
})
// 主进程监听退出并安装事件
ipcMain.on('quitAndInstall', () => {
autoUpdater.downloadUpdate()
autoUpdater.quitAndInstall()
})
// 开始检测是否有更新
@@ -40,9 +46,8 @@ export function initUpdater (win = BrowserWindow) {
win.webContents.send('download-progress', progressObj)
})
// 下载完成并退出安装
// 下载完成
autoUpdater.on('update-downloaded', () => {
win.webContents.send('update-downloaded')
autoUpdater.quitAndInstall()
})
}

View File

@@ -28,7 +28,8 @@ export default new Vuex.Store({
},
appState: {
windowIsOnTop: false
}
},
DetailCache: {}
},
getters: {
getView: state => {
@@ -48,6 +49,9 @@ export default new Vuex.Store({
},
getAppState: state => {
return state.appState
},
getDetailCache: state => {
return state.DetailCache
}
},
mutations: {
@@ -68,6 +72,9 @@ export default new Vuex.Store({
},
SET_APPSTATE: (state, payload) => {
state.appState = payload
},
set_DetailCache: (state, payload) => {
state.DetailCache = payload
}
}
})

View File

@@ -1,6 +1,6 @@
module.exports = {
pages: {
index: 'src/main.js',
index: 'src/main.js'
},
pluginOptions: {
electronBuilder: {

1671
yarn.lock

File diff suppressed because it is too large Load Diff