Compare commits

...

132 Commits

Author SHA1 Message Date
haiyangcui
efd2095261 v2.4.7 2020-09-18 19:09:51 +02:00
haiyangcui
cbedb859c3 电影列表页面屏蔽福利片 2020-09-17 16:08:12 +02:00
haiyangcui
65aedc84cf 有最新版本时,在设置页面有提示 2020-09-16 22:17:04 +02:00
haiyangcui
95c58095ee 屏蔽福利片选项 2020-09-16 20:00:22 +02:00
haiyangcui
b5da3c7f81 屏蔽主分类 2020-09-13 15:05:54 +02:00
haiyangcui
5b0211b2ed 如果编辑源页面没有打开,无需更新 2020-09-13 14:51:22 +02:00
haiyangcui
0bb6409687 只有EditSites的资源列表变化时才更新Film页面 2020-09-13 08:19:57 +02:00
haiyangcui
7078e05668 修复编辑第三方播放器地址的bug 2020-09-12 22:35:54 +02:00
haiyangcui
0172a204b1 2.4.6 2020-09-12 08:28:30 +02:00
haiyangcui
c6b0781a02 添加源后清空输入框 2020-09-11 17:33:53 +02:00
haiyangcui
b3e67bcc7d Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player 2020-09-10 23:53:51 +02:00
haiyangcui
cbca6db97e 改进EditSites页面 2020-09-10 21:20:30 +02:00
haiyangcui
814ecaf25b 添加时检查name和api不能为空 2020-09-10 21:14:32 +02:00
haiyangcui
4a07520336 源更新时更新Film页面的源列表 2020-09-10 21:13:42 +02:00
haiyangcui
2e121a901c ChangeView的时候关闭EditSites页面 2020-09-10 20:59:28 +02:00
haiyangcui
3a2a16a168 增加"添加新源"页面 2020-09-10 17:59:36 +02:00
Hunlongyu
881ab6cfa5 更换蓝奏云链接 2020-09-10 23:14:35 +08:00
haiyangcui
3151b33a23 支持源页面可拖曳 2020-09-09 17:34:23 +02:00
haiyangcui
f154f44772 添加编辑源页面 2020-09-09 17:12:18 +02:00
haiyangcui
a1458cf8bf 收藏页面添加观看至第几集的信息 2020-09-07 23:37:11 +02:00
haiyangcui
595136c799 支持收藏列表的拖曳排序 2020-09-07 20:48:33 +02:00
haiyangcui
b7e497e2d9 历史记录和收藏页面,添加表头 2020-09-07 13:35:04 +02:00
haiyangcui
dce8ad2ef2 支持直接编辑第三方播放器路径 2020-09-07 12:16:28 +02:00
haiyangcui
79ba45b63a 新播放的历史记录置顶 2020-09-05 23:39:16 +02:00
haiyangcui
b6fcbcb934 Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player 2020-09-04 22:08:47 +02:00
haiyangcui
54c4409311 同步的时候不清除有更新标记 2020-09-02 23:05:40 +02:00
Hunlongyu
5e9c273048 更新详情截图 2020-09-02 19:57:49 +08:00
Hunlongyu
197df4e830 Update README.md 2020-09-02 19:44:39 +08:00
Hunlongyu
5271dfa490 Update README.md 2020-09-02 19:43:05 +08:00
Hunlongyu
7efde45aed Update README.md 2020-09-02 19:42:39 +08:00
Hunlongyu
4c92918dc3 Update README.md 2020-09-02 19:28:34 +08:00
haiyangcui
6c0bd5125b 美化精简模式的按钮 2020-09-02 13:16:39 +02:00
haiyangcui
785f9b20e7 用m3u文件替代dpl文件 2020-09-01 23:03:24 +02:00
haiyangcui
8d052d2904 无搜索结果时,不需要提醒 2020-09-01 21:57:28 +02:00
haiyangcui
69a397e24e Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player 2020-08-30 13:48:01 +02:00
haiyangcui
c216352268 改进详情页面宽度 2020-08-30 13:47:39 +02:00
haiyangcui
89448d390f Install cheerio 2020-08-30 13:32:07 +02:00
haiyangcui
a853b02aa0 v2.4.5 2020-08-30 13:23:00 +02:00
haiyangcui
0443ce248f 重置源放最后 2020-08-30 13:03:51 +02:00
haiyangcui
8009c3197e 比较头两个搜索结果 2020-08-30 12:09:23 +02:00
haiyangcui
978d4e7181 2.4.5 2020-08-30 11:35:20 +02:00
haiyangcui
138b70263a 添加豆瓣评分 2020-08-30 11:34:22 +02:00
haiyangcui
7f48c91f97 如果没有选择本地播放器,选择在线播放 2020-08-30 10:49:08 +02:00
haiyangcui
7a494a54c0 详情页添加"豆瓣" 2020-08-30 10:28:57 +02:00
haiyangcui
97812af0fd v2.4.4 2020-08-28 15:00:56 +02:00
haiyangcui
490d7c963e 支持导入所有剧集到PotPlayer 2020-08-28 14:53:09 +02:00
haiyangcui
ed1afa2026 添加“重置源”功能 2020-08-28 12:58:16 +02:00
haiyangcui
7ef114d0e3 Change View的时候关闭Detail 2020-08-28 12:43:09 +02:00
haiyangcui
7f9b7fc2c8 Detail页面左边留出60px,侧边栏的宽度 2020-08-28 12:40:26 +02:00
haiyangcui
511820f873 修复历史记录里的下载功能 2020-08-27 17:01:22 +02:00
haiyangcui
af6dcb11bf 修复剧集的分享功能 2020-08-27 16:53:46 +02:00
haiyangcui
7ecad1ad8e 同步历史和收藏的operate buttons 2020-08-27 16:45:29 +02:00
haiyangcui
4d169f9d51 删除无用代码 2020-08-27 16:36:04 +02:00
haiyangcui
f3862e8385 格式化快捷键数据 2020-08-27 16:20:03 +02:00
haiyangcui
490d4839b7 没有更新的收藏跳过clearHasUpdateFlag 2020-08-27 16:06:46 +02:00
haiyangcui
033bcb3df7 修复下载功能 2020-08-27 15:52:56 +02:00
haiyangcui
df22efa980 导入收藏时不清空 2020-08-27 15:26:47 +02:00
haiyangcui
5428891297 删除多余信息 2020-08-27 15:14:03 +02:00
haiyangcui
bb3502774b 同步时更新收藏信息 2020-08-27 15:05:57 +02:00
haiyangcui
fa67ad03d2 恢复播放页面的收藏逻辑 2020-08-27 15:03:28 +02:00
haiyangcui
8a196ae52a 解决收藏项无法直接播放的bug 2020-08-27 14:48:11 +02:00
haiyangcui
ebfb681205 导出时先更新数据 2020-08-27 14:32:29 +02:00
haiyangcui
ba9f3246d2 Fix typo bug 2020-08-27 14:25:22 +02:00
haiyangcui
51cb181acd 改进播放页面的收藏功能 2020-08-27 13:58:05 +02:00
haiyangcui
33537638b0 更新version到2.4.3 2020-08-27 13:39:50 +02:00
haiyangcui
fa9f85a177 使用star.key 2020-08-27 13:33:21 +02:00
haiyangcui
3e1ecaac74 Fix bug in checkStar 2020-08-27 13:24:55 +02:00
haiyangcui
5cc9849e8a 导入收藏时升级数据格式 2020-08-27 13:13:45 +02:00
haiyangcui
e353b629c5 添加"更新收藏" 2020-08-27 13:08:41 +02:00
haiyangcui
4d20d5bc68 No need to record site info in star 2020-08-27 13:08:22 +02:00
haiyangcui
a921ce7adf 添加“清空所有收藏” 2020-08-27 12:39:54 +02:00
haiyangcui
1dd8d0dd24 在视图和列表里添加备注 2020-08-26 18:15:02 +02:00
haiyangcui
b90c359670 Reverse the list return by zy.list, to get video list from new to old 2020-08-26 16:24:57 +02:00
haiyangcui
27eb8c06ff 添加“最新”到class列表 2020-08-26 11:42:15 +02:00
haiyangcui
add2bedb61 v2.4.2 2020-08-25 16:59:59 +02:00
haiyangcui
3dc970e198 更新已存在的收藏 2020-08-25 15:54:07 +02:00
haiyangcui
3f2def4455 添加第三方播放器路径设置 2020-08-25 14:38:46 +02:00
haiyangcui
5b8d883af4 Format JSON string 2020-08-25 13:48:33 +02:00
haiyangcui
7113567475 可以使用PotPlayer打开视频 2020-08-25 00:31:34 +02:00
haiyangcui
e30da35e72 Import sites from file 2020-08-24 13:10:30 +02:00
haiyangcui
55b7396a2e Export sites to file 2020-08-24 12:54:31 +02:00
haiyangcui
83568fa499 No need to copy to clipboard 2020-08-24 12:32:51 +02:00
haiyangcui
838d3a64cd v2.4.1 2020-08-23 17:48:10 +02:00
haiyangcui
506be03e3e 从文件中导入收藏 2020-08-23 17:45:19 +02:00
haiyangcui
cfdb561473 加入取消操作识别 2020-08-23 15:03:47 +02:00
haiyangcui
ab2f45189f 导出收藏到文件 2020-08-23 14:33:59 +02:00
haiyangcui
f4b9a73b18 Simple favorites export import 2020-08-22 17:55:10 +02:00
haiyangcui
6f99d789f4 2.4.0 2020-08-22 16:08:38 +02:00
haiyangcui
e71090dad2 Wrap the items in Settings theme-box 2020-08-22 15:54:39 +02:00
haiyangcui
afe3351837 Add left margin to name 2020-08-22 15:35:28 +02:00
haiyangcui
5dc0613aeb 列表自适应 2020-08-22 15:06:10 +02:00
haiyangcui
1c6385ae6c 使用v-show显示隐藏class element,这样可以保持其占位 2020-08-22 10:58:47 +02:00
haiyangcui
88a95a8bfb 保存搜索选项设置 2020-08-22 00:20:18 +02:00
haiyangcui
951e6ffa37 更好的修复bug#183 2020-08-21 23:49:00 +02:00
haiyangcui
b83aed0a97 解决bug #183 2020-08-21 23:36:17 +02:00
haiyangcui
67ce537039 Add search style 2020-08-21 23:00:21 +02:00
haiyangcui
975562a66b Fix a bug in Detail.vue 2020-08-19 23:31:20 +02:00
haiyangcui
8e7015c9d6 调整"搜索所有资源"位置 2020-08-19 23:09:42 +02:00
haiyangcui
b841552dc7 转移"搜索所有资源"到设置页面 2020-08-19 18:07:37 +02:00
haiyangcui
0a9b939dc3 v2.3.8 2020-08-19 11:36:08 +02:00
haiyangcui
14ff252cce 定义--highlight-color 2020-08-19 08:30:18 +02:00
haiyangcui
afd016be93 取消“搜索所有资源”的边框 2020-08-18 23:00:05 +02:00
haiyangcui
9cd7f7d267 高亮有更新的收藏项 2020-08-18 22:51:03 +02:00
haiyangcui
8a76ec4e87 Null check on site 2020-08-18 17:39:39 +02:00
haiyangcui
a00d965ce9 v2.3.7 2020-08-18 16:00:05 +02:00
Hunlongyu
c9eb0c3e22 Merge pull request #178 from cuiocean/master
添加搜索所有资源功能
2020-08-18 16:13:08 +08:00
haiyangcui
c80c0bd948 Video中添加site信息 2020-08-16 23:02:18 +02:00
haiyangcui
65dcfa04ae 在star中添加key信息 2020-08-16 22:58:12 +02:00
haiyangcui
4e4ab82ea2 添加site信息在Detail里 2020-08-16 22:30:54 +02:00
haiyangcui
dd2adf4f99 更新格式 2020-08-15 16:44:43 +02:00
haiyangcui
777fbde528 在历史界面显示资源网站名,而不是资源标识 2020-08-15 16:30:09 +02:00
haiyangcui
472898f978 Fix downloadEvent 2020-08-15 12:27:44 +02:00
haiyangcui
7f41d415f1 删除searchSingleSiteEvent 2020-08-15 12:18:40 +02:00
haiyangcui
5d37a36c21 添加简单的style 2020-08-15 11:23:19 +02:00
haiyangcui
3a499766af playEvent starEvent shareEvent downloadEvent all take site as the input argument 2020-08-15 10:56:48 +02:00
haiyangcui
cea797b6d0 Save whole site data to res.site 2020-08-14 21:55:41 +02:00
haiyangcui
37b41b0e20 无搜索结果时,提示资源网站名字 2020-08-14 19:41:00 +02:00
haiyangcui
1105c46f08 添加“搜索所有资源”checkbox 2020-08-14 19:29:39 +02:00
haiyangcui
1df7b41edd 搜索结果显示资源网名字 2020-08-13 22:04:50 +02:00
haiyangcui
f76c71c950 Merge searchClickEvent and searchEvent 2020-08-13 21:46:23 +02:00
Hunlongyu
cd77384bd9 😄 v2.3.6 😆 2020-08-12 15:47:33 +08:00
Hunlongyu
64adc9d6c1 Merge pull request #170 from cuiocean/master
添加"备注"到搜索结果
2020-08-12 15:46:16 +08:00
haiyangcui
e9095e50a6 添加"备注"到搜索结果 2020-08-06 08:51:39 +02:00
Hunlongyu
acfa742b4f 👮‍♂️ v2.3.5 👨‍🚀 2020-08-06 10:15:34 +08:00
Hunlongyu
a79c48cba0 👨‍🚒👩‍🚒 v2.3.4 👨‍🚀👩‍🚀 2020-08-06 10:13:51 +08:00
Hunlongyu
904ef5ccea modify css 2020-08-06 09:58:39 +08:00
Hunlongyu
3db281c87f Merge pull request #168 from cuiocean/master
添加"备注"列到收藏试图
2020-08-06 09:57:29 +08:00
haiyangcui
076e6e99e4 当有更新时,刷新list列表 2020-08-05 17:06:17 +02:00
haiyangcui
32f6ac0310 添加"备注"列到收藏试图 2020-08-05 16:39:46 +02:00
haiyangcui
b8706ea432 添加播放视频点击事件到历史记录里的片名 2020-08-05 16:27:11 +02:00
haiyangcui
509d6c7900 添加"备注"列到收藏试图 2020-08-05 16:25:45 +02:00
Hunlongyu
51454d828c 🐱💻 v2.3.3 🐱🐉 2020-08-05 11:37:37 +08:00
23 changed files with 1140 additions and 431 deletions

