Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02c5cc0f1d | ||
|
|
dba13326cd | ||
|
|
14eed868c1 | ||
|
|
f9ff6bbde9 | ||
|
|
b208dfc3b6 | ||
|
|
23d221979e | ||
|
|
b36b6bd9e4 | ||
|
|
4c8b3ae36b | ||
|
|
58f1ecdb57 | ||
|
|
b5a9d4482b | ||
|
|
8c05489b06 | ||
|
|
bf0ccef8ba | ||
|
|
0161e2ee1d | ||
|
|
9df844dbe8 | ||
|
|
e723fe0cf1 | ||
|
|
3d94ab2f6c | ||
|
|
dabf4390bd | ||
|
|
93c7392754 | ||
|
|
426bb05e1c | ||
|
|
c9f30ce42f | ||
|
|
e126beb59c | ||
|
|
851d4a7ce7 | ||
|
|
d1dd9491da | ||
|
|
36e1372f51 | ||
|
|
f05a361265 | ||
|
|
4a01550ad6 | ||
|
|
b09954ca7c | ||
|
|
ec6bc18fbf | ||
|
|
2bdaf70be1 | ||
|
|
f0ad01fe5a | ||
|
|
e9f172d1b1 | ||
|
|
6e2e8e457e | ||
|
|
87760286db | ||
|
|
d9207d6429 | ||
|
|
0e9acb2bab | ||
|
|
6fa2c1f1cc |
2
.env
@@ -1 +1 @@
|
||||
GH_TOKEN=0f8ce868776fa1af5908d0353cc175663908ff5f
|
||||
GH_TOKEN=fc794ea58a602f31788c54d612afa4bbd1e225c4
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
29
AppVeyor.yml
@@ -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
|
||||
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Hunlongyu
|
||||
Copyright (c) 2020 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
|
||||
|
||||
63
README.md
@@ -1,49 +1,40 @@
|
||||
# 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
|
||||
|
||||
资源播放器, 提供影视资源的搜索,查看,播放,搜藏等功能.
|
||||
|
||||
### 截图:
|
||||

|
||||
主界面 ⬆
|
||||

|
||||
搜索 ⬆
|
||||

|
||||
详情 ⬆
|
||||

|
||||
播放 ⬆
|
||||

|
||||
搜藏 ⬆
|
||||
主界面 ⬇
|
||||

|
||||
搜索 ⬇
|
||||

|
||||
搜索结果 ⬇
|
||||

|
||||
详情 ⬇
|
||||

|
||||
播放 ⬇
|
||||

|
||||
搜藏 ⬇
|
||||

|
||||
|
||||
### 下载地址:
|
||||
### 下载:
|
||||
|
||||
蓝奏云: https://www.lanzous.com/i8jnk9e
|
||||
[下载地址](https://github.com/Hunlongyu/ZY-Player/releases)
|
||||
|
||||
### 未完成:
|
||||
1. 主题: 暗黑主题
|
||||
2. 更新: 自动更新以及手动更新
|
||||
2. 更新: 自动更新
|
||||
3. 图标: 求一个 zy 的logo, 256x256 像素的
|
||||
|
||||
### 重要:
|
||||
所有资源来自网上, 该软件不参与任何制作, 上传, 储存, 下载等内容. 该软件仅供学习参考, 请于安装后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/).
|
||||
|
||||
@@ -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
|
After Width: | Height: | Size: 93 KiB |
BIN
build/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
build/icons/16x16.png
Normal file
|
After Width: | Height: | Size: 497 B |
BIN
build/icons/24x24.png
Normal file
|
After Width: | Height: | Size: 891 B |
BIN
build/icons/256x256.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
build/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
build/icons/48x48.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
build/icons/512x512.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
build/icons/64x64.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
build/icons/icon.icns
Normal file
BIN
build/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 353 KiB |
15183
package-lock.json
generated
53
package.json
@@ -1,46 +1,51 @@
|
||||
{
|
||||
"name": "zy-player",
|
||||
"version": "0.6.3",
|
||||
"private": false,
|
||||
"name": "zy",
|
||||
"version": "0.7.16",
|
||||
"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",
|
||||
"bp": "vue-cli-service 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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
BIN
public/app.ico
|
Before Width: | Height: | Size: 264 KiB |
BIN
public/app.png
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 91 KiB |
BIN
public/icon.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
@@ -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 -->
|
||||
|
||||
154
src/App.vue
@@ -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>
|
||||
|
||||
@@ -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
|
After Width: | Height: | Size: 18 KiB |
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/assets/theme/global.scss
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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>
|
||||
@@ -1,13 +0,0 @@
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'zy_content',
|
||||
methods: {
|
||||
switchTheme (e) {
|
||||
this.theme = e
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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>
|
||||
@@ -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
|
||||
})
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
35
src/plugins/dexie/index.ts
Normal 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()
|
||||
42
src/plugins/dexie/setting.ts
Normal 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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
69
src/plugins/dexie/video.ts
Normal 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
@@ -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
|
||||
@@ -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
@@ -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
@@ -0,0 +1,4 @@
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue'
|
||||
export default Vue
|
||||
}
|
||||
57
src/store.js
@@ -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
@@ -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: {}
|
||||
})
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
@@ -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"
|
||||
]
|
||||
}
|
||||
@@ -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']
|
||||
|
||||