Compare commits

...

33 Commits

Author SHA1 Message Date
hunlongyu
f9ff6bbde9 v0.7.13 测试自动更新 2020-01-19 17:16:58 +08:00
hunlongyu
b208dfc3b6 add update 2020-01-19 17:16:32 +08:00
hunlongyu
23d221979e README.MD 2020-01-19 17:03:39 +08:00
hunlongyu
b36b6bd9e4 v 2020-01-19 16:39:16 +08:00
hunlongyu
4c8b3ae36b v0.7.12 主题 2020-01-19 16:39:04 +08:00
hunlongyu
58f1ecdb57 更改图标 2020-01-19 16:14:23 +08:00
hunlongyu
b5a9d4482b v0.7.11 app name 2020-01-19 16:06:06 +08:00
hunlongyu
8c05489b06 v 2020-01-19 15:55:14 +08:00
hunlongyu
bf0ccef8ba v0.7.10 播放视频页面优化提示, 测试icon 2020-01-19 15:54:58 +08:00
hunlongyu
0161e2ee1d v0.7.9 测试icon, 优化界面 2020-01-19 15:37:02 +08:00
hunlongyu
9df844dbe8 add icon 2020-01-19 15:23:51 +08:00
hunlongyu
e723fe0cf1 remove ico 2020-01-19 15:01:57 +08:00
hunlongyu
3d94ab2f6c v0.7.7 增加软件icon 2020-01-19 15:01:29 +08:00
hunlongyu
dabf4390bd v0.7.6 资源播放 优化样式 2020-01-19 14:37:22 +08:00
hunlongyu
93c7392754 v0.7.5 获取资源列表, 获取详细信息, 播放资源, 收藏资源 2020-01-18 18:46:49 +08:00
hunlongyu
426bb05e1c v0.7.4 最终确认使用 dexie 来存储数据 2020-01-17 16:42:40 +08:00
hunlongyu
c9f30ce42f test dbdb 2020-01-17 14:32:59 +08:00
hunlongyu
e126beb59c test nedb 2020-01-17 14:28:39 +08:00
hunlongyu
851d4a7ce7 remove package 2020-01-17 10:54:27 +08:00
hunlongyu
d1dd9491da test db 2020-01-17 10:52:26 +08:00
hunlongyu
36e1372f51 由于找不到合适的数据存储插件, 暂停开发. 准备先开发一个 ts 的 indexedDB 插件 2020-01-15 16:19:58 +08:00
hunlongyu
f05a361265 remove rxdb rxjs 2020-01-15 14:39:54 +08:00
hunlongyu
4a01550ad6 remove nedb, search new database 2020-01-14 18:04:06 +08:00
hunlongyu
b09954ca7c v0.7.3 移除无用的插件 2020-01-14 16:20:48 +08:00
hunlongyu
ec6bc18fbf v0.7.2 完成所有界面布局. 逻辑暂未开始 2020-01-14 16:19:39 +08:00
hunlongyu
2bdaf70be1 列表页面 doing 2020-01-13 18:20:20 +08:00
hunlongyu
f0ad01fe5a add new files and remove some files 2020-01-13 14:06:51 +08:00
hunlongyu
e9f172d1b1 v0.7.0 init 2020-01-13 11:16:43 +08:00
hunlongyu
6e2e8e457e remove all files and reset project 2020-01-13 10:42:21 +08:00
hunlongyu
87760286db Update Player.vue 2020-01-13 10:39:48 +08:00
hunlongyu
d9207d6429 v0.6.5 test update 2020-01-10 17:13:43 +08:00
hunlongyu
0e9acb2bab add badge 2020-01-10 16:57:34 +08:00
hunlongyu
6fa2c1f1cc v0.6.4 2020-01-10 16:08:32 +08:00
68 changed files with 4280 additions and 18371 deletions

2
.env
View File

@@ -1 +1 @@
GH_TOKEN=0f8ce868776fa1af5908d0353cc175663908ff5f
tk8b3600eb86beceafc3580faeff9242168aafc7b2

View File

@@ -5,13 +5,14 @@ module.exports = {
},
'extends': [
'plugin:vue/essential',
'@vue/standard'
'@vue/standard',
'@vue/typescript'
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
},
parserOptions: {
parser: 'babel-eslint'
parser: '@typescript-eslint/parser'
}
}

View File

@@ -1,29 +0,0 @@
version: 0.1.{build}
branches:
only:
- master
image: Visual Studio 2017
platform:
- x64
cache:
- node_modules
- '%APPDATA%\npm-cache'
- '%USERPROFILE%\.electron'
- '%USERPROFILE%\AppData\Local\Yarn\cache'
init:
- git config --global core.autocrlf input
install:
- ps: Install-Product node 8 x64
- git reset --hard HEAD
- yarn
- node --version
build_script:
- yarn run release
test: off