196
README.md
View File

@@ -21,160 +21,72 @@
<img src="https://img.shields.io/github/workflow/status/Hunlongyu/ZY-Player/release-build?style=for-the-badge">
<p>
# ZY Player 资源播放器
# 目录
- [特性](#特性)
- [重要](#重要)
- [快捷键](#快捷键)
- [下载](#下载)
- [赞助](#赞助)
- [截图](#截图)
- [开发向导](#开发向导)
- [准备环境](#准备环境)
- [nodejs安装](#nodejs安装)
- [yarn新一代包管理器安利](#yarn新一代包管理器安利)
- [npm配置](#npm配置)
- [翻墙代理设置](#翻墙代理设置)
- [镜像源设置](#镜像源设置)
- [代码IDE](#代码IDE)
- [安装依赖](#安装依赖)
- [调试开发](#调试开发)
- [打包发布](#打包发布)
<h1 align="center">ZY Player 资源播放器</h1>
## 特性
1. 全平台支持. Windows, Mac, Linux
2. 支持更多视频源,支持自定义视频源
3. 支持海报模式和列表模式
4. 支持瀑布流 无限加载
5. 支持历史播放记录,自动跳转播放进度
6. 支持快捷键,使用更便捷,支持自定义快捷键
7. 支持搜索历史记录
8. 支持分享功能
9. 支持精简模式,划水新高度
10. 支持导出资源下载链接
11. 收藏夹同步功能,追剧更方便
12. 自动更新
觉得软件不错的, 点击右上角 star 收藏关注一波呀~
### ✨特性
## 重要
从 V1.x 版本升级到 V2.x 版本, 需要重置软件, 由于数据库改动较大, 重置后收藏的资源会丢失. 重置功能在设置界面里.
- 🍕 全平台支持. Windows, Mac, Linux
- 🍔 视频源支持自定义, 支持导入, 导出
- 🍟 支持海报模式和列表模式浏览资源
- 🌭 播放历史, 自动跳转历史进度
- 🍿 收藏夹支持导入,导出, 支持同步追剧
- 🥙 支持精简模式, 摸鱼划水
- 🥪 一键搜索所有资源, 支持历史搜索记录
- 🌮 导出资源下载链接
- 🍣 支持第三方播放器播放
- 🍤 显示豆瓣评分
#### 快捷键
### 🌴 下载
- 🍓 [Github -- 官方下载](https://github.com/Hunlongyu/ZY-Player/releases)
- 🍉 [蓝奏云 -- 快速下载](https://www.lanzoux.com/b04s6a3re) 密码:95px
- 🍒 适用于32位操作系统的x86软件,在蓝奏云网盘里, 后缀名: ZY Player * 32位.exe
### 🚀 快捷键
播放窗口 和 Mini窗口
| 快捷键 | 说明 | 快捷键 | 说明 |
| :----------------------: | ---------- | :----------------------: | ---------- |
| `Alt + Space` | 聚焦或取消聚焦(全局快捷键)| | |
| `→` | 快进 5 秒 | `←` | 快退 5 秒 |
| `↑` | 音量调高 | `↓` | 音量调低 |
| `m` | 静音 | `t` | 置顶或退出置顶 |
| `f` | 进入或退出全屏 | `esc` | 退出全屏 |
| `Alt + →` | 下一集 | `Alt + ←` | 上一集 |
| `Alt + ↑` | 透明度调高 | `Alt + ↓` | 透明度调低 |
| `home` | 跳到视频开始位置 | `end` | 跳到视频结束位置 |
| `pgUp` | 播放倍速加快 0.25 | `pgDown` | 播放倍速减慢 0.25 |
| `Alt + m` | 进入或退出 Mini 模式 | `space` | 播放或暂停 |
| 快捷键 | 说明 | 快捷键 | 说明 |
| :-----------: | ---------------------------- | :-------: | ----------------- |
| `Alt + Space` | 聚焦或取消聚焦(全局快捷键) | | |
| `→` | 快进 5 秒 | `←` | 快退 5 秒 |
| `↑` | 音量调高 | `↓` | 音量调低 |
| `m` | 静音 | `t` | 置顶或退出置顶 |
| `f` | 进入或退出全屏 | `esc` | 退出全屏 |
| `Alt + →` | 下一集 | `Alt + ←` | 上一集 |
| `Alt + ↑` | 透明度调高 | `Alt + ↓` | 透明度调低 |
| `home` | 跳到视频开始位置 | `end` | 跳到视频结束位置 |
| `pgUp` | 播放倍速加快 0.25 | `pgDown` | 播放倍速减慢 0.25 |
| `Alt + m` | 进入或退出 Mini 模式 | `space` | 播放或暂停 |
#### 下载:
### 🎨 截图
1. [Github -- 官方下载](https://github.com/Hunlongyu/ZY-Player/releases)
| 🥼 浏览 (海报模式) | 🧥 浏览 (列表模式) |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
| ![海报.png](https://i.loli.net/2020/09/02/ZAfGjcqLxoslpWQ.png) | ![列表.png](https://i.loli.net/2020/09/02/jrEkX3yiOGPFazs.png) |
| 👔 搜索 | 👕 详情 |
| ![搜索.png](https://i.loli.net/2020/09/02/HdMos8gent4kTmW.png) | ![详情.png](https://i.loli.net/2020/09/02/S2Np4GAmBz8Rj6P.png) |
| 👖 播放 (普通模式) | 🩳 播放 (精简模式) |
| ![播放.png](https://i.loli.net/2020/09/02/RLBoaZyuS2DCkJ3.png) | ![精简.png](https://i.loli.net/2020/09/02/f21SNdiVFHmeh6b.png) |
| 🧣 历史记录 | 🧤 收藏夹 |
| ![历史.png](https://i.loli.net/2020/09/02/ZhNXatyJi9Dvr3d.png) | ![收藏.png](https://i.loli.net/2020/09/02/wy4H76m2sQ8YdKi.png) |
| 👗 白色主题 | 🥻 黑色主题 |
| ![白色.png](https://i.loli.net/2020/09/02/gslBIYvTaSZRwfU.png) | ![黑色.png](https://i.loli.net/2020/09/02/dMmETUq1ACuGsI6.png) |
| 👘 绿色主题 | 👚 粉色主题 |
| ![绿色.png](https://i.loli.net/2020/09/02/nxJF71b3qusUclZ.png) | ![粉色.png](https://i.loli.net/2020/09/02/8rGL45p6kSqRCOz.png) |
2. [蓝奏云 -- 快速下载](https://www.lanzous.com/b04s6a3re) 密码:95px
### 🍭 开发者
3. 适用于32位操作系统的x86软件,在蓝奏云网盘里, 后缀名: ZY Player * 32位.exe
| [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" /> |
| 💻 🎨 🐛 | 💻 🐛 |
### 🧧 赞助
#### 赞助
[![LATOPAY](https://latopay.com/w/lt-bg-2062.png)](https://latopay.com/@Hunlongyu)
#### 截图:
1. 浏览 ⇣ ↓
![海报.png](https://i.loli.net/2020/07/22/QDRqv9X1uWbVcpi.png)
![列表.png](https://i.loli.net/2020/07/22/1Rnm6QDyPYWw9ec.png)
2. 搜索 ⇣ ↓
![搜索.png](https://i.loli.net/2020/07/22/qFdyYfc7iNxZXKT.png)
3. 详情 ⇣ ↓
![详情.png](https://i.loli.net/2020/07/22/aPUwFHSXYKTn3dC.png)
4. 播放 ⇣ ↓
![播放.png](https://i.loli.net/2020/07/22/xhWCRlOFKw52Vzi.png)
![Mini.png](https://i.loli.net/2020/07/22/suf4bQkoP3gMORH.png)
5. 收藏 ⇣ ↓
![收藏.png](https://i.loli.net/2020/07/22/32THQIEqdo8YyS6.png)
6. 白色主题皮肤 ⇣ ↓
![白色.png](https://i.loli.net/2020/07/22/ci9oAXC2SE4gqka.png)
7. 绿色主题皮肤 ⇣ ↓
![绿色.png](https://i.loli.net/2020/07/22/WOfDG5P8CR2cXju.png)
8. 粉色色主题皮肤 ⇣ ↓
![粉色.png](https://i.loli.net/2020/07/22/48euCatIfnZ6qX7.png)
### 重要:
所有资源来自网上, 该软件不参与任何制作, 上传, 储存, 下载等内容. 该软件仅供学习参考, 请于安装后24小时内删除.
## 开发指导
软件基于nodejs, vue, electron, 如果想成为一个有追求的码农你先考虑一下自己是否具备上述几个关键词的知识储备如果没有建议先百度了解下。这里的开发环境基于linux发行版mint, 其他大同小异,自行发挥。
### 准备环境
#### nodejs安装
* [LINK1](https://nodejs.org/zh-cn/)
* [LINK2](https://www.jianshu.com/p/13f45e24b1de/)
#### yarn新一代包管理器安利
最近管理员用[yarn](https://yarn.bootcss.com/)替换了npm本人测试后表示非常好用。。连翻墙都不想要了强烈推荐, 安装好使用yarn代理npm命令即可, [官网安装向导](https://yarn.bootcss.com/docs/install/)
#### npm配置
天朝的网络环境都有耳闻,想顺利开发,要么翻墙,要么镜像源,以下按照自己的水平二选一
#### 翻墙代理设置
自备http代理sock5代理转换参考privoxy
```bash
设置代理
npm config set proxy=http://127.0.0.1:8087
npm config set registry=http://registry.npmjs.org
关于https
经过上面设置使用了http开头的源因此不需要设https_proxy了否则还要增加一句:
npm config set https-proxy http://server:port
代理用户名和密码
npm config set proxy http://username:password@server:port
npm confit set https-proxy http://username:password@server:port
取消代理
npm config delete proxy
npm config delete https-proxy
```
#### 镜像源设置
这里使用阿里的npm镜像
```bash
npm install -g cnpm --registry=https://registry.npm.taobao.org
```
搞定后使用cnpm代替所有的npm命令即可切记使用一致切记切记
* [参考网页链接](https://developer.aliyun.com/mirror/NPM?from=tnpm)
### 代码IDE
随心所欲吧这里安利vscode
### 安装依赖
```bash
npm install
or
cnpm install
```
### 调试开发
```bash
npm run dev
```
### 打包发布
```
npm run electron:build
```

View File

@@ -1,6 +1,6 @@
{
"name": "zy",
"version": "2.3.2",
"version": "2.4.7",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -18,6 +18,8 @@
"main": "background.js",
"dependencies": {
"axios": "^0.19.2",
"cheerio": "^1.0.0-rc.3",
"child_process": "^1.0.2",
"core-js": "^3.6.5",
"cors": "^2.8.5",
"dexie": "^3.0.1",
@@ -29,9 +31,11 @@
"modern-normalize": "^0.6.0",
"mousetrap": "^1.6.5",
"qrcode.vue": "^1.7.0",
"randomstring": "^1.1.5",
"vue": "^2.6.11",
"vue-infinite-loading": "^2.4.5",
"vue-waterfall-plugin": "^1.0.7",
"vuedraggable": "^2.24.1",
"vuex": "^3.4.0",
"xgplayer": "^2.9.10",
"xgplayer-hls.js": "^2.2.3"

View File

@@ -15,6 +15,9 @@
<transition name="slide">
<Share v-if="share.show"/>
</transition>
<transition name="slide">
<EditSites v-if="editSites.show"/>
</transition>
</div>
</template>
@@ -38,6 +41,9 @@ export default {
},
setting () {
return this.$store.getters.getSetting
},
editSites () {
return this.$store.getters.getEditSites
}
},
watch: {

View File

@@ -78,7 +78,20 @@
}
}
}
.zy-checkbox{
position: relative;
display: inline-block;
width: 200px;
height: 30px;
vertical-align: middle;
align-items: center;
.search-all-check-input{
cursor: pointer;
}
}
.zy-highlighted{
color: var(--highlight-color);
}
// table
.zy-table{
display: flex;
@@ -118,41 +131,42 @@
cursor: pointer;
span{
display: flex;
width: 180px;
font-size: 13px;
height: 50px;
line-height: 50px;
overflow: hidden;
margin-right: 5px;
&.name{
flex: 1;
padding-left: 15px;
overflow: hidden;
text-overflow: ellipsis;
min-width: 100px;
white-space: nowrap;
}
&.note{
width: 180px;
margin-left: 10px;
}
&.type{
width: 120px;
}
&.last{
width: 160px;
width: 10%;
}
&.time{
width: 60px;
width: 10%;
}
&.from{
width: 120px;
&.last{
width: 10%;
}
&.site{
width: 10%;
}
&.note{
width: 10%;
}
&.operate{
width: 170px;
.btn{
width: 40px;
}
}
}
}
}
}
}
// scroll
.zy-scroll{
&::-webkit-scrollbar{
@@ -168,7 +182,6 @@
position: absolute;
}
}
// loading
.zy-loading{
width: 100%;

View File

@@ -1,4 +1,6 @@
:root{
// general
--highlight-color: #38dd77;
// light
--l-c-0: #823aa0;
--l-c-1: #823aa011;

View File

@@ -32,6 +32,10 @@
}
}
}
.zy-checkbox{
color: var(--d-fc-1);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
}
.zy-table{
color: var(--d-fc-2);
.tHeader{
@@ -286,7 +290,7 @@
}
}
}
.view, .shortcut, .site{
.view, .search, .shortcut, .site{
.title{
color: var(--d-fc-1);
}

View File

@@ -32,6 +32,9 @@
}
}
}
.zy-checkbox{
color: var(--g-fc-1);
}
.zy-table{
color: var(--g-fc-2);
.tHeader{
@@ -286,7 +289,7 @@
}
}
}
.view, .shortcut, .site{
.view, .search, .shortcut, .site{
.title{
color: var(--g-fc-1);
}
@@ -336,4 +339,8 @@
background-color: var(--g-bgc-1);
}
}
.history{
background-color: var(--g-bgc-1);
box-shadow: var(--g-bsc);
}
}

View File

@@ -32,6 +32,9 @@
}
}
}
.zy-checkbox{
color: var(--l-fc-1);
}
.zy-table{
color: var(--l-fc-2);
.tHeader{
@@ -286,7 +289,7 @@
}
}
}
.view, .shortcut, .site{
.view, .search, .shortcut, .site{
.title{
color: var(--l-fc-1);
}
@@ -336,4 +339,8 @@
background-color: var(--l-bgc-1);
}
}
.history{
background-color: var(--l-bgc-1);
box-shadow: var(--l-bsc);
}
}

View File

@@ -32,6 +32,9 @@
}
}
}
.zy-checkbox{
color: var(--p-fc-1);
}
.zy-table{
color: var(--p-fc-2);
.tHeader{
@@ -286,7 +289,7 @@
}
}
}
.view, .shortcut, .site{
.view, .search, .shortcut, .site{
.title{
color: var(--p-fc-1);
}
@@ -336,4 +339,8 @@
background-color: var(--p-bgc-1);
}
}
.history{
background-color: var(--p-bgc-1);
box-shadow: var(--p-bsc);
}
}

View File

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

View File

@@ -25,6 +25,7 @@
<div class="year" v-show="info.year">上映: {{info.year}}</div>
<div class="last" v-show="info.last">更新: {{info.last}}</div>
<div class="note" v-show="info.note">备注: {{info.note}}</div>
<div class="rate" v-show="info.rate">豆瓣评分: {{info.rate}}</div>
</div>
</div>
<div class="operate">
@@ -32,6 +33,7 @@
<span @click="starEvent">收藏</span>
<span @click="downloadEvent">下载</span>
<span @click="shareEvent">分享</span>
<span @click="doubanLinkEvent">豆瓣</span>
</div>
<div class="desc" v-show="info.des">{{info.des}}</div>
<div class="m3u8">
@@ -121,9 +123,9 @@ export default {
playEvent (n) {
history.find({ site: this.detail.key, ids: this.detail.info.id }).then(res => {
if (res) {
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: n } }
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: n, site: this.detail.site } }
} else {
this.video = { key: this.detail.key, info: { id: this.detail.info.id, name: this.detail.info.name, index: n } }
this.video = { key: this.detail.key, info: { id: this.detail.info.id, name: this.detail.info.name, index: n, site: this.detail.site } }
}
})
@@ -131,17 +133,18 @@ export default {
this.detail.show = false
},
starEvent () {
star.find({ site: this.detail.key, ids: this.info.id }).then(res => {
star.find({ key: this.detail.key, ids: this.info.id }).then(res => {
if (res) {
this.$message.info('已存在')
this.$message.info('该影片已被收藏')
} else {
const docs = {
site: this.detail.key,
key: this.detail.key,
ids: this.info.id,
name: this.info.name,
type: this.info.type,
year: this.info.year,
last: this.info.last
last: this.info.last,
note: this.info.note
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
@@ -153,7 +156,7 @@ export default {
},
downloadEvent () {
zy.download(this.detail.key, this.info.id).then(res => {
if (res) {
if (res && res.dl && res.dl.dd) {
const text = res.dl.dd._t
if (text) {
const list = text.split('#')
@@ -186,12 +189,72 @@ export default {
info: this.detail.info
}
},
doubanLinkEvent () {
const open = require('open')
const axios = require('axios')
const cheerio = require('cheerio')
const name = this.detail.info.name.trim()
// 豆瓣搜索链接
var doubanSearchLink = 'https://www.douban.com/search?q=' + name
var link = doubanSearchLink
axios.get(doubanSearchLink).then(res => {
const $ = cheerio.load(res.data)
// 比较第一和第二豆瓣搜索结果, 如果名字相符, 就打开该链接,否则打开搜索页面
var nameInDouban = $($('div.result')[0]).find('div>div>h3>a').first()
if (name.replace(/\s/g, '') === nameInDouban.text().replace(/\s/g, '')) {
link = nameInDouban.attr('href')
} else {
nameInDouban = $($('div.result')[1]).find('div>div>h3>a').first()
if (name.replace(/\s/g, '') === nameInDouban.text().replace(/\s/g, '')) {
link = nameInDouban.attr('href')
}
}
open(link)
})
},
getDoubanRate () {
const axios = require('axios')
const cheerio = require('cheerio')
const name = this.detail.info.name.trim()
// 豆瓣搜索链接
var doubanSearchLink = 'https://www.douban.com/search?q=' + name
axios.get(doubanSearchLink).then(res => {
const $ = cheerio.load(res.data)
// 比较第一和第二给豆瓣搜索结果, 看名字是否相符
var link = ''
var nameInDouban = $($('div.result')[0]).find('div>div>h3>a').first()
if (name.replace(/\s/g, '') === nameInDouban.text().replace(/\s/g, '')) {
link = nameInDouban.attr('href')
} else {
nameInDouban = $($('div.result')[1]).find('div>div>h3>a').first()
if (name.replace(/\s/g, '') === nameInDouban.text().replace(/\s/g, '')) {
link = nameInDouban.attr('href')
}
}
// 如果找到链接,就打开该链接获取评分
if (link) {
axios.get(link).then(response => {
const parsedHtml = cheerio.load(response.data)
var rating = parsedHtml('body').find('#interest_sectl').first().find('strong').first()
if (rating.text()) {
this.info.rate = rating.text()
} else {
this.info.rate = '暂无评分'
}
})
} else {
this.info.rate = '暂无评分'
}
})
},
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.m3u8Parse(res)
this.getDoubanRate()
this.loading = false
}
})
@@ -205,9 +268,10 @@ export default {
<style lang="scss" scoped>
.detail{
position: absolute;
left: 0;
left: 80px;
right: 20px;
bottom: 0;
width: 100%;
width: calc(100% - 100px);
height: calc(100% - 40px);
z-index: 888;
.detail-content{
@@ -262,6 +326,11 @@ export default {
font-size: 14px;
line-height: 26px;
}
.rate{
font-size: 16px;
line-height: 26px;
font-weight: bolder;
}
}
}
.operate{

View File

@@ -0,0 +1,200 @@
<template>
<div class="detail">
<div class="detail-content">
<div class="detail-header">
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="openAddSite">添加新源</div>
</div>
<span class="detail-close zy-svg" @click="close">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="closeIconTitle">
<title id="closeIconTitle">关闭</title>
<path d="M6.34314575 6.34314575L17.6568542 17.6568542M6.34314575 17.6568542L17.6568542 6.34314575"></path>
</svg>
</span>
</div>
<div class="detail-body zy-scroll">
<div class="zy-table">
<div class="tBody zy-scroll">
<div class="addSites-box zy-scroll" v-show="showAddSite">
<ul>
<li >
<span class="name">源名称</span>
<span class="name">API接口</span>
<span class="name">DOWNLOAD接口</span>
<span class="operate">
<span class="btn"></span>
<span class="btn"></span>
</span>
</li>
<li>
<span class="name" style="display:inline-block;vertical-align:middle">
<input style="height: 30px" v-model="newSite.name">
</span>
<span class="name" style="display:inline-block;vertical-align:middle">
<input style="height: 30px" v-model="newSite.api">
</span>
<span class="name" style="display:inline-block;vertical-align:middle">
<input style="height: 30px" v-model="newSite.download" placeholder="可以为空">
</span>
<span class="operate">
<span class="btn" @click="addNewSite">添加</span>
<span class="btn" @click="closeAddSite">关闭</span>
</span>
</li>
<li ></li>
</ul>
</div>
<ul>
<draggable v-model="sites" @change="listUpdatedEvent">
<transition-group>
<li v-for="(i, j) in sites" :key="j">
<span class="name">{{i.name}}</span>
<span class="operate">
<span class="btn" @click.stop="removeEvent(i)">删除</span>
</span>
</li>
</transition-group>
</draggable>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { sites } from '../lib/dexie'
import draggable from 'vuedraggable'
export default {
name: 'editSites',
data () {
return {
show: false,
sites: [],
showAddSite: false,
newSite: {
name: '',
api: '',
download: ''
}
}
},
components: {
draggable
},
computed: {
setting: {
get () {
return this.$store.getters.getSetting
},
set (val) {
this.SET_SETTING(val)
}
},
editSites: {
get () {
return this.$store.getters.getEditSites
},
set (val) {
this.SET_EDITSITES(val)
}
}
},
methods: {
...mapMutations(['SET_SETTING', 'SET_EDITSITES']),
close () {
this.editSites.show = false
},
getSites () {
sites.all().then(res => {
this.sites = res
})
},
removeEvent (e) {
sites.remove(e.id).then(res => {
this.getSites()
}).catch(err => {
this.$message.warning('删除源失败, 错误信息: ' + err)
})
},
listUpdatedEvent () {
sites.clear().then(res1 => {
// 重新排序
var id = 1
this.sites.forEach(element => {
element.id = id
sites.add(element)
id += 1
})
})
},
openAddSite () {
this.showAddSite = true
},
closeAddSite () {
this.showAddSite = false
},
addNewSite () {
if (!this.newSite.name || !this.newSite.api) {
this.$message.error('名称和API接口不能为空。')
return
}
var randomstring = require('randomstring')
var doc = {
key: randomstring.generate(6),
id: this.sites[this.sites.length - 1].id + 1,
name: this.newSite.name,
api: this.newSite.api,
download: this.newSite.download
}
sites.add(doc).then(res => {
this.newSite = {
name: '',
api: '',
download: ''
}
this.$message.success('添加新源成功!')
this.getSites()
})
}
},
created () {
this.getSites()
}
}
</script>
<style lang="scss" scoped>
.detail{
position: absolute;
left: 80px;
right: 20px;
bottom: 0;
width: calc(100% - 100px);
height: calc(100% - 40px);
z-index: 888;
.detail-content{
height: calc(100% - 10px);
padding: 0 60px;
position: relative;
.detail-header{
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
.detail-title{
font-size: 16px;
}
.detail-close{
cursor: pointer;
}
}
}
.detail-body{
height: calc(100% - 50px);
overflow-y: auto;
}
}
</style>

View File

@@ -9,7 +9,7 @@
</ul>
</div>
</div>
<div class="zy-select" @mouseleave="show.classList = false" v-if="show.class">
<div class="zy-select" @mouseleave="show.classList = false" v-show="show.class">
<div class="vs-placeholder" @click="show.classList = true">{{type.name}}</div>
<div class="vs-options" v-show="show.classList">
<ul class="zy-scroll" style="max-height: 600px;">
@@ -18,10 +18,10 @@
</div>
</div>
<div class="zy-select" @mouseleave="show.search = false">
<div class="vs-input" @click="show.search = true"><input v-model.trim="searchTxt" type="text" placeholder="搜索" @keyup.enter="searchEvent"></div>
<div class="vs-input" @click="show.search = true"><input v-model.trim="searchTxt" type="text" placeholder="搜索" @keyup.enter="searchEvent(searchTxt)"></div>
<div class="vs-options" v-show="show.search">
<ul class="zy-scroll" style="max-height: 600px">
<li v-for="(i, j) in searchList" :key="j" @click="searchClickEvent(i)">{{i.keywords}}</li>
<li v-for="(i, j) in searchList" :key="j" @click="searchEvent(i.keywords)">{{i.keywords}}</li>
<li v-show="searchList.length >= 1" @click="clearSearch">清空历史记录</li>
</ul>
</div>
@@ -35,20 +35,21 @@
animationEffect="fadeInUp"
backgroundColor="rgba(0, 0, 0, 0)">
<template slot="item" slot-scope="props">
<div class="card">
<div class="card" v-show="!setting.excludeR18Films || !containsR18Keywords(props.data.type)">
<div class="img">
<img style="width: 100%" :src="props.data.pic" alt="" @load="$refs.waterfall.refresh()" @click="detailEvent(props.data)">
<img style="width: 100%" :src="props.data.pic" alt="" @load="$refs.waterfall.refresh()" @click="detailEvent(site, props.data)">
<div class="operate">
<div class="operate-wrap">
<span class="o-play" @click="playEvent(props.data)">播放</span>
<span class="o-star" @click="starEvent(props.data)">收藏</span>
<span class="o-share" @click="shareEvent(props.data)">分享</span>
<span class="o-play" @click="playEvent(site, props.data)">播放</span>
<span class="o-star" @click="starEvent(site, props.data)">收藏</span>
<span class="o-share" @click="shareEvent(site, props.data)">分享</span>
</div>
</div>
</div>
<div class="name" @click="detailEvent(props.data)">{{props.data.name}}</div>
<div class="name" @click="detailEvent(site, props.data)">{{props.data.name}}</div>
<div class="info">
<span>{{props.data.year}}</span>
<span>{{props.data.note}}</span>
<span>{{props.data.type}}</span>
</div>
</div>
@@ -60,16 +61,17 @@
<div class="zy-table">
<div class="tBody">
<ul>
<li v-for="(i, j) in list" :key="j" @click="detailEvent(i)">
<li v-for="(i, j) in list" :key="j" @click="detailEvent(site, i)" v-show="!setting.excludeR18Films || !containsR18Keywords(i.type)">
<span class="name">{{i.name}}</span>
<span class="type">{{i.type}}</span>
<span class="time">{{i.year}}</span>
<span class="time">{{i.note}}</span>
<span class="last">{{i.last}}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(i)">播放</span>
<span class="btn" @click.stop="starEvent(i)">收藏</span>
<span class="btn" @click.stop="shareEvent(i)">分享</span>
<span class="btn" @click.stop="downloadEvent(i)">下载</span>
<span class="btn" @click.stop="playEvent(site, i)">播放</span>
<span class="btn" @click.stop="starEvent(site, i)">收藏</span>
<span class="btn" @click.stop="shareEvent(site, i)">分享</span>
<span class="btn" @click.stop="downloadEvent(site, i)">下载</span>
</span>
</li>
</ul>
@@ -81,18 +83,19 @@
<div class="body-box" v-show="show.find">
<div class="show-table">
<div class="zy-table">
<div class="tBody">
<div class="tBody zy-scroll">
<ul>
<li v-for="(i, j) in searchContents" :key="j" @click="detailEvent(i)">
<li v-for="(i, j) in searchContents" :key="j" @click="detailEvent(i.site, i)">
<span class="name">{{i.name}}</span>
<span class="type">{{i.type}}</span>
<span class="time">{{i.year}}</span>
<span class="last">{{i.last}}</span>
<span class="site">{{i.site.name}}</span>
<span class="note">{{i.note}}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(i)">播放</span>
<span class="btn" @click.stop="starEvent(i)">收藏</span>
<span class="btn" @click.stop="shareEvent(i)">分享</span>
<span class="btn" @click.stop="downloadEvent(i)">下载</span>
<span class="btn" @click.stop="playEvent(i.site, i)">播放</span>
<span class="btn" @click.stop="starEvent(i.site, i)">收藏</span>
<span class="btn" @click.stop="shareEvent(i.site, i)">分享</span>
<span class="btn" @click.stop="downloadEvent(i.site, i)">下载</span>
</span>
</li>
</ul>
@@ -133,7 +136,9 @@ export default {
infiniteId: +new Date(),
searchList: [],
searchTxt: '',
searchContents: []
searchContents: [],
// 福利片关键词
r18KeyWords: ['伦理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番']
}
},
components: {
@@ -184,11 +189,14 @@ export default {
searchTxt () {
this.searchChangeEvent()
},
'setting.site': {
'setting.sitesList': {
handler (nv) {
this.getAllsites(nv)
this.getAllsites()
},
deep: true
},
'$store.state.editSites.sites': function () {
this.getAllsites()
}
},
methods: {
@@ -199,7 +207,7 @@ export default {
this.show.site = false
this.show.class = false
if (this.searchTxt.length > 0) {
this.searchEvent()
this.searchSingleSiteEvent(this.site, this.searchTxt)
} else {
this.classList = []
this.type = {}
@@ -224,17 +232,39 @@ export default {
getClass () {
return new Promise((resolve, reject) => {
const key = this.site.key
// 屏蔽主分类
const classToHide = ['电影', '电影片', '电视剧', '连续剧', '综艺', '动漫']
zy.class(key).then(res => {
this.classList = res.class
var allClass = [{ name: '最新', tid: 0 }]
res.class.forEach(element => {
if (!classToHide.includes(element.name)) {
if (this.setting.excludeR18Films) {
const containKeyWord = this.containsR18Keywords(element.name)
if (!containKeyWord) {
allClass.push(element)
}
} else {
allClass.push(element)
}
}
})
this.classList = allClass
this.show.class = true
this.pagecount = res.pagecount
this.type = { name: '最新', tid: 0 }
this.type = this.classList[0]
resolve(true)
}).catch(err => {
reject(err)
})
})
},
containsR18Keywords (name) {
var containKeyWord = false
if (!name) {
return containKeyWord
}
return this.r18KeyWords.some(v => name.includes(v))
},
getPage () {
return new Promise((resolve, reject) => {
const key = this.site.key
@@ -264,7 +294,8 @@ export default {
$state.complete()
}
if (type === '[object Array]') {
this.list.push(...res)
// zy.list 返回的是按时间从旧到新排列, 我门需要翻转为从新到旧
this.list.push(...res.reverse())
}
if (type === '[object Object]') {
this.list.push(res)
@@ -275,35 +306,37 @@ export default {
}
})
},
detailEvent (e) {
detailEvent (site, e) {
this.detail = {
show: true,
key: this.site.key,
key: site.key,
site: site,
info: e
}
},
playEvent (e) {
history.find({ site: this.site.key, ids: e.id }).then(res => {
playEvent (site, e) {
history.find({ site: site.key, ids: e.id }).then(res => {
if (res) {
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: res.index } }
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: res.index, site: site } }
} else {
this.video = { key: this.site.key, info: { id: e.id, name: e.name, index: 0 } }
this.video = { key: site.key, info: { id: e.id, name: e.name, index: 0, site: site } }
}
})
this.view = 'Play'
},
starEvent (e) {
star.find({ site: this.site.key, ids: e.id }).then(res => {
starEvent (site, e) {
star.find({ key: site.key, ids: e.id }).then(res => {
if (res) {
this.$message.info('已存在')
} else {
const docs = {
site: this.site.key,
key: site.key,
ids: e.id,
name: e.name,
type: e.type,
year: e.year,
last: e.last
last: e.last,
note: e.note
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
@@ -313,16 +346,16 @@ export default {
this.$message.warning('收藏失败')
})
},
shareEvent (e) {
shareEvent (site, e) {
this.share = {
show: true,
key: this.site.key,
key: site.key,
info: e
}
},
downloadEvent (e) {
zy.download(this.site.key, e.id).then(res => {
if (res.length > 0) {
downloadEvent (site, e) {
zy.download(site.key, e.id).then(res => {
if (res && res.length > 0 && res.dl && res.dl.dd) {
const text = res.dl.dd._t
if (text) {
const list = text.split('#')
@@ -338,7 +371,7 @@ export default {
}
} else {
let m3u8List = []
const dd = e.dl.dd
const dd = res.dl.dd
const type = Object.prototype.toString.call(dd)
if (type === '[object Array]') {
for (const i of dd) {
@@ -376,8 +409,8 @@ export default {
this.searchList = res.reverse()
})
},
searchEvent () {
const wd = this.searchTxt
searchAllSitesEvent (sites, wd) {
this.searchTxt = wd
this.searchContents = []
this.pagecount = 0
this.show.search = false
@@ -389,18 +422,21 @@ export default {
}
this.getAllSearch()
})
zy.search(this.site.key, wd).then(res => {
const type = Object.prototype.toString.call(res)
if (type === '[object Undefined]') {
this.$message.info('无搜索结果')
}
if (type === '[object Array]') {
this.searchContents.push(...res)
}
if (type === '[object Object]') {
this.searchContents.push(res)
}
})
sites.forEach(site =>
zy.search(site.key, wd).then(res => {
const type = Object.prototype.toString.call(res)
if (type === '[object Array]') {
res.forEach(element => {
element.site = site
this.searchContents.push(element)
})
}
if (type === '[object Object]') {
res.site = site
this.searchContents.push(res)
}
})
)
} else {
this.show.find = false
this.getClass().then(res => {
@@ -410,28 +446,17 @@ export default {
})
}
},
searchClickEvent (e) {
this.searchContents = []
this.pagecount = 0
this.searchTxt = e.keywords
this.show.search = false
this.show.find = true
search.remove(e.id).then(res => {
search.add({ keywords: e.keywords })
this.getAllSearch()
})
zy.search(this.site.key, e.keywords).then(res => {
const type = Object.prototype.toString.call(res)
if (type === '[object Undefined]') {
this.$message.info('无搜索结果')
}
if (type === '[object Array]') {
this.searchContents.push(...res)
}
if (type === '[object Object]') {
this.searchContents.push(res)
}
})
searchEvent (wd) {
if (this.setting.searchAllSites) {
this.searchAllSitesEvent(this.sites, wd)
} else {
this.searchSingleSiteEvent(this.site, wd)
}
},
searchSingleSiteEvent (site, wd) {
var sites = []
sites.push(this.site)
this.searchAllSitesEvent(sites, wd)
},
clearSearch () {
search.clear().then(res => {

View File

@@ -8,12 +8,24 @@
<div class="tBody zy-scroll">
<ul>
<li v-show="this.history.length === 0">无数据</li>
<li v-show="this.history.length > 0">
<span class="name">名字</span>
<span class="site">片源</span>
<span class="note">观看至</span>
<span class="operate">
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
</span>
</li>
<li v-for="(i, j) in history" :key="j" @click="historyItemEvent(i)">
<span class="name">{{i.name}}</span>
<span class="site">{{i.site}}</span>
<span class="index">{{i.index+1}}</span>
<span class="operate" style="width: 220px">
<span class="name" @click.stop="detailEvent(i)">{{i.name}}</span>
<span class="site">{{getSiteName(i.site)}}</span>
<span class="note">{{i.index+1}}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(i)">播放</span>
<span class="btn" @click.stop="shareEvent(i)">分享</span>
<span class="btn" @click.stop="downloadEvent(i)">下载</span>
<span class="btn" @click.stop="removeHistoryItem(i)">删除</span>
</span>
@@ -26,14 +38,15 @@
</template>
<script>
import { mapMutations } from 'vuex'
import { history } from '../lib/dexie'
import { history, sites } from '../lib/dexie'
import zy from '../lib/site/tools'
const { clipboard } = require('electron')
export default {
name: 'history',
data () {
return {
history: history
history: history,
sites: []
}
},
computed: {
@@ -73,6 +86,7 @@ export default {
watch: {
view () {
this.getAllhistory()
this.getAllsites()
}
},
methods: {
@@ -97,9 +111,16 @@ export default {
})
this.view = 'Play'
},
shareEvent (e) {
this.share = {
show: true,
key: e.site,
info: e
}
},
downloadEvent (e) {
zy.download(e.site, e.ids).then(res => {
if (res) {
if (res && res.dl && res.dl.dd) {
const text = res.dl.dd._t
if (text) {
const list = text.split('#')
@@ -114,14 +135,28 @@ export default {
this.$message.warning('没有查询到下载链接.')
}
} else {
const list = [...this.m3u8List]
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
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』格式的链接已复制, 快去下载吧!')
})
}
})
},
@@ -135,6 +170,17 @@ export default {
this.history = res.reverse()
})
},
getAllsites () {
sites.all().then(res => {
this.sites = res
})
},
getSiteName (key) {
var site = this.sites.find(e => e.key === key)
if (site) {
return site.name
}
},
historyItemEvent (e) {
this.video = {
key: e.site,

View File

@@ -3,6 +3,12 @@
<div class="box">
<div class="title">
<span v-if="this.right.list.length > 1"> {{(video.info.index + 1)}} </span>{{name}}
<span v-if="video.key" class="right" @click="playWithExternalPalyerEvent" title="使用第三方播放器">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<polygon points="20 8 20 20 4 20 4 8"></polygon>
<polyline stroke-linejoin="round" points="8 4 12 7.917 16 4"></polyline>
</svg>
</span>
<span v-if="video.key" class="right" @click="issueEvent" title="复制调试信息">
<svg t="1596338860607" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3127" width="24" height="24">
<path d="M503.803829 63.578014c-247.050676 0-447.328072 200.277396-447.328072 447.327048 0 247.054769 200.277396 447.333188 447.328072 447.333188 247.054769 0 447.332165-200.278419 447.332165-447.333188C951.13497 263.85541 750.858598 63.578014 503.803829 63.578014L503.803829 63.578014zM503.803829 894.313336c-211.749682 0-383.408273-171.659615-383.408273-383.408273 0-211.749682 171.659615-383.40725 383.408273-383.40725 211.753775 0 383.412366 171.658591 383.412366 383.40725C887.216195 722.653721 715.557604 894.313336 503.803829 894.313336L503.803829 894.313336zM447.745069 255.897158l127.914298 0L575.659367 383.576095 447.745069 383.576095 447.745069 255.897158 447.745069 255.897158zM447.745069 425.470251l127.914298 0 0 342.058516L447.745069 767.528767 447.745069 425.470251 447.745069 425.470251zM447.745069 425.470251" p-id="3128"></path>
@@ -378,7 +384,6 @@ export default {
history.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
const doc = {
id: res.id,
site: res.site,
ids: res.ids,
name: res.name,
@@ -387,7 +392,8 @@ export default {
index: this.video.info.index,
time: res.time
}
history.update(res.id, doc)
history.remove(res.id)
history.add(doc)
} else {
const doc = {
site: this.video.key,
@@ -401,6 +407,7 @@ export default {
history.add(doc)
}
})
this.updateStar()
this.timerEvent()
},
changeVideo () {
@@ -459,24 +466,34 @@ export default {
this.right.history = res.reverse()
})
},
updateStar () {
const info = this.video.info
star.find({ key: this.video.key, ids: info.id }).then(res => {
if (res) {
res.index = info.index
star.update(res.id, res)
}
}).catch(() => {
this.$message.warning('检查收藏失败')
})
},
starEvent () {
const info = this.video.info
star.find({ site: this.video.key, ids: info.id }).then(res => {
star.find({ key: this.video.key, ids: info.id }).then(res => {
const doc = {
key: this.video.key,
ids: info.id,
name: info.name,
type: info.type,
year: info.year,
last: info.last,
note: info.note,
index: info.index
}
if (res) {
star.remove(res.id).then(e => {
this.$message.info('取消收藏')
this.isStar = false
})
star.update(res.id, doc)
} else {
const docs = {
site: this.video.key,
ids: info.id,
name: info.name,
type: info.type,
year: info.year,
last: info.last
}
star.add(docs).then(res => {
star.add(doc).then(starRes => {
this.$message.success('收藏成功')
this.isStar = true
})
@@ -536,8 +553,47 @@ export default {
clipboard.writeText(JSON.stringify(info, null, 4))
this.$message.success('视频信息复制成功')
},
playWithExternalPalyerEvent () {
this.fetchM3u8List().then(m3u8Arr => {
const fs = require('fs')
var externalPlayer = this.setting.externalPlayer
if (!externalPlayer) {
this.$message.error('请设置第三方播放器路径')
// 在线播放该视频
var link = 'https://www.m3u8play.com/?play=' + m3u8Arr[this.video.info.index]
const open = require('open')
open(link)
} else {
var m3uFile = this.generateM3uFile(this.video.info.name, m3u8Arr, this.video.info.index)
if (fs.existsSync(externalPlayer)) {
var execFile = require('child_process').execFile
execFile(externalPlayer, [m3uFile])
} else {
var exec = require('child_process').exec
exec(externalPlayer, [m3uFile])
}
}
})
},
generateM3uFile (fileName, m3u8Arr, startIndex) {
const path = require('path')
const os = require('os')
const fs = require('fs')
var filePath = path.join(os.tmpdir(), fileName + '.m3u')
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath)
}
var str = '#EXTM3U' + os.EOL
for (let ind = startIndex; ind < m3u8Arr.length; ind++) {
str += `#EXTINF: -1, 第${ind + 1}` + os.EOL
str += m3u8Arr[ind] + os.EOL
}
str += '#EXT-X-ENDLIST' + os.EOL
fs.writeFileSync(filePath, str)
return filePath
},
checkStar () {
star.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
star.find({ key: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
this.isStar = true
} else {

View File

@@ -5,7 +5,8 @@
<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 @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/issues')">当前版本v{{pkg.version}} 反馈</a>
<a style="color:#38dd77" @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/releases/tag/v' + latestVersion)" v-show="latestVersion !== pkg.version" >最新版本v{{latestVersion}}</a>
</div>
<div class="view">
<div class="title">视图</div>
@@ -39,31 +40,62 @@
<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="openDoc('shortcut')">说明文档</div>
</div>
</div>
</div>
<div class='site'>
<div class="title">收藏管理</div>
<div class="site-box">
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="exportFavorites">导出</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="importFavorites">导入</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="clearFavorites">清空收藏</div>
</div>
</div>
</div>
<div class='search'>
<div class="title">搜索</div>
<div class="zy-checkbox">
<input type="checkbox" v-model="setting.searchAllSites" @change="updateSearchOption($event)"> 搜索所有资源
</div>
</div>
<div class='site'>
<div class="title">第三方播放</div>
<div class="site-box">
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="selectLocalPlayer">选择本地播放器</div>
</div>
<div class="zy-select" @click = "editPlayerPath = true">
<div class="vs-placeholder vs-noAfter" v-show = "editPlayerPath == false">
<label>编辑</label>
</div>
<input class="vs-input" v-show = "editPlayerPath == true" v-model = "d.externalPlayer"
@blur= "updatePlayerPath"
@keyup.enter = "updatePlayerPath">
</div>
</div>
</div>
<div class="site">
<div class="title">源管理</div>
<div class="site-box">
<div class="zy-select" @mouseleave="show.site = false">
<div class="vs-placeholder" @click="show.site = true">默认源</div>
<div class="vs-options" v-show="show.site">
<ul class="zy-scroll" style="height: 300px">
<li :class="d.site === i.key ? 'active' : ''" v-for="(i, j) in sitesList" :key="j" @click="siteClick(i.key)">{{ i.name }}</li>
</ul>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="exportSites">导出</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="expSites"></div>
<div class="vs-placeholder vs-noAfter" @click="importSites"></div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="impSites">导入</div>
<div class="vs-placeholder vs-noAfter" @click="editSitesEvent">编辑源</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="openDoc('sites')">说明文档</div>
<div class="vs-placeholder vs-noAfter" @click="resetSites">重置源</div>
</div>
<div class="zy-checkbox">
<input type="checkbox" v-model="setting.excludeR18Films" @change="updateExcludeR18FilmOption($event)"> 屏蔽福利片
</div>
</div>
</div>
<div class="theme">
@@ -115,9 +147,11 @@
<script>
import { mapMutations } from 'vuex'
import pkg from '../../package.json'
import { setting, sites, shortcut } from '../lib/dexie'
import { setting, sites, shortcut, star } from '../lib/dexie'
import { shell, clipboard, remote } from 'electron'
import db from '../lib/dexie/dexie'
import { sites as defaultSites } from '../lib/dexie/initData'
import fs from 'fs'
export default {
name: 'setting',
data () {
@@ -125,17 +159,26 @@ export default {
pkg: pkg,
sitesList: [],
shortcutList: [],
favoritesList: [],
show: {
site: false,
shortcut: false,
view: false
},
externalPlayer: '',
editPlayerPath: false,
excludeR18Films: false,
latestVersion: pkg.version,
d: {
id: 0,
site: '',
theme: '',
shortcut: true,
view: 'picture'
searchAllSites: true,
view: 'picture',
externalPlayer: '',
editPlayerPath: false,
excludeR18Films: true
}
}
},
@@ -147,10 +190,18 @@ export default {
set (val) {
this.SET_SETTING(val)
}
},
editSites: {
get () {
return this.$store.getters.getEditSites
},
set (val) {
this.SET_EDITSITES(val)
}
}
},
methods: {
...mapMutations(['SET_SETTING']),
...mapMutations(['SET_SETTING', 'SET_EDITSITES']),
linkOpen (e) {
shell.openExternal(e)
},
@@ -161,7 +212,11 @@ export default {
site: res.site,
theme: res.theme,
shortcut: res.shortcut,
view: res.view
view: res.view,
searchAllSites: res.searchAllSites,
externalPlayer: res.externalPlayer,
editPlayerPath: false,
excludeR18Films: res.excludeR18Films
}
this.setting = this.d
})
@@ -176,6 +231,11 @@ export default {
this.shortcutList = res
})
},
getFavorites () {
star.all().then(res => {
this.favoritesList = res
})
},
changeView (e) {
this.d.view = e
setting.update(this.d).then(res => {
@@ -192,24 +252,185 @@ export default {
this.show.site = false
})
},
expSites () {
const arr = [...this.sitesList]
const str = JSON.stringify(arr)
clipboard.writeText(str)
this.$message.success('已复制到剪贴板')
updateSearchOption (e) {
this.d.searchAllSites = this.setting.searchAllSites
setting.update(this.setting)
},
impSites () {
const str = clipboard.readText()
const json = JSON.parse(str)
sites.clear().then(res => {
this.$message.info('已清空原数据')
sites.add(json).then(e => {
this.$message.success('已添加成功')
this.getSites()
this.d.site = json[0].key
updateExcludeR18FilmOption (e) {
this.d.excludeR18Films = this.setting.excludeR18Films
setting.update(this.setting)
},
exportFavorites () {
this.getFavorites()
const arr = [...this.favoritesList]
const str = JSON.stringify(arr, null, 4)
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
{ name: 'Normal text file', extensions: ['txt'] },
{ name: 'All types', extensions: ['*'] }
]
}
remote.dialog.showSaveDialog(options).then(result => {
if (!result.canceled) {
fs.writeFileSync(result.filePath, str)
this.$message.success('已保存成功')
}
}).catch(err => {
this.$message.error(err)
})
},
importFavorites () {
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
{ name: 'Normal text file', extensions: ['txt'] },
{ name: 'All types', extensions: ['*'] }
],
properties: ['openFile', 'multiSelections']
}
remote.dialog.showOpenDialog(options).then(result => {
if (!result.canceled) {
result.filePaths.forEach(file => {
var str = fs.readFileSync(file)
const json = JSON.parse(str)
star.bulkAdd(json).then(e => {
this.getFavorites()
})
this.upgradeFavorites()
})
this.$message.success('导入收藏成功')
}
}).catch(err => {
this.$message.error(err)
})
},
clearFavorites () {
star.clear().then(e => {
this.getFavorites()
this.$message.success('清空所有收藏成功')
})
},
upgradeFavorites () {
star.all().then(res => {
res.forEach(element => {
const docs = {
key: element.key,
ids: element.ids,
name: element.name,
type: element.type,
year: element.year,
last: element.last,
note: element.note
}
star.find({ key: element.key, ids: element.ids }).then(res => {
if (!res) {
star.add(docs)
}
})
})
this.getFavorites()
})
},
selectLocalPlayer () {
const options = {
filters: [
{ name: 'Executable file', extensions: ['exe'] },
{ name: 'All types', extensions: ['*'] }
],
properties: ['openFile']
}
remote.dialog.showOpenDialog(options).then(result => {
if (!result.canceled) {
var playerPath = result.filePaths[0].replace(/\\/g, '/')
this.$message.success('设定第三方播放器路径为:' + result.filePaths[0])
this.d.externalPlayer = playerPath
this.externalPlayer = playerPath
setting.update(this.d).then(res => {
this.setting = this.d
})
}
}).catch(err => {
this.$message.error(err)
})
},
resetLocalPlayer () {
this.d.externalPlayer = ''
setting.update(this.d).then(res => {
this.setting = this.d
this.$message.success('重置第三方播放器成功')
})
},
updatePlayerPath () {
this.$message.success('设定第三方播放器路径为:' + this.d.externalPlayer)
this.editPlayerPath = false
setting.update(this.d).then(res => {
this.setting = this.d
})
},
exportSites () {
this.getSites()
const arr = [...this.sitesList]
const str = JSON.stringify(arr, null, 4)
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
{ name: 'Normal text file', extensions: ['txt'] },
{ name: 'All types', extensions: ['*'] }
]
}
remote.dialog.showSaveDialog(options).then(result => {
if (!result.canceled) {
fs.writeFileSync(result.filePath, str)
this.$message.success('已保存成功')
}
}).catch(err => {
this.$message.error(err)
})
},
importSites () {
const options = {
filters: [
{ name: 'JSON file', extensions: ['json'] },
{ name: 'Normal text file', extensions: ['txt'] },
{ name: 'All types', extensions: ['*'] }
],
properties: ['openFile']
}
remote.dialog.showOpenDialog(options).then(result => {
if (!result.canceled) {
sites.clear()
result.filePaths.forEach(file => {
var str = fs.readFileSync(file)
const json = JSON.parse(str)
sites.bulkAdd(json).then(e => {
this.getSites()
this.d.site = json[0].key
setting.update(this.d).then(res => {
this.setting = this.d
})
})
this.$message.success('导入成功')
}).catch(err => {
this.$message.error(err)
})
}
})
},
editSitesEvent () {
this.editSites = {
show: true,
sites: this.sitesList
}
},
resetSites () {
sites.clear()
sites.bulkAdd(defaultSites).then(e => {
this.getSites()
this.d.site = defaultSites[0].key
setting.update(this.d).then(res => {
this.setting = this.d
this.$message.success('重置源成功')
})
})
},
@@ -229,7 +450,7 @@ export default {
},
expShortcut () {
const arr = [...this.shortcutList]
const str = JSON.stringify(arr)
const str = JSON.stringify(arr, null, 4)
clipboard.writeText(str)
this.$message.success('已复制到剪贴板')
},
@@ -260,12 +481,25 @@ export default {
this.linkOpen('http://zyplayer.fun/doc/shortcut/')
return false
}
},
getLatestVersion () {
const cheerio = require('cheerio')
const axios = require('axios')
var url = 'https://github.com/Hunlongyu/ZY-Player/releases'
axios.get(url).then(res => {
const $ = cheerio.load(res.data)
var e = $('div.release-header')[0]
var firstResult = $(e).find('div>div>a')
this.latestVersion = firstResult.text()
})
}
},
created () {
this.getSetting()
this.getSites()
this.getShortcut()
this.getFavorites()
this.getLatestVersion()
}
}
</script>
@@ -313,6 +547,11 @@ export default {
}
}
}
.search{
width: 100%;
padding: 20px;
margin-top: 20px;
}
.site{
width: 100%;
padding: 20px;
@@ -341,6 +580,7 @@ export default {
margin-top: 20px;
.theme-box{
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
margin-top: 10px;
.theme-item{

View File

@@ -70,16 +70,22 @@ export default {
zy.detail(this.share.key, id).then(res => {
if (res) {
this.pic = res.pic
const text = res.dl.dd
for (const i of text) {
if (i._flag.indexOf('m3u8') >= 0) {
const arr = i._t.split('#')
const url = arr[0].split('$')[1]
this.link = 'http://zyplayer.fun/player/player.html?url=' + url + '&title=' + this.share.info.name
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('#')
}
this.loading = false
const url = m3u8List[1]
this.link = 'http://zyplayer.fun/player/player.html?url=' + url + '&title=' + this.share.info.name
}
this.loading = false
})
},
picLoadEvent () {

View File

@@ -7,19 +7,40 @@
</div>
<div class="tBody zy-scroll">
<ul>
<li v-for="(i, j) in list" :key="j" @click="detailEvent(i)">
<span class="name">{{i.name}}</span>
<span class="type">{{i.type}}</span>
<span class="time">{{i.year}}</span>
<span class="from">{{i.site}}</span>
<span class="operate" style="width: 220px">
<span class="btn" @click.stop="playEvent(i)">播放</span>
<span class="btn" @click.stop="deleteEvent(i)">删除</span>
<span class="btn" @click.stop="shareEvent(i)">分享</span>
<span class="btn" @click.stop="updateEvent(i)">同步</span>
<span class="btn" @click.stop="downloadEvent(i)">下载</span>
<li v-show="this.list.length > 0">
<span class="name">名字</span>
<span class="type">类型</span>
<span class="time">上映</span>
<span class="site">片源</span>
<span class="note">备注</span>
<span class="note">观看至</span>
<span class="operate">
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
</span>
</li>
<draggable v-model="list" @change="listUpdatedEvent">
<transition-group>
<li v-for="(i, j) in list" :key="j" @click="detailEvent(i)" :class="[i.hasUpdate ? 'zy-highlighted': '']">
<span class="name">{{i.name}}</span>
<span class="type">{{i.type}}</span>
<span class="time">{{i.year}}</span>
<span class="site">{{getSiteName(i.key)}}</span>
<span class="note">{{i.note}}</span>
<span class="note">{{getHistoryNote(i.index)}}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(i)">播放</span>
<span class="btn" @click.stop="shareEvent(i)">分享</span>
<span class="btn" @click.stop="updateEvent(i)">同步</span>
<span class="btn" @click.stop="downloadEvent(i)">下载</span>
<span class="btn" @click.stop="deleteEvent(i)">删除</span>
</span>
</li>
</transition-group>
</draggable>
</ul>
</div>
</div>
@@ -28,16 +49,22 @@
</template>
<script>
import { mapMutations } from 'vuex'
import { star, history } from '../lib/dexie'
import { star, history, sites } from '../lib/dexie'
import zy from '../lib/site/tools'
import draggable from 'vuedraggable'
const { clipboard } = require('electron')
export default {
name: 'star',
data () {
return {
list: []
list: [],
sites: []
}
},
components: {
draggable
},
computed: {
view: {
get () {
@@ -75,6 +102,7 @@ export default {
watch: {
view () {
this.getStarList()
this.getAllsites()
}
},
methods: {
@@ -82,21 +110,27 @@ export default {
detailEvent (e) {
this.detail = {
show: true,
key: e.site,
key: e.key,
info: {
id: e.ids,
name: e.name
}
}
if (e.hasUpdate) {
this.clearHasUpdateFlag(e)
}
},
playEvent (e) {
history.find({ site: e.site, ids: e.ids }).then(res => {
history.find({ site: e.key, ids: e.ids }).then(res => {
if (res) {
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: res.index } }
this.video = { key: e.key, info: { id: res.ids, name: res.name, index: res.index } }
} else {
this.video = { key: e.site, info: { id: e.ids, name: e.name, index: 0 } }
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 } }
}
})
if (e.hasUpdate) {
this.clearHasUpdateFlag(e)
}
this.view = 'Play'
},
deleteEvent (e) {
@@ -112,30 +146,54 @@ export default {
shareEvent (e) {
this.share = {
show: true,
key: e.site,
key: e.key,
info: e
}
},
clearHasUpdateFlag (e) {
star.find({ id: e.id }).then(res => {
res.hasUpdate = false
star.update(e.id, res)
this.getStarList()
})
},
listUpdatedEvent () {
star.clear().then(res1 => {
// 重新排序
var id = this.list.length
this.list.forEach(element => {
element.id = id
star.add(element)
id -= 1
})
})
},
updateEvent (e) {
zy.detail(e.site, e.ids).then(res => {
if (e.last === res.last) {
var msg = `同步"${e.name}"成功, 未查询到更新。`
this.$message.info(msg)
} else {
const doc = {
id: e.id,
ids: res.id,
last: res.last,
name: res.name,
site: e.site,
type: res.type,
year: res.year
}
star.update(e.id, doc).then(res => {
var msg = `同步"${e.name}"成功, 检查到更新。`
this.$message.success(msg)
})
zy.detail(e.key, e.ids).then(res => {
var doc = {
key: e.key,
id: e.id,
ids: res.id,
last: res.last,
name: res.name,
type: res.type,
year: res.year,
note: res.note
}
star.get(e.id).then(resStar => {
doc.hasUpdate = resStar.hasUpdate
var msg = ''
if (e.last === res.last) {
msg = `同步"${e.name}"成功, 未查询到更新。`
this.$message.info(msg)
} else {
doc.hasUpdate = true
msg = `同步"${e.name}"成功, 检查到更新。`
this.$message.success(msg)
}
star.update(e.id, doc)
this.getStarList()
})
}).catch(err => {
var msg = `同步"${e.name}"失败, 请重试。`
this.$message.warning(msg, err)
@@ -147,8 +205,8 @@ export default {
})
},
downloadEvent (e) {
zy.download(e.site, e.ids).then(res => {
if (res) {
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('#')
@@ -163,25 +221,58 @@ export default {
this.$message.warning('没有查询到下载链接.')
}
} else {
const list = [...this.m3u8List]
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
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』格式的链接已复制, 快去下载吧!')
})
}
})
},
getSiteName (key) {
var site = this.sites.find(e => e.key === key)
if (site) {
return site.name
}
},
getHistoryNote (index) {
if (index !== null && index !== undefined) {
return `${index + 1}`
} else {
return ''
}
},
getStarList () {
star.all().then(res => {
this.list = res.reverse()
})
},
getAllsites () {
sites.all().then(res => {
this.sites = res
})
}
},
created () {
this.getStarList()
window.Sortable = require('sortablejs').Sortable
}
}
</script>

View File

@@ -8,6 +8,7 @@ import Setting from './Setting'
import Detail from './Detail'
import Share from './Share'
import History from './History'
import EditSites from './EditSites'
export default {
registerComponents () {
@@ -20,5 +21,6 @@ export default {
Vue.component('Detail', Detail)
Vue.component('Share', Share)
Vue.component('History', History)
Vue.component('EditSites', EditSites)
}
}

View File

@@ -7,7 +7,16 @@ export default {
async clear () {
return await sites.clear()
},
async add (doc) {
async bulkAdd (doc) {
return await sites.bulkAdd(doc)
},
async find (doc) {
return await sites.get(doc)
},
async add (doc) {
return await sites.add(doc)
},
async remove (id) {
return await sites.delete(id)
}
}

View File

@@ -4,6 +4,9 @@ export default {
async add (doc) {
return await star.add(doc)
},
async bulkAdd (doc) {
return await star.bulkAdd(doc)
},
async find (doc) {
return await star.get(doc)
},
@@ -15,5 +18,11 @@ export default {
},
async remove (id) {
return await star.delete(id)
},
async get (id) {
return await star.get(id)
},
async clear () {
return await star.clear()
}
}

View File

@@ -23,13 +23,16 @@
</div>
<div class="right">
<span class="top" @click="frameClickEvent('top')" title="置顶">
<svg t="1595919317571" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1188" style="width:12px;height:16px"><path d="M43.072 974.72l380.864-301.952 151.936 161.6c0 0 63.424 17.28 67.328-30.72l-3.904-163.584 225.088-259.648 98.048-5.696c0 0 76.928-15.488 21.184-82.752l-275.072-276.928c0 0-74.944-9.6-69.248 59.584l0 75.008L383.552 367.104 225.856 376.64c0 0-57.728 19.2-36.608 69.248l148.16 146.176L43.072 974.72 43.072 974.72z" p-id="1189" :fill="isAlwaysOnTop ? '#555555' : '#ffffff'"></path></svg>
<svg t="1595919317571" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1188" style="width:10px;height:14px"><path d="M43.072 974.72l380.864-301.952 151.936 161.6c0 0 63.424 17.28 67.328-30.72l-3.904-163.584 225.088-259.648 98.048-5.696c0 0 76.928-15.488 21.184-82.752l-275.072-276.928c0 0-74.944-9.6-69.248 59.584l0 75.008L383.552 367.104 225.856 376.64c0 0-57.728 19.2-36.608 69.248l148.16 146.176L43.072 974.72 43.072 974.72z" p-id="1189" :fill="isAlwaysOnTop ? '#555555' : '#ffffff'"></path></svg>
</span>
<span class="min" @click="frameClickEvent('min')" title="最小化">
<svg t="1595917239849" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1155" style="width:10px;height:16px"><path d="M0 479.936C0 444.64 28.448 416 64.064 416L959.936 416C995.328 416 1024 444.736 1024 479.936L1024 544.064C1024 579.392 995.552 608 959.936 608L64.064 608C28.672 608 0 579.264 0 544.064L0 479.936Z" p-id="1156" fill="#ffffff"></path></svg>
<svg t="1595917239849" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1155" style="width:8px;height:14px"><path d="M0 479.936C0 444.64 28.448 416 64.064 416L959.936 416C995.328 416 1024 444.736 1024 479.936L1024 544.064C1024 579.392 995.552 608 959.936 608L64.064 608C28.672 608 0 579.264 0 544.064L0 479.936Z" p-id="1156" fill="#ffffff"></path></svg>
</span>
<span class="max" @click="frameClickEvent('max')" title="最大化">
<svg t="1595917343956" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1540" style="width:8px;height:14px"><path d="M416 416 64.064 416C28.448 416 0 444.64 0 479.936L0 544.064C0 579.264 28.672 608 64.064 608L416 608 416 959.936C416 995.552 444.64 1024 479.936 1024L544.064 1024C579.264 1024 608 995.328 608 959.936L608 608 959.936 608C995.552 608 1024 579.36 1024 544.064L1024 479.936C1024 444.736 995.328 416 959.936 416L608 416 608 64.064C608 28.448 579.36 0 544.064 0L479.936 0C444.736 0 416 28.672 416 64.064L416 416Z" p-id="1541" fill="#ffffff"></path></svg>
</span>
<span class="close" @click="frameClickEvent('close')" title="关闭">
<svg t="1595917372551" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1685" style="width:10px;height:16px"><path d="M511.968 376.224 796.096 92.096C833.536 54.624 894.4 54.624 931.84 92.096 969.312 129.568 969.312 190.4 931.84 227.872L647.744 512 931.84 796.096C969.312 833.568 969.312 894.4 931.84 931.872 894.4 969.344 833.536 969.344 796.096 931.872L511.968 647.744 227.84 931.872C190.4 969.344 129.536 969.344 92.096 931.872 54.624 894.4 54.624 833.568 92.096 796.096L376.224 512 92.096 227.872C54.624 190.4 54.624 129.568 92.096 92.096 129.536 54.624 190.4 54.624 227.84 92.096L511.968 376.224Z" p-id="1686" fill="#ffffff"></path></svg>
<svg t="1595917372551" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1685" style="width:8px;height:14px"><path d="M511.968 376.224 796.096 92.096C833.536 54.624 894.4 54.624 931.84 92.096 969.312 129.568 969.312 190.4 931.84 227.872L647.744 512 931.84 796.096C969.312 833.568 969.312 894.4 931.84 931.872 894.4 969.344 833.536 969.344 796.096 931.872L511.968 647.744 227.84 931.872C190.4 969.344 129.536 969.344 92.096 931.872 54.624 894.4 54.624 833.568 92.096 796.096L376.224 512 92.096 227.872C54.624 190.4 54.624 129.568 92.096 92.096 129.536 54.624 190.4 54.624 227.84 92.096L511.968 376.224Z" p-id="1686" fill="#ffffff"></path></svg>
</span>
</div>
</div>
@@ -80,10 +83,12 @@ export default {
},
methods: {
frameClickEvent (e) {
const win = remote.getCurrentWindow()
if (e === 'min') {
const win = remote.getCurrentWindow()
win.minimize()
return false
}
if (e === 'max') {
win.isMaximized() ? win.unmaximize() : win.maximize()
}
if (e === 'close') {
ipcRenderer.send('win')
@@ -91,7 +96,6 @@ export default {
}
if (e === 'top') {
this.isAlwaysOnTop = !this.isAlwaysOnTop
const win = remote.getCurrentWindow()
win.setAlwaysOnTop(this.isAlwaysOnTop)
}
},
@@ -125,54 +129,6 @@ export default {
}
})
})
// zy.detail(res.site, res.ids).then(e => {
// this.name = e.name
// this.detail = e
// const dd = e.dl.dd
// const type = Object.prototype.toString.call(dd)
// let m3u8Txt = []
// if (type === '[object Array]') {
// for (const i of dd) {
// if (i._t.indexOf('m3u8') >= 0) {
// m3u8Txt = i._t.split('#')
// }
// }
// } else {
// m3u8Txt = dd._t.split('#')
// }
// const m3u8Arr = []
// for (const i of m3u8Txt) {
// const j = i.split('$')
// if (j.length > 1) {
// for (let m = 0; m < j.length; m++) {
// if (j[m].indexOf('m3u8') >= 0) {
// m3u8Arr.push(j[m])
// }
// }
// } else {
// m3u8Arr.push(j[0])
// }
// }
// this.m3u8Arr = m3u8Arr
// this.xg.src = m3u8Arr[res.index]
// if (res.time !== 0 || res.time !== '') {
// this.xg.play()
// this.xg.once('playing', () => {
// this.xg.currentTime = res.time
// })
// } else {
// this.xg.play()
// }
// this.videoPlaying()
// this.xg.once('ended', () => {
// if (m3u8Arr.length > 1 && (m3u8Arr.length - 1 > res.index)) {
// this.video.time = 0
// this.video.index++
// this.xg.src = m3u8Arr[this.video.index]
// this.xg.play()
// }
// })
// })
})
},
fetchM3u8List (info) {
@@ -507,15 +463,18 @@ html,body{
span{
-webkit-app-region: no-drag;
display: inline-block;
width: 16px;
height: 16px;
width: 14px;
height: 14px;
text-align: center;
line-height: 16px;
line-height: 14px;
border-radius: 50%;
margin-right: 10px;
cursor: pointer;
opacity: 0.4;
&.min{
background-color: #32dc36;
}
&.max{
background-color: #ffbe2a;
}
&.close{

View File

@@ -25,6 +25,10 @@ export default new Vuex.Store({
video: {
key: '',
info: {}
},
editSites: {
show: false,
sites: []
}
},
getters: {
@@ -42,6 +46,9 @@ export default new Vuex.Store({
},
getVideo: state => {
return state.video
},
getEditSites: state => {
return state.editSites
}
},
mutations: {
@@ -59,6 +66,9 @@ export default new Vuex.Store({
},
SET_VIDEO: (state, payload) => {
state.video = payload
},
SET_EDITSITES: (state, payload) => {
state.editSites = payload
}
}
})