Compare commits

..

95 Commits
hly ... v2.5.8

Author SHA1 Message Date
haiyangcui
483449aad8 v2.5.8 2020-10-30 11:52:31 +01:00
haiyangcui
2b7a425f6f 影视推荐位置上移,更靠近Film页面 2020-10-30 11:50:17 +01:00
haiyangcui
a06c9e9d32 缩小地区和类型的按钮大小 2020-10-30 11:28:35 +01:00
haiyangcui
32a21952a2 推荐页面,视图添加地区信息 2020-10-30 11:25:47 +01:00
haiyangcui
ff01abe88c 改进推荐页面的过滤功能 2020-10-30 11:22:09 +01:00
haiyangcui
962f6b46c3 推荐页面,支持地区,类型的过滤 2020-10-30 10:58:31 +01:00
haiyangcui
b16604add6 更新推荐列表 2020-10-30 10:03:47 +01:00
haiyangcui
3028939ed9 改进豆瓣评分获取的算法 2020-10-30 09:43:04 +01:00
haiyangcui
1ffcb38b4b 优化'豆瓣评分'样式 2020-10-30 09:11:12 +01:00
Hunlongyu
2ec05b6ab5 🎳 移除request, 使用已有同样功能的axios重写.优化豆瓣评分样式 2020-10-30 10:05:45 +08:00
Hunlongyu
9cbd37d143 Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player into master 2020-10-30 09:30:59 +08:00
Hunlongyu
b9ab1b11f4 🎱 关闭有可能导致安全软件报警的代码 2020-10-30 09:30:53 +08:00
haiyangcui
428f49a2e8 更新推荐电影 2020-10-29 23:50:29 +01:00
haiyangcui
2e8941c6cc 记录推荐页的视图模式 2020-10-29 23:12:40 +01:00
haiyangcui
91e2fc56b7 添加recommandation数据库,支持更新推荐 2020-10-29 22:55:14 +01:00
haiyangcui
e2b124e4ac 添加“影视推荐”页面 2020-10-29 21:51:34 +01:00
haiyangcui
6b37e3ebd5 定义pictureView样式并复用 2020-10-29 21:36:12 +01:00
haiyangcui
5dcdb6a410 记录sartViewMode 2020-10-29 18:29:23 +01:00
haiyangcui
5d41f783bc 统一海报视图的格式 2020-10-29 15:47:12 +01:00
haiyangcui
28ad8f3313 详情页面收藏的话,如果收藏已存在,更新收藏信息 2020-10-29 12:49:30 +01:00
haiyangcui
c475fbdf46 收藏视图页面添加豆瓣评分信息 2020-10-29 12:46:06 +01:00
Hunlongyu
ee622f88da 🏉 收藏新增海报模式. 2020-10-29 18:12:54 +08:00
haiyangcui
1b2461c423 Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player 2020-10-29 10:10:37 +01:00
Hunlongyu
3044bfcbc4 🏈 mini 界面优化 2020-10-29 16:59:25 +08:00
haiyangcui
4038e5e6be Merge branch 'dev_improveStar' 2020-10-29 09:32:28 +01:00
haiyangcui
bea1ef4c73 收藏页面添加视图开关 2020-10-29 09:12:33 +01:00
Hunlongyu
7af9a6d76d 🏐 编辑源增加状态排序, 修复换源功能里源的标识问题. 优化搜索结果样式变形 2020-10-29 15:32:56 +08:00
Hunlongyu
2140d90a87 🏀 源编辑,加入单个源检测功能. 2020-10-29 14:45:46 +08:00
Hunlongyu
e86eef1b05 添加快捷键指南, 修复换源功能阻塞问题 2020-10-29 14:35:58 +08:00
haiyangcui
42dd8fe5e4 star添加豆瓣评分记录 2020-10-29 00:09:29 +01:00
haiyangcui
9fd8c60dd5 "获取豆瓣评分"功能到tools.js 2020-10-28 23:29:39 +01:00
haiyangcui
49c480061b 收藏数据里记录detail所有内容 2020-10-28 18:09:10 +01:00
hunlongyu
022b1f4090 紧急修复视频源检测bug, 以及增加手动更新提示。 2020-10-28 22:44:53 +08:00
hunlongyu
9c08a64524 💎 手动更新提示 2020-10-28 22:11:41 +08:00
hunlongyu
90ec848c11 v2.5.6 2020-10-28 21:45:48 +08:00
hunlongyu
82270a702f v2.5.5 2020-10-28 21:39:29 +08:00
Hunlongyu
61dcb1ec49 💍 优化视频源分组功能 2020-10-28 18:22:18 +08:00
Hunlongyu
40b853361e 💄 新增或编辑源的时候, 校验key值的唯一性 2020-10-28 18:00:59 +08:00
Hunlongyu
57ff3325f0 💋 优化图标显示逻辑 2020-10-28 17:38:02 +08:00
Hunlongyu
c4283e2da0 🎓 优化播放页图标, 优化视频停止播放后不显示历史记录提示. 修复报错 2020-10-28 17:30:46 +08:00
Hunlongyu
7052bd7e05 🎩 新增播放视频时换源功能. 2020-10-28 17:04:44 +08:00
Hunlongyu
64a12a06c4 👒 移除百度统计, 主要是记录缺失, 并且不准确 2020-10-28 15:38:02 +08:00
Hunlongyu
3df3b385ce ⛑ 新增分类过滤器, 主要针对豆瓣资源网分类出错问题 2020-10-28 15:34:33 +08:00
Hunlongyu
e77db4711e 🧢 优化视频源检测功能 2020-10-28 15:33:30 +08:00
Hunlongyu
fb3fd26870 Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player into master 2020-10-28 12:00:14 +08:00
Hunlongyu
72790f5f3e 👑 添加了视频源检测功能. 2020-10-28 11:59:55 +08:00
haiyangcui
ea2815969d 把IPTV的初始数据放入单独的Json文件中 2020-10-27 23:12:30 +01:00
Hunlongyu
e330cff7d4 🧦 electron 优化, 忽略证书错误, 关闭沙盒. 2020-10-27 11:43:11 +08:00
haiyangcui
da5b91535e 搜索后获取详细信息并展示 2020-10-26 23:53:12 +01:00
haiyangcui
00c8f0c1e2 v2.5.4 2020-10-26 17:26:49 +01:00
haiyangcui
924fd439d9 更新导入收藏,支持一次导入多个文件 2020-10-26 16:37:44 +01:00
haiyangcui
ed039894c8 导入时无需调用upgradeFavorites 2020-10-26 16:20:08 +01:00
haiyangcui
005bdf7ea6 监听并更新收藏及源列表 2020-10-26 16:19:48 +01:00
haiyangcui
19071faf70 更新福利片关键词 2020-10-26 15:13:18 +01:00
hunlongyu
176d9f6b5a Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player 2020-10-26 20:57:31 +08:00
hunlongyu
31a8dd0f32 🧤 升级 xgplayer 依赖 2020-10-26 20:57:25 +08:00
haiyangcui
336ea08c69 Film页面只获取active的源站 2020-10-26 13:47:47 +01:00
haiyangcui
8e4a0870a1 播放电视频道时,更新播放页面的左上角标题 2020-10-26 13:42:44 +01:00
hunlongyu
86130e6c53 🧣 修复第一次加载资源失败的bug 2020-10-26 20:24:35 +08:00
Hunlongyu
e984d0a349 🩳 修复修改数据库,导致无法播放的bug 2020-10-26 18:18:07 +08:00
Hunlongyu
506991f3b1 👖 优化 history 和 star 数据库的搜索.后台不再提示warning.需要重置数据库. 2020-10-26 18:06:01 +08:00
Hunlongyu
04c35ba7a9 👔 开发所有系统的更新功能 2020-10-26 16:46:41 +08:00
Hunlongyu
275b6757bf 👕 实现手动更新功能 2020-10-26 16:33:00 +08:00
Hunlongyu
612930cc8b 🥼 修复停止播放后,再次播放有2个声音的问题 2020-10-26 11:19:15 +08:00
haiyangcui
80e36fa99c 源站排序总是有问题, 先移除掉 2020-10-25 15:40:18 +01:00
haiyangcui
f069044f28 简单重构 2020-10-25 16:23:31 +01:00
haiyangcui
c77ac7ea7f 重构一下EditSites页面的代码 2020-10-25 15:33:39 +01:00
haiyangcui
803181b4f7 修复源页面编辑后Film页面不更新的bug 2020-10-25 13:49:58 +01:00
haiyangcui
ac2474903d 恢复滚动条 2020-10-24 23:38:02 +02:00
haiyangcui
04b7e139da 更新内置电视频道 2020-10-24 23:00:58 +02:00
haiyangcui
b49e1b82bf 支持自选源列排序 2020-10-24 23:00:40 +02:00
haiyangcui
dc00c690e7 重新排序后更新数据库 2020-10-24 22:58:15 +02:00
haiyangcui
44ba552435 改进IPTV页面逻辑 2020-10-24 16:20:49 +02:00
haiyangcui
8d84d1bf22 统一字体大小 2020-10-24 14:29:30 +02:00
haiyangcui
c78368dac9 新增源时,支持输入源标识 2020-10-24 14:24:18 +02:00
haiyangcui
11af5a7ecd 导入收藏时,更新收藏项的源站信息 2020-10-24 14:14:44 +02:00
haiyangcui
ecfeecf45f 为表头按钮添加图标 2020-10-24 14:06:25 +02:00
haiyangcui
455f5dae84 统一star数据格式的定义 2020-10-24 13:19:47 +02:00
Hunlongyu
ffbffde74f Merge branch 'master' of https://github.com/Hunlongyu/ZY-Player into master 2020-10-24 13:52:29 +08:00
Hunlongyu
0be0433355 🥼 移除master 分支下的更新功能 2020-10-24 13:52:20 +08:00
haiyangcui
cc2dc19e39 Revert "统一star数据格式的定义"
This reverts commit aa4583898a.
2020-10-23 23:31:44 +02:00
haiyangcui
aa4583898a 统一star数据格式的定义 2020-10-23 23:31:01 +02:00
haiyangcui
2e96812a1f 最后一列的表头向右对齐 2020-10-23 23:23:25 +02:00
haiyangcui
6f52d73d52 鼠标划过的行添加少许放大效果 2020-10-23 22:45:04 +02:00
haiyangcui
a3ebba641f 源站编辑,支持编辑分组 2020-10-23 22:29:52 +02:00
haiyangcui
8ceffab2fe 搜索结果里添加“源站”列 2020-10-23 16:43:33 +02:00
haiyangcui
454d192141 更好支持中文排序 2020-10-23 15:53:21 +02:00
haiyangcui
a1423993c8 固定表头 2020-10-23 15:14:12 +02:00
haiyangcui
1c9a84dbc6 开关批处理,无需返回页面顶部 2020-10-23 13:37:41 +02:00
haiyangcui
304b0d10b4 Fix typo 2020-10-23 13:32:46 +02:00
cuiocean
aff97f6d46 Merge pull request #307 from buvta/patch-1
IPTV小调整
2020-10-23 12:27:23 +02:00
haiyangcui
650c882f58 移除自选源列的排序 2020-10-23 12:20:17 +02:00
buvta
c708292051 IPTV变更分租状态开关时表格回到顶部 2020-10-23 00:02:18 +08:00
buvta
79932a74bc 固定IPTV和源编辑表头 2020-10-22 23:48:19 +08:00
buvta
3f79f31550 IPTV转移总频道数到表头
这PR也太好混了吧
2020-10-22 23:40:28 +08:00
34 changed files with 8362 additions and 2323 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "zy",
"version": "2.5.3",
"version": "2.5.8",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -41,7 +41,7 @@
"vue-waterfall-plugin": "^1.1.0",
"vuedraggable": "^2.24.2",
"vuex": "^3.5.1",
"xgplayer": "^2.12.2",
"xgplayer": "^2.13.0",
"xgplayer-hls.js": "^2.2.5"
},
"devDependencies": {

View File

@@ -4,17 +4,8 @@
<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 %>icon.png">
<title><%= htmlWebpackPlugin.options.title %></title>
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?62aeb2505bfa26a2461d2a7a3b485096";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>

View File

@@ -10,6 +10,7 @@
<History v-show="view === 'History'" />
<Setting v-show="view === 'Setting'" />
<EditSites v-if="view === 'EditSites'"/>
<Recommandation v-show="view === 'Recommandation'" />
</div>
<transition name="slide">
<Detail v-if="detail.show"/>
@@ -43,6 +44,9 @@ export default {
},
editSites () {
return this.$store.getters.getEditSites
},
recommandation () {
return this.$store.getters.recommandation
}
},
watch: {

View File

@@ -152,7 +152,7 @@
&.name{
flex: 1;
min-width: 100px;
white-space: nowrap;
overflow: hidden;
margin-left: 10px;
}
&.type{
@@ -170,6 +170,9 @@
&.note{
width: 10%;
}
&.info{
width: 10%;
}
&.operate{
.btn{
width: 40px;
@@ -183,7 +186,7 @@
// scroll
.zy-scroll{
&::-webkit-scrollbar{
width: 5px;
width: 10px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
@@ -209,6 +212,7 @@
.listpage-content{
height: 100%;
position: relative;
font-size: 1rem;
.listpage-header{
width: 100%;
height: 40px;
@@ -217,11 +221,6 @@
justify-content: space-between;
padding-left: 50px;
padding-right: 50px;
.btn{
&:hover{
cursor: pointer;
}
}
.el-button{
font-size: 1rem;
border: none;
@@ -229,6 +228,9 @@
cursor: pointer;
}
}
.is-loading:before {
background-color: none !important;
}
.el-input{
width: 200px;
}
@@ -241,15 +243,33 @@
}
.el-table{
height: 100%;
width: 100%;
overflow-y: auto;
font-size: 1rem;
}
.el-table__body-wrapper{
&::-webkit-scrollbar{
width: 10px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
position: absolute;
}
&::-webkit-scrollbar-track {
border-radius: 10px;
position: absolute;
}
}
.el-input{
width: 200px;
}
.el-table__body td,.el-table__body th{
border-bottom: 1px solid;
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
transform: scale(1.02);
}
.el-table .highlight{
color: var(--highlight-color) !important;
}
@@ -303,19 +323,4 @@
box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;
}
}
}
.el-table, .el-table__body-wrapper{
&::-webkit-scrollbar{
width: 5px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
position: absolute;
}
&::-webkit-scrollbar-track {
border-radius: 10px;
position: absolute;
}
}

View File

@@ -170,7 +170,7 @@
}
}
}
.film{
.pictureView{
.body{
background-color: var(--d-bgc-1);
box-shadow: var(--d-bsc);
@@ -286,10 +286,6 @@
}
}
}
.star{
background-color: var(--d-bgc-1);
box-shadow: var(--d-bsc);
}
.setting{
background-color: var(--d-bgc-1);
box-shadow: var(--d-bsc);
@@ -399,6 +395,18 @@
background-color: var(--d-bgc-1);
border-bottom-color: var(--d-c-2);
}
.el-table__body-wrapper{
&:hover{
&::-webkit-scrollbar-thumb {
box-shadow: var(--d-bsc-scroll);
background: var(--d-c-5);
}
&::-webkit-scrollbar-track {
box-shadow: var(--d-bsc-scroll);
background: var(--d-bgc-1);
}
}
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
background-color: var(--d-bgc-2);
}