21
LICENSE
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 Hunlongyu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,22 +1,34 @@
# ZY-Player
<p align="center">
<img src="https://forthebadge.com/images/badges/built-with-love.svg">
<img src="https://forthebadge.com/images/badges/made-with-vue.svg">
<p>
<p align="center">
<img alt="GitHub" src="https://img.shields.io/github/license/Hunlongyu/ZY-Player?style=for-the-badge">
<img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/Hunlongyu/ZY-Player/total?style=for-the-badge">
<img alt="GitHub release (latest by date including pre-releases)" src="https://img.shields.io/github/v/release/Hunlongyu/ZY-Player?include_prereleases&style=for-the-badge">
<p>
# ZY Player
资源播放器, 提供影视资源的搜索,查看,播放,搜藏等功能.
### 截图:
![001.png](https://i.loli.net/2020/01/08/Fs1VyNzBfAldajr.png)
主界面 ⬆
![002.png](https://i.loli.net/2020/01/08/MOiRmvG17STYbp4.png)
搜索 ⬆
![003.png](https://i.loli.net/2020/01/08/XzJm4HYdnOjMGqN.png)
详情 ⬆
![004.png](https://i.loli.net/2020/01/08/t6GWIOghBUAEZuD.png)
播放 ⬆
![005.png](https://i.loli.net/2020/01/08/kqhtTD8WoUsvdyw.png)
搜藏 ⬆
主界面 ⬇
![film.png](https://i.loli.net/2020/01/19/U1EPzoJHhTDnuxA.png)
搜索 ⬇
![search.png](https://i.loli.net/2020/01/19/BPvJKxlnNfquRI4.png)
搜索结果 ⬇
![search-keyword.png](https://i.loli.net/2020/01/19/6wfY3rPBokM15hl.png)
详情 ⬇
![detail.png](https://i.loli.net/2020/01/19/CN8E1ikyMbhzo9t.png)
播放 ⬇
![play.png](https://i.loli.net/2020/01/19/4XlJRqmx2y8zAec.png)
搜藏 ⬇
![star.png](https://i.loli.net/2020/01/19/Q2fkWUvaXKZJcS4.png)
### 下载地址:
### 下载:
蓝奏云: https://www.lanzous.com/i8jnk9e
[下载地址](https://github.com/Hunlongyu/ZY-Player/releases)
### 未完成:
1. 主题: 暗黑主题
@@ -25,25 +37,3 @@
### 重要:
所有资源来自网上, 该软件不参与任何制作, 上传, 储存, 下载等内容. 该软件仅供学习参考, 请于安装后24小时内删除.
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@@ -1,9 +1,14 @@
module.exports = {
presets: [
'@vue/app'
'presets': [
'@vue/cli-plugin-babel/preset'
],
plugins: [['import', {
'libraryName': 'view-design',
'libraryDirectory': 'src/components'
}]]
'plugins': [
[
'component',
{
'libraryName': 'element-ui',
'styleLibraryName': 'theme-chalk'
}
]
]
}

BIN
build/icons/1024x1024.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
build/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
build/icons/16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

BIN
build/icons/24x24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

BIN
build/icons/256x256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
build/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
build/icons/48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
build/icons/512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
build/icons/64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
build/icons/icon.icns Normal file

Binary file not shown.

BIN
build/icons/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

15183
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +1,51 @@
{
"name": "zy-player",
"version": "0.6.3",
"private": false,
"name": "zy",
"version": "0.7.13",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"electron:build": "vue-cli-service electron:build",
"dev": "vue-cli-service electron:serve",
"electron:build": "vue-cli-service electron:build",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps",
"eb": "vue-cli-service electron:build -p always",
"patch": "npm version patch && git push origin master && git push origin --tags",
"minor": "npm version minor && git push origin master && git push origin --tags",
"major": "npm version major && git push origin master && git push origin --tags"
"electron:generate-icons": "electron-icon-builder --input=./public/icon.png --output=build --flatten",
"publish": "electron:build --win -p always"
},
"main": "background.js",
"dependencies": {
"axios": "^0.19.0",
"core-js": "^2.6.5",
"nedb": "^1.8.0",
"view-design": "^4.0.2",
"core-js": "^3.4.4",
"dexie": "^2.0.4",
"electron-updater": "^4.2.0",
"element-ui": "^2.4.5",
"flyio": "^0.6.14",
"vue": "^2.6.10",
"vue-router": "^3.0.3",
"vuex": "^3.0.1",
"xgplayer": "^2.4.1",
"vue-class-component": "^7.0.2",
"vue-property-decorator": "^8.3.0",
"vuex": "^3.1.2",
"xgplayer": "^2.4.7",
"xgplayer-hls.js": "^2.1.6"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.12.0",
"@vue/cli-plugin-eslint": "^3.12.0",
"@vue/cli-service": "^3.12.0",
"@vue/cli-plugin-babel": "^4.1.0",
"@vue/cli-plugin-eslint": "^4.1.0",
"@vue/cli-plugin-router": "^4.1.0",
"@vue/cli-plugin-typescript": "^4.1.0",
"@vue/cli-plugin-vuex": "^4.1.0",
"@vue/cli-service": "^4.1.0",
"@vue/eslint-config-standard": "^4.0.0",
"babel-eslint": "^10.0.1",
"babel-plugin-import": "^1.13.0",
"electron": "7.1.7",
"electron-updater": "^4.2.0",
"@vue/eslint-config-typescript": "^4.0.0",
"babel-plugin-component": "^1.1.1",
"electron": "^7.1.8",
"electron-icon-builder": "^1.0.2",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"sass": "^1.19.0",
"sass": "^1.23.7",
"sass-loader": "^8.0.0",
"vue-cli-plugin-electron-builder": "^1.4.2",
"typescript": "~3.5.3",
"vue-cli-plugin-electron-builder": "^1.4.4",
"vue-cli-plugin-element": "^1.0.1",
"vue-template-compiler": "^2.6.10"
}
}

View File

@@ -1,5 +0,0 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

BIN
public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -4,12 +4,12 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="icon" href="<%= BASE_URL %>icon.png">
<title>ZY Player</title>
</head>
<body>
<noscript>
<strong>We're sorry but evt doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
<strong>We're sorry but zy doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->

View File

@@ -1,73 +1,127 @@
<template>
<div id="app" :class="getTheme.color">
<Layout class="box">
<Sider class="sider" width="70"><ZYSider /></Sider>
<Layout>
<Header class="header"><ZYHeader /></Header>
<ZYContent class="content">
<router-view />
</ZYContent>
</Layout>
</Layout>
</div>
<el-container id="app" class="theme-light">
<el-header class="Header">
<i class="el-icon-minus" @click="clickFrameEvent('min')"></i>
<i class="el-icon-plus" @click="clickFrameEvent('max')"></i>
<i class="el-icon-close" @click="clickFrameEvent('close')"></i>
<el-row class="Header-box">
</el-row>
</el-header>
<el-container>
<el-aside class="Aside" width="70px">
<el-row class="top">
<i :class="Main === 'Film' ? 'el-icon-film active' : 'el-icon-film'" @click="asideMenuClick('Film')"></i>
<i :class="Main === 'Search' ? 'el-icon-search active' : 'el-icon-search'" @click="asideMenuClick('Search')"></i>
<i :class="Main === 'Player' ? 'el-icon-video-play active' : 'el-icon-video-play'" @click="asideMenuClick('Player')"></i>
<i :class="Main === 'Star' ? 'el-icon-star-off active' : 'el-icon-star-off'" @click="asideMenuClick('Star')"></i>
</el-row>
<el-row class="bottom">
<i :class="Main === 'Setting' ? 'el-icon-setting active' : 'el-icon-setting'" @click="asideMenuClick('Setting')"></i>
</el-row>
</el-aside>
<el-main class="Main">
<Film v-show="Main === 'Film'" />
<Search v-show="Main === 'Search'" />
<Player v-show="Main === 'Player'" />
<Star v-show="Main === 'Star'" />
<Setting v-show="Main === 'Setting'" />
</el-main>
</el-container>
<el-drawer class="drawer" :visible.sync="detail.show" :show-close="true" size="90%" :with-header="true" direction="btt" title="详情">
<Detail />
</el-drawer>
</el-container>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import ZYSider from '@/components/zy_sider.vue'
import ZYHeader from '@/components/zy_header.vue'
import ZYContent from '@/components/zy_content.vue'
import setting from './plugin/nedb/setting'
export default {
name: 'app',
<script lang="ts">
import Vue from 'vue'
import Detail from '@/components/detail.vue'
import { mapMutations } from 'vuex'
const { ipcRenderer: ipc } = require('electron')
export default Vue.extend({
data () {
return {}
},
computed: {
...mapGetters([
'getTheme'
])
Main: {
get () {
return this.$store.getters.getMain
},
set (val) {
this.SET_MAIN(val)
}
},
detail: {
get () {
return this.$store.getters.getDetail
},
set (val) {
this.SET_DETAIL(val)
}
}
},
components: {
ZYSider,
ZYHeader,
ZYContent
Detail
},
methods: {
...mapActions([
'changeTheme'
])
...mapMutations(['SET_DETAIL', 'SET_MAIN']),
clickFrameEvent (e:string) {
ipc.send(e)
},
asideMenuClick (e:string) {
this.Main = e
}
},
beforeCreate () {},
created () {
setting.find({ $or: [{ theme: 'light' }, { theme: 'dark' }] }).then(e => {
if (e.length <= 0) {
setting.add({ theme: 'light' }).then(res => {
this.changeTheme({ id: res._id, color: res.theme })
})
} else {
this.changeTheme({ id: e[0]._id, color: e[0].theme })
}
})
}
}
created () {}
})
</script>
<style lang="scss">
@import './assets/global/global.scss';
@import './assets/theme/dark.scss';
@import './assets/theme/light.scss';
html, body, #app, .box{
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,body{
height: 100%;
}
.box{
.header{
width: 100%;
height: 50px;
#app{
height: 100%;
.Header{
display: flex;
justify-content: flex-end;
align-items: center;
-webkit-app-region: drag;
-webkit-user-select: none;
padding: 0;
i{
width: 60px;
height: 60px;
font-size: 30px;
cursor: pointer;
-webkit-app-region: no-drag;
display: flex;
justify-content: center;
align-items: center;
}
}
.content{
width: 100%;
.Aside{
-webkit-app-region: drag;
-webkit-user-select: none;
text-align: center;
i{
-webkit-app-region: no-drag;
font-size: 32px;
width: 70px;
height: 70px;
line-height: 70px;
cursor: pointer;
}
}
.Main{
height: 100%;
overflow: hidden;
}
}
</style>

View File

@@ -1,50 +0,0 @@
.detail{
padding: 10px;
.detail-box{
border: 1px solid #ddd;
padding: 10px;
display: flex;
justify-content: flex-start;
align-items: flex-start;
margin-bottom: 10px;
flex-wrap: wrap;
.vodImg{
width: 200px;
img{
width: 100%;
height: auto;
}
}
.vodInfo{
flex: 1;
overflow: hidden;
margin-left: 20px;
.vodh{
h2{
display: inline-block;
}
span{
font-size: 12px;
color: #999;
margin-left: 10px;
}
label{
font-size: 20px;
font-weight: bold;
color: #f90;
margin-left: 20px;
}
}
li{
list-style: none;
font-size: 12px;
color: #666;
height: 20px;
overflow: hidden;
a{
pointer-events: none;
}
}
}
}
}

BIN
src/assets/image/light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,7 +1,23 @@
.dark{
.sider,.header,.content{
background-color: #000;
color: #eee;
border: 1px solid #ccc;
@import './global.scss';
.theme-dark{
color: var(--d-c);
background-color: var(--d-bgc);
.Header, .Aside{
i{
color: var(--d-icon);
&:hover{
color: var(--d-icon-h);
background-color: var(--d-bgc-h);
}
}
}
.Aside{
i{
&.active{
color: var(--d-icon-h);
background-color: var(--d-bgc-h);
border-left: 4px solid var(--d-icon-h);
}
}
}
}

View File

@@ -0,0 +1,19 @@
:root{
// light
--l-c: #808695;
--l-c-h: #515a6e;
--l-icon: #C0C4CC;
--l-icon-h: #515a6e;
--l-bgc: #ffffff;
--l-bgc-h: #efefef;
--l-bdc: #dcdee2;
// dark
--d-c: #919191;
--d-c-h: #454545;
--d-icon: #919191;
--d-icon-h: #919191;
--d-bgc: #242424;
--d-bgc-h: #454545;
--d-bdc: #dcdee2;
}

View File

@@ -1,55 +1,74 @@
.light{
.sider,.header,.content{
background-color: #fff;
color: #515a6e;
@import './global.scss';
.theme-light{
color: var(--l-c);
background-color: var(--l-bgc);
.el-table, .el-tabs__item, .btn-next, input{
color: var(--l-c);
}
.sider{
.sider-box{
color: #808695;
i{
&:hover{
color: #515a6e;
background-color: #efefef;
}
&.active{
color: #515a6e;
background-color: #efefef;
border-left: 4px solid #515a6e;
}
.el-pagination, .el-pagination .el-pagination__total, .el-pagination .el-pagination__jump,
.el-pagination .btn-next, .el-pagination .btn-prev{
color: var(--l-c);
}
.el-drawer{
overflow: auto;
&::-webkit-scrollbar{
width: 0px;
}
}
.Header, .Aside{
i{
color: var(--l-icon);
&:hover{
color: var(--l-icon-h);
background-color: var(--l-bgc-h);
}
}
}
.header{
.header-box{
color: #808695;
i{
&:hover{
color: #515a6e;
background-color: #efefef;
border-bottom: 1px solid #808695;
}
.Aside{
i{
&.active{
color: var(--l-icon-h);
background-color: var(--l-bgc-h);
border-left: 4px solid var(--l-icon-h);
}
}
}
.content{
border: 1px solid #dcdee2;
}
.search{
.search-middle{
.ivu-table-cell{
button{
margin: 0 4px;
.Main{
.film, .search, .star, .player{
.table-box{
&::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px var(--l-bdc);
}
&::-webkit-scrollbar-thumb {
background-color: var(--l-icon);
outline: 1px solid var(--l-icon);
}
}
}
.search-bottom{
border-top: 1px solid #f0f0f0;
.setting{
.el-link, .card{
color: var(--l-c);
}
}
}
.collection{
.ivu-table-cell{
button{
margin: 0 4px;
.detail{
color: var(--l-c);
&::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px var(--l-bdc);
}
&::-webkit-scrollbar-thumb {
background-color: var(--l-icon);
outline: 1px solid var(--l-icon);
}
.box, .info, .urls{
border: 1px solid var(--l-bdc);
}
.vodInfo{
li, span, a{
color: var(--l-c);
}
label{
color: #f90;
}
}
}

View File

@@ -2,15 +2,16 @@
import { app, protocol, ipcMain, BrowserWindow } from 'electron'
import {
createProtocol
createProtocol,
installVueDevtools
} from 'vue-cli-plugin-electron-builder/lib'
import path from 'path'
import { autoUpdater } from 'electron-updater'
const isDevelopment = process.env.NODE_ENV !== 'production'
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win
let win: BrowserWindow | null
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])
@@ -24,12 +25,14 @@ function createWindow () {
webPreferences: {
webSecurity: false,
nodeIntegration: true
}
},
// @ts-ignore
icon: path.join(__static, 'icon.png')
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
@@ -60,15 +63,25 @@ app.on('activate', () => {
}
})
ipcMain.on('min', e => win.minimize())
ipcMain.on('max', e => {
if (win.isMaximized()) {
win.unmaximize()
} else {
win.maximize()
ipcMain.on('min', () => {
if (win) {
win.minimize()
}
})
ipcMain.on('max', e => {
if (win) {
if (win.isMaximized()) {
win.unmaximize()
} else {
win.maximize()
}
}
})
ipcMain.on('close', e => {
if (win) {
win.close()
}
})
ipcMain.on('close', e => win.close())
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.

159
src/components/detail.vue Normal file
View File

@@ -0,0 +1,159 @@
<template>
<el-row class="detail">
<el-row class="detail-box" v-loading="loading">
<el-row v-html="detail.box" class="box"></el-row>
<el-row v-html="detail.info" class="info"></el-row>
<el-row class="urls">
<el-button size="mini" v-for="(i, j) in detail.urls" :key="j" @click="playBtn(i, j)">{{i | ftLink}}</el-button>
</el-row>
</el-row>
</el-row>
</template>
<script lang="ts">
import Vue from 'vue'
import zy from '@/lib/util.zy'
import { mapMutations } from 'vuex'
export default Vue.extend({
name: 'detail',
data () {
return {
detail: {},
loading: true
}
},
computed: {
d () {
return this.$store.getters.getDetail
},
video: {
get () {
return this.$store.getters.getVideo
},
set (val) {
this.SET_VIDEO(val)
}
},
Main: {
get () {
return this.$store.getters.getMain
},
set (val) {
this.SET_MAIN(val)
}
}
},
watch: {
d () {
this.getDetailEvent()
}
},
filters: {
ftLink (e: string) {
let name = e.split('$')[0]
return name
}
},
methods: {
...mapMutations(['SET_MAIN', 'SET_VIDEO']),
getDetailEvent () {
let url = this.d.video.detail
this.detail = {}
this.loading = true
zy.detail(url).then((res: any) => {
this.detail = res
this.loading = false
})
},
playBtn (i: string, j: number) {
if (this.Main !== 'Player') {
this.d.video.index = j
this.video = this.d.video
this.Main = 'Player'
} else {
this.d.video.index = j
this.video = this.d.video
}
this.d.show = false
}
},
created () {
this.getDetailEvent()
},
mounted () {}
})
</script>
<style lang="scss">
.detail{
box-sizing: border-box;
padding: 0 60px;
.detail-box{
width: 100%;
.box{
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
width: 100%;
padding: 10px;
.vodImg{
width: 200px;
img{
width: 100%;
height: auto;
}
}
.vodAd{
display: none;
}
.vodInfo{
flex: 1;
margin-left: 20px;
.vodh{
h2{
display: inline-block;
}
span{
font-size: 12px;
margin-left: 10px;
}
label{
font-size: 20px;
font-weight: bold;
margin-left: 20px;
}
}
.cont, .tags{
display: none;
}
li{
list-style: none;
font-size: 14px;
height: 20px;
overflow: hidden;
a{
display: none;
pointer-events: none;
}
span{
word-wrap:break-word
}
}
}
}
.info, .urls{
padding: 10px;
margin-top: 10px;
}
.info{
font-size: 14px;
}
.urls{
margin-bottom: 20px;
padding-bottom: 0;
button{
margin: 0 10px 10px 0;
}
}
}
}
</style>

View File

@@ -1,13 +0,0 @@
<template>
<router-view />
</template>
<script>
export default {
name: 'zy_content',
methods: {
switchTheme (e) {
this.theme = e
}
}
}
</script>

View File

@@ -1,39 +0,0 @@
<template>
<Row class="header-box">
<Icon type="md-remove" @click="clickFrameEvent('min')" />
<Icon type="md-add" @click="clickFrameEvent('max')" />
<Icon type="md-close" @click="clickFrameEvent('close')" />
</Row>
</template>
<script>
const { ipcRenderer: ipc } = require('electron')
export default {
name: 'zy_header',
methods: {
clickFrameEvent (e) {
ipc.send(e)
}
}
}
</script>
<style lang="scss" scoped>
.header-box{
position: absolute;
top: 0;
left: 70px;
width: calc(100% - 70px);
height: 50px;
-webkit-app-region: drag;
-webkit-user-select: none;
display: flex;
justify-content: flex-end;
i{
font-size: 20px;
width:50px;
height:50px;
line-height:50px;
cursor: pointer;
-webkit-app-region: no-drag;
}
}
</style>

View File

@@ -1,52 +0,0 @@
<template>
<Row class="sider-box">
<div class="top">
<Icon title="搜索" :class="iconActive === 'search' ? 'active': ''" type="md-search" @click="iconClickEvent('search')"/>
<Icon title="详情" v-show="Object.keys(video).length !== 0" :class="iconActive === 'detail' ? 'active': ''" type="md-list" @click="iconClickEvent('detail')"/>
<Icon title="播放" v-show="Object.keys(video).length !== 0" :class="iconActive === 'play' ? 'active': ''" type="md-play" @click="iconClickEvent('play')"/>
<Icon title="收藏" :class="iconActive === 'collection' ? 'active': ''" type="md-star" @click="iconClickEvent('collection')"/>
</div>
<div class="bottom">
<Icon title="设置" :class="iconActive === 'settings' ? 'active': ''" type="md-settings" @click="iconClickEvent('settings')"/>
</div>
</Row>
</template>
<script>
export default {
name: 'zy-sider',
computed: {
iconActive () {
return this.$store.getters.getIconActive
},
video () {
return this.$store.getters.getVideo
}
},
methods: {
iconClickEvent (e) {
this.$router.push({ name: e })
this.$store.commit('SET_ICON_ACTIVE', e)
}
}
}
</script>
<style lang="scss" scoped>
.sider-box{
height: 100%;
position: relative;
-webkit-app-region: drag;
-webkit-user-select: none;
.bottom{
position: absolute;
bottom: 0;
}
i{
-webkit-app-region: no-drag;
font-size: 32px;
width: 70px;
height: 70px;
line-height: 70px;
cursor: pointer;
}
}
</style>

View File

@@ -1,29 +0,0 @@
const sites = [
{
id: 'okzy',
name: 'OK资源网',
url: 'https://www.okzy.co'
},
{
id: 'zuidazy',
name: '最大资源网',
url: 'http://www.zuidazy1.com'
},
{
id: 'subo',
name: '速播资源站',
url: 'https://www.subo988.com'
},
{
id: 'zuixinzy',
name: '最新资源网',
url: 'http://www.zuixinzy.cc'
},
{
id: '123ku',
name: '123资源网',
url: 'https://www.123ku.com'
}
]
export default sites

41
src/lib/sites.ts Normal file
View File

@@ -0,0 +1,41 @@
interface Site {
id: string
name: string
url: string
type: Array<any>
}
const sites:Array<Site> = [
{
id: 'okzy',
name: 'OK资源网',
url: 'https://www.okzy.co',
type: [[ '最新', '0' ], [ '电影', '1' ], [ '电视剧', '2' ], [ '综艺', '3' ], [ '动漫', '4' ], [ '伦理', '21' ], [ '福利', '22' ], [ '解说', '33' ]]
},
{
id: 'zuidazy',
name: '最大资源网',
url: 'http://www.zuidazy1.com',
type: [[ '最新', '0' ], [ '电影', '1' ], [ '电视剧', '2' ], [ '综艺', '3' ], [ '动漫', '4' ], [ '福利', '16' ], [ '伦理', '17' ], [ '音乐', '18' ]]
},
{
id: 'subo',
name: '速播资源站',
url: 'https://www.subo988.com',
type: [[ '最新', '0' ], [ '电影', '1' ], [ '电视剧', '2' ], [ '综艺', '3' ], [ '动漫', '4' ], [ '伦理', '16' ], [ '音乐', '20' ]]
},
{
id: 'zuixinzy',
name: '最新资源网',
url: 'http://www.zuixinzy.cc',
type: [[ '最新', '0' ], [ '电影', '1' ], [ '电视剧', '2' ], [ '综艺', '3' ], [ '动漫', '4' ], ['伦理', '21'], ['情色', '23'], [ '福利', '30' ], [ '解说', '34' ]]
},
{
id: '123ku',
name: '123资源网',
url: 'https://www.123ku.com',
type: [[ '最新', '0' ], [ '电影', '1' ], [ '电视剧', '2' ], [ '综艺', '3' ], [ '动漫', '4' ], [ '伦理', '16' ]]
}
]
export default sites

View File

@@ -1,94 +0,0 @@
import axios from 'axios'
import sites from './sites'
const zy = {
num: 0,
page: 1,
key: '',
site: {},
list: [],
getInfoRequire () {
return new Promise((resolve, reject) => {
const key = encodeURI(this.key)
const params = `${this.site.url}/index.php?m=vod-search-pg-${this.page}-wd-${key}.html`
axios.get(params).then(res => {
this.getInfoHtml(res.data).then(res => {
resolve(res)
})
}).catch(err => {
reject(err)
})
})
},
getInfoHtml (txt) {
return new Promise((resolve, reject) => {
const parser = new DOMParser()
const html = parser.parseFromString(txt, 'text/html')
const list = html.querySelectorAll('.xing_vb li')
let d = {
list: [],
num: 0
}
for (let i = 1; i < list.length - 1; i++) {
let info = {
name: list[i].childNodes[1].innerText,
detail: this.site.url + list[i].childNodes[1].childNodes[0].getAttribute('href'),
category: list[i].childNodes[3].innerText,
time: list[i].childNodes[5].innerText,
index: 0,
urls: [],
check: false
}
d.list.push(info)
}
let num = html.querySelectorAll('.nvc dd span')[1].innerText
num = parseInt(num)
d.num = num
resolve(d)
})
},
info (n = 0, p = 1, k = null) {
return new Promise((resolve, reject) => {
this.page = p
this.key = k
this.num = n
this.site = sites[this.num]
this.getInfoRequire().then(res => {
resolve(res)
})
})
},
detail (url) {
return new Promise((resolve, reject) => {
axios.get(url).then(res => {
resolve(this.getDetailUrls(res.data))
}).catch(err => {
reject(err)
})
})
},
getDetailUrls (txt) {
return new Promise((resolve, reject) => {
const parser = new DOMParser()
let html = parser.parseFromString(txt, 'text/html')
let data = {
box: null,
info: null,
urls: []
}
data.box = html.querySelector('.vodBox').innerHTML
data.info = html.querySelector('.vodplayinfo').innerHTML
let urls = html.querySelectorAll('.vodplayinfo li')
let arr = []
for (let i in urls) {
let j = urls[i].innerText
if (j !== undefined && j.indexOf('.m3u8') !== -1) {
arr.push(urls[i].innerText)
}
}
data.urls = arr
resolve(data)
})
}
}
export default zy

171
src/lib/util.zy.ts Normal file
View File

@@ -0,0 +1,171 @@
import fly from 'flyio'
import sites from './sites'
import { TimeSelect } from 'element-ui'
interface ZY {
num: number
page: number
key: string
site: any
list: Array<string>
getInfoRequire: any
getInfoHtml: any
info: any
detail: any
films: any
}
interface info {
name?: string
type?: string
time?: string
detail?: string
urls?: Array<string>
index?: number
}
interface detail {
box?: any
info?: any
urls?: Array<string>
}
const zy: ZY = {
num: 0,
page: 1,
key: '',
site: {},
list: [],
getInfoRequire () {
return new Promise((resolve, reject) => {
let key = encodeURI(this.key)
const params = `${this.site.url}/index.php?m=vod-search-pg-${this.page}-wd-${key}.html`
fly.get(params).then(res => {
this.getInfoHtml(res.data).then((data: any) => {
resolve(data)
})
}).catch(err => {
reject(err)
})
})
},
getInfoHtml (txt: string): any {
return new Promise((resolve, reject) => {
try {
const parser = new DOMParser()
const html = parser.parseFromString(txt, 'text/html')
const list: any = html.querySelectorAll('.xing_vb li')
let d: any = { list: [], total: 0 }
for (let i = 1; i < list.length - 1; i++) {
let info: info = {
name: list[i].childNodes[1].innerText,
type: list[i].childNodes[3].innerText,
time: list[i].childNodes[5].innerText,
detail: this.site.url + list[i].childNodes[1].childNodes[0].getAttribute('href'),
index: 0,
urls: []
}
d.list.push(info)
}
let num: any = (<HTMLImageElement>html.querySelectorAll('.nvc dd span')[1]).innerText
num = parseInt(num)
d.total = num
resolve(d)
} catch (err) {
reject(err)
}
})
},
info (n: number = 0, p: number = 1, k: string = '') {
return new Promise((resolve, reject) => {
this.num = n
this.page = p
this.key = k
this.site = sites[n]
this.getInfoRequire().then((res: any) => {
resolve(res)
}).catch((err: any) => {
reject(err)
})
})
},
detail (url: string) {
return new Promise((resolve, reject) => {
if (!url) return
fly.get(url).then(res => {
const parser = new DOMParser()
let html = parser.parseFromString(res.data, 'text/html')
let data: detail = {
box: '',
info: '',
urls: []
}
let vodBox = html.querySelector('.vodBox')
if (vodBox) {
data.box = vodBox.innerHTML
}
let vodInfo = {}
if (url.indexOf('123ku') !== -1 || url.indexOf('subo988') !== -1) {
vodInfo = html.querySelectorAll('.vodplayinfo')[1]
} else {
vodInfo = <HTMLImageElement>html.querySelector('.vodplayinfo')
}
data.info = (<HTMLImageElement>vodInfo).innerText
let urls = html.querySelectorAll('.vodplayinfo li')
let arr = []
for (let i in urls) {
let j = (<HTMLImageElement>urls[i]).innerText
if (j !== undefined && j.indexOf('.m3u8') !== -1) {
arr.push((<HTMLImageElement>urls[i]).innerText)
}
}
data.urls = arr
resolve(data)
}).catch(err => {
reject(err)
})
})
},
films (n: number = 0, p: number = 1, type: string = '0') {
return new Promise((resolve, reject) => {
this.site = sites[n]
let url: string = sites[n].url
let params: string = ''
if (type === '0') {
params = `${url}/?m=vod-index-pg-${p}.html`
} else {
params = `${url}/?m=vod-type-id-${type}-pg-${p}.html`
}
fly.get(params).then(res => {
const parser = new DOMParser()
const html = parser.parseFromString(res.data, 'text/html')
const list: any = html.querySelectorAll('.xing_vb li')
let d: any = { list: [], total: 0 }
for (let i = 1; i < list.length - 1; i++) {
let info: info = {
name: list[i].childNodes[1].innerText,
type: list[i].childNodes[3].innerText,
time: list[i].childNodes[5].innerText,
detail: this.site.url + list[i].querySelector('a').getAttribute('href'),
index: 0,
urls: []
}
d.list.push(info)
}
let num: any = html.querySelectorAll('.pages')
if (num) {
let n = num[0].innerText
n = n.split('条')[0]
n = n.split('共')[1]
n = parseInt(n)
d.total = n
}
resolve(d)
}).catch(err => {
reject(err)
})
})
}
}
export default zy

View File

@@ -1,13 +1,14 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/plugin/iview/'
import './plugins/element.ts'
import Register from './page/register'
Vue.config.productionTip = false
Register.registerComponents()
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')

166
src/page/film.vue Normal file
View File

@@ -0,0 +1,166 @@
<template>
<el-row class="film">
<el-row class="film-tabs">
<el-tabs v-model="tabs" @tab-click="tabClick">
<el-tab-pane class="film-tabpane" v-for="(i, j) in sites[site].type" :key="j" :label="i[0]" :name="i[1]"></el-tab-pane>
</el-tabs>
</el-row>
<el-row class="film-table-box table-box">
<el-table :data="filmData" stripe class="film-table" v-loading="loading" size="mini">
<el-table-column prop="name" label="影片名称"></el-table-column>
<el-table-column prop="type" label="影片类别" width="120"></el-table-column>
<el-table-column prop="time" label="更新时间" width="180"></el-table-column>
<el-table-column label="操作" width="130">
<template slot-scope="scope">
<el-button type="text" @click="tableBtnClick('detail', scope.row)">详情</el-button>
<el-button type="text" @click="tableBtnClick('star', scope.row)">收藏</el-button>
<el-button type="text" @click="tableBtnClick('play', scope.row)">播放</el-button>
</template>
</el-table-column>
</el-table>
</el-row>
<el-row class="film-bottom" type="flex" justify="space-between">
<el-select v-model="site" placeholder="请选择" size="small" @change="selectSite">
<el-option
v-for="(i, j) in sites"
:key="i.id"
:label="i.name"
:value="j">
</el-option>
</el-select>
<el-pagination
small
layout="total, prev, pager, next, jumper"
:current-page="filmPage"
:page-size="50"
:total="filmTotal">
</el-pagination>
</el-row>
</el-row>
</template>
<script lang="ts">
import Vue from 'vue'
import zy from '@/lib/util.zy'
import sites from '@/lib/sites'
import { mapMutations } from 'vuex'
import video from '@/plugins/dexie/video'
export default Vue.extend({
data () {
return {
sites: sites,
tabs: '0',
filmPage: 1,
filmTotal: 0,
filmData: [],
loading: false
}
},
computed: {
video: {
get () {
return this.$store.getters.getVideo
},
set (val) {
this.SET_VIDEO(val)
}
},
site: {
get () {
return this.$store.getters.getSite
},
set (val) {
this.SET_SITE(val)
}
},
Main: {
get () {
return this.$store.getters.getMain
},
set (val) {
this.SET_MAIN(val)
}
}
},
methods: {
...mapMutations(['SET_SITE', 'SET_DETAIL', 'SET_MAIN', 'SET_VIDEO']),
tabClick (tab:any) {
this.getFilmList(this.site, this.filmPage, tab.name)
},
selectSite (e:any) {
this.site = e
this.tabs = '0'
this.filmPage = 1
this.getFilmList(e, 1, '0')
},
getFilmList (n: number = 0, p: number = 1, type: string = '0') {
this.loading = true
this.filmData = []
this.tabs = type
zy.films(n, p, type).then((res: any) => {
this.filmTotal = res.total
this.filmData = res.list
this.filmPage = p
this.loading = false
})
},
tableBtnClick (type: string, e: any) {
if (type === 'detail') {
let d = {
show: true,
video: e
}
this.SET_DETAIL(d)
}
if (type === 'star') {
video.find({ detail: e.detail }).then(res => {
if (res) {
this.$message.warning('已存在')
} else {
video.add(e).then(res => {
this.$message.success('收藏成功')
})
}
})
}
if (type === 'play') {
this.Main = 'Player'
this.video = e
}
}
},
created () {
this.getFilmList()
}
})
</script>
<style lang="scss" scoped>
.film{
height: 100%;
position: relative;
.film-tabs{
position: absolute;
top: 0px;
left: 0;
width: 100%;
height: 60px;
}
.film-table-box{
position: absolute;
top: 54px;
width: 100%;
height: calc(100% - 100px);
overflow: auto;
&::-webkit-scrollbar{
width: 6px;
}
}
.film-bottom{
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 40px;
align-items: center;
}
}
</style>

237
src/page/player.vue Normal file
View File

@@ -0,0 +1,237 @@
<template>
<el-row class="player">
<el-row class="player-show" v-if="Object.keys(video).length > 0">
<el-row class="player-title">
<el-row class="player-title-box" type="flex" justify="space-between">
<span>
<span>{{video.name ? video.name + ' -- ' : '' }}</span>
<span>{{ num }}</span>
</span>
<span>
<el-button size="mini" @click="openDetail" icon="el-icon-document" circle></el-button>
<el-button size="mini" v-show="!star" @click="starEvent" icon="el-icon-star-off" circle></el-button>
<el-button size="mini" v-show="star" @click="starEvent" icon="el-icon-star-on" circle></el-button>
</span>
</el-row>
</el-row>
<el-row class="player-box">
<div id="xg"></div>
</el-row>
<el-row class="player-films table-box">
<el-row class="player-films-box">
<el-button :type="j === video.index ? 'primary' : ''" size="mini" v-for="(i, j) in urls" :key="j" @click="playBtnClick(i, j)" plain>{{i | ftLink}}</el-button>
</el-row>
</el-row>
</el-row>
<el-row class="player-none" v-if="Object.keys(video).length <= 0">
<el-row class="tips">
浏览或者搜索资源, 点击播放视频, 即可观看~
</el-row>
<el-row class="btns">
<el-button size="small" @click="goView('Film')" icon="el-icon-film">浏览</el-button>
<el-button size="small" @click="goView('Search')" icon="el-icon-search">搜索</el-button>
</el-row>
</el-row>
</el-row>
</template>
<script lang="ts">
import Vue from 'vue'
import { mapMutations } from 'vuex'
import zy from '@/lib/util.zy'
import 'xgplayer'
// @ts-ignore
import Hls from 'xgplayer-hls.js'
import video from '@/plugins/dexie/video'
export default Vue.extend({
data () {
return {
xg: null,
config: {
id: 'xg',
url: '',
fluid: false,
autoplay: true,
videoInit: true,
keyShortcut: 'on',
defaultPlaybackRate: 1,
playbackRate: [0.5, 0.75, 1, 1.5, 2]
},
urls: [],
num: '',
star: false
}
},
computed: {
d () {
return this.$store.getters.getDetail
},
video: {
get () {
return this.$store.getters.getVideo
},
set (val) {
this.SET_VIDEO(val)
}
},
Main: {
get () {
return this.$store.getters.getMain
},
set (val) {
this.SET_MAIN(val)
}
}
},
watch: {
video: {
handler () {
this.getUrls()
},
deep: true
}
},
filters: {
ftLink (e: string) {
let name = e.split('$')[0]
return name
}
},
methods: {
...mapMutations(['SET_DETAIL', 'SET_VIDEO', 'SET_MAIN']),
goView (e: string) {
this.Main = e
},
openDetail () {
let d = {
show: true,
video: this.video
}
this.SET_DETAIL(d)
},
getUrls () {
if (this.xg) {
// @ts-ignore
this.xg.destroy(true)
this.xg = null
}
this.checkStar()
zy.detail(this.video.detail).then((res: any) => {
this.urls = res.urls
if (this.xg === null) {
let info: any = this.urls[this.video.index]
let url = info.split('$')[1]
this.num = info.split('$')[0]
this.config.url = url
this.$nextTick(() => {
this.xg = new Hls(this.config)
// @ts-ignore
this.xg.on('error', () => {
this.$message.error('播放失败请重试~')
})
})
}
})
},
checkStar () {
video.find({ detail: this.video.detail }).then(res => {
if (res) {
this.star = true
} else {
this.star = false
}
})
},
starEvent () {
video.find({ detail: this.video.detail }).then(res => {
if (res) {
video.remove(res.id).then(res => {
if (!res) {
this.$message.success('删除成功')
this.star = false
} else {
this.$message.warning('删除失败, 请重试~')
}
})
} else {
video.add(this.video).then(res => {
this.star = true
this.$message.success('收藏成功')
})
}
})
},
playBtnClick (i: string, j: number) {
if (this.video.index !== j) {
let url = i.split('$')[1]
this.num = i.split('$')[0]
this.video.index = j
// @ts-ignore
this.xg.src = url
}
}
}
})
</script>
<style lang="scss" scoped>
.player{
height: 100%;
width: 100%;
position: relative;
.player-show{
height: 100%;
width: 100%;
position: relative;
}
.player-none{
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.tips{
font-size: 14px;
margin-bottom: 10px;
}
}
.player-title{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 50px;
line-height: 50px;
.player-title-box{
width: 600px;
margin: 0 auto;
}
}
.player-box{
position: absolute;
top: 50px;
left: 0;
width: 100%;
height: 350px;
#xg{
margin: 0 auto;
}
}
.player-films{
position: absolute;
top: 400px;
left: 0;
width: 100%;
height: calc(100% - 400px);
overflow: auto;
button{
margin: 0 10px 10px 0;
}
&::-webkit-scrollbar{
width: 6px;
}
.player-films-box{
width: 600px;
margin: 0 auto;
}
}
}
</style>

16
src/page/register.ts Normal file
View File

@@ -0,0 +1,16 @@
import Vue from 'vue'
import Film from './film.vue'
import Search from './search.vue'
import Player from './player.vue'
import Star from './star.vue'
import Setting from './setting.vue'
export default {
registerComponents () {
Vue.component('Film', Film)
Vue.component('Search', Search)
Vue.component('Player', Player)
Vue.component('Star', Star)
Vue.component('Setting', Setting)
}
}

193
src/page/search.vue Normal file
View File

@@ -0,0 +1,193 @@
<template>
<el-row class="search">
<el-row class="search-box" :class="table === true ? 'search-box hasTable' : 'search-box'">
<el-input class="search-input" size="medium" clearable placeholder="请输入内容" v-model.trim="keywords" @change="searchEvent">
<el-select v-model="site" slot="prepend" placeholder="请选择" @change="selectSite" style="width: 130px;">
<el-option v-for="(i, j) in sites" :key="i.id" :label="i.name" :value="j"></el-option>
</el-select>
<el-button slot="append" icon="el-icon-search" @click="searchEvent"></el-button>
</el-input>
</el-row>
<el-row v-show="table" class="search-table-box table-box">
<el-table :data="filmData" stripe class="search-table" size="mini" v-loading="loading">
<el-table-column prop="name" label="影片名称"></el-table-column>
<el-table-column prop="type" label="影片类别" width="120"></el-table-column>
<el-table-column prop="time" label="更新时间" width="180"></el-table-column>
<el-table-column label="操作" width="130">
<template slot-scope="scope">
<el-button type="text" @click="tableBtnClick('detail', scope.row)">详情</el-button>
<el-button type="text" @click="tableBtnClick('star', scope.row)">收藏</el-button>
<el-button type="text" @click="tableBtnClick('play', scope.row)">播放</el-button>
</template>
</el-table-column>
</el-table>
</el-row>
<el-row v-show="table" class="search-bottom" type="flex" justify="end">
<el-pagination
small
layout="total, prev, pager, next, jumper"
:current-page="filmPage"
:page-size="50"
@current-change="pageChange"
:total="filmTotal">
</el-pagination>
</el-row>
</el-row>
</template>
<script lang="ts">
import Vue from 'vue'
import sites from '@/lib/sites'
import zy from '@/lib/util.zy'
import { mapMutations } from 'vuex'
import video from '@/plugins/dexie/video'
export default Vue.extend({
data () {
return {
table: false,
sites: sites,
keywords: '',
filmData: [],
filmPage: 1,
filmTotal: 0,
loading: false
}
},
computed: {
video: {
get () {
return this.$store.getters.getVideo
},
set (val) {
this.SET_VIDEO(val)
}
},
site: {
get () {
return this.$store.getters.getSite
},
set (val) {
this.SET_SITE(val)
}
},
Main: {
get () {
return this.$store.getters.getMain
},
set (val) {
this.SET_MAIN(val)
}
}
},
methods: {
...mapMutations(['SET_SITE', 'SET_DETAIL', 'SET_MAIN', 'SET_VIDEO']),
selectSite (e:number) {
this.site = e
this.filmData = []
this.filmPage = 1
this.filmTotal = 0
this.searchEvent()
},
searchEvent () {
if (this.keywords !== '') {
this.loading = true
this.table = true
zy.info(this.site, this.filmPage, this.keywords).then((res: any) => {
this.filmData = res.list
this.filmTotal = res.total
this.loading = false
})
}
},
pageChange (e:number) {
this.filmPage = e
this.searchEvent()
},
tableBtnClick (type: string, e: any) {
if (type === 'detail') {
let d = {
show: true,
video: e
}
this.SET_DETAIL(d)
}
if (type === 'star') {
video.find({ detail: e.detail }).then(res => {
if (res) {
this.$message.warning('已存在')
} else {
video.add(e).then(res => {
this.$message.success('收藏成功')
})
}
})
}
if (type === 'play') {
this.Main = 'Player'
this.video = e
}
}
},
created () {}
})
</script>
<style lang="scss" scoped>
.search{
height: 100%;
position: relative;
.search-box{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
animation: slideDown 0.6s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
@keyframes slideDown {
from {
height: 40px;
}
to{
height: 90%;
}
}
&.hasTable{
animation: slideUp 0.2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
@keyframes slideUp {
from {
height: 90%;
opacity: 0;
}
to{
height: 40px;
opacity: 1;
}
}
}
.search-table-box{
position: absolute;
top: 40px;
width: 100%;
height: calc(100% - 100px);
overflow: auto;
&::-webkit-scrollbar{
width: 6px;
}
.search-table{
width: 100%;
}
}
.search-bottom{
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 40px;
display: flex;
justify-content: flex-end;
align-items: center;
}
}
</style>

92
src/page/setting.vue Normal file
View File

@@ -0,0 +1,92 @@
<template>
<el-row class="setting">
<el-row class="item about">
<el-row class="title"><i class="el-icon-view"></i><span>关于</span></el-row>
<el-row class="info">
<ul>
<li><el-link :underline="false" @click="linkOpen('https://zy_player.hunlongyu.fun')">官网: ZY Player</el-link></li>
<li><el-link :underline="false" @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/issues')">反馈: Issues</el-link></li>
</ul>
</el-row>
</el-row>
<el-row class="item theme">
<el-row class="title"><i class="el-icon-picture-outline-round"></i><span>主题 (即将来到...)</span></el-row>
<el-row class="card-box">
<el-card shadow="hover" class="card">
<img src="@/assets/image/light.png" class="image">
<span size="mini">Light</span>
</el-card>
<el-card shadow="hover" class="card">
<img src="@/assets/image/light.png" class="image">
<span size="mini">Dark</span>
</el-card>
</el-row>
</el-row>
<!-- <el-row class="item update">
<el-row class="title"><i class="el-icon-refresh"></i><span>更新</span></el-row>
<el-row class="btns">
<el-button size="small">检查更新</el-button>
</el-row>
</el-row> -->
</el-row>
</template>
<script lang="ts">
import Vue from 'vue'
import { shell } from 'electron'
import setting from '@/plugins/dexie/setting'
export default Vue.extend({
methods: {
linkOpen (e:string) {
if (e) {
shell.openExternal(e)
}
}
},
created () {}
})
</script>
<style lang="scss" scoped>
.setting{
.item{
margin-bottom: 30px;
.title{
height: 24px;
line-height: 24px;
margin-bottom: 10px;
display: flex;
align-items: center;
i{
font-size: 24px;
margin-right: 6px;
}
}
}
.about{
ul{
list-style: none;
li{
height: 30px;
}
}
}
.theme{
.card-box{
display: flex;
justify-content: flex-start;
.card{
width: 160px;
margin-right: 20px;
text-align: center;
cursor: pointer;
img{
width: 100%;
}
span{
font-size: 14px;
margin-top: 10px;
}
}
}
}
}
</style>

128
src/page/star.vue Normal file
View File

@@ -0,0 +1,128 @@
<template>
<el-row class="star">
<el-row class="star-table-box table-box">
<el-table :data="filmData" stripe class="film-table" size="mini" empty-text="收藏夹里空空的~快去填满我吧~">
<el-table-column prop="name" label="影片名称"></el-table-column>
<el-table-column prop="type" label="影片类别" width="120"></el-table-column>
<el-table-column prop="time" label="更新时间" width="180"></el-table-column>
<el-table-column label="操作" width="130">
<template slot-scope="scope">
<el-button type="text" @click="tableBtnClick('detail', scope.row)">详情</el-button>
<el-button type="text" @click="tableBtnClick('delete', scope.row)">删除</el-button>
<el-button type="text" @click="tableBtnClick('play', scope.row)">播放</el-button>
</template>
</el-table-column>
</el-table>
</el-row>
<el-row class="star-bottom">
<el-pagination
small
layout="total, prev, pager, next, jumper"
:current-page="filmPage"
:page-size="50"
:total="filmTotal">
</el-pagination>
</el-row>
</el-row>
</template>
<script lang="ts">
import Vue from 'vue'
import video from '@/plugins/dexie/video'
import { mapMutations } from 'vuex'
export default Vue.extend({
data () {
return {
filmData: [],
filmPage: 1,
filmTotal: 0
}
},
computed: {
video: {
get () {
return this.$store.getters.getVideo
},
set (val) {
this.SET_VIDEO(val)
}
},
Main: {
get () {
return this.$store.getters.getMain
},
set (val) {
this.SET_MAIN(val)
}
}
},
watch: {
Main: {
handler () {
this.getAllStar()
},
deep: true
}
},
methods: {
...mapMutations(['SET_DETAIL', 'SET_VIDEO', 'SET_MAIN']),
getAllStar () {
video.all().then(res => {
this.filmData = res
this.filmTotal = res.length
})
},
tableBtnClick (type: string, e: any) {
if (type === 'detail') {
let d = {
show: true,
video: e
}
this.SET_DETAIL(d)
}
if (type === 'delete') {
video.remove(e.id).then(res => {
if (!res) {
this.$message.success('删除成功')
} else {
this.$message.warning('删除失败, 请重试~')
}
this.getAllStar()
})
}
if (type === 'play') {
this.Main = 'Player'
this.video = e
}
}
},
created () {
this.getAllStar()
}
})
</script>
<style lang="scss" scoped>
.star{
height: 100%;
position: relative;
.star-table-box{
position: absolute;
top: 0px;
width: 100%;
height: calc(100% - 40px);
overflow: auto;
&::-webkit-scrollbar{
width: 6px;
}
}
.star-bottom{
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 40px;
display: flex;
justify-content: flex-end;
align-items: center;
}
}
</style>

View File

@@ -1,27 +0,0 @@
import Vue from 'vue'
import 'view-design/dist/styles/iview.css'
import {
Layout, Sider, Header, Content, Row, Col,
Icon, Button, Input, Select, Option, Table,
Message, Notice, Page
} from 'view-design'
Vue.component('Layout', Layout)
Vue.component('Sider', Sider)
Vue.component('Header', Header)
Vue.component('Content', Content)
Vue.component('Row', Row)
Vue.component('Col', Col)
Vue.component('Icon', Icon)
Vue.component('Button', Button)
Vue.component('Input', Input)
Vue.component('Select', Select)
Vue.component('Option', Option)
Vue.component('Table', Table)
Vue.component('Page', Page)
Vue.prototype.$Message = Message
Vue.prototype.$Notice = Notice
Vue.prototype.$Notice.config({
top: 60
})

View File

@@ -1,36 +0,0 @@
import Nedb from 'nedb'
export default class {
constructor () {
this.db = null
}
create (db) {
const name = process.env.NODE_ENV === 'development' ? 'ZY-dev' : 'ZY'
const database = {}
database.setting = new Nedb({
filename: name + db.setting,
autoload: true
})
database.video = new Nedb({
filename: name + db.video,
autoload: true
})
return database
}
init () {
if (this.db) {
return this.db
}
this.db = this.create({
setting: '-setting',
video: '-video'
})
return this.db
}
}

View File

@@ -1,32 +0,0 @@
import DB from './index'
const db = new DB()
const connect = db.init()
const setting = connect.setting
export default {
add (data) {
return new Promise((resolve, reject) => {
setting.insert(data, (err, docs) => {
if (err) { reject(err) }
resolve(docs)
})
})
},
find (data) {
return new Promise((resolve, reject) => {
setting.find(data, (err, docs) => {
if (err) { reject(err) }
resolve(docs)
})
})
},
update (id, data) {
return new Promise((resolve, reject) => {
setting.update({ _id: id }, { $set: data }, (err, docs) => {
if (err) { reject(err) }
resolve(docs)
})
})
}
}

View File

@@ -1,49 +0,0 @@
// import DB from './index'
import DB from './index'
const db = new DB()
const connect = db.init()
const video = connect.video
export default {
add (data) {
return new Promise((resolve, reject) => {
video.insert(data, (err, docs) => {
if (err) { reject(err) }
resolve(docs)
})
})
},
find (data) {
return new Promise((resolve, reject) => {
video.find(data, (err, docs) => {
if (err) { reject(err) }
resolve(docs)
})
})
},
update (id, data) {
return new Promise((resolve, reject) => {
video.find({ _id: id }, { $set: data }, (err, docs) => {
if (err) { reject(err) }
resolve(docs)
})
})
},
remove (id) {
return new Promise((resolve, reject) => {
video.remove({ _id: id }, {}, (err, docs) => {
if (err) { reject(err) }
resolve(docs)
})
})
},
removeAll () {
return new Promise((resolve, reject) => {
video.remove({}, { multi: true }, (err, docs) => {
if (err) { reject(err) }
resolve(docs)
})
})
}
}

View File

@@ -0,0 +1,35 @@
import Dexie from 'dexie'
class ZYDB extends Dexie {
setting: Dexie.Table<setting, number>
video: Dexie.Table<video, number>
constructor () {
super('ZYDB')
this.version(1).stores({
setting: '++id, theme, site',
video: '++id, name, type, time, detail, urls, index'
})
this.setting = this.table('setting')
this.video = this.table('video')
}
}
export interface setting {
id?: number
theme?: string
site?: object
}
export interface video {
id?: number
name?: string
type?: string
time?: string
detail?: string
urls?: Array<string>
index?: number
}
export default new ZYDB()

View File

@@ -0,0 +1,42 @@
import db from './index'
export default {
add (data: any): Promise<any> {
return new Promise((resolve, reject) => {
db.setting.add(data).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
},
find (data?: any): Promise<any> {
return new Promise((resolve, reject) => {
db.setting.get(data).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
},
update (id: number, data: any): Promise<any> {
return new Promise((resolve, reject) => {
db.setting.update(id, data).then(updated => {
if (updated) {
resolve(updated)
} else {
reject(updated)
}
})
})
},
count (): Promise<any> {
return new Promise((resolve, reject) => {
db.setting.count().then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
}

View File

@@ -0,0 +1,69 @@
import db from './index'
export default {
add (data: any): Promise<any> {
return new Promise((resolve, reject) => {
db.video.add(data).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
},
find (data?: any): Promise<any> {
return new Promise((resolve, reject) => {
db.video.get(data).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
},
all (): Promise<any> {
return new Promise((resolve, reject) => {
db.video.toArray().then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
},
update (id: number, data: any): Promise<any> {
return new Promise((resolve, reject) => {
db.video.update(id, data).then(updated => {
if (updated) {
resolve(updated)
} else {
reject(updated)
}
})
})
},
count (): Promise<any> {
return new Promise((resolve, reject) => {
db.video.count().then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
},
remove (id: number) {
return new Promise((resolve, reject) => {
db.video.delete(id).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
},
clear () {
return new Promise((resolve, reject) => {
db.video.clear().then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
}

30
src/plugins/element.ts Normal file
View File

@@ -0,0 +1,30 @@
import Vue from 'vue'
import {
Container, Row, Col, Header, Aside, Main, Drawer,
Tabs, TabPane, Button, Select, Option, Pagination,
Table, TableColumn, Input, Card, Link, Loading,
Notification, Message
} from 'element-ui'
Vue.use(Container)
Vue.use(Row)
Vue.use(Col)
Vue.use(Header)
Vue.use(Aside)
Vue.use(Main)
Vue.use(Drawer)
Vue.use(Tabs)
Vue.use(TabPane)
Vue.use(Button)
Vue.use(Select)
Vue.use(Option)
Vue.use(Pagination)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(Input)
Vue.use(Card)
Vue.use(Link)
Vue.use(Loading)
Vue.prototype.$notify = Notification
Vue.prototype.$message = Message

View File

@@ -1,37 +0,0 @@
import Vue from 'vue'
import Router from 'vue-router'
import Search from './views/Search.vue'
Vue.use(Router)
export default new Router({
// mode: 'history',
// base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'search',
component: Search
},
{
path: '/detail',
name: 'detail',
component: () => import(/* webpackChunkName: "about" */ './views/Detail.vue')
},
{
path: '/settings',
name: 'settings',
component: () => import(/* webpackChunkName: "about" */ './views/Settings.vue')
},
{
path: '/play',
name: 'play',
component: () => import(/* webpackChunkName: "about" */ './views/Player.vue')
},
{
path: '/collection',
name: 'collection',
component: () => import(/* webpackChunkName: "about" */ './views/Collection.vue')
}
]
})

13
src/shims-tsx.d.ts vendored Normal file
View File

@@ -0,0 +1,13 @@
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any
}
}
}

4
src/shims-vue.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}

View File

@@ -1,57 +0,0 @@
import Vue from 'vue'
import Vuex from 'vuex'
import setting from '@/plugin/nedb/setting'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
site: 0,
theme: {
id: '',
color: 'light'
},
iconActive: 'search',
video: {}
},
getters: {
getSite: state => {
return state.site
},
getTheme: state => {
return state.theme
},
getIconActive: state => {
return state.iconActive
},
getVideo: state => {
return state.video
}
},
mutations: {
SET_SITE: (state, payload) => {
state.site = payload
},
SET_THEME: (state, payload) => {
state.theme = payload
},
SET_ICON_ACTIVE: (state, payload) => {
state.iconActive = payload
},
SET_VIDEO: (state, payload) => {
state.video = payload
}
},
actions: {
changeTheme: ({ commit }, payload) => {
setting.update(payload.id, { theme: payload.color }).then(res => {
commit('SET_THEME', payload)
})
},
changeSite: ({ commit }, payload) => {
setting.update(payload.id, { site: payload.site }).then(res => {
commit('SET_SITE', payload)
})
}
}
})

47
src/store/index.ts Normal file
View File

@@ -0,0 +1,47 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
Main: 'Search',
site: 0,
detail: {
show: false,
video: ''
},
dUrl: '',
video: {}
},
getters: {
getMain: state => {
return state.Main
},
getSite: state => {
return state.site
},
getDetail: state => {
return state.detail
},
getVideo: state => {
return state.video
}
},
mutations: {
SET_MAIN: (state, payload) => {
state.Main = payload
},
SET_SITE: (state, payload) => {
state.site = payload
},
SET_DETAIL: (state, payload) => {
state.detail = payload
},
SET_VIDEO: (state, payload) => {
state.video = payload
}
},
actions: {},
modules: {}
})

View File

@@ -1,90 +0,0 @@
<template>
<Row class="collection">
<div class="collectionBox">
<Table stripe :columns="columns" :data="data" :loading="loading">
<template slot-scope="{ row }" slot="action" >
<Button size="small" @click="play(row)">Play</Button>
<Button size="small" type="info" ghost @click="detailShow(row)">Detail</Button>
<Button size="small" type="error" ghost @click="deleteLi(row)">Delete</Button>
</template>
</Table>
</div>
</Row>
</template>
<script>
import db from '@/plugin/nedb/video'
export default {
name: 'collection',
data () {
return {
columns: [
{
title: 'Name',
key: 'name',
sortable: true
},
{
title: 'Category',
key: 'category',
width: 120,
align: 'center'
},
{
title: 'Time',
key: 'time',
width: 180,
align: 'center'
},
{
title: 'Action',
slot: 'action',
align: 'center',
width: 260
}
],
data: [],
loading: false,
detail: false
}
},
methods: {
getList () {
db.find().then(res => {
this.data = res
})
},
play (e) {
this.$router.push({ name: 'play' })
this.$store.commit('SET_ICON_ACTIVE', 'play')
this.$store.commit('SET_VIDEO', e)
},
detailShow (e) {
this.$store.commit('SET_ICON_ACTIVE', 'detail')
this.$store.commit('SET_VIDEO', e)
this.$router.push({ name: 'detail' })
},
deleteLi (e) {
db.remove(e._id).then(res => {
this.getList()
})
}
},
created () {
this.getList()
}
}
</script>
<style lang="scss" scoped>
.collection{
height: 100%;
position: relative;
.collectionBox{
position: absolute;
width: 100%;
top: 0px;
height: 100%;
overflow: scroll;
&::-webkit-scrollbar { display: none }
}
}
</style>

View File

@@ -1,71 +0,0 @@
<template>
<div class="detail">
<div v-show="box" class="detail-box" v-html="data.box"></div>
<div v-show="box" class="detail-box" v-html="data.info"></div>
<div v-show="box" class="detail-box">
<Button v-for="(i, j) in data.urls" :key="j" @click="playBtn(i, j, video)">{{i | ftLink}}</Button>
</div>
</div>
</template>
<script>
import zy from '@/lib/util.zy'
import { mapMutations } from 'vuex'
export default {
name: 'detail',
data () {
return {
data: {
box: null,
info: null,
m3u8: null
},
box: false
}
},
filters: {
ftLink (e) {
let name = e.split('$')[0]
return name
}
},
computed: {
video () {
return this.$store.getters.getVideo
}
},
methods: {
...mapMutations([
'SET_VIDEO'
]),
async getDetail () {
this.box = false
let url = this.video.detail
this.data = await zy.detail(url)
this.video.urls = this.data.urls
this.video.check = true
this.box = true
},
playBtn (i, j, e) {
this.video.index = j
this.$store.commit('SET_VIDEO', this.video)
this.$router.push({ name: 'play' })
this.$store.commit('SET_ICON_ACTIVE', 'play')
}
},
created () {
this.getDetail()
}
}
</script>
<style lang="scss" scoped>
.detail{
.btns{
margin-bottom: 10px;
}
.detail-box{
button{
margin: 4px;
}
}
}
</style>

View File

@@ -1,121 +0,0 @@
<template>
<Row class="player">
<div class="title">{{ video.name }} -- {{ info }}</div>
<div class="playerBox">
<div id="xg"></div>
</div>
<div class="list">
<Button v-for="(i, j) in video.urls" :key="j" @click="playBtn(i, j, video)">{{i | ftLink}}</Button>
</div>
</Row>
</template>
<script>
import 'xgplayer'
import Hls from 'xgplayer-hls.js'
import zy from '@/lib/util.zy'
// import haku from '@/lib/util.666zy'
export default {
name: 'player',
data () {
return {
data: {},
url: null,
xg: null,
info: '',
config: {
id: 'xg',
url: null,
fluid: true,
autoplay: true,
keyShortcut: 'on',
defaultPlaybackRate: 1,
playbackRate: [0.5, 0.75, 1, 1.5, 2]
}
}
},
filters: {
ftLink (e) {
let name = e.split('$')[0]
return name
}
},
computed: {
video () {
return this.$store.getters.getVideo
}
},
methods: {
init () {
if (this.video.check) {
let url = this.video.urls[this.video.index].split('$')[1]
this.info = this.video.urls[this.video.index].split('$')[0]
this.$nextTick(() => {
this.playEvent(url)
})
} else {
this.getDetail()
}
},
async getDetail () {
let d = this.video.detail
let index = this.video.index
this.data = await zy.detail(d)
let urls = this.data.urls
this.video.urls = urls
this.video.check = true
let playUrl = urls[index].split('$')[1]
this.info = urls[index].split('$')[0]
this.$nextTick(() => {
this.playEvent(playUrl)
})
},
playEvent (e) {
this.config.url = e
this.xg = new Hls(this.config)
},
playBtn (i, j, e) {
this.video.index = j
let url = this.video.urls[this.video.index].split('$')[1]
this.info = this.video.urls[this.video.index].split('$')[0]
this.xg.src = url
}
},
created () {
this.init()
},
destroyed () {
this.xg.destroy()
}
}
</script>
<style lang="scss" scoped>
.player{
padding: 10px;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.title{
margin-bottom: 8px;
font-size: 18px;
text-align: left;
width: 800px;
}
.playerBox{
width: 100%;
max-width: 800px;
border: 1px solid #000;
}
.list{
margin-top: 10px;
width: 800px;
text-align: left;
button{
margin-right: 10px;
margin-bottom: 10px;
}
}
}
</style>

View File

@@ -1,221 +0,0 @@
<template>
<div class="search">
<div :class="active ? 'search-top haveList': 'search-top'" >
<Input class="search-input" v-model.trim="txt" size="large" search placeholder="输入需要搜索的资源名称..." @on-search="searchEvent" clearable @on-clear="searchClear">
<Select slot="prepend" v-model="site" style="width: 120px;">
<Option v-for="(i, j) in sites" :key="j" :value="j">{{i.name}}</Option>
</Select>
<!-- eslint-disable-next-line -->
</Input>
</div>
<div class="search-middle" v-if="active">
<Table stripe :columns="columns" :data="data" :loading="loading">
<template slot-scope="{ row }" slot="action" >
<Button size="small" @click="play(row)">Play</Button>
<Button size="small" @click="collection(row)">Star</Button>
<Button size="small" @click="detail(row)">Detail</Button>
</template>
</Table>
</div>
<div class="search-bottom" v-if="active">
<Page :total="num" :current.sync="page" :page-size="50" show-total @on-change="onChange" />
</div>
</div>
</template>
<script>
import db from '@/plugin/nedb/video'
import zy from '@/lib/util.zy'
import sites from '@/lib/sites'
import { mapGetters, mapMutations } from 'vuex'
export default {
name: 'search',
data () {
return {
sites: sites,
txt: '',
active: false,
columns: [
{
title: 'Name',
key: 'name',
minWidth: 240
},
{
title: 'Category',
key: 'category',
width: 100,
align: 'center'
},
{
title: 'Time',
key: 'time',
width: 110,
align: 'center'
},
{
title: 'Action',
slot: 'action',
align: 'center',
width: 260
}
],
data: [],
page: 1,
num: 0,
loading: true
}
},
computed: {
...mapGetters(['getVideo']),
...mapGetters({
getSite: 'getSite'
}),
site: {
get () {
return this.getSite
},
set (val) {
this.SET_SITE(val)
}
}
},
methods: {
...mapMutations(['SET_SITE']),
async searchEvent () {
if (this.txt !== '') {
this.active = true
this.loading = true
this.page = 1
let z = await zy.info(this.site, this.page, this.txt)
this.data = z.list
this.num = z.num
this.loading = false
}
},
searchClear () {
this.txt = ''
this.active = false
this.loading = true
},
async onChange () {
let z = await zy.info(this.site, this.page, this.txt)
this.data = z.list
this.num = z.num
},
play (e) {
if (this.getVideo.detail !== e.detail) {
this.$store.commit('SET_VIDEO', e)
}
this.$store.commit('SET_ICON_ACTIVE', 'play')
this.$router.push({ name: 'play' })
},
async collection (e) {
let d = await zy.detail(e.detail)
let data = {
category: e.category,
detail: e.detail,
name: e.name,
time: e.time,
type: 'single',
index: 0,
urls: [],
check: false
}
data.urls = d.urls
data.check = true
this.$store.commit('SET_VIDEO', data)
db.find({ detail: data.detail }).then(res => {
if (res.length >= 1) {
this.$Notice.warning({
title: '资源已存在',
backgroud: true
})
} else {
db.add(data).then(res => {
this.$Notice.success({
title: '收藏成功',
backgroud: true
})
})
}
})
},
detail (e) {
this.$store.commit('SET_VIDEO', e)
this.$store.commit('SET_ICON_ACTIVE', 'detail')
this.$router.push({ name: 'detail' })
}
},
created () {
// this.sites = sites
}
}
</script>
<style lang="scss" scoped>
.search{
height: 100%;
position: relative;
.search-top{
position: absolute;
top: 0;
left: 0;
height: 60px;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
animation: slideDown 0.6s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
.search-input{
width: 80%;
}
&.haveList{
animation: slideUp 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
@keyframes slideUp {
from {
height: 100%;
}
to{
height: 60px;
}
}
@keyframes slideDown {
from {
height: 60px;
}
to{
height: 100%;
}
}
}
.search-middle{
position: absolute;
top: 60px;
width: 100%;
height: calc(100% - 120px);
padding: 10px;
overflow: scroll;
&::-webkit-scrollbar { display: none }
animation: fade-in 1.4s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}
@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.search-bottom{
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 60px;
display: flex;
align-items: center;
justify-content: space-around;
padding: 0 10px;
}
}
</style>

View File

@@ -1,62 +0,0 @@
<template>
<Row class="setting">
<div class="item about">
<div class="title">关于:</div>
<ul>
<li>作者: <a href="https://github.com/Hunlongyu">Hunlongyu</a></li>
<li>官网: <a href="https://zy_player.hunlongyu.fun">ZY Player</a></li>
<li>反馈: <a href="https://github.com/Hunlongyu/ZY-Player/issues">Issues</a></li>
</ul>
</div>
<div class="item upgrade">
<div class="title">更新:</div>
<div class="btns">版本: v0.6.3</div>
<div class="btns"><Button @click="checkUpgrade">检查更新</Button></div>
</div>
<div class="item theme">
<div class="title">主题:</div>
<div class="btns">
<Button @click="changeTheme({ id: getTheme.id, color: 'light' })">light</Button>
<Button @click="changeTheme({ id: getTheme.id, color: 'dark' })">Dark</Button>
</div>
</div>
</Row>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
name: 'settings',
computed: {
...mapGetters([
'getTheme'
])
},
methods: {
...mapActions([
'changeTheme'
]),
checkUpgrade () {}
}
}
</script>
<style lang="scss" scoped>
.setting{
padding: 10px;
.item{
margin-bottom: 10px;
.title{
font-size: 16px;
}
ul{
margin-left: 10px;
list-style: none;
}
.btns{
margin-left: 10px;
button{
margin-right: 10px;
}
}
}
}
</style>

39
tsconfig.json Normal file
View File

@@ -0,0 +1,39 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

View File

@@ -3,10 +3,7 @@ module.exports = {
electronBuilder: {
builderOptions: {
win: {
icon: './public/app.ico'
},
mac: {
icon: './public/app.png'
icon: './build/icons/icon.ico'
},
productName: 'ZY Player',
publish: ['github']

4363
yarn.lock

File diff suppressed because it is too large Load Diff