Compare commits

...

34 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
17 changed files with 546 additions and 254 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.4.5",
"version": "2.4.7",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -31,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

@@ -80,7 +80,7 @@
}
.zy-checkbox{
position: relative;
display: flex;
display: inline-block;
width: 200px;
height: 30px;
vertical-align: middle;

View File

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

View File

@@ -134,20 +134,18 @@ export default {
},
starEvent () {
star.find({ key: this.detail.key, ids: this.info.id }).then(res => {
const docs = {
key: this.detail.key,
ids: this.info.id,
name: this.info.name,
type: this.info.type,
year: this.info.year,
last: this.info.last,
note: this.info.note
}
if (res) {
star.update(res.id, docs).then(res => {
this.$message.success('已存在,更新成功')
})
this.$message.info('该影片已被收藏')
} else {
const docs = {
key: this.detail.key,
ids: this.info.id,
name: this.info.name,
type: this.info.type,
year: this.info.year,
last: this.info.last,
note: this.info.note
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
})

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

@@ -35,7 +35,7 @@
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(site, props.data)">
<div class="operate">
@@ -61,7 +61,7 @@
<div class="zy-table">
<div class="tBody">
<ul>
<li v-for="(i, j) in list" :key="j" @click="detailEvent(site, 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>
@@ -136,7 +136,9 @@ export default {
infiniteId: +new Date(),
searchList: [],
searchTxt: '',
searchContents: []
searchContents: [],
// 福利片关键词
r18KeyWords: ['伦理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番']
}
},
components: {
@@ -187,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: {
@@ -227,8 +232,22 @@ export default {
getClass () {
return new Promise((resolve, reject) => {
const key = this.site.key
// 屏蔽主分类
const classToHide = ['电影', '电影片', '电视剧', '连续剧', '综艺', '动漫']
zy.class(key).then(res => {
var allClass = [{ name: '最新', tid: 0 }].concat(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
@@ -239,6 +258,13 @@ export default {
})
})
},
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
@@ -399,9 +425,6 @@ export default {
sites.forEach(site =>
zy.search(site.key, wd).then(res => {
const type = Object.prototype.toString.call(res)
if (type === '[object Undefined]') {
this.$message.info(site.name + ' 无搜索结果')
}
if (type === '[object Array]') {
res.forEach(element => {
element.site = site

View File

@@ -8,6 +8,17 @@
<div class="tBody zy-scroll">
<ul>
<li v-show="this.history.length === 0">无数据</li>
<li v-show="this.history.length > 0">
<span class="name">名字</span>
<span class="site">片源</span>
<span class="note">观看至</span>
<span class="operate">
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
</span>
</li>
<li v-for="(i, j) in history" :key="j" @click="historyItemEvent(i)">
<span class="name" @click.stop="detailEvent(i)">{{i.name}}</span>
<span class="site">{{getSiteName(i.site)}}</span>

View File

@@ -384,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,
@@ -393,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,
@@ -407,6 +407,7 @@ export default {
history.add(doc)
}
})
this.updateStar()
this.timerEvent()
},
changeVideo () {
@@ -465,21 +466,33 @@ export default {
this.right.history = res.reverse()
})
},
starEvent () {
updateStar () {
const info = this.video.info
star.find({ key: this.video.key, ids: info.id }).then(res => {
if (res) {
this.$message.info('已存在')
res.index = info.index
star.update(res.id, res)
}
}).catch(() => {
this.$message.warning('检查收藏失败')
})
},
starEvent () {
const info = this.video.info
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.update(res.id, doc)
} else {
const doc = {
key: this.video.key,
ids: info.id,
name: info.name,
type: info.type,
year: info.year,
last: info.last,
note: info.note
}
star.add(doc).then(starRes => {
this.$message.success('收藏成功')
this.isStar = true
@@ -544,38 +557,38 @@ export default {
this.fetchM3u8List().then(m3u8Arr => {
const fs = require('fs')
var externalPlayer = this.setting.externalPlayer
if (!fs.existsSync(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 exec = require('child_process').execFile
var dplFile = this.generateDplFile(this.video.info.name, m3u8Arr, this.video.info.index)
exec(externalPlayer, [dplFile])
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])
}
}
})
},
generateDplFile (fileName, m3u8Arr, index) {
generateM3uFile (fileName, m3u8Arr, startIndex) {
const path = require('path')
const os = require('os')
const fs = require('fs')
var filePath = path.join(os.tmpdir(), fileName + '.dpl')
var filePath = path.join(os.tmpdir(), fileName + '.m3u')
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath)
}
var str = 'DAUMPLAYLIST' + os.EOL
str += 'playname=' + m3u8Arr[index] + os.EOL
str += 'topindex=' + 0 + os.EOL
str += 'saveplaypos=' + index + os.EOL
var ind = 1
m3u8Arr.forEach(element => {
str += ind + '*title*第' + ind + '集' + os.EOL
str += ind + '*file*' + element + os.EOL
ind += 1
})
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
},

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,9 +40,6 @@
<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'>
@@ -70,8 +68,13 @@
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="selectLocalPlayer">选择本地播放器</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="resetLocalPlayer">重置</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>
@@ -85,19 +88,14 @@
<div class="vs-placeholder vs-noAfter" @click="importSites">导入</div>
</div>
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="resetSites">重置</div>
</div>
<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="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">
@@ -167,6 +165,10 @@ export default {
shortcut: false,
view: false
},
externalPlayer: '',
editPlayerPath: false,
excludeR18Films: false,
latestVersion: pkg.version,
d: {
id: 0,
site: '',
@@ -174,7 +176,9 @@ export default {
shortcut: true,
searchAllSites: true,
view: 'picture',
externalPlayer: ''
externalPlayer: '',
editPlayerPath: false,
excludeR18Films: true
}
}
},
@@ -186,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)
},
@@ -202,7 +214,9 @@ export default {
shortcut: res.shortcut,
view: res.view,
searchAllSites: res.searchAllSites,
externalPlayer: res.externalPlayer
externalPlayer: res.externalPlayer,
editPlayerPath: false,
excludeR18Films: res.excludeR18Films
}
this.setting = this.d
})
@@ -240,9 +254,11 @@ export default {
},
updateSearchOption (e) {
this.d.searchAllSites = this.setting.searchAllSites
setting.update(this.d).then(res => {
this.setting = this.d
})
setting.update(this.setting)
},
updateExcludeR18FilmOption (e) {
this.d.excludeR18Films = this.setting.excludeR18Films
setting.update(this.setting)
},
exportFavorites () {
this.getFavorites()
@@ -329,6 +345,7 @@ export default {
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
})
@@ -344,6 +361,13 @@ export default {
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]
@@ -379,7 +403,7 @@ export default {
result.filePaths.forEach(file => {
var str = fs.readFileSync(file)
const json = JSON.parse(str)
sites.add(json).then(e => {
sites.bulkAdd(json).then(e => {
this.getSites()
this.d.site = json[0].key
setting.update(this.d).then(res => {
@@ -393,9 +417,15 @@ export default {
}
})
},
editSitesEvent () {
this.editSites = {
show: true,
sites: this.sitesList
}
},
resetSites () {
sites.clear()
sites.add(defaultSites).then(e => {
sites.bulkAdd(defaultSites).then(e => {
this.getSites()
this.d.site = defaultSites[0].key
setting.update(this.d).then(res => {
@@ -451,6 +481,17 @@ 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 () {
@@ -458,6 +499,7 @@ export default {
this.getSites()
this.getShortcut()
this.getFavorites()
this.getLatestVersion()
}
}
</script>

View File

@@ -7,20 +7,40 @@
</div>
<div class="tBody zy-scroll">
<ul>
<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>
<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" @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 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>
@@ -31,6 +51,8 @@
import { mapMutations } from 'vuex'
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',
@@ -40,6 +62,9 @@ export default {
sites: []
}
},
components: {
draggable
},
computed: {
view: {
get () {
@@ -132,6 +157,17 @@ export default {
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.key, e.ids).then(res => {
var doc = {
@@ -144,20 +180,20 @@ export default {
year: res.year,
note: res.note
}
if (e.last === res.last) {
doc.hasUpdate = false
star.update(e.id, doc).then(res => {
var msg = `同步"${e.name}"成功, 未查询到更新。`
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
star.update(e.id, doc).then(res => {
var msg = `同步"${e.name}"成功, 检查到更新。`
} else {
doc.hasUpdate = true
msg = `同步"${e.name}"成功, 检查到更新。`
this.$message.success(msg)
})
}
this.getStarList()
}
star.update(e.id, doc)
this.getStarList()
})
}).catch(err => {
var msg = `同步"${e.name}"失败, 请重试。`
this.$message.warning(msg, err)
@@ -216,6 +252,13 @@ export default {
return site.name
}
},
getHistoryNote (index) {
if (index !== null && index !== undefined) {
return `${index + 1}`
} else {
return ''
}
},
getStarList () {
star.all().then(res => {
this.list = res.reverse()
@@ -229,6 +272,7 @@ export default {
},
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,10 +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

@@ -19,6 +19,9 @@ 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)
}
},
@@ -459,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
}
}
})