View File

@@ -166,7 +166,7 @@
}
}
}
.film{
.pictureView{
.body{
background-color: var(--g-bgc-1);
box-shadow: var(--g-bsc);
@@ -282,10 +282,6 @@
}
}
}
.star{
background-color: var(--g-bgc-1);
box-shadow: var(--g-bsc);
}
.setting{
background-color: var(--g-bgc-1);
box-shadow: var(--g-bsc);
@@ -395,6 +391,18 @@
background-color: var(--g-bgc-1);
border-bottom-color: var(--g-c-2);
}
.el-table__body-wrapper{
&:hover{
&::-webkit-scrollbar-thumb {
box-shadow: var(--g-bsc-scroll);
background: var(--g-c-5);
}
&::-webkit-scrollbar-track {
box-shadow: var(--g-bsc-scroll);
background: var(--g-bgc-1);
}
}
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
background-color: var(--g-bgc-2);
}

View File

@@ -166,7 +166,7 @@
}
}
}
.film{
.pictureView{
.body{
background-color: var(--l-bgc-1);
box-shadow: var(--l-bsc);
@@ -282,10 +282,6 @@
}
}
}
.star{
background-color: var(--l-bgc-1);
box-shadow: var(--l-bsc);
}
.setting{
background-color: var(--l-bgc-1);
box-shadow: var(--l-bsc);
@@ -395,6 +391,18 @@
background-color: var(--l-bgc-1);
border-bottom-color: var(--l-c-2);
}
.el-table__body-wrapper{
&:hover{
&::-webkit-scrollbar-thumb {
box-shadow: var(--l-bsc-scroll);
background: var(--l-c-5);
}
&::-webkit-scrollbar-track {
box-shadow: var(--l-bsc-scroll);
background: var(--l-bgc-1);
}
}
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
background-color: var(--l-bgc-2);
}

View File

@@ -165,7 +165,7 @@
}
}
}
.film{
.pictureView{
.body{
background-color: var(--p-bgc-1);
box-shadow: var(--p-bsc);
@@ -281,10 +281,6 @@
}
}
}
.star{
background-color: var(--p-bgc-1);
box-shadow: var(--p-bsc);
}
.setting{
background-color: var(--p-bgc-1);
box-shadow: var(--p-bsc);
@@ -394,6 +390,18 @@
background-color: var(--p-bgc-1);
border-bottom-color: var(--p-c-2);
}
.el-table__body-wrapper{
&:hover{
&::-webkit-scrollbar-thumb {
box-shadow: var(--p-bsc-scroll);
background: var(--p-c-5);
}
&::-webkit-scrollbar-track {
box-shadow: var(--p-bsc-scroll);
background: var(--p-bgc-1);
}
}
}
.el-table--enable-row-hover .el-table__body tr:hover>td{
background-color: var(--p-bgc-2);
}

View File

@@ -3,20 +3,18 @@
import './lib/site/server'
import { app, protocol, BrowserWindow, globalShortcut, ipcMain } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import { autoUpdater } from 'electron-updater'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
import { initUpdater } from './lib/update/update'
const isDevelopment = process.env.NODE_ENV !== 'production'
// 允许跨域
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors')
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors') // 允许跨域
// app.commandLine.appendSwitch('--ignore-certificate-errors', 'true') // 忽略证书相关错误
let win
let mini
protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])
autoUpdater.autoDownload = false
function createWindow () {
win = new BrowserWindow({
width: 1080,
@@ -38,6 +36,8 @@ function createWindow () {
win.loadURL('app://./index.html')
}
initUpdater(win)
win.on('closed', () => {
win = null
})
@@ -52,6 +52,7 @@ function createMini () {
frame: false,
resizable: true,
webPreferences: {
sandbox: false,
webSecurity: false,
enableRemoteModule: true,
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
@@ -76,6 +77,7 @@ if (process.platform === 'darwin') {
}
if (process.platform === 'Linux') {
app.disableHardwareAcceleration()
app.commandLine.appendSwitch('--no-sandbox') // linux 关闭沙盒模式
}
app.allowRendererProcessReuse = true
@@ -100,16 +102,6 @@ ipcMain.on('win', () => {
win.webContents.send('miniClosed')
})
ipcMain.on('update', async () => {
const checkForUpdates = await autoUpdater.checkForUpdates()
win.webContents.send('update-replay-check', checkForUpdates)
const res = await autoUpdater.downloadUpdate()
win.webContents.send('update-replay-download', res)
autoUpdater.on('update-downloaded', () => {
win.webContents.send('update-replay-downloaded', 'downloaded')
})
})
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()

View File

@@ -14,6 +14,13 @@
</g>
</svg>
</span>
<span :class="[view === 'Recommandation' ? 'active ': ''] + 'zy-svg'" @click="changeView('Recommandation')">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="thumbUpIconTitle" stroke="#2329D6" stroke-width="1" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#2329D6">
<title id="thumbUpIconTitle">影视推荐</title>
<path d="M8,8.73984815 C8,8.26242561 8.17078432,7.80075162 8.4814868,7.43826541 L13.2723931,1.84887469 C13.7000127,1.34998522 14.4122932,1.20614658 15,1.5 C15.5737957,1.78689785 15.849314,2.45205792 15.6464466,3.06066017 L14,8 L18.6035746,8 C18.7235578,8 18.8432976,8.01079693 18.9613454,8.03226018 C20.0480981,8.22985158 20.7689058,9.27101818 20.5713144,10.3577709 L19.2985871,17.3577709 C19.1256814,18.3087523 18.2974196,19 17.3308473,19 L10,19 C8.8954305,19 8,18.1045695 8,17 L8,8.73984815 Z"/>
<path d="M4,18 L4,9"/>
</svg>
</span>
<span :class="[view === 'IPTV' ? 'active ': ''] + 'zy-svg'" @click="changeView('IPTV')">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="tvIconTitle">
<title id="tvIconTitle">电视直播</title>
@@ -78,8 +85,6 @@ export default {
this.detail = {
show: false
}
const _hmt = window._hmt
_hmt.push(['_trackEvent', 'page', 'view', e])
}
}
}

View File

@@ -141,59 +141,54 @@ export default {
this.m3u8List = dd._t.split('#')
}
},
playEvent (n) {
async playEvent (n) {
if (!this.playOnline) {
history.find({ site: this.detail.key, ids: this.detail.info.id }).then(res => {
if (res) {
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: n, site: this.detail.site } }
} else {
this.video = { key: this.detail.key, info: { id: this.detail.info.id, name: this.detail.info.name, index: n, site: this.detail.site } }
}
})
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
if (db) {
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: n, site: this.detail.site } }
} else {
this.video = { key: this.detail.key, info: { id: this.detail.info.id, name: this.detail.info.name, index: n, site: this.detail.site } }
}
this.view = 'Play'
this.detail.show = false
} else {
history.find({ site: this.detail.key, ids: this.detail.info.id }).then(res => {
if (res) {
res.index = n
history.update(res.id, res)
} else {
const doc = {
site: this.detail.key,
ids: this.detail.info.id,
name: this.detail.info.name,
type: this.detail.info.type,
year: this.detail.info.year,
index: n,
time: ''
}
history.add(doc)
const db = await history.find({ site: this.detail.key, ids: this.detail.info.id })
if (db) {
db.index = n
history.update(db.id, db)
} else {
const doc = {
site: this.detail.key,
ids: this.detail.info.id,
name: this.detail.info.name,
type: this.detail.info.type,
year: this.detail.info.year,
index: n,
time: ''
}
})
history.add(doc)
}
onlineVideo.playVideoOnline(this.selectedOnlineSite, this.detail.info.name, n)
}
},
starEvent () {
star.find({ key: this.detail.key, ids: this.info.id }).then(res => {
if (res) {
this.$message.info('该影片已被收藏')
} else {
const docs = {
key: this.detail.key,
ids: this.info.id,
name: this.info.name,
type: this.info.type,
year: this.info.year,
last: this.info.last,
note: this.info.note
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
})
}
}).catch(() => {
this.$message.warning('收藏失败')
})
async starEvent () {
const db = await star.find({ key: this.detail.key, ids: this.info.id })
const doc = {
key: this.detail.key,
ids: this.info.id,
site: this.detail.site,
name: this.info.name,
detail: this.info,
rate: this.info.rate
}
if (db) {
star.update(db.id, doc)
this.$message.success('收藏更新成功')
} else {
star.add(doc).then(res => {
this.$message.success('收藏成功')
})
}
},
togglePlayOnlineEvent () {
this.playOnline = !this.playOnline
@@ -284,38 +279,9 @@ export default {
})
},
getDoubanRate () {
const axios = require('axios')
const cheerio = require('cheerio')
const name = this.detail.info.name.trim()
// 豆瓣搜索链接
var doubanSearchLink = 'https://www.douban.com/search?q=' + name
axios.get(doubanSearchLink).then(res => {
const $ = cheerio.load(res.data)
// 比较第一和第二给豆瓣搜索结果, 看名字是否相符
var link = ''
var nameInDouban = $($('div.result')[0]).find('div>div>h3>a').first()
if (name.replace(/\s/g, '') === nameInDouban.text().replace(/\s/g, '')) {
link = nameInDouban.attr('href')
} else {
nameInDouban = $($('div.result')[1]).find('div>div>h3>a').first()
if (name.replace(/\s/g, '') === nameInDouban.text().replace(/\s/g, '')) {
link = nameInDouban.attr('href')
}
}
// 如果找到链接,就打开该链接获取评分
if (link) {
axios.get(link).then(response => {
const parsedHtml = cheerio.load(response.data)
var rating = parsedHtml('body').find('#interest_sectl').first().find('strong').first()
if (rating.text()) {
this.info.rate = rating.text()
} else {
this.info.rate = '暂无评分'
}
})
} else {
this.info.rate = '暂无评分'
}
zy.doubanRate(name).then(res => {
this.info.rate = res
})
},
getDetailInfo () {
@@ -329,9 +295,6 @@ export default {
this.loading = false
}
})
const _hmt = window._hmt
const name = this.detail.info.name
_hmt.push(['_trackEvent', 'detail', 'view', name])
}
},
created () {

View File

@@ -1,37 +1,38 @@
<template>
<div class="listpage" id="editSites">
<div class="listpage-content">
<div class="listpage-header" v-show="!eableBatchEdit">
<el-switch v-model="eableBatchEdit" active-text="批处理分组">></el-switch>
<el-button @click.stop="addSite" type="text">添加</el-button>
<el-button @click.stop="exportSites" type="text">导出</el-button>
<el-button @click.stop="importSites" type="text">导入</el-button>
<el-button @click.stop="removeAllSites" type="text">清空</el-button>
<el-button @click.stop="resetSitesEvent" type="text">重置</el-button>
<div class="listpage-header" v-show="!enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理分组">></el-switch>
<el-button @click="addSite" icon="el-icon-document-add">新增</el-button>
<el-button @click="exportSites" icon="el-icon-upload2" >导出</el-button>
<el-button @click="importSites" icon="el-icon-download">导入</el-button>
<el-button @click="checkAllSite" icon="el-icon-refresh" :loading="checkAllSiteLoading">检测</el-button>
<el-button @click="removeAllSites" icon="el-icon-delete-solid">清空</el-button>
<el-button @click="resetSitesEvent" icon="el-icon-refresh-left">重置</el-button>
</div>
<div class="listpage-header" v-show="eableBatchEdit">
<el-switch v-model="eableBatchEdit" active-text="批处理分组"></el-switch>
<div class="listpage-header" v-show="enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理分组"></el-switch>
<el-input placeholder="新组名" v-model="batchGroupName"></el-input>
<el-switch v-model="batchIsActive" :active-value="1" :inactive-value="0" active-text="自选源"></el-switch>
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit">保存</el-button>
</div>
<div class="listpage-body" id="sites-table">
<el-table
size="mini"
ref="editSitesTable"
size="mini" fit height="100%" row-key="id"
:data="sites"
row-key="id"
@selection-change="handleSelectionChange">
:key="tableKey"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange">
<el-table-column
type="selection"
v-if="eableBatchEdit">
v-if="enableBatchEdit">
</el-table-column>
<el-table-column
prop="name"
label="资源名">
</el-table-column>
<el-table-column
:sort-by="['isActive', 'name']"
sortable
prop="isActive"
label="自选源">
<template slot-scope="scope">
@@ -53,6 +54,19 @@
<el-button type="text">{{scope.row.group}}</el-button>
</template>
</el-table-column>
<el-table-column
label="状态"
sortable
:sort-by="['status']"
width="120">
<template slot-scope="scope">
<span v-show="scope.row.status === ''">
<i class="el-icon-loading"></i>
检测中...
</span>
<span v-show="scope.row.status !== ''">{{scope.row.status}}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
header-align="center"
@@ -60,6 +74,7 @@
<template slot-scope="scope">
<el-button size="mini" @click.stop="moveToTopEvent(scope.row)" type="text">置顶</el-button>
<el-button size="mini" @click.stop="editSite(scope.row)" type="text">编辑</el-button>
<el-button size="mini" @click.stop="checkSimpleSite(scope.row)" type="text">检测</el-button>
<el-button size="mini" @click.stop="removeEvent(scope.row)" type="text">删除</el-button>
</template>
</el-table-column>
@@ -67,7 +82,7 @@
</div>
<!-- 编辑页面 -->
<div>
<el-dialog :visible.sync="dialogVisible" v-if='dialogVisible' :title="dialogType==='edit'?'编辑源':'添加源'" :append-to-body="true" @close="closeDialog">
<el-dialog :visible.sync="dialogVisible" v-if='dialogVisible' :title="dialogType==='edit'?'编辑源':'新增源'" :append-to-body="true" @close="closeDialog">
<el-form :model="siteInfo" ref='siteInfo' label-width="75px" label-position="left" :rules="rules">
<el-form-item label="源站名" prop='name'>
<el-input v-model="siteInfo.name" placeholder="请输入源站名" />
@@ -78,6 +93,14 @@
<el-form-item label="下载接口" prop='download'>
<el-input v-model="siteInfo.download" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入Download接口地址可以空着"/>
</el-form-item>
<el-form-item label="分组" prop='group'>
<el-select v-model="siteInfo.group" allow-create filterable default-first-option placeholder="请输入分组">
<el-option v-for="item in siteGroup" :key="item" :label="item" :value="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="源站标识" prop='key'>
<el-input v-model="siteInfo.key" placeholder="请输入源站标识,如果为空,系统则自动生成" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取消</el-button>
@@ -86,12 +109,12 @@
</el-dialog>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { sites } from '../lib/dexie'
import zy from '../lib/site/tools'
import { remote } from 'electron'
import { sites as defaultSites } from '../lib/dexie/initData'
import fs from 'fs'
@@ -106,10 +129,14 @@ export default {
dialogType: 'new',
dialogVisible: false,
siteInfo: {
key: '',
name: '',
api: '',
download: ''
download: '',
group: '',
isActive: 1
},
siteGroup: [],
rules: {
name: [
{ required: true, message: '源站名不能为空', trigger: 'blur' }
@@ -121,10 +148,13 @@ export default {
{ required: false, trigger: 'blur' }
]
},
eableBatchEdit: false,
enableBatchEdit: false,
batchGroupName: '',
batchIsActive: 1,
multipleSelection: []
multipleSelection: [],
tableKey: 1,
checkAllSiteLoading: false,
editeOldkey: ''
}
},
computed: {
@@ -165,6 +195,9 @@ export default {
handleSelectionChange (rows) {
this.multipleSelection = rows
},
handleSortChange (column, prop, order) {
this.updateDatabase(this.sites)
},
saveBatchEdit () {
this.multipleSelection.forEach(ele => {
if (this.batchGroupName) {
@@ -181,26 +214,52 @@ export default {
sites: res
}
})
for (const i of this.sites) {
delete i.status
}
},
getSitesGroup () {
const arr = []
for (const i of this.sites) {
if (arr.indexOf(i.group) < 0) {
arr.push(i.group)
}
}
this.siteGroup = arr
},
addSite () {
this.getSitesGroup()
this.dialogType = 'new'
this.dialogVisible = true
this.siteInfo = {
key: '',
name: '',
api: '',
download: ''
download: '',
group: '',
isActive: 1
}
},
editSite (siteInfo) {
this.getSitesGroup()
if (this.checkAllSiteLoading) {
this.$message.info('正在检测, 请勿操作.')
return false
}
this.dialogType = 'edit'
this.dialogVisible = true
this.siteInfo = siteInfo
this.editeOldkey = siteInfo.key
},
closeDialog () {
this.dialogVisible = false
this.getSites()
},
removeEvent (e) {
if (this.checkAllSiteLoading) {
this.$message.info('正在检测, 请勿操作.')
return false
}
sites.remove(e.id).then(res => {
this.getSites()
}).catch(err => {
@@ -218,32 +277,51 @@ export default {
})
})
},
checkSiteKey (e) {
if (this.dialogType === 'edit' && this.editeOldkey === this.siteInfo.key) {
return true
} else {
for (const i of this.sites) {
if (i.key === this.siteInfo.key) {
this.$message.warning(`源站标识: ${i.key} 已存在, 请勿重复填写.`)
return false
}
}
return true
}
},
addOrEditSite () {
if (!this.siteInfo.name || !this.siteInfo.api) {
this.$message.error('名称和API接口不能为空。')
return
return false
}
if (!this.checkSiteKey()) {
return false
}
var randomstring = require('randomstring')
var doc = {
key: this.dialogType === 'edit' ? this.siteInfo.key : randomstring.generate(6),
key: this.dialogType === 'edit' ? this.siteInfo.key : this.siteInfo.key ? this.siteInfo.key : randomstring.generate(6),
id: this.dialogType === 'edit' ? this.siteInfo.id : this.sites[this.sites.length - 1].id + 1,
name: this.siteInfo.name,
api: this.siteInfo.api,
download: this.siteInfo.download
download: this.siteInfo.download,
group: this.siteInfo.group,
isActive: this.siteInfo.isActive
}
const _hmt = window._hmt
_hmt.push(['_trackEvent', 'site', 'add', `${this.siteInfo.name}: ${this.siteInfo.api}`])
if (this.dialogType === 'edit') sites.remove(this.siteInfo.id)
sites.add(doc).then(res => {
this.siteInfo = {
key: '',
name: '',
api: '',
download: ''
download: '',
group: ''
}
this.dialogType === 'edit' ? this.$message.success('修改成功!') : this.$message.success('添加新源成功!')
this.dialogType === 'edit' ? this.$message.success('修改成功!') : this.$message.success('新源成功!')
this.dialogVisible = false
this.getSites()
})
this.editeOldkey = ''
},
exportSites () {
this.getSites()
@@ -304,10 +382,19 @@ export default {
this.$message.success('重置源成功')
},
moveToTopEvent (i) {
if (this.checkAllSiteLoading) {
this.$message.info('正在检测, 请勿操作.')
return false
}
this.sites.sort(function (x, y) { return x.key === i.key ? -1 : y.key === i.key ? 1 : 0 })
this.updateDatabase()
},
isActiveChangeEvent () {
syncTableData () {
if (this.$refs.editSitesTable.tableData && this.$refs.editSitesTable.tableData.length === this.sites.length) {
this.sites = this.$refs.editSitesTable.tableData
}
},
isActiveChangeEvent (row) {
this.updateDatabase()
},
resetId (inArray) {
@@ -318,6 +405,8 @@ export default {
})
},
updateDatabase () {
// 因为el-table的数据是单向绑定,我们先同步el-table里的数据和其绑定的数据
this.syncTableData()
sites.clear().then(res => {
var id = 1
this.sites.forEach(ele => {
@@ -332,7 +421,7 @@ export default {
},
rowDrop () {
const tbody = document.getElementById('sites-table').querySelector('.el-table__body-wrapper tbody')
const _this = this
var _this = this
Sortable.create(tbody, {
onEnd ({ newIndex, oldIndex }) {
const currRow = _this.sites.splice(oldIndex, 1)[0]
@@ -340,10 +429,41 @@ export default {
_this.updateDatabase()
}
})
},
async checkAllSite () {
this.checkAllSiteLoading = true
for (const i of this.sites) {
i.status = ''
this.tableKey = Math.random()
const flag = await zy.check(i.key)
if (flag) {
i.status = '可用'
} else {
i.status = '失效'
i.isActive = 0
}
this.tableKey = Math.random()
}
this.checkAllSiteLoading = false
this.updateDatabase()
},
async checkSimpleSite (row) {
this.checkAllSiteLoading = true
const flag = await zy.check(row.key)
if (flag) {
row.status = '可用'
} else {
row.status = '失效'
row.isActive = 0
}
this.updateDatabase()
this.tableKey = Math.random()
this.checkAllSiteLoading = false
}
},
mounted () {
this.rowDrop()
this.checkAllSiteLoading = false
},
created () {
this.getSites()

View File

@@ -1,5 +1,5 @@
<template>
<div class="film">
<div class="film pictureView">
<div class="header">
<div class="zy-select" @mouseleave="show.site = false">
<div class="vs-placeholder" @click="show.site = true">{{site.name}}</div>
@@ -13,7 +13,7 @@
<div class="vs-placeholder" @click="show.classList = true">{{type.name}}</div>
<div class="vs-options" v-show="show.classList">
<ul class="zy-scroll" style="max-height: 600px;">
<li :class="type.tid === i.tid ? 'active' : ''" v-for="i in classList" :key="i.tid" @click="classClick(i)">{{ i.name }}</li>
<li :class="type.tid === i.tid ? 'active' : ''" v-for="i in classList" :key="i.tid" @click="classClick(i)">{{ i.name | classNameFilter }}</li>
</ul>
</div>
</div>
@@ -87,10 +87,9 @@
<ul>
<li v-for="(i, j) in searchContents" :key="j" @click="detailEvent(i.site, i)">
<span class="name">{{i.name}}</span>
<span class="type">{{i.type}}</span>
<span class="time">{{i.year}}</span>
<span class="note">{{i.note}}</span>
<span class="last">{{i.last}}</span>
<span class="info">{{i.site.name}}</span>
<span class="info">{{i.type}}</span>
<span class="info">{{i.note}}</span>
<span class="operate">
<span class="btn" @click.stop="playEvent(i.site, i)">播放</span>
<span class="btn" @click.stop="starEvent(i.site, i)">收藏</span>
@@ -136,7 +135,7 @@ export default {
searchTxt: '',
searchContents: [],
// 福利片关键词
r18KeyWords: ['伦理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番']
r18KeyWords: ['伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番']
}
},
components: {
@@ -183,6 +182,11 @@ export default {
return this.$store.getters.getEditSites.sites // 需要监听的数据
}
},
filters: {
classNameFilter: (name) => {
return name.replace(/[^\u4e00-\u9fa5]/gi, '')
}
},
watch: {
view () {
this.changeView()
@@ -213,8 +217,6 @@ export default {
}
})
}
const _hmt = window._hmt
_hmt.push(['_trackEvent', 'site', 'change', e.name])
},
classClick (e) {
this.show.classList = false
@@ -225,8 +227,6 @@ export default {
this.infiniteId += 1
}
})
const _hmt = window._hmt
_hmt.push(['_trackEvent', 'class', 'change', e.name])
},
getClass () {
return new Promise((resolve, reject) => {
@@ -313,37 +313,33 @@ export default {
info: e
}
},
playEvent (site, e) {
history.find({ site: site.key, ids: e.id }).then(res => {
if (res) {
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: res.index, site: site } }
} else {
this.video = { key: site.key, info: { id: e.id, name: e.name, index: 0, site: site } }
}
})
async playEvent (site, e) {
const db = await history.find({ site: site.key, ids: e.id })
if (db) {
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: db.index, site: site } }
} else {
this.video = { key: site.key, info: { id: e.id, name: e.name, index: 0, site: site } }
}
this.view = 'Play'
},
starEvent (site, e) {
star.find({ key: site.key, ids: e.id }).then(res => {
if (res) {
this.$message.info('已存在')
} else {
async starEvent (site, e) {
const db = await star.find({ key: site.key, ids: e.id })
if (db) {
this.$message.info('已存在')
} else {
zy.detail(site.key, e.id).then(detailRes => {
const docs = {
key: site.key,
ids: e.id,
site: site,
name: e.name,
type: e.type,
year: e.year,
last: e.last,
note: e.note
detail: detailRes
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
})
}
}).catch(() => {
this.$message.warning('收藏失败')
})
})
}
},
shareEvent (site, e) {
this.share = {
@@ -412,8 +408,6 @@ export default {
} else {
this.searchSingleSiteEvent(this.site, wd)
}
const _hmt = window._hmt
_hmt.push(['_trackEvent', 'film', 'search', wd])
},
searchAllSitesEvent (sites, wd) {
this.searchTxt = wd
@@ -433,13 +427,17 @@ export default {
const type = Object.prototype.toString.call(res)
if (type === '[object Array]') {
res.forEach(element => {
element.site = site
this.searchContents.push(element)
zy.detail(site.key, element.id).then(detailRes => {
detailRes.site = site
this.searchContents.push(detailRes)
})
})
}
if (type === '[object Object]') {
res.site = site
this.searchContents.push(res)
zy.detail(site.key, res.id).then(detailRes => {
detailRes.site = site
this.searchContents.push(detailRes)
})
}
})
})
@@ -481,7 +479,9 @@ export default {
this.type = {}
this.list = []
} else {
this.sites = res.filter(x => x.isActive)
this.sites = res.filter((item, index, self) => {
return self.indexOf(item) >= 0 && item.isActive
})
this.site = this.sites[0]
this.siteClick(this.site)
}

View File

@@ -2,12 +2,12 @@
<div class="listpage" id="history">
<div class="listpage-content">
<div class="listpage-header">
<el-button @click.stop="exportHistory" type="text">导出</el-button>
<el-button @click.stop="importHistory" type="text">导入</el-button>
<el-button @click.stop="clearAllHistory" type="text">清空</el-button>
<el-button @click.stop="exportHistory" icon="el-icon-upload2">导出</el-button>
<el-button @click.stop="importHistory" icon="el-icon-download">导入</el-button>
<el-button @click.stop="clearAllHistory" icon="el-icon-delete-solid">清空</el-button>
</div>
<div class="listpage-body" id="history-table">
<el-table size="mini" fit :data="history" row-key="id" @row-click="detailEvent">
<el-table size="mini" fit height="100%" :data="history" row-key="id" @row-click="detailEvent">
<el-table-column
prop="name"
label="片名">
@@ -30,7 +30,7 @@
</el-table-column>
<el-table-column
label="操作"
header-align="center"
header-align="right"
align="right">
<template slot-scope="scope">
<el-button @click.stop="playEvent(scope.row)" type="text">播放</el-button>
@@ -113,14 +113,13 @@ export default {
}
}
},
playEvent (e) {
history.find({ site: e.site, ids: e.ids }).then(res => {
if (res) {
this.video = { key: res.site, info: { id: res.ids, name: res.name, index: res.index } }
} else {
this.video = { key: e.site, info: { id: e.ids, name: e.name, index: 0 } }
}
})
async playEvent (e) {
const db = await history.find({ site: e.site, ids: e.ids })
if (db) {
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: db.index } }
} else {
this.video = { key: e.site, info: { id: e.ids, name: e.name, index: 0 } }
}
this.view = 'Play'
},
shareEvent (e) {

View File

@@ -1,60 +1,68 @@
<template>
<div class="listpage" id="IPTV">
<div class="listpage-content">
<div class="listpage-header" v-show="!eableBatchEdit">
<el-switch v-model="eableBatchEdit" active-text="批处理分组"></el-switch>
<el-button type="text">总频道数:{{iptvList.length}}</el-button>
<el-button @click.stop="exportChannels" type="text"></el-button>
<el-button @click.stop="importChannels" type="text">导入</el-button>
<el-button @click.stop="removeAllChannels" type="text">清空</el-button>
<el-button @click.stop="resetChannelsEvent" type="text">重置</el-button>
<div class="listpage-header" v-show="!enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理分组"></el-switch>
<el-button @click.stop="exportChannels" icon="el-icon-upload2" >导出</el-button>
<el-button @click.stop="importChannels" icon="el-icon-download"></el-button>
<el-button @click.stop="removeAllChannels" icon="el-icon-delete-solid">清空</el-button>
<el-button @click.stop="resetChannelsEvent" icon="el-icon-refresh-left">重置</el-button>
</div>
<div class="listpage-header" v-show="eableBatchEdit">
<el-switch v-model="eableBatchEdit" active-text="批处理分组"></el-switch>
<div class="listpage-header" v-show="enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理分组"></el-switch>
<el-input placeholder="新组名" v-model="batchGroupName"></el-input>
<el-button type="primary" icon="el-icon-edit" @click.stop="saveBatchEdit">保存</el-button>
</div>
<div class="listpage-body" id="iptv-table">
<el-table
:data="filteredTableData"
row-key="id"
@row-click="playEvent"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
v-if="eableBatchEdit">
</el-table-column>
<el-table-column
prop="name"
label="频道名">
<template #header>
<el-input
placeholder="搜索"
size="mini"
v-model.trim="searchTxt">
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
</template>
</el-table-column>
<el-table-column
prop="group"
label="分组"
:filters="getFilters"
:filter-method="filterHandle"
filter-placement="bottom-end">
<template slot-scope="scope">
<el-button type="text">{{scope.row.group}}</el-button>
</template>
</el-table-column>
<el-table-column
label="操作"
header-align="center"
ref="iptvTable"
size="mini" fit height="100%" row-key="id"
:data="filteredTableData"
@row-click="playEvent"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange">>
<el-table-column
type="selection"
v-if="enableBatchEdit">
</el-table-column>
<el-table-column
default-sort="ascending"
prop="name"
label="频道名">
<template #header>
<el-input
placeholder="搜索"
size="mini"
v-model.trim="searchTxt">
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
</template>
</el-table-column>
<el-table-column
sort-by="['group', 'name']"
sortable
:sort-method="sortByGroup"
prop="group"
label="分组"
:filters="getFilters"
:filter-method="filterHandle"
filter-placement="bottom-end">
<template slot-scope="scope">
<el-button type="text">{{scope.row.group}}</el-button>
</template>
</el-table-column>
<el-table-column
label="操作"
header-align="right"
align="right">
<template slot-scope="scope">
<el-button @click.stop="moveToTopEvent(scope.row)" type="text">置顶</el-button>
<el-button @click.stop="removeEvent(scope.row)" type="text">删除</el-button>
</template>
</el-table-column>
<template #header>
<span>总频道数:{{ iptvList.length }}</span>
</template>
<template slot-scope="scope">
<el-button @click.stop="moveToTopEvent(scope.row)" type="text">置顶</el-button>
<el-button @click.stop="removeEvent(scope.row)" type="text">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
@@ -74,7 +82,7 @@ export default {
iptvList: [],
searchTxt: '',
searchRecordList: [],
eableBatchEdit: false,
enableBatchEdit: false,
batchGroupName: '',
multipleSelection: [],
show: {
@@ -131,9 +139,15 @@ export default {
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
sortByGroup (a, b) {
return a.group.localeCompare(b.group, 'zh')
},
handleSelectionChange (rows) {
this.multipleSelection = rows
},
handleSortChange (column, prop, order) {
this.updateDatabase()
},
saveBatchEdit () {
if (this.multipleSelection && this.batchGroupName) {
this.multipleSelection.forEach(ele => {
@@ -214,17 +228,17 @@ export default {
var docs = this.iptvList
var id = docs.length
result.filePaths.forEach(file => {
if (file.endsWith('m3u')) {
if (file.endsWith('m3u') || file.endsWith('m3u8')) {
const parser = require('iptv-playlist-parser')
const playlist = fs.readFileSync(file, { encoding: 'utf-8' })
const result = parser.parse(playlist)
result.items.forEach(ele => {
if (ele.name && ele.url && ele.url.includes('.m3u8')) {
if (ele.name && ele.url && ele.url.endsWith('.m3u8')) {
var doc = {
id: id,
name: ele.name,
url: ele.url,
group: this.determineGroup(ele.group, ele.name)
group: this.determineGroup(ele.name)
}
id += 1
docs.push(doc)
@@ -235,12 +249,12 @@ export default {
var str = fs.readFileSync(file)
const json = JSON.parse(str)
json.forEach(ele => {
if (ele.name && ele.url && ele.url.includes('.m3u8')) {
if (ele.name && ele.url && ele.url.endsWith('.m3u8')) {
var doc = {
id: id,
name: ele.name,
url: ele.url,
group: ele.group === undefined ? this.determineGroup(ele.group, ele.name) : ele.group
group: this.determineGroup(ele.name)
}
id += 1
docs.push(doc)
@@ -248,8 +262,8 @@ export default {
})
}
})
// 获取url不重复的列表
const uniqueList = [...new Map(docs.map(item => [item.url, item])).values()]
// 获取name不重复的列表
const uniqueList = [...new Map(docs.map(item => [item.name, item])).values()]
iptv.clear().then(res => {
iptv.bulkAdd(uniqueList).then(e => {
this.getChannels()
@@ -259,13 +273,17 @@ export default {
}
})
},
determineGroup (group, name) {
if (!group) {
return group
determineGroup (name) {
if (name.toLowerCase().includes('cctv') && (name.includes('蓝光') || name.includes('高清'))) {
return '央视高清'
} else if (name.toLowerCase().includes('cctv')) {
return '央视'
} else if (name.includes('卫视')) {
return '卫视'
} else if (name.includes('香港') || name.includes('澳门') || name.includes('台湾') || name.includes('凤凰')) {
return '港澳台'
} else if (name.includes('高清') || name.includes('蓝光') || name.includes('1080P')) {
return '高清'
} else {
return '其他'
}
@@ -313,10 +331,16 @@ export default {
this.iptvList.sort(function (x, y) { return (x.name === i.name && x.url === i.url) ? -1 : (y.name === i.name && y.url === i.url) ? 1 : 0 })
this.updateDatabase()
},
syncTableData () {
if (this.$refs.iptvTable.tableData && this.$refs.iptvTable.tableData.length === this.iptvList.length) {
this.iptvList = this.$refs.iptvTable.tableData
}
},
updateDatabase () {
this.syncTableData()
iptv.clear().then(res => {
this.resetId(this.iptvList)
iptv.bulkAdd(this.iptvList).then(this.getChannels())
iptv.bulkAdd(this.iptvList)
})
},
resetId (inArray) {

View File

@@ -3,23 +3,20 @@
<div class="box">
<div class="title">
<span v-if="this.right.list.length > 1"> {{(video.info.index + 1)}} </span>{{name}}
<span v-if="video.key" class="right" @click="playWithExternalPalyerEvent" title="使用第三方播放器">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<polygon points="20 8 20 20 4 20 4 8"></polygon>
<polyline stroke-linejoin="round" points="8 4 12 7.917 16 4"></polyline>
</svg>
</span>
<span v-if="video.key" class="right" @click="issueEvent" title="复制调试信息">
<svg t="1596338860607" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3127" width="24" height="24">
<path d="M503.803829 63.578014c-247.050676 0-447.328072 200.277396-447.328072 447.327048 0 247.054769 200.277396 447.333188 447.328072 447.333188 247.054769 0 447.332165-200.278419 447.332165-447.333188C951.13497 263.85541 750.858598 63.578014 503.803829 63.578014L503.803829 63.578014zM503.803829 894.313336c-211.749682 0-383.408273-171.659615-383.408273-383.408273 0-211.749682 171.659615-383.40725 383.408273-383.40725 211.753775 0 383.412366 171.658591 383.412366 383.40725C887.216195 722.653721 715.557604 894.313336 503.803829 894.313336L503.803829 894.313336zM447.745069 255.897158l127.914298 0L575.659367 383.576095 447.745069 383.576095 447.745069 255.897158 447.745069 255.897158zM447.745069 425.470251l127.914298 0 0 342.058516L447.745069 767.528767 447.745069 425.470251 447.745069 425.470251zM447.745069 425.470251" p-id="3128"></path>
</svg>
</span>
</div>
<div class="player">
<div id="xgplayer"></div>
</div>
<div class="more">
<span class="zy-svg" @click="nextEvent" v-show="showNext">
<span class="zy-svg" @click="otherEvent" v-show="name !== ''">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="coloursIconTitle">
<title id="coloursIconTitle">换源</title>
<circle cx="12" cy="9" r="5"></circle>
<circle cx="9" cy="14" r="5"></circle>
<circle cx="15" cy="14" r="5"></circle>
</svg>
</span>
<span class="zy-svg" @click="nextEvent" v-show="right.list.length > 1">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="forwardIconTitle">
<title id="forwardIconTitle">下一集</title>
<path d="M10 14.74L3 19V5l7 4.26V5l12 7-12 7v-4.26z"></path>
@@ -58,8 +55,14 @@
</svg>
</span>
<span class="zy-svg" @click="miniEvent" v-show="right.list.length > 0">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="diamondIconTitle">
<title id="diamondIconTitle">精简模式</title>
<path d="M12 20L3 11M12 20L21 11M12 20L8 11M12 20L16 11M3 11L7 5M3 11H8M7 5L8 11M7 5H12M17 5L21 11M17 5L16 11M17 5H12M21 11H16M8 11H16M8 11L12 5M16 11L12 5"></path>
</svg>
</span>
<span class="zy-svg" @click="playWithExternalPalyerEvent" v-show="right.list.length > 0">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="tvIconTitle">
<title id="tvIconTitle">精简模式</title>
<title id="tvIconTitle" >使用第三方播放器</title>
<polygon points="20 8 20 20 4 20 4 8"></polygon>
<polyline stroke-linejoin="round" points="8 4 12 7.917 16 4"></polyline>
</svg>
@@ -81,13 +84,30 @@
<rect x="17" y="6" width="1" height="1"></rect>
</svg>
</span>
<span class="zy-svg" @click="showShortcutEvent" v-show="right.list.length > 0">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="sendIconTitle">
<title id="sendIconTitle">快捷键指南</title>
<polygon points="21.368 12.001 3 21.609 3 14 11 12 3 9.794 3 2.394"></polygon>
</svg>
</span>
<span class="zy-svg" @click="issueEvent" v-show="right.list.length > 0">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="infoIconTitle">
<title id="infoIconTitle">复制调试信息</title>
<path d="M12,12 L12,15"></path>
<line x1="12" y1="9" x2="12" y2="9"></line>
<circle cx="12" cy="12" r="10"></circle>
</svg>
</span>
<span class="last-tip" v-if="!video.key && right.history.length > 0" @click="historyItemEvent(right.history[0])">上次播放到{{right.history[0].site}}{{right.history[0].name}} {{right.history[0].index+1}}</span>
</div>
</div>
<transition name="slideX">
<div v-if="right.show" class="list">
<div class="list-top">
<span class="list-top-title">{{ right.type === 'list' ? '播放列表' : '历史记录' }}</span>
<span class="list-top-title" v-if="right.type === 'list'">播放列表</span>
<span class="list-top-title" v-if="right.type === 'history'">历史记录</span>
<span class="list-top-title" v-if="right.type === 'shortcut'">快捷键指南</span>
<span class="list-top-title" v-if="right.type === 'other'">其他源的视频</span>
<span class="list-top-close zy-svg" @click="closeListEvent">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="closeIconTitle">
<title id="closeIconTitle">关闭</title>
@@ -106,6 +126,13 @@
<li v-show="right.history.length === 0">无数据</li>
<li @click="historyItemEvent(m)" :class="video.info.id === m.ids ? 'active' : ''" v-for="(m, n) in right.history" :key="n"><span class="title" :title="'【' + m.site + '】' + m.name + ' 第' + (m.index+1) + '集'">【{{m.site}}】{{m.name}} 第{{m.index+1}}集</span><span @click.stop="removeHistoryItem(m)" class="detail-delete">删除</span></li>
</ul>
<ul v-show="right.type === 'shortcut'" class="list-shortcut">
<li v-for="(m, n) in right.shortcut" :key="n"><span class="title">{{m.desc}} -- [ {{m.key}} ]</span></li>
</ul>
<ul v-show="right.type === 'other'" class="list-other">
<li v-show="right.other.length === 0">无数据</li>
<li @click="otherItemEvent(m)" v-for="(m, n) in right.other" :key="n"><span class="title">{{m.name}} - [{{m.site}}]</span></li>
</ul>
</div>
</div>
</transition>
@@ -113,7 +140,7 @@
</template>
<script>
import { mapMutations } from 'vuex'
import { star, history, setting, shortcut, mini, iptv } from '../lib/dexie'
import { star, history, setting, shortcut, mini, iptv, sites } from '../lib/dexie'
import zy from '../lib/site/tools'
import Player from 'xgplayer'
import Hls from 'xgplayer-hls.js'
@@ -171,7 +198,9 @@ export default {
show: false,
type: '',
list: [],
history: []
history: [],
shortcut: [],
other: []
},
config: {
id: 'xgplayer',
@@ -202,7 +231,6 @@ export default {
length: 0,
timer: null,
scroll: false,
showNext: false,
isStar: false,
isTop: false,
mini: {},
@@ -287,7 +315,10 @@ export default {
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
getUrls () {
async getUrls () {
if (this.video.key === '') {
return false
}
this.name = ''
if (this.timer !== null) {
clearInterval(this.timer)
@@ -302,19 +333,16 @@ export default {
this.playUrl(this.video.iptv.url)
this.name = this.video.iptv.name
this.getIptvList()
const _hmt = window._hmt
_hmt.push(['_trackEvent', 'IPTV', 'play', this.name])
} else {
const index = this.video.info.index | 0
let time = 0
history.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
if (res.index === index) {
time = res.time
}
const db = await history.find({ site: this.video.key, ids: this.video.info.id })
if (db) {
if (db.index === index) {
time = db.time
}
this.playVideo(index, time)
})
}
this.playVideo(index, time)
}
},
playUrl (url) {
@@ -324,7 +352,6 @@ export default {
playVideo (index = 0, time = 0) {
this.fetchM3u8List().then(m3u8Arr => {
this.xg.src = m3u8Arr[index]
this.showNext = m3u8Arr.length > 1
if (time !== 0) {
this.xg.play()
@@ -351,12 +378,9 @@ export default {
if (VIDEO_DETAIL_CACHE[cacheKey]) {
this.name = VIDEO_DETAIL_CACHE[cacheKey].name
resolve(VIDEO_DETAIL_CACHE[cacheKey].list)
return
}
zy.detail(this.video.key, this.video.info.id).then(res => {
this.name = res.name
const _hmt = window._hmt
_hmt.push(['_trackEvent', 'film', 'play', res.name])
const dd = res.dl.dd
const type = Object.prototype.toString.call(dd)
let m3u8Txt = []
@@ -393,34 +417,33 @@ export default {
})
})
},
videoPlaying () {
async videoPlaying () {
this.changeVideo()
history.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
const doc = {
site: res.site,
ids: res.ids,
name: res.name,
type: res.type,
year: res.year,
index: this.video.info.index,
time: res.time
}
history.remove(res.id)
history.add(doc)
} else {
const doc = {
site: this.video.key,
ids: this.video.info.id,
name: this.video.info.name,
type: this.video.info.type,
year: this.video.info.year,
index: this.video.info.index,
time: ''
}
history.add(doc)
const db = await history.find({ site: this.video.key, ids: this.video.info.id })
if (db) {
const doc = {
site: db.site,
ids: db.ids,
name: db.name,
type: db.type,
year: db.year,
index: this.video.info.index,
time: db.time
}
})
history.remove(db.id)
history.add(doc)
} else {
const doc = {
site: this.video.key,
ids: this.video.info.id,
name: this.video.info.name,
type: this.video.info.type,
year: this.video.info.year,
index: this.video.info.index,
time: ''
}
history.add(doc)
}
this.updateStar()
this.timerEvent()
},
@@ -429,15 +452,14 @@ export default {
this.checkTop()
},
timerEvent () {
this.timer = setInterval(() => {
history.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
const doc = { ...res }
doc.time = this.xg.currentTime
delete doc.id
history.update(res.id, doc)
}
})
this.timer = setInterval(async () => {
const db = await history.find({ site: this.video.key, ids: this.video.info.id })
if (db) {
const doc = { ...db }
doc.time = this.xg.currentTime
delete doc.id
history.update(db.id, doc)
}
}, 10000)
},
prevEvent () {
@@ -502,41 +524,41 @@ export default {
this.right.history = res.reverse()
})
},
updateStar () {
async updateStar () {
const info = this.video.info
star.find({ key: this.video.key, ids: info.id }).then(res => {
if (res) {
res.index = info.index
star.update(res.id, res)
}
}).catch(() => {
this.$message.warning('检查收藏失败')
})
const db = await star.find({ key: this.video.key, ids: info.id })
if (db) {
db.index = info.index
star.update(db.id, db)
}
},
starEvent () {
async starEvent () {
const info = this.video.info
star.find({ key: this.video.key, ids: info.id }).then(res => {
const doc = {
key: this.video.key,
ids: info.id,
name: info.name,
type: info.type,
year: info.year,
last: info.last,
note: info.note,
index: info.index
}
if (res) {
star.update(res.id, doc)
} else {
star.add(doc).then(starRes => {
const db = await star.find({ key: this.video.key, ids: info.id })
if (db) {
star.remove(db.id).then(res => {
if (res) {
this.$message.warning('取消收藏失败')
} else {
this.$message.success('取消收藏成功')
this.isStar = false
}
})
} else {
zy.detail(this.video.key, info.id).then(detailRes => {
const docs = {
key: this.video.key,
ids: info.id,
name: info.name,
detail: detailRes,
index: info.index
}
star.add(docs).then(res => {
this.$message.success('收藏成功')
this.isStar = true
})
}
}).catch(() => {
this.$message.warning('检查收藏失败')
})
})
}
},
detailEvent () {
this.detail = {
@@ -628,14 +650,13 @@ export default {
fs.writeFileSync(filePath, str)
return filePath
},
checkStar () {
star.find({ key: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
this.isStar = true
} else {
this.isStar = false
}
})
async checkStar () {
const db = await star.find({ key: this.video.key, ids: this.video.info.id })
if (db) {
this.isStar = true
} else {
this.isStar = false
}
},
checkTop () {
const win = remote.getCurrentWindow()
@@ -690,6 +711,7 @@ export default {
if (this.video.iptv) {
var channel = this.iptvList[n]
this.video.iptv = channel
this.name = this.video.iptv.name
// 是直播源,直接播放
this.playUrl(channel.url)
} else {
@@ -722,6 +744,49 @@ export default {
this.$message.warning('删除历史记录失败, 错误信息: ' + err)
})
},
async getAllsites () {
const all = await sites.all()
this.right.other = []
for (const i of all) {
if (i.isActive) {
try {
const searchRes = await zy.search(i.key, this.name)
const type = Object.prototype.toString.call(searchRes)
if (type === '[object Array]') {
searchRes.forEach(async item => {
const detailRes = item
detailRes.key = i.key
detailRes.site = i.site
this.right.other.push(detailRes)
})
}
if (type === '[object Object]') {
const detailRes = searchRes
detailRes.key = i.key
detailRes.site = i.name
this.right.other.push(detailRes)
}
} catch (err) {
console.error(err)
}
}
}
},
otherEvent (m) {
this.right.type = 'other'
this.getAllsites()
this.right.show = true
},
async otherItemEvent (e) {
const db = await history.find({ site: e.key, ids: e.id })
if (db) {
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: db.index, site: e.key } }
} else {
this.video = { key: e.key, info: { id: e.id, name: e.name, index: 0, site: e.key } }
}
this.right.show = false
this.right.type = ''
},
mtEvent () {
setting.find().then(res => {
if (res.shortcut) {
@@ -1002,18 +1067,17 @@ export default {
let timerID
ev.forEach(item => {
this.xg.root.addEventListener(item, () => {
if (!this.xg.fullscreen) {
return
if (this.xg && this.xg.fullscreen) {
const videoTitle = document.querySelector('.xg-view-videoTitle')
videoTitle.style.display = 'block'
clearTimeout(timerID)
timerID = setTimeout(() => {
// 播放中自动消失
if (this.xg && !this.xg.paused) {
videoTitle.style.display = 'none'
}
}, 3000)
}
const videoTitle = document.querySelector('.xg-view-videoTitle')
videoTitle.style.display = 'block'
clearTimeout(timerID)
timerID = setTimeout(() => {
// 播放中自动消失
if (this.xg && !this.xg.paused) {
videoTitle.style.display = 'none'
}
}, 3000)
})
})
@@ -1025,16 +1089,20 @@ export default {
if (this.xg.fullscreen) {
this.xg.exitFullscreen()
}
this.xg.destroy()
clearInterval(this.timer)
this.video.key = ''
this.xg.src = ''
this.config.src = ''
this.xg.destroy(false)
this.xg = null
this.name = ''
this.right.list = []
this.showNext = false
this.getAllhistory()
setTimeout(() => {
this.playerInstall()
this.xg = new Hls(this.config)
this.playerInstall()
this.bindEvent()
}, 500)
}, 1000)
},
minMaxEvent () {
const win = remote.getCurrentWindow()
@@ -1075,6 +1143,14 @@ export default {
}
addPlayerView.bind(this, 'videoTitle', `<span>${title}</span>`, {})()
})
},
showShortcutEvent () {
this.right.show = !this.right.show
shortcut.all().then(res => {
this.right.type = 'shortcut'
this.right.shortcut = res
console.log(res)
})
}
},
created () {
@@ -1084,16 +1160,15 @@ export default {
mounted () {
this.playerInstall()
this.xg = new Hls(this.config)
ipcRenderer.on('miniClosed', () => {
history.find({ site: this.video.key, ids: this.video.info.id }).then(res => {
if (res) {
if (this.video.info.index !== res.index) {
this.video.info.index = res.index
} else {
this.getUrls()
}
ipcRenderer.on('miniClosed', async () => {
const db = await history.find({ site: this.video.key, ids: this.video.info.id })
if (db) {
if (this.video.info.index !== db.index) {
this.video.info.index = db.index
} else {
this.getUrls()
}
})
}
})
this.bindEvent()
this.minMaxEvent()

View File

@@ -0,0 +1,405 @@
<template>
<div class="listpage recommandataions pictureView">
<div class="listpage-content">
<div class="listpage-header">
<el-switch v-model="viewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="list" @change="updateViewMode"></el-switch>
<el-select size="mini" v-model="selectedAreas" multiple collapse-tags style="margin-left: 20px;" placeholder="地区">
<el-option
v-for="item in areas"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-select size="mini" v-model="selectedTypes" multiple collapse-tags style="margin-left: 20px;" placeholder="类型">
<el-option
v-for="item in types"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-button :loading="loading" @click.stop="updateEvent" icon="el-icon-refresh">更新推荐</el-button>
</div>
<div class="listpage-body" id="recommandataions-table" v-show="viewMode === 'list'">
<el-table size="mini" fit height="100%" row-key="id"
ref="recommandataionsTable"
:data="filteredRecommandations"
@row-click="detailEvent">
<el-table-column
prop="name"
label="片名">
</el-table-column>
<el-table-column
prop="detail.area"
label="地区"
width="100">
</el-table-column>
<el-table-column
prop="detail.type"
label="类型"
width="100">
</el-table-column>
<el-table-column
prop="detail.year"
label="上映"
width="100"
align="center">
</el-table-column>
<el-table-column v-if="filteredRecommandations.some(e => e.detail.note)"
prop="detail.note"
width="120"
label="备注">
</el-table-column>
<el-table-column v-if="filteredRecommandations.some(e => e.rate)"
prop="rate"
width="120"
label="豆瓣评分">
</el-table-column>
<el-table-column
label="操作"
header-align="right"
align="right">
<template slot-scope="scope">
<el-button @click.stop="playEvent(scope.row)" type="text">播放</el-button>
<el-button @click.stop="shareEvent(scope.row)" type="text">分享</el-button>
<el-button @click.stop="downloadEvent(scope.row)" type="text">下载</el-button>
<el-button @click.stop="deleteEvent(scope.row)" type="text">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="body zy-scroll" id="star-picture" v-show="viewMode === 'picture'">
<div class="show-img">
<Waterfall ref="waterfall" :list="filteredRecommandations" :gutter="20" :width="240"
:breakpoints="{ 1200: { rowPerView: 4 } }"
animationEffect="fadeInUp"
backgroundColor="rgba(0, 0, 0, 0)">
<template slot="item" slot-scope="props">
<div class="card">
<div class="img">
<div class="rate" v-if="props.data.rate && props.data.rate !== '暂无评分'">
<span>豆瓣: {{props.data.rate}}</span>
</div>
<img style="width: 100%" :src="props.data.detail.pic" alt="" @load="$refs.waterfall.refresh()" @click="detailEvent(props.data)">
<div class="operate">
<div class="operate-wrap">
<span class="o-play" @click="playEvent(props.data)">播放</span>
<span class="o-share" @click="shareEvent(props.data)">分享</span>
<span class="o-star" @click="downloadEvent(props.data)">下载</span>
<span class="o-star" @click="deleteEvent(props.data)">删除</span>
</div>
</div>
</div>
<div class="name" @click="detailEvent(props.data)">{{props.data.name}}</div>
<div class="info">
<span>{{props.data.detail.area}}</span>
<span>{{props.data.detail.year}}</span>
<span>{{props.data.detail.note}}</span>
<span>{{props.data.detail.type}}</span>
</div>
</div>
</template>
</Waterfall>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { history, recommandation, setting } from '../lib/dexie'
import zy from '../lib/site/tools'
import Waterfall from 'vue-waterfall-plugin'
const { clipboard } = require('electron')
export default {
name: 'recommandations',
data () {
return {
recommandations: [],
sites: [],
viewMode: 'picture',
loading: false,
types: [],
selectedTypes: [],
areas: [],
selectedAreas: []
}
},
components: {
Waterfall
},
computed: {
view: {
get () {
return this.$store.getters.getView
},
set (val) {
this.SET_VIEW(val)
}
},
video: {
get () {
return this.$store.getters.getVideo
},
set (val) {
this.SET_VIDEO(val)
}
},
detail: {
get () {
return this.$store.getters.getDetail
},
set (val) {
this.SET_DETAIL(val)
}
},
share: {
get () {
return this.$store.getters.getShare
},
set (val) {
this.SET_SHARE(val)
}
},
filteredRecommandations () {
var filteredData = this.recommandations.filter(x => (this.selectedAreas.length === 0) || this.selectedAreas.includes(x.detail.area))
filteredData = filteredData.filter(x => (this.selectedTypes.length === 0) || this.selectedTypes.includes(x.detail.type))
return filteredData
}
},
watch: {
view () {
this.getRecommandations()
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
detailEvent (e) {
this.detail = {
show: true,
key: e.key,
info: {
id: e.ids,
name: e.name
}
}
},
updateEvent () {
const url = 'https://raw.githubusercontent.com/Hunlongyu/ZY-Player/master/src/lib/dexie/iniData/Recommandations.json'
this.loading = true
const axios = require('axios')
axios.get(url).then(res => {
if (res.status === 200) {
if (res.data.length > 0) {
this.recommandations = res.data
this.recommandations.sort(function (a, b) {
return b.detail.year - a.detail.year
})
recommandation.clear().then(recommandation.bulkAdd(this.recommandations))
this.$message.success('更新推荐成功')
}
}
this.loading = false
})
},
async playEvent (e) {
const db = await history.find({ site: e.key, ids: e.ids })
if (db) {
this.video = { key: e.key, info: { id: db.ids, name: db.name, index: db.index } }
} else {
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 } }
}
this.view = 'Play'
},
deleteEvent (e) {
recommandation.remove(e.id).then(res => {
if (res) {
this.$message.warning('删除失败')
} else {
this.$message.success('删除成功')
}
this.getRecommandations()
})
},
shareEvent (e) {
this.share = {
show: true,
key: e.key,
info: e
}
},
downloadEvent (e) {
zy.download(e.key, e.ids).then(res => {
if (res && res.dl && res.dl.dd) {
const text = res.dl.dd._t
if (text) {
const list = text.split('#')
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『MP4』格式的链接已复制, 快去下载吧!')
} else {
this.$message.warning('没有查询到下载链接.')
}
} else {
var m3u8List = {}
zy.detail(e.key, e.ids).then(res => {
const dd = res.dl.dd
const type = Object.prototype.toString.call(dd)
if (type === '[object Array]') {
for (const i of dd) {
if (i._flag.indexOf('m3u8') >= 0) {
m3u8List = i._t.split('#')
}
}
} else {
m3u8List = dd._t.split('#')
}
const list = [...m3u8List]
let downloadUrl = ''
for (const i of list) {
const url = encodeURI(i.split('$')[1])
downloadUrl += (url + '\n')
}
clipboard.writeText(downloadUrl)
this.$message.success('『M3U8』格式的链接已复制, 快去下载吧!')
})
}
})
},
getRecommandations () {
recommandation.all().then(res => {
this.recommandations = res
this.recommandations.sort(function (a, b) {
return b.detail.year - a.detail.year
})
this.getFilterData()
})
},
getFilterData () {
this.types = [...new Set(this.recommandations.map(ele => ele.detail.type))].filter(x => x)
this.areas = [...new Set(this.recommandations.map(ele => ele.detail.area))].filter(x => x)
},
getViewMode () {
setting.find().then(res => {
this.viewMode = res.recommandationViewMode
})
},
updateViewMode () {
setting.find().then(res => {
res.recommandationViewMode = this.viewMode
setting.update(res)
})
}
},
created () {
this.getRecommandations()
this.getViewMode()
}
}
</script>
<style lang="scss" scoped>
.recommandataions{
.body{
height: calc(100% - 40px);
display: flex;
position: relative;
overflow-y: auto;
&::-webkit-scrollbar{
width: 5px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
position: absolute;
}
&::-webkit-scrollbar-track {
border-radius: 10px;
position: absolute;
}
.body-box{
height: 100%;
width: 100%;
}
.show-img{
height: 100%;
width: 100%;
padding: 10px;
.card{
border-radius: 6px;
overflow: hidden;
.img{
position: relative;
min-height: 40px;
img{
width: 100%;
height: auto;
cursor: pointer;
}
.rate{
position: absolute;
top: 10%;
right: -35%;
width: 100%;
background-color: #111111aa;
color: #cdcdcd;
height: 30px;
line-height: 30px;
font-size: 14px;
text-align: center;
transform: rotate(45deg);
}
.operate{
display: none;
position: absolute;
left: 0;
bottom: 0;
background-color: #111111aa;
width: 100%;
font-size: 13px;
.operate-wrap{
display: flex;
justify-content: space-between;
.o-play, .o-star, .o-share{
cursor: pointer;
display: inline-block;
width: 80px;
height: 36px;
text-align: center;
line-height: 36px;
color: #cdcdcd;
&:hover{
background-color: #111;
}
}
}
}
}
.name{
font-size: 16px;
padding: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
}
.info{
display: flex;
justify-content: space-between;
font-size: 12px;
padding: 10px;
}
&:hover{
.operate{
display: block;
}
}
}
}
}
}
</style>

View File

@@ -6,8 +6,7 @@
<a @click="linkOpen('http://zyplayer.fun/')">官网</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player')">Github</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/issues')">当前版本v{{pkg.version}} 反馈</a>
<a style="color:#38dd77" @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/releases/tag/v' + latestVersion)" v-show="latestVersion !== pkg.version" >最新版本v{{latestVersion}}</a>
<a @click="checkUpdate()">检查更新</a>
<a style="color:#38dd77" @click="quitAndInstall()" v-show="latestVersion !== pkg.version" >最新版本v{{latestVersion}}</a>
</div>
<div class="view">
<div class="title">视图</div>
@@ -146,7 +145,6 @@ import { setting, sites, shortcut } from '../lib/dexie'
import { sites as defaultSites } from '../lib/dexie/initData'
import { shell, clipboard, remote, ipcRenderer } from 'electron'
import db from '../lib/dexie/dexie'
const _hmt = window._hmt
export default {
name: 'setting',
data () {
@@ -212,17 +210,7 @@ export default {
},
getSetting () {
setting.find().then(res => {
this.d = {
id: res.id,
theme: res.theme,
shortcut: res.shortcut,
view: res.view,
externalPlayer: res.externalPlayer,
searchAllSites: res.searchAllSites,
excludeRootClasses: res.excludeRootClasses,
excludeR18Films: res.excludeR18Films,
forwardTimeInSec: res.forwardTimeInSec
}
this.d = res
this.setting = this.d
})
},
@@ -311,7 +299,6 @@ export default {
changeTheme (e) {
this.d.theme = e
this.updateSettingEvent()
_hmt.push(['_trackEvent', 'setting', 'theme', e])
},
changeShortcut (e) {
this.d.shortcut = e
@@ -353,15 +340,20 @@ export default {
}
},
getLatestVersion () {
const cheerio = require('cheerio')
const axios = require('axios')
var url = 'https://github.com/Hunlongyu/ZY-Player/releases'
axios.get(url).then(res => {
const $ = cheerio.load(res.data)
var e = $('div.release-header')[0]
var firstResult = $(e).find('div>div>a')
this.latestVersion = firstResult.text()
ipcRenderer.send('checkForUpdate')
ipcRenderer.on('update-available', (e, info) => {
this.latestVersion = info.version
})
ipcRenderer.on('update-error', () => {
this.$message.warning = '更新出错.'
})
ipcRenderer.on('update-downloaded', () => {
this.$message.info = '下载完毕, 退出安装'
})
},
quitAndInstall () {
this.$message.success('已开始下载更新,下载完毕后,将自动退出安装。')
ipcRenderer.send('quitAndInstall')
},
createContextMenu () {
const { Menu, MenuItem } = remote
@@ -373,18 +365,6 @@ export default {
e.preventDefault()
menu.popup(remote.getCurrentWindow())
})
},
checkUpdate () {
ipcRenderer.send('update')
ipcRenderer.on('update-replay-check', (e, res) => {
console.log(res, 'update-replay-check')
})
ipcRenderer.on('update-replay-download', (e, res) => {
console.log(res, 'update-replay-download')
})
ipcRenderer.on('update-replay-downloaded', (e, res) => {
console.log(res, 'update-replay-downloaded')
})
}
},
created () {

View File

@@ -69,8 +69,6 @@ export default {
const id = this.share.info.ids || this.share.info.id
zy.detail(this.share.key, id).then(res => {
if (res) {
const _hmt = window._hmt
_hmt.push(['_trackEvent', 'film', 'share', res.name])
this.pic = res.pic
var m3u8List = {}
const dd = res.dl.dd

View File

@@ -1,14 +1,20 @@
<template>
<div class="listpage" id="star">
<div class="listpage star pictureView">
<div class="listpage-content">
<div class="listpage-header">
<el-button @click.stop="exportFavoritesEvent" type="text">导出</el-button>
<el-button @click.stop="importFavoritesEvent" type="text"></el-button>
<el-button @click.stop="clearFavoritesEvent" type="text">清空</el-button>
<el-button @click.stop="updateAllEvent" type="text">同步所有收藏</el-button>
<el-switch v-model="viewMode" active-text="海报" active-value="picture" inactive-text="列表" inactive-value="list" @change="updateViewMode"></el-switch>
<el-button @click.stop="exportFavoritesEvent" icon="el-icon-upload2"></el-button>
<el-button @click.stop="importFavoritesEvent" icon="el-icon-download">导入</el-button>
<el-button @click.stop="clearFavoritesEvent" icon="el-icon-delete-solid">清空</el-button>
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">同步所有收藏</el-button>
</div>
<div class="listpage-body" id="star-table">
<el-table size="mini" fit :data="list" height="100%" row-key="id" :cell-class-name="checkUpdate" @row-click="detailEvent">
<div class="listpage-body" id="star-table" v-show="viewMode === 'list'">
<el-table size="mini" fit height="100%" row-key="id"
ref="starTable"
:data="list"
:cell-class-name="checkUpdate"
@row-click="detailEvent"
@sort-change="handleSortChange">
<el-table-column
sortable
:sort-method="sortByName"
@@ -16,37 +22,39 @@
label="片名">
</el-table-column>
<el-table-column
:sort-by="['type', 'name']"
:sort-by="['detail.type', 'name']"
sortable
:sort-method="sortByType"
prop="type"
prop="detail.type"
label="类型"
width="100">
</el-table-column>
<el-table-column
sortable
:sort-by="['year', 'name']"
prop="year"
:sort-by="['detail.year', 'name']"
prop="detail.year"
label="上映"
width="100"
align="center">
</el-table-column>
<el-table-column
:sort-by="['site', 'name']"
sortable
:sort-method="sortBySite"
prop="site"
prop="site.name"
width="120"
label="源">
label="源">
<template slot-scope="scope">
<span>{{ getSiteName(scope.row.key) }}</span>
<span>{{ getSiteName(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column v-if="list.some(e => e.note)"
prop="note"
<el-table-column v-if="list.some(e => e.detail.note)"
prop="detail.note"
width="120"
label="备注">
</el-table-column>
<el-table-column v-if="list.some(e => e.rate)"
prop="rate"
width="120"
label="豆瓣评分">
</el-table-column>
<el-table-column v-if="list.some(e => e.index >= 0)"
prop="index"
width="120"
@@ -57,7 +65,7 @@
</el-table-column>
<el-table-column
label="操作"
header-align="center"
header-align="right"
align="right">
<template slot-scope="scope">
<el-button @click.stop="playEvent(scope.row)" type="text">播放</el-button>
@@ -68,25 +76,63 @@
</el-table-column>
</el-table>
</div>
<div class="body zy-scroll" id="star-picture" v-show="viewMode === 'picture'">
<div class="show-img">
<Waterfall ref="waterfall" :list="list" :gutter="20" :width="240"
:breakpoints="{ 1200: { rowPerView: 4 } }"
animationEffect="fadeInUp"
backgroundColor="rgba(0, 0, 0, 0)">
<template slot="item" slot-scope="props">
<div class="card">
<div class="img">
<div class="rate" v-if="props.data.rate && props.data.rate !== '暂无评分'">
<span>豆瓣: {{props.data.rate}}</span>
</div>
<img style="width: 100%" :src="props.data.detail.pic" alt="" @load="$refs.waterfall.refresh()" @click="detailEvent(props.data)">
<div class="operate">
<div class="operate-wrap">
<span class="o-play" @click="playEvent(props.data)">播放</span>
<span class="o-share" @click="shareEvent(props.data)">分享</span>
<span class="o-star" @click="downloadEvent(props.data)">下载</span>
<span class="o-star" @click="deleteEvent(props.data)">删除</span>
</div>
</div>
</div>
<div class="name" @click="detailEvent(props.data)">{{props.data.name}}</div>
<div class="info">
<span>{{props.data.detail.year}}</span>
<span>{{props.data.detail.note}}</span>
<span>{{props.data.detail.type}}</span>
</div>
</div>
</template>
</Waterfall>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { star, history, sites } from '../lib/dexie'
import { star, history, sites, setting } from '../lib/dexie'
import zy from '../lib/site/tools'
import { remote } from 'electron'
import fs from 'fs'
import Sortable from 'sortablejs'
import Waterfall from 'vue-waterfall-plugin'
const { clipboard } = require('electron')
export default {
name: 'star',
data () {
return {
list: [],
sites: []
sites: [],
viewMode: 'picture'
}
},
components: {
Waterfall
},
computed: {
view: {
get () {
@@ -123,26 +169,21 @@ export default {
},
watch: {
view () {
this.getFavorites()
this.getAllsites()
this.getFavorites()
}
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE']),
handleSortChange (column, prop, order) {
this.updateDatabase()
},
sortByName (a, b) {
return a.name.localeCompare(b.name)
return a.name.localeCompare(b.name, 'zh')
},
sortByType (a, b) {
return a.type.localeCompare(b.type)
},
sortBySite (a, b) {
const siteA = this.getSiteName(a.key)
if (!siteA) {
return -1
} else {
return siteA.localeCompare(this.getSiteName(b.key))
}
},
detailEvent (e) {
this.detail = {
show: true,
@@ -156,14 +197,13 @@ export default {
this.clearHasUpdateFlag(e)
}
},
playEvent (e) {
history.find({ site: e.key, ids: e.ids }).then(res => {
if (res) {
this.video = { key: e.key, info: { id: res.ids, name: res.name, index: res.index } }
} else {
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 } }
}
})
async playEvent (e) {
const db = await history.find({ site: e.key, ids: e.ids })
if (db) {
this.video = { key: e.key, info: { id: db.ids, name: db.name, index: db.index } }
} else {
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 } }
}
if (e.hasUpdate) {
this.clearHasUpdateFlag(e)
}
@@ -191,29 +231,28 @@ export default {
return 'highlight'
}
},
clearHasUpdateFlag (e) {
star.find({ id: e.id }).then(res => {
res.hasUpdate = false
star.update(e.id, res)
async clearHasUpdateFlag (e) {
const db = await star.find({ id: e.id })
if (db) {
db.hasUpdate = false
star.update(e.id, db)
this.getFavorites()
})
}
},
updateEvent (e) {
zy.detail(e.key, e.ids).then(res => {
zy.detail(e.key, e.ids).then(detailRes => {
var doc = {
key: e.key,
id: e.id,
ids: res.id,
last: res.last,
name: res.name,
type: res.type,
year: res.year,
note: res.note
key: e.key,
ids: e.ids,
site: e.site,
name: e.name,
detail: detailRes,
index: e.index
}
star.get(e.id).then(resStar => {
doc.hasUpdate = resStar.hasUpdate
var msg = ''
if (e.last === res.last) {
if (e.detail.last === detailRes.last) {
msg = `同步"${e.name}"成功, 未查询到更新。`
this.$message.info(msg)
} else {
@@ -276,10 +315,14 @@ export default {
}
})
},
getSiteName (key) {
var site = this.sites.find(e => e.key === key)
if (site) {
return site.name
getSiteName (row) {
if (row.site) {
return row.site.name
} else {
var site = this.sites.find(e => e.key === row.key)
if (site) {
return site.name
}
}
},
getHistoryNote (index) {
@@ -329,54 +372,64 @@ export default {
}
remote.dialog.showOpenDialog(options).then(result => {
if (!result.canceled) {
var starList = this.list
result.filePaths.forEach(file => {
var str = fs.readFileSync(file)
const json = JSON.parse(str)
star.bulkAdd(json).then(e => {
this.getFavorites()
json.forEach(ele => {
const starExists = starList.includes(x => x.key === ele.key && x.ids === ele.ids)
if (!starExists) {
var doc = {
key: ele.key,
ids: ele.ids,
site: ele.site === undefined ? ele.site = this.sites.find(x => x.key === ele.key) : ele.site,
name: ele.name,
hasUpdate: ele.hasUpdate,
index: ele.index,
rate: ele.rate,
detail: ele.detail === undefined ? {
director: ele.director,
actor: ele.actor,
type: ele.type,
area: ele.area,
lang: ele.lang,
year: ele.year,
last: ele.last,
note: ele.note
} : ele.detail
}
starList.push(doc)
}
})
this.upgradeFavorites()
})
this.$message.success('导入收藏成功')
star.clear().then(star.bulkAdd(starList).then(res => {
this.getFavorites()
this.$message.success('导入收藏成功')
}))
}
}).catch(err => {
this.$message.error(err)
})
},
upgradeFavorites () {
star.all().then(res => {
res.forEach(element => {
const docs = {
key: element.key,
ids: element.ids,
name: element.name,
type: element.type,
year: element.year,
last: element.last,
note: element.note
}
star.find({ key: element.key, ids: element.ids }).then(res => {
if (!res) {
star.add(docs)
}
})
})
this.getFavorites()
})
},
clearFavoritesEvent () {
star.clear().then(e => {
this.getFavorites()
})
},
updateDatabase (data) {
syncTableData () {
if (this.$refs.starTable.tableData && this.$refs.starTable.tableData.length === this.list.length) {
this.list = this.$refs.starTable.tableData
}
},
updateDatabase () {
this.syncTableData()
star.clear().then(res => {
var id = length
data.forEach(ele => {
var id = this.list.length
this.list.forEach(ele => {
ele.id = id
id -= 1
})
star.bulkAdd(data)
star.bulkAdd(this.list)
})
},
rowDrop () {
@@ -386,9 +439,20 @@ export default {
onEnd ({ newIndex, oldIndex }) {
const currRow = _this.list.splice(oldIndex, 1)[0]
_this.list.splice(newIndex, 0, currRow)
_this.updateDatabase(_this.list)
_this.updateDatabase()
}
})
},
getViewMode () {
setting.find().then(res => {
this.viewMode = res.starViewMode
})
},
updateViewMode () {
setting.find().then(res => {
res.starViewMode = this.viewMode
setting.update(res)
})
}
},
mounted () {
@@ -396,6 +460,108 @@ export default {
},
created () {
this.getFavorites()
this.getViewMode()
}
}
</script>
<style lang="scss" scoped>
.star{
.body{
height: calc(100% - 40px);
display: flex;
position: relative;
overflow-y: auto;
&::-webkit-scrollbar{
width: 5px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
position: absolute;
}
&::-webkit-scrollbar-track {
border-radius: 10px;
position: absolute;
}
.body-box{
height: 100%;
width: 100%;
}
.show-img{
height: 100%;
width: 100%;
padding: 10px;
.card{
border-radius: 6px;
overflow: hidden;
.img{
position: relative;
min-height: 40px;
img{
width: 100%;
height: auto;
cursor: pointer;
}
.rate{
position: absolute;
top: 10%;
right: -35%;
width: 100%;
background-color: #111111aa;
color: #cdcdcd;
height: 30px;
line-height: 30px;
font-size: 14px;
text-align: center;
transform: rotate(45deg);
}
.operate{
display: none;
position: absolute;
left: 0;
bottom: 0;
background-color: #111111aa;
width: 100%;
font-size: 13px;
.operate-wrap{
display: flex;
justify-content: space-between;
.o-play, .o-star, .o-share{
cursor: pointer;
display: inline-block;
width: 80px;
height: 36px;
text-align: center;
line-height: 36px;
color: #cdcdcd;
&:hover{
background-color: #111;
}
}
}
}
}
.name{
font-size: 16px;
padding: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
}
.info{
display: flex;
justify-content: space-between;
font-size: 12px;
padding: 10px;
}
&:hover{
.operate{
display: block;
}
}
}
}
}
}
</style>

View File

@@ -10,7 +10,7 @@ import Share from './Share'
import History from './History'
import EditSites from './EditSites'
import IPTV from './IPTV'
import Recommandation from './Recommandation'
export default {
registerComponents () {
Vue.component('Aside', Aside)
@@ -24,5 +24,6 @@ export default {
Vue.component('History', History)
Vue.component('EditSites', EditSites)
Vue.component('IPTV', IPTV)
Vue.component('Recommandation', Recommandation)
}
}

View File

@@ -1,16 +1,17 @@
import Dexie from 'dexie'
import { setting, sites, localKey, iptv } from './initData'
import { setting, sites, localKey, iptv, recommandations } from './initData'
const db = new Dexie('zy')
db.version(3).stores({
db.version(4).stores({
search: '++id, keywords',
iptvSearch: '++id, keywords',
setting: 'id, theme, site, shortcut, view, externalPlayer, searchAllSites, excludeRootClasses, excludeR18Films, forwardTimeInSec',
setting: 'id, theme, site, shortcut, view, externalPlayer, searchAllSites, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode',
shortcut: 'name, key, desc',
star: '++id, site, ids, name, type, year, index',
star: '++id, [key+ids], site, name, detail, index, rate, hasUpdate',
recommandation: '++id, [key+ids], site, name, detail, index, rate, hasUpdate',
sites: '++id, key, name, api, download, isActive, group',
history: '++id, site, ids, name, type, year, index, time',
history: '++id, [site+ids], name, type, year, index, time',
mini: 'id, site, ids, name, index, time',
iptv: '++id, name, url, group'
})
@@ -20,6 +21,7 @@ db.on('populate', () => {
db.sites.bulkAdd(sites)
db.shortcut.bulkAdd(localKey)
db.iptv.bulkAdd(iptv)
db.recommandation.bulkAdd(recommandations)
})
db.open()

View File

@@ -8,7 +8,7 @@ export default {
return await history.bulkAdd(doc)
},
async find (doc) {
return await history.get(doc)
return await history.where(doc).first()
},
async update (id, docs) {
return await history.update(id, docs)

View File

@@ -7,6 +7,7 @@ import sites from './sites'
import search from './search'
import iptvSearch from './iptvSearch'
import iptv from './iptv'
import recommandation from './recommandation'
export {
history,
@@ -17,5 +18,6 @@ export {
sites,
iptv,
search,
iptvSearch
iptvSearch,
recommandation
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -8,7 +8,7 @@ export default {
return await star.bulkAdd(doc)
},
async find (doc) {
return await star.get(doc)
return await star.where(doc).first()
},
async update (id, docs) {
return await star.update(id, docs)

View File

@@ -1,5 +1,5 @@
import Vue from 'vue'
import { Message, Button, Table, TableColumn, Tag, Input, Dialog, Form, FormItem, Switch } from 'element-ui'
import { Message, Button, Table, TableColumn, Tag, Input, Dialog, Form, FormItem, Switch, Select, Option } from 'element-ui'
import Plugin from 'v-fit-columns'
Vue.use(Button)
Vue.use(Table)
@@ -11,4 +11,6 @@ Vue.use(Form)
Vue.use(FormItem)
Vue.use(Switch)
Vue.use(Plugin)
Vue.use(Select)
Vue.use(Option)
Vue.prototype.$message = Message

View File

@@ -1,6 +1,8 @@
import { sites } from '../dexie'
import axios from 'axios'
import parser from 'fast-xml-parser'
import cheerio from 'cheerio'
const zy = {
xmlConfig: { // XML 转 JSON 配置
trimValues: true,
@@ -193,6 +195,69 @@ const zy = {
}
})
})
},
/**
* 检查资源
* @param {*} key 资源网 key
* @returns boolean
*/
async check (key, id) {
try {
const cls = await this.class(key)
if (cls) {
return true
} else {
return false
}
} catch (e) {
return false
}
},
/**
* 获取豆瓣评分
* @param {*} name 视频名称
* @returns 豆瓣评分
*/
doubanRate (name) {
return new Promise((resolve, reject) => {
// 豆瓣搜索链接
var nameToSearch = name.replace(/\s/g, '')
var doubanSearchLink = 'https://www.douban.com/search?q=' + nameToSearch
axios.get(doubanSearchLink).then(res => {
const $ = cheerio.load(res.data)
// 比较第一和第二给豆瓣搜索结果, 看名字是否相符
var link = ''
var linkInDouban = $($('div.result')[0]).find('div>div>h3>a').first()
var nameInDouban = linkInDouban.text().replace(/\s/g, '')
if (nameToSearch.includes(nameInDouban) || nameInDouban.includes(nameToSearch)) {
link = linkInDouban.attr('href')
} else {
linkInDouban = $($('div.result')[1]).find('div>div>h3>a').first()
nameInDouban = linkInDouban.text().replace(/\s/g, '')
if (nameToSearch.includes(nameInDouban) || nameInDouban.includes(nameToSearch)) {
link = linkInDouban.attr('href')
}
}
// 如果找到链接,就打开该链接获取评分
if (link) {
axios.get(link).then(response => {
const parsedHtml = cheerio.load(response.data)
var rating = parsedHtml('body').find('#interest_sectl').first().find('strong').first()
if (rating.text()) {
resolve(rating.text())
} else {
resolve('暂无评分')
}
}).catch(err => {
reject(err)
})
} else {
resolve('暂无评分')
}
}).catch(err => {
reject(err)
})
})
}
}

48
src/lib/update/update.js Normal file
View File

@@ -0,0 +1,48 @@
import { BrowserWindow, ipcMain } from 'electron'
import { autoUpdater } from 'electron-updater'
export function initUpdater (win = BrowserWindow) {
autoUpdater.autoDownload = false
autoUpdater.autoInstallOnAppQuit = false
// 主进程监听检查更新事件
ipcMain.on('checkForUpdate', () => {
autoUpdater.checkForUpdates()
})
// 主进程监听退出并安装事件
ipcMain.on('quitAndInstall', () => {
autoUpdater.downloadUpdate()
})
// 开始检测是否有更新
autoUpdater.on('checking-for-update', () => {
win.webContents.send('checking-for-update')
})
// 检测到有可用的更新
autoUpdater.on('update-available', (info) => {
win.webContents.send('update-available', info)
})
// 没有检测到有可用的更新
autoUpdater.on('update-not-available', () => {
win.webContents.send('update-not-available')
})
// 更新出错
autoUpdater.on('update-error', err => {
win.webContents.send('update-error', err)
})
// 下载更新进度
autoUpdater.on('download-progress', (progressObj) => {
win.webContents.send('download-progress', progressObj)
})
// 下载完成并退出安装
autoUpdater.on('update-downloaded', () => {
win.webContents.send('update-downloaded')
autoUpdater.quitAndInstall()
})
}

View File

@@ -1,6 +1,6 @@
<template>
<div class="mini">
<div class="top">
<div class="header">
<div class="left">
<span class="title">
<span v-if="m3u8Arr.length > 1"> {{(video.index + 1)}} </span>{{name}}
@@ -22,7 +22,7 @@
<span class="progress" v-show="progress > 0">播放进度: {{progress}}%</span>
</div>
<div class="right">
<span class="top" @click="frameClickEvent('top')" title="置顶">
<span class="topping" @click="frameClickEvent('top')" title="置顶">
<svg t="1595919317571" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1188" style="width:10px;height:14px"><path d="M43.072 974.72l380.864-301.952 151.936 161.6c0 0 63.424 17.28 67.328-30.72l-3.904-163.584 225.088-259.648 98.048-5.696c0 0 76.928-15.488 21.184-82.752l-275.072-276.928c0 0-74.944-9.6-69.248 59.584l0 75.008L383.552 367.104 225.856 376.64c0 0-57.728 19.2-36.608 69.248l148.16 146.176L43.072 974.72 43.072 974.72z" p-id="1189" :fill="isAlwaysOnTop ? '#555555' : '#ffffff'"></path></svg>
</span>
<span class="min" @click="frameClickEvent('min')" title="最小化">
@@ -31,12 +31,12 @@
<span class="max" @click="frameClickEvent('max')" title="最大化">
<svg t="1595917343956" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1540" style="width:8px;height:14px"><path d="M416 416 64.064 416C28.448 416 0 444.64 0 479.936L0 544.064C0 579.264 28.672 608 64.064 608L416 608 416 959.936C416 995.552 444.64 1024 479.936 1024L544.064 1024C579.264 1024 608 995.328 608 959.936L608 608 959.936 608C995.552 608 1024 579.36 1024 544.064L1024 479.936C1024 444.736 995.328 416 959.936 416L608 416 608 64.064C608 28.448 579.36 0 544.064 0L479.936 0C444.736 0 416 28.672 416 64.064L416 416Z" p-id="1541" fill="#ffffff"></path></svg>
</span>
<span class="close" @click="frameClickEvent('close')" title="关闭">
<span class="close" @click="frameClickEvent('close')" title="退出精简模式">
<svg t="1595917372551" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1685" style="width:8px;height:14px"><path d="M511.968 376.224 796.096 92.096C833.536 54.624 894.4 54.624 931.84 92.096 969.312 129.568 969.312 190.4 931.84 227.872L647.744 512 931.84 796.096C969.312 833.568 969.312 894.4 931.84 931.872 894.4 969.344 833.536 969.344 796.096 931.872L511.968 647.744 227.84 931.872C190.4 969.344 129.536 969.344 92.096 931.872 54.624 894.4 54.624 833.568 92.096 796.096L376.224 512 92.096 227.872C54.624 190.4 54.624 129.568 92.096 92.096 129.536 54.624 190.4 54.624 227.84 92.096L511.968 376.224Z" p-id="1686" fill="#ffffff"></path></svg>
</span>
</div>
</div>
<div class="bottom">
<div class="footer">
<div id="xg"></div>
</div>
</div>
@@ -175,49 +175,48 @@ export default {
})
})
},
videoPlaying () {
history.find({ site: this.video.site, ids: this.video.ids }).then(res => {
if (res) {
res.index = this.video.index
history.update(res.id, res)
} else {
const doc = {
site: this.video.site,
ids: this.video.ids,
name: this.video.name,
index: this.video.index,
time: 0
}
history.add(doc)
async videoPlaying () {
const db = await history.find({ site: this.video.site, ids: this.video.ids })
if (db) {
db.index = this.video.index
history.update(db.id, db)
} else {
const doc = {
site: this.video.site,
ids: this.video.ids,
name: this.video.name,
index: this.video.index,
time: 0
}
})
history.add(doc)
}
this.timerEvent()
},
timerEvent () {
this.timer = setInterval(() => {
this.timer = setInterval(async () => {
const endTime = this.xg.duration
const currentTime = this.xg.currentTime
const progress = (currentTime / endTime) * 100
this.progress = progress.toFixed(2)
history.find({ site: this.video.site, ids: this.video.ids }).then(res => {
if (res) {
const v = res
v.time = this.xg.currentTime
v.index = this.video.index
const id = v.id
delete v.id
history.update(id, v)
}
})
const db = await history.find({ site: this.video.site, ids: this.video.ids })
if (db) {
const v = db
v.time = this.xg.currentTime
v.index = this.video.index
const id = v.id
delete v.id
history.update(id, v)
}
}, 10000)
},
prevEvent () {
async prevEvent () {
if (this.video.index === 0) {
this.$message.info('已是第一集.')
return false
}
history.find({ site: this.video.site, ids: this.video.ids }).then(res => {
const v = res
const db = await history.find({ site: this.video.site, ids: this.video.ids })
if (db) {
const v = db
const id = v.id
v.index--
delete v.id
@@ -225,15 +224,16 @@ export default {
this.xg.src = this.m3u8Arr[v.index]
this.video.index--
})
})
}
},
nextEvent () {
async nextEvent () {
if (this.video.index >= this.m3u8Arr.length - 1) {
this.$message.info('已是最后一集.')
return false
}
history.find({ site: this.video.site, ids: this.video.ids }).then(res => {
const v = res
const db = await history.find({ site: this.video.site, ids: this.video.ids })
if (db) {
const v = db
v.index++
const id = v.id
delete v.id
@@ -241,7 +241,7 @@ export default {
this.xg.src = this.m3u8Arr[v.index]
this.video.index++
})
})
}
},
playbackRateEvent (e) {
let rate = this.xg.playbackRate
@@ -395,8 +395,6 @@ export default {
this.xg = new Hls(this.config)
this.mtEvent()
this.getUrls()
const _hmt = window._hmt
_hmt.push(['_trackEvent', 'page', 'view', 'mini'])
},
beforeDestroy () {
clearInterval(this.timer)
@@ -421,9 +419,10 @@ html,body{
justify-content: center;
align-items: flex-start;
flex-direction: column;
.top{
.header{
width: 100%;
height: 30px;
min-height: 30px;
display: flex;
justify-content: space-between;
align-items: center;
@@ -457,22 +456,27 @@ html,body{
}
}
.right{
width: 80px;
width: 100px;
height: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
span{
-webkit-app-region: no-drag;
display: inline-block;
width: 14px;
height: 14px;
display: flex;
align-items: center;
justify-content: center;
width: 15px;
height: 15px;
text-align: center;
line-height: 14px;
line-height: 15px;
border-radius: 50%;
margin-right: 10px;
cursor: pointer;
opacity: 0.4;
&.topping{
background-color: #f3bab7;
}
&.min{
background-color: #32dc36;
}
@@ -482,9 +486,6 @@ html,body{
&.close{
background-color: #ff5f56;
}
&.top{
background-color: #f3bab7;
}
&:hover{
animation: heartbeat 3s ease-in-out infinite both;
}
@@ -515,7 +516,7 @@ html,body{
}
}
}
.bottom{
.footer{
width: 100%;
flex: 1;
.xgplayer-start{

View File

@@ -10035,10 +10035,10 @@ xgplayer-hls.js@^2.2.5:
event-emitter "^0.3.5"
eventemitter3 "^4.0.7"
xgplayer@^2.12.2:
version "2.12.2"
resolved "https://registry.npm.taobao.org/xgplayer/download/xgplayer-2.12.2.tgz#72111cfbb21f97cbe454b0f5d17e85f720892b2a"
integrity sha1-chEc+7Ifl8vkVLD10X6F9yCJKyo=
xgplayer@^2.13.0:
version "2.13.0"
resolved "https://registry.npmjs.org/xgplayer/-/xgplayer-2.13.0.tgz#24df20fd527a87cccc5cee3db70937408eab86ed"
integrity sha512-QMjwnpO6o8bd2ZywHtANaOqvey476UAmPS+hXC0im9wiESMKqxPsCmeqhIxFFVao/pUtIyopHADcor9ipai5Rg==
dependencies:
chalk "^2.3.2"
commander "^2.15.1"