Compare commits

...

82 Commits

Author SHA1 Message Date
hunlongyu
81b34ff54a release: v2.2.8 2022-07-13 18:34:33 +08:00
hunlongyu
9b9db55f49 feat: 移除推荐,修改默认远程视频源 2022-07-13 18:33:50 +08:00
hunlongyu
e7c7367ea8 release: v2.8.7 2022-07-13 16:44:48 +08:00
hunlongyu
5e37ef32fd release: v2.8.6 2022-07-13 10:58:16 +08:00
hunlongyu
85525745a1 feat: remove qrcode and modify sites url 2022-07-13 10:56:50 +08:00
hunlongyu
c7f581a088 fix: 让项目能正常跑起来 2022-07-13 09:49:12 +08:00
Hunlongyu
a88dfb1bc3 Merge pull request #769 from Hunlongyu/dependabot/npm_and_yarn/eventsource-1.1.1
Bump eventsource from 1.0.7 to 1.1.1
2022-07-03 21:29:37 +08:00
Hunlongyu
410cd8fc1a Merge pull request #767 from cuiocean/dependabot/npm_and_yarn/dexie-3.2.2
Bump dexie from 3.0.3 to 3.2.2
2022-06-16 09:43:55 +08:00
dependabot[bot]
bb30be3c4d Bump eventsource from 1.0.7 to 1.1.1
Bumps [eventsource](https://github.com/EventSource/eventsource) from 1.0.7 to 1.1.1.
- [Release notes](https://github.com/EventSource/eventsource/releases)
- [Changelog](https://github.com/EventSource/eventsource/blob/master/HISTORY.md)
- [Commits](https://github.com/EventSource/eventsource/compare/v1.0.7...v1.1.1)

---
updated-dependencies:
- dependency-name: eventsource
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-01 18:54:40 +00:00
dependabot[bot]
6230c9e87a Bump dexie from 3.0.3 to 3.2.2
Bumps [dexie](https://github.com/dfahlander/Dexie.js) from 3.0.3 to 3.2.2.
- [Release notes](https://github.com/dfahlander/Dexie.js/releases)
- [Commits](https://github.com/dfahlander/Dexie.js/compare/v3.0.3...v3.2.2)

---
updated-dependencies:
- dependency-name: dexie
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-23 20:15:41 +00:00
Hunlongyu
93efb64211 Merge pull request #759 from cuiocean/dependabot/npm_and_yarn/electron-13.6.6
Bump electron from 11.5.0 to 13.6.6
2022-03-27 17:21:26 +08:00
dependabot[bot]
3c4dd91d36 Bump electron from 11.5.0 to 13.6.6
Bumps [electron](https://github.com/electron/electron) from 11.5.0 to 13.6.6.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v11.5.0...v13.6.6)

---
updated-dependencies:
- dependency-name: electron
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-25 19:09:35 +00:00
Hunlongyu
45041fdab1 Merge pull request #746 from cuiocean/dependabot/npm_and_yarn/axios-0.21.2
Bump axios from 0.21.1 to 0.21.2
2021-10-13 08:57:18 +08:00
dependabot[bot]
bcf0613ff0 Bump axios from 0.21.1 to 0.21.2
Bumps [axios](https://github.com/axios/axios) from 0.21.1 to 0.21.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.21.1...v0.21.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-13 00:56:23 +00:00
Hunlongyu
3107b851c5 Merge pull request #745 from cuiocean/dependabot/npm_and_yarn/electron-11.5.0
Bump electron from 11.3.0 to 11.5.0
2021-10-13 08:55:37 +08:00
dependabot[bot]
b27883ad0f Bump electron from 11.3.0 to 11.5.0
Bumps [electron](https://github.com/electron/electron) from 11.3.0 to 11.5.0.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v11.3.0...v11.5.0)

---
updated-dependencies:
- dependency-name: electron
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-12 22:51:49 +00:00
haiyangcui
e0c3502c3f Update x86.yml 2021-08-10 16:10:30 +02:00
haiyangcui
4c2db002eb 推荐页面添加"豆瓣华语电影" 2021-08-10 15:56:16 +02:00
haiyangcui
898f769d24 电影页面删除重复的数据 2021-08-10 15:24:02 +02:00
cuiocean
d6549c6a6b Update x86.yml 2021-08-10 15:23:13 +02:00
haiyangcui
eea4081d58 v2.8.5 2021-07-22 23:07:53 +02:00
haiyangcui
1e9f48e48c "按上映年份"排序改为降序排序,从新到旧排序 2021-06-05 22:01:35 +02:00
haiyangcui
0816933679 删除"作者推荐" 2021-05-28 23:10:52 +02:00
haiyangcui
8af6ff2bd7 修复分类刷新的一个bug 2021-05-24 22:29:55 +02:00
haiyangcui
c5d979fd28 引入xgplayer-mp4模块 2021-05-24 22:28:35 +02:00
haiyangcui
beb0c29326 源站增加"倒序排列"开关 2021-05-24 22:06:46 +02:00
haiyangcui
5509ace412 添加'豆瓣冷门佳片' 2021-05-09 23:45:07 +02:00
haiyangcui
6f296a1170 v2.8.4 2021-04-23 17:04:32 +02:00
haiyangcui
f8228a71f3 添加"热门美剧""热门英剧""热门国产剧"推荐列表 2021-04-22 11:47:09 +02:00
haiyangcui
9886f04b48 推荐页面添加"按评分"排序 2021-04-22 11:31:47 +02:00
haiyangcui
37c4aad4b0 Update lock files 2021-04-22 11:31:29 +02:00
haiyangcui
77d7b28e26 Update lock files 2021-04-21 21:10:51 +02:00
haiyangcui
3505b22344 简化调用解析网址的逻辑 2021-04-21 21:00:04 +02:00
haiyangcui
80cec08982 v2.8.3 2021-04-21 18:59:53 +02:00
haiyangcui
f92094daec 支持"豆瓣热门动画电影" 2021-04-21 18:57:54 +02:00
haiyangcui
2c06ef1da6 支持"豆瓣热门综艺" 2021-04-21 18:52:42 +02:00
haiyangcui
06403ece3f 因不明原因,全屏时上一集按钮被遮盖而无法点取.简单粗暴的解决方法:把所有按钮向右移动一下. 2021-04-21 18:42:26 +02:00
haiyangcui
39a6491403 添加"豆瓣热门纪录片" 2021-04-21 16:37:17 +02:00
haiyangcui
edb82eb3be 支持更新豆瓣推荐 2021-04-20 22:57:23 +02:00
haiyangcui
c7ca3df50d 修复设置默认解析网址 2021-04-20 22:13:16 +02:00
haiyangcui
0b198381b1 升级依赖 2021-04-20 22:11:53 +02:00
hunlongyu
2dcda83741 升级依赖 2021-04-16 09:09:33 +08:00
haiyangcui
5b3c3f0ff2 推荐页面,移除"删除" 2021-04-13 23:00:02 +02:00
haiyangcui
23454f7c7f v2.8.2 2021-04-13 17:49:02 +02:00
haiyangcui
0fdfe29343 增加"豆瓣高分电影" 2021-04-13 17:44:43 +02:00
haiyangcui
fa7a799e2b 支持缓存电影数据 2021-04-13 17:34:46 +02:00
haiyangcui
9c1a707279 初始的数据都移到iniData文件夹下 2021-04-13 17:07:18 +02:00
haiyangcui
7b69bb05d4 支持设置是否最小化时暂停播放 2021-04-13 17:06:33 +02:00
haiyangcui
3b6b6ea11b 记录窗口大小及位置 2021-04-12 17:57:56 +02:00
haiyangcui
9ec65ab027 Introduce searchFirstDetail 2021-04-12 16:48:58 +02:00
haiyangcui
da5531a947 主分类过滤, 检测关键词是否包含分类名. 福利过滤,检测分类名是否包含关键词 2021-04-12 14:55:06 +02:00
haiyangcui
4744f91f6b 过滤开启改为开关 2021-04-12 14:41:37 +02:00
haiyangcui
36b80c1d7e Import axios in Recommendation.vue 2021-04-12 12:56:26 +02:00
haiyangcui
15f4ab7248 修复"动漫"子分类消失的问题 2021-04-12 12:30:56 +02:00
haiyangcui
0e25c25480 推荐页面,加入"豆瓣热门电影"和"豆瓣热门剧集" 2021-04-12 12:26:29 +02:00
haiyangcui
7ff48a407d 删除log 2021-04-11 21:42:10 +02:00
haiyangcui
bbc371b1c5 添加初始化的设置文件 2021-04-11 20:31:00 +02:00
haiyangcui
d141d60e77 v2.8.1 2021-04-11 17:53:44 +02:00
haiyangcui
80af701e5c 删除经常跳出来的一个无用信息 2021-04-11 17:52:53 +02:00
haiyangcui
f0e70e03cb 在refreshClass内部调用classClick 2021-04-11 17:48:46 +02:00
haiyangcui
aba3472f2e 避免rootClassFilter r18ClassFilter数据为空时出错 2021-04-11 17:48:10 +02:00
haiyangcui
8772076d76 检测site变化,以及时刷新数据 2021-04-11 17:36:26 +02:00
haiyangcui
4abe03347a 修正数据库版本号 2021-04-11 17:31:31 +02:00
haiyangcui
b3e6e817dd 过滤开关关联局部变量,否则点击取消的话, 设置也会记录在setting里 2021-04-11 16:27:10 +02:00
haiyangcui
e48445c224 过滤设置更新后,刷新分类列表 2021-04-11 16:19:52 +02:00
haiyangcui
159f19d5ec 支持分开设置分类过滤和福利分类过滤 2021-04-11 16:11:20 +02:00
haiyangcui
c2953a530c 替换函数containsR18Keywords 2021-04-10 14:35:13 +02:00
haiyangcui
9166f129d8 移除过滤关键词列表中的空格 2021-04-10 13:52:59 +02:00
haiyangcui
72ac3eafdd 调高过滤词输入窗口 2021-04-10 13:52:40 +02:00
haiyangcui
8610b41fad 支持定义分类过滤关键词 2021-04-10 13:44:51 +02:00
haiyangcui
06e4c0e83e 只有当视频的核心信息变化时,才刷新播放页面 2021-04-09 14:10:03 +02:00
haiyangcui
5db3fd9f9b 历史页面初始化filteredList,否则视图切换有问题 2021-04-09 12:14:55 +02:00
haiyangcui
978afb2b38 删除无用代码 2021-04-08 15:38:07 +02:00
haiyangcui
b00b69a582 更新调用API的逻辑,如果某些重要数据没有API返回结果中,很可能是该源站已经失效,直接返回空数据 2021-04-06 16:49:00 +02:00
haiyangcui
9a33b0cfb3 搜索分三类,站内,组内,全站 2021-04-06 16:25:18 +02:00
haiyangcui
f4bf42bf07 将"有更新"切换按钮从工具栏移到顶层按钮区 2021-04-04 22:40:41 +02:00
haiyangcui
671a6e32d0 v2.8.0 2021-03-31 21:24:54 +02:00
haiyangcui
d05534fb17 恢复推荐视频的收藏,分享和详情功能 2021-03-31 20:47:02 +02:00
haiyangcui
b655c8c8bc 调整样式 2021-03-31 20:47:02 +02:00
haiyangcui
8fbea8ab57 添加"喜欢这部电影的人也喜欢" 2021-03-31 20:47:01 +02:00
haiyangcui
2ddfc66104 解决某些网址返回分类里含有{}字符串的问题 2021-03-30 09:54:00 +02:00
haiyangcui
26d62cdef4 历史页面增加按完成度排序 2021-03-29 22:41:08 +02:00
32 changed files with 4094 additions and 5741 deletions

View File

@@ -1,4 +1,4 @@
name: release-build
name: x86-release-build
on:
push:
@@ -25,11 +25,3 @@ jobs:
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: dist_electron
path: dist_electron/*.exe
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

6216
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "zy",
"version": "2.7.9",
"version": "2.8.8",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -17,14 +17,16 @@
},
"main": "background.js",
"dependencies": {
"axios": "^0.21.1",
"cheerio": "^1.0.0-rc.5",
"core-js": "^3.9.1",
"dexie": "^3.0.3",
"@electron/remote": "^2.0.8",
"axios": "^0.21.2",
"bootstrap-vue": "^2.22.0",
"cheerio": "1.0.0-rc.6",
"core-js": "^3.10.2",
"dexie": "^3.2.2",
"electron-localshortcut": "^3.2.1",
"electron-proxy-agent": "^1.2.0",
"electron-updater": "^4.3.8",
"element-ui": "^2.15.1",
"element-ui": "^2.15.9",
"fast-xml-parser": "^3.19.0",
"html2canvas": "^1.0.0-rc.7",
"iptv-playlist-parser": "^0.6.0",
@@ -39,34 +41,26 @@
"session": "^0.1.0",
"sortablejs": "^1.13.0",
"v-fit-columns": "^0.2.0",
"vue": "^2.6.12",
"vue": "^2.6.14",
"vue-infinite-loading": "^2.4.5",
"vue-waterfall-plugin": "^1.1.0",
"vuedraggable": "^2.24.3",
"vuex": "^3.6.2",
"xgplayer": "2.17.13",
"xgplayer": "2.19.1",
"xgplayer-flv.js": "^2.3.0",
"xgplayer-hls.js": "^2.4.2"
"xgplayer-hls.js": "^2.4.2",
"xgplayer-mp4": "^1.2.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.11",
"@vue/cli-plugin-eslint": "~4.5.11",
"@vue/cli-plugin-vuex": "~4.5.11",
"@vue/cli-service": "~4.5.11",
"@vue/eslint-config-standard": "^6.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-component": "^1.1.1",
"electron": "^11.3.0",
"electron": "^13.0.0",
"electron-devtools-installer": "^3.1.1",
"eslint": "^7.20.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-standard": "^4.1.0",
"eslint-plugin-vue": "^7.6.0",
"sass": "^1.30.0",
"sass-loader": "^10.1.0",
"vue-cli-plugin-electron-builder": "2.0.0-rc.6",
"vue-template-compiler": "^2.6.12"
"vue-template-compiler": "^2.6.14"
}
}

View File

@@ -10,7 +10,6 @@
<Setting v-show="view === 'Setting'" />
<IPTV v-show="view === 'IPTV'" />
<EditSites v-if="view === 'EditSites'"/>
<Recommendation v-show="view === 'Recommendation'" />
</div>
<transition name="slide">
<Detail v-if="detail.show"/>
@@ -22,11 +21,62 @@
</template>
<script>
import { setting } from './lib/dexie'
const remote = require('@electron/remote')
export default {
name: 'App',
data () {
return {
appTheme: 'theme-light'
appTheme: 'theme-light',
winSizePosition: {
x: 0,
y: 0,
width: 0,
height: 0
}
}
},
created () {
// 窗口创建口,检查是否有窗口大小位置的记录,如果有的话,更新窗口位置及大小
setting.find().then(res => {
if (res.restoreWindowPositionAndSize) {
var win = remote.getCurrentWindow()
win.setBounds({
x: res.windowPositionAndSize.x,
y: res.windowPositionAndSize.y,
width: res.windowPositionAndSize.width,
height: res.windowPositionAndSize.height
})
this.winSizePosition = {
x: win.getPosition()[0],
y: win.getPosition()[1],
width: win.getSize()[0],
height: win.getSize()[1]
}
}
})
},
updated () {
// 本来想hook up到beforedestroy 但不工作
// 每当窗口更新时检查窗口大小及位置记录到setting数据库中
if (this.setting.restoreWindowPositionAndSize) {
const win = remote.getCurrentWindow()
var newWinSizePosition = {
x: win.getPosition()[0],
y: win.getPosition()[1],
width: win.getSize()[0],
height: win.getSize()[1]
}
if (newWinSizePosition.x !== this.winSizePosition.x ||
newWinSizePosition.y !== this.winSizePosition.y ||
newWinSizePosition.width !== this.winSizePosition.width ||
newWinSizePosition.height !== this.winSizePosition.height) {
this.winSizePosition = newWinSizePosition
setting.find().then(res => {
res.windowPositionAndSize = newWinSizePosition
setting.update(res)
})
}
}
},
computed: {

View File

@@ -441,3 +441,134 @@
}
}
}
// detail
.detail{
.detail-content{
.detail-body{
.m3u8{
.show-picture{
height: 100%;
width: 100%;
padding: 10px;
.card{
border-radius: 6px;
overflow: hidden;
position: relative;
cursor: pointer;
transition: 0.2s;
&:hover {
width: 102%;
height: 102%
}
.img{
position: relative;
min-height: 40px;
img{
width: 100%;
height: auto;
cursor: pointer;
}
.rate{
position: absolute;
top: 3%;
right: -40%;
width: 100%;
background-color: #111111aa;
color:#2f90b9;
height: 30px;
line-height: 30px;
font-size: 14px;
font-weight: bolder;
text-align: center;
transform: rotate(45deg);
}
.site{
position: absolute;
top: 0%;
left: 0%;
width: 100%;
background-color: #111111aa;
color:#2f90b9;
height: 30px;
line-height: 30px;
font-size: 14px;
font-weight: bolder;
text-align: center;
}
.progress{
position: absolute;
bottom: 10%;
left: 0%;
width: 40%;
background-color: #111111aa;
color: #f8df70;
height: 30px;
line-height: 30px;
font-size: 14px;
font-weight: bolder;
text-align: left;
}
.update{
position: absolute;
top: 5%;
left: -40%;
width: 100%;
background-color: #68b88e;
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;
}
.info{
display: flex;
justify-content: space-between;
font-size: 12px;
padding: 10px;
}
&:hover{
.operate{
display: block;
}
}
}
}
}
}
}
}

View File

@@ -138,6 +138,17 @@
}
}
}
.show-picture{
color: var(--d-fc-1);
.card{
background-color: var(--d-bgc-3);
box-shadow: var(--d-bsc);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
&:hover{
box-shadow: var(--d-bsc-hover);
}
}
}
}
}
}

View File

@@ -138,6 +138,17 @@
}
}
}
.show-picture{
color: var(--g-fc-1);
.card{
background-color: var(--g-bgc-3);
box-shadow: var(--g-bsc);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
&:hover{
box-shadow: var(--g-bsc-hover);
}
}
}
}
}
}

View File

@@ -1,5 +1,6 @@
.theme-light{
background-color: var(--l-bgc-1);
// background-color: var(--l-bgc-1);
background: rgba(0, 0, 0, 0);
.zy-select{
color: var(--l-fc-1);
background-color: var(--l-bgc-1);
@@ -138,6 +139,17 @@
}
}
}
.show-picture{
color: var(--l-fc-1);
.card{
background-color: var(--l-bgc-3);
box-shadow: var(--l-bsc);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
&:hover{
box-shadow: var(--l-bsc-hover);
}
}
}
}
}
}

View File

@@ -138,6 +138,17 @@
}
}
}
.show-picture{
color: var(--p-fc-1);
.card{
background-color: var(--p-bgc-3);
box-shadow: var(--p-bsc);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
&:hover{
box-shadow: var(--p-bsc-hover);
}
}
}
}
}
}

View File

@@ -4,7 +4,10 @@ import { app, protocol, BrowserWindow, globalShortcut } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
import { initUpdater } from './lib/update/update'
require('@electron/remote/main').initialize()
const isDevelopment = process.env.NODE_ENV !== 'production'
// const log = require('electron-log') // 用于调试主程序
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors') // 允许跨域
@@ -23,7 +26,8 @@ function createWindow () {
webPreferences: {
webSecurity: false,
enableRemoteModule: true,
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
nodeIntegration: true,
contextIsolation: false,
allowRunningInsecureContent: false
}
})
@@ -35,12 +39,13 @@ function createWindow () {
createProtocol('app')
win.loadURL('app://./index.html')
}
// 修改request headers
// Sec-Fetch下禁止修改浏览器自动加上请求头 https://www.cnblogs.com/fulu/p/13879080.html 暂时先用index.html的meta referer policy替代
const filter = {
urls: ['http://*/*', 'http://*/*']
}
require("@electron/remote/main").enable(win.webContents)
win.webContents.session.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
const url = new URL(details.url)
details.requestHeaders.Origin = url.origin
@@ -63,7 +68,7 @@ function createWindow () {
if (process.platform === 'darwin') {
app.dock.show()
}
if (process.platform === 'Linux') {
if (process.platform === 'linux') {
app.disableHardwareAcceleration()
app.commandLine.appendSwitch('--no-sandbox') // linux 关闭沙盒模式
}

View File

@@ -14,13 +14,13 @@
</g>
</svg>
</span>
<span :class="[view === 'Recommendation' ? 'active ': ''] + 'zy-svg'" @click="changeView('Recommendation')">
<!-- <span :class="[view === 'Recommendation' ? 'active ': ''] + 'zy-svg'" @click="changeView('Recommendation')">
<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> -->
<span :class="[view === 'Play' ? 'active ': ''] + 'zy-svg'" @click="changeView('Play')">
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="playIconTitle">
<title id="playIconTitle">播放</title>

View File

@@ -17,7 +17,7 @@
</svg>
</span>
</div>
<div class="detail-body zy-scroll" v-show="!loading">
<div class="detail-body zy-scroll listpage" v-show="!loading">
<div class="info">
<div class="info-left">
<img :src="info.pic" alt="">
@@ -37,9 +37,9 @@
</div>
<div class="operate">
<span @click="playEvent(selectedEpisode)">播放</span>
<span @click="starEvent">收藏</span>
<span @click="starEvent(info)">收藏</span>
<span @click="downloadEvent">下载</span>
<span @click="shareEvent">分享</span>
<span @click="shareEvent(info,selectedEpisode)">分享</span>
<span @click="doubanLinkEvent">豆瓣</span>
<span @click="togglePlayOnlineEvent">
<input type="checkbox" v-model="playOnline"> 播放在线高清视频
@@ -64,6 +64,47 @@
<span v-bind:class="{ selected: j === selectedEpisode }" v-for="(i, j) in videoList" :key="j" @click="playEvent(j)" @mouseenter="() => { selectedEpisode = j }">{{ i | ftName(j) }}</span>
</div>
</div>
<div class="m3u8">
<div class="show-picture" v-show="info.recommendations && info.recommendations.length > 0">
<span>喜欢这部电影的人也喜欢 · · · · · ·</span>
<Waterfall :list="info.recommendations" :gutter="20" :width="240"
:breakpoints="{
1200: { //当屏幕宽度小于等于1200
rowPerView: 4,
},
800: { //当屏幕宽度小于等于800
rowPerView: 3,
},
500: { //当屏幕宽度小于等于500
rowPerView: 2,
}
}"
animationEffect="fadeIn"
backgroundColor="rgba(0, 0, 0, 0)">
<template slot="item" slot-scope="props">
<div class="card">
<div class="img">
<img style="width: 100%" :src="props.data.pic" alt="" @click="detailEvent(props.data)">
<div class="operate">
<div class="operate-wrap">
<span class="o-play" @click="playRecommendationEvent(props.data)">播放</span>
<span class="o-star" @click="starEvent(props.data)">收藏</span>
<span class="o-share" @click="shareEvent(props.data, 0)">分享</span>
</div>
</div>
</div>
<div class="name">{{props.data.name}}</div>
<div class="info">
<span>{{props.data.area}}</span>
<span>{{props.data.year}}</span>
<span>{{props.data.note}}</span>
<span>{{props.data.type}}</span>
</div>
</div>
</template>
</Waterfall>
</div>
</div>
</div>
<div class="detail-mask zy-loading" v-show="loading">
<div class="loader"></div>
@@ -73,6 +114,7 @@
</template>
<script>
import { mapMutations } from 'vuex'
import Waterfall from 'vue-waterfall-plugin'
import zy from '../lib/site/tools'
import onlineVideo from '../lib/site/onlineVideo'
import { star, history } from '../lib/dexie'
@@ -85,6 +127,8 @@ export default {
videoFlag: '',
videoList: [],
videoFullList: [],
key: '',
site: {},
info: {},
playOnline: false,
selectedEpisode: 0, // 选定集数
@@ -144,8 +188,22 @@ export default {
}
}
},
components: {
Waterfall
},
methods: {
...mapMutations(['SET_VIEW', 'SET_VIDEO', 'SET_DETAIL', 'SET_SHARE', 'SET_DetailCache']),
async playRecommendationEvent (e) {
const db = await history.find({ site: this.detail.key, ids: e.id })
if (db) {
this.video = { key: db.site, info: { id: db.ids, name: db.name, index: db.index, site: this.detail.site } }
} else {
this.video = { key: this.detail.key, info: { id: e.id, name: e.name, index: 0, site: this.detail.site } }
}
this.video.detail = e
this.view = 'Play'
this.detail.show = false
},
addClass (flag) {
if (flag === this.videoFlag) {
return 'selectedBox'
@@ -200,15 +258,15 @@ export default {
onlineVideo.playVideoOnline(this.selectedOnlineSite, this.detail.info.name, n)
}
},
async starEvent () {
const db = await star.find({ key: this.detail.key, ids: this.info.id })
async starEvent (info) {
const db = await star.find({ key: this.detail.key, ids: info.id })
const doc = {
key: this.detail.key,
ids: this.info.id,
ids: info.id,
site: this.detail.site,
name: this.info.name,
detail: this.info,
rate: this.info.rate
name: info.name,
detail: info,
rate: info.rate
}
if (db) {
star.update(db.id, doc)
@@ -219,6 +277,10 @@ export default {
})
}
},
detailEvent (info) {
this.detail.info = info
this.getDetailInfo()
},
togglePlayOnlineEvent () {
this.playOnline = !this.playOnline
},
@@ -257,12 +319,12 @@ export default {
this.$message.error(err.info)
})
},
shareEvent () {
shareEvent (info, selectedEpisode) {
this.share = {
show: true,
key: this.detail.key,
info: this.info,
index: this.selectedEpisode
info: info,
index: selectedEpisode
}
},
doubanLinkEvent () {
@@ -277,6 +339,17 @@ export default {
const name = this.info.name.trim()
const year = this.info.year
this.info.rate = await zy.doubanRate(name, year)
const recommendations = await zy.doubanRecommendations(name, year)
if (recommendations) {
this.info.recommendations = []
recommendations.forEach(element => {
zy.searchFirstDetail(this.detail.key, element).then(detailRes => {
if (detailRes) {
this.info.recommendations.push(detailRes)
}
})
})
}
},
async getDetailInfo () {
const id = this.detail.info.ids || this.detail.info.id
@@ -293,13 +366,14 @@ export default {
if (res) {
this.info = res
this.$set(this.info, 'rate', this.DetailCache[cacheKey].rate || '')
this.$set(this.info, 'recommendations', this.DetailCache[cacheKey].recommendations || [])
this.videoFlag = this.videoFlag || res.fullList[0].flag
this.videoList = res.fullList[0].list
this.videoFullList = res.fullList
this.loading = false
if (!this.info.rate) {
await this.getDoubanRate()
this.DetailCache[cacheKey].rate = this.info.rate
this.DetailCache[cacheKey] = this.info
}
}
}

View File

@@ -2,7 +2,7 @@
<div class="listpage" id="sites">
<div class="listpage-header" v-show="!enableBatchEdit">
<el-switch v-model="enableBatchEdit" active-text="批处理分组">></el-switch>
<el-checkbox v-model="setting.excludeR18Films" @change="excludeR18FilmsChangeEvent">屏蔽福利片</el-checkbox>
<el-button @click="openFilterKeywordsDiag" icon="el-icon-key">关键词过滤</el-button>
<el-button @click="addSite" icon="el-icon-document-add">新增</el-button>
<el-button @click="exportSites" icon="el-icon-upload2" title="导出全部,自动添加扩展名">导出</el-button>
<el-button @click="importSites" icon="el-icon-download" title="支持同时导入多个文件">导入</el-button>
@@ -41,7 +41,18 @@
<template slot-scope="scope">
<el-switch
v-model="scope.row.isActive"
@click.native.stop='isActiveChangeEvent(scope.row)'>
@click.native.stop='propChangeEvent(scope.row)'>
</el-switch>
</template>
</el-table-column>
<el-table-column
prop="reverseOrder"
width="120"
label="倒序排列">
<template slot-scope="scope">
<el-switch
v-model="scope.row.reverseOrder"
@click.native.stop='propChangeEvent(scope.row)'>>
</el-switch>
</template>
</el-table-column>
@@ -83,7 +94,7 @@
</div>
<!-- 编辑页面 -->
<div>
<el-dialog :visible.sync="dialogVisible" v-if='dialogVisible' :title="dialogType==='edit'?'编辑源':'新增源'" :append-to-body="true" @close="closeDialog">
<el-dialog :visible.sync="editSiteDialogVisible" v-if='editSiteDialogVisible' :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="请输入源站名" />
@@ -112,15 +123,36 @@
</span>
</el-dialog>
</div>
<!-- 设置过滤关键词页面 -->
<div>
<el-dialog :visible.sync="filterKeywordsDialogVisible" v-if='filterKeywordsDialogVisible' :title="'分类过滤'" :append-to-body="true" @close="closeDialog">
<el-form>
<el-switch v-model="excludeRootClasses" active-text="开启主分类过滤">></el-switch>
<el-form-item>
<el-input v-model="rootClassFilterKeywords" :autosize="{ minRows: 3, maxRows: 6}" type="textarea" placeholder="请输入过滤关键词" />
</el-form-item>
</el-form>
<el-form>
<el-switch v-model="excludeR18Films" active-text="开启福利分类过滤">></el-switch>
<el-form-item>
<el-input v-model="r18ClassFilterKeywords" :autosize="{ minRows: 3, maxRows: 6}" type="textarea" placeholder="请输入过滤关键词" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取消</el-button>
<el-button type="primary" @click="saveFilterKeywords">保存</el-button>
</span>
</el-dialog>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { sites, setting } from '../lib/dexie'
import zy from '../lib/site/tools'
import { remote } from 'electron'
import fs from 'fs'
import Sortable from 'sortablejs'
const remote = require('@electron/remote')
export default {
name: 'editSites',
@@ -129,7 +161,8 @@ export default {
show: false,
sites: [],
dialogType: 'new',
dialogVisible: false,
editSiteDialogVisible: false,
filterKeywordsDialogVisible: false,
siteInfo: {
key: '',
name: '',
@@ -139,6 +172,10 @@ export default {
group: '',
isActive: true
},
excludeRootClasses: true,
excludeR18Films: true,
rootClassFilterKeywords: [],
r18ClassFilterKeywords: [],
siteGroup: [],
rules: {
name: [
@@ -204,12 +241,6 @@ export default {
},
methods: {
...mapMutations(['SET_SETTING']),
excludeR18FilmsChangeEvent () {
setting.find().then(res => {
res.excludeR18Films = this.setting.excludeR18Films
setting.update(res)
})
},
selectionCellClick (selection, row) {
if (this.shiftDown && this.selectionBegin !== '' && selection.includes(row)) {
this.selectionEnd = row.id
@@ -249,6 +280,11 @@ export default {
},
getSites () {
sites.all().then(res => {
res.forEach(element => {
if (element.reverseOrder === null || element.reverseOrder === undefined) {
element.reverseOrder = false
}
})
this.sites = res
})
},
@@ -261,6 +297,29 @@ export default {
}
this.siteGroup = arr
},
openFilterKeywordsDiag () {
this.excludeRootClasses = this.setting.excludeRootClasses
this.excludeR18Films = this.setting.excludeR18Films
this.rootClassFilterKeywords = this.setting.rootClassFilter?.join()
this.r18ClassFilterKeywords = this.setting.r18ClassFilter?.join()
this.filterKeywordsDialogVisible = true
},
saveFilterKeywords () {
// 移除空格,然后按逗号分开
this.setting.rootClassFilter = this.rootClassFilterKeywords?.replace(/\s/g, '').split(',')
this.setting.r18ClassFilter = this.r18ClassFilterKeywords?.replace(/\s/g, '').split(',')
this.setting.classFilter = []
this.setting.excludeRootClasses = this.excludeRootClasses
if (this.excludeRootClasses) {
this.setting.classFilter = this.setting.classFilter.concat(this.setting.rootClassFilter)
}
this.setting.excludeR18Films = this.excludeR18Films
if (this.excludeR18Films) {
this.setting.classFilter = this.setting.classFilter.concat(this.setting.r18ClassFilter)
}
setting.update(this.setting)
this.filterKeywordsDialogVisible = false
},
addSite () {
if (this.checkAllSitesLoading) {
this.$message.info('正在检测, 请勿操作.')
@@ -268,7 +327,7 @@ export default {
}
this.getSitesGroup()
this.dialogType = 'new'
this.dialogVisible = true
this.editSiteDialogVisible = true
this.siteInfo = {
key: '',
name: '',
@@ -286,12 +345,13 @@ export default {
}
this.getSitesGroup()
this.dialogType = 'edit'
this.dialogVisible = true
this.editSiteDialogVisible = true
this.siteInfo = siteInfo
this.editOldkey = siteInfo.key
},
closeDialog () {
this.dialogVisible = false
this.editSiteDialogVisible = false
this.filterKeywordsDialogVisible = false
this.getSites()
},
removeEvent (e) {
@@ -348,7 +408,7 @@ export default {
group: ''
}
this.dialogType === 'edit' ? this.$message.success('修改成功!') : this.$message.success('新增源成功!')
this.dialogVisible = false
this.editSiteDialogVisible = false
this.getSites()
})
this.editOldkey = ''
@@ -437,8 +497,7 @@ export default {
resetSitesEvent () {
let url = this.setting.sitesDataURL
if (!url) {
// 如果没有设置源站文件链接,使用默认的gitee源
url = 'https://gitee.com/cuiocean/ZY-Player-Resources/raw/main/Sites/Sites.json'
url = 'https://raw.iqiq.io/Hunlongyu/ZY-Player-Resources/main/Sites/20220713.json'
}
zy.getDefaultSites(url).then(res => {
if (res.length > 0) {
@@ -463,9 +522,10 @@ export default {
this.sites = this.$refs.editSitesTable.tableData
}
},
isActiveChangeEvent (row) {
propChangeEvent (row) {
sites.remove(row.id)
sites.add(row)
this.getSites()
},
resetId (inArray) {
let id = 1

View File

@@ -281,7 +281,7 @@
animationEffect="fadeIn"
backgroundColor="rgba(0, 0, 0, 0)">
<template slot="item" slot-scope="props">
<div class="card" v-show="!setting.excludeR18Films || !containsR18Keywords(props.data.type)">
<div class="card" v-show="!setting.excludeR18Films || !containsClassFilterKeyword(props.data.type)">
<div class="img">
<div class="site">
<span>{{props.data.site.name}}</span>
@@ -344,9 +344,8 @@ export default {
filteredSearchContents: [],
currentColumn: '',
searchGroup: '',
searchGroups: [],
// 福利片关键词
r18KeyWords: ['伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番', 'VIP'],
searchGroups: ['站内', '组内', '全站'],
classFilterKeywords: [],
filteredList: [],
areas: [],
searchRunning: false,
@@ -414,12 +413,13 @@ export default {
}
},
filterSettings () {
return this.$store.getters.getSetting.excludeR18Films // 需要监听的数据
return this.$store.getters.getSetting.classFilter // 需要监听的数据
},
searchSites () {
if (this.searchGroup === '站内') return [this.site]
if (this.searchGroup === '组内') return this.sites.filter(site => site.group === this.site.group)
if (this.searchGroup === '全站') return this.sites
return this.sites.filter(site => site.group === this.searchGroup)
return this.sites.filter(site => site.isActive)
}
},
filters: {
@@ -444,7 +444,7 @@ export default {
}
},
filterSettings () {
this.siteClick(this.site.name)
this.refreshClass()
},
list: {
handler (list) {
@@ -456,9 +456,12 @@ export default {
siteSearchCount () {
if (this.siteSearchCount === this.searchSites.length) this.searchRunning = false
},
site () {
this.siteClick(this.site.name)
},
searchContents: {
handler (list) {
list = list.filter(res => !this.setting.excludeR18Films || !this.containsR18Keywords(res.type))
list = list.filter(res => !this.setting.excludeR18Films || !this.containsClassFilterKeyword(res.type))
this.areas = [...new Set(list.map(ele => ele.area))].filter(x => x)
this.searchClassList = [...new Set(list.map(ele => ele.type))].filter(x => x)
this.refreshFilteredList()
@@ -500,14 +503,14 @@ export default {
let filteredData = this.showFind ? this.searchContents : this.list
if (this.showFind) filteredData = filteredData.filter(x => (this.selectedSearchClassNames.length === 0) || this.selectedSearchClassNames.includes(x.type))
filteredData = filteredData.filter(x => (this.selectedAreas.length === 0) || this.selectedAreas.includes(x.area))
filteredData = filteredData.filter(res => !this.setting.excludeR18Films || !this.containsR18Keywords(res.type))
filteredData = filteredData.filter(res => !this.setting.excludeR18Films || !this.containsClassFilterKeyword(res.type))
filteredData = filteredData.filter(res => res.year >= this.selectedYears.start)
filteredData = filteredData.filter(res => res.year <= this.selectedYears.end)
if (!this.showFind) this.selectedClassName = this.type.name + ' ' + filteredData.length + '/' + this.recordcount
switch (this.sortKeyword) {
case '按上映年份':
filteredData.sort(function (a, b) {
return a.year - b.year
return b.year - a.year
})
break
case '按片名':
@@ -521,8 +524,14 @@ export default {
})
break
default:
filteredData.sort(function (a, b) {
return new Date(b.last) - new Date(a.last)
})
break
}
// Get unique film data
filteredData = Array.from(new Set(filteredData))
if (this.showFind) {
this.filteredSearchContents = filteredData
} else {
@@ -592,6 +601,16 @@ export default {
})
}
},
refreshClass () {
this.getClass().then(res => {
this.classList = res
// cache classList data
FILM_DATA_CACHE[this.site.key] = {
classList: this.classList
}
this.classClick(this.type.name)
})
},
classClick (className) {
this.list = []
this.type = this.classList.find(x => x.name === className)
@@ -619,20 +638,11 @@ export default {
getClass () {
return new Promise((resolve, reject) => {
const key = this.site.key
// 屏蔽主分类
const classToHide = ['电影', '电影片', '电视剧', '连续剧', '综艺', '动漫']
zy.class(key).then(res => {
const allClass = [{ name: '最新', tid: 0 }]
res.class.forEach(element => {
if (!this.setting.excludeRootClasses || !classToHide.includes(element.name)) {
if (this.setting.excludeR18Films) {
const containKeyWord = this.containsR18Keywords(element.name)
if (!containKeyWord) {
allClass.push(element)
}
} else {
allClass.push(element)
}
if (!this.containsClassFilterKeyword(element.name)) {
allClass.push(element)
}
})
resolve(allClass)
@@ -641,19 +651,20 @@ export default {
})
})
},
containsR18Keywords (name) {
const containKeyWord = false
if (!name) {
return containKeyWord
containsClassFilterKeyword (name) {
let ret = false
// 主分类过滤, 检测关键词是否包含分类名
if (this.setting.excludeRootClasses) {
ret = this.setting.rootClassFilter?.some(v => v.includes(name))
}
return this.r18KeyWords.some(v => name.includes(v))
// 福利过滤,检测分类名是否包含关键词
if (this.setting.excludeR18Films && !ret) {
ret = this.setting.r18ClassFilter?.some(v => name?.includes(v))
}
return ret
},
toFlipPagecount () {
// 似乎需要解析的网站的视频排序和其他m3u8采集站的顺序正好相反
if (this.site.jiexiUrl) {
return true
}
return false
return this.site.reverseOrder
},
infiniteHandler ($state) {
const key = this.site.key
@@ -861,12 +872,34 @@ export default {
this.showFind = false
}
},
async getDefaultSites () {
const s = await setting.find()
zy.getDefaultSites(s.sitesDataURL).then(res => {
if (res && typeof res === 'string') {
const json = JSON.parse(res)
sites.clear().then(sites.bulkAdd(json))
}
if (res && typeof res === 'object') {
sites.clear().then(sites.bulkAdd(res))
}
sites.all().then(res => {
if (res) {
this.sites = res.filter(item => item.isActive)
if (this.site === undefined || !this.sites.some(x => x.key === this.site.key)) {
this.site = this.sites[0]
this.selectedSiteName = this.sites[0].name
}
}
})
}).catch(error => {
this.$message.error('获取云端源站失败. ' + error)
})
},
getAllSites () {
sites.all().then(res => {
if (res.length <= 0) {
this.site = {}
this.type = {}
this.list = []
this.$message.warning('检测到视频源未能正常加载, 即将重置源.')
this.getDefaultSites()
} else {
this.sites = res.filter(item => item.isActive)
if (this.site === undefined || !this.sites.some(x => x.key === this.site.key)) {
@@ -874,10 +907,6 @@ export default {
this.selectedSiteName = this.sites[0].name
}
}
this.searchGroups = [...new Set(this.sites.map(site => site.group))]
if (this.searchGroups.length === 1) this.searchGroups = []
this.searchGroups.unshift('站内')
this.searchGroups.push('全站')
this.searchGroup = this.setting.searchGroup
if (this.searchGroup === undefined) setting.find().then(res => { this.searchGroup = res.searchGroup })
})

View File

@@ -15,7 +15,7 @@
</div>
</template>
<script>
const { remote } = require('electron')
const remote = require('@electron/remote')
export default {
name: 'frame',
computed: {

View File

@@ -4,10 +4,12 @@
<el-button @click.stop="exportHistory" icon="el-icon-upload2" title="导出全部,自动添加扩展名">导出</el-button>
<el-button @click.stop="importHistory" icon="el-icon-download" title="支持同时导入多个文件">导入</el-button>
<el-button @click.stop="removeSelectedItems" icon="el-icon-delete-solid">{{ multipleSelection.length === 0 ? "清空" : "删除所选" }}</el-button>
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">检查更新</el-button>
<b-button-group>
<el-switch v-model="onlyShowItemsHasUpdate" active-text="有更新" inactive-text="全部" @change="refreshFilteredList"></el-switch>
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">检查更新</el-button>
</b-button-group>
</div>
<div class="toolbar" v-show="showToolbar">
<el-switch v-model="onlyShowItemsHasUpdate" active-text="有更新" inactive-text="全部" @change="refreshFilteredList"></el-switch>
<el-select v-model="selectedAreas" size="small" multiple placeholder="地区" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
<el-option
v-for="item in areas"
@@ -79,10 +81,10 @@
</template>
</el-table-column>
<el-table-column v-if="list.some(e => e.time)"
width="150"
width="200"
label="时间进度">
<template slot-scope="scope">
<span v-if="scope.row.time && scope.row.duration">{{fmtMSS(scope.row.time.toFixed(0))}}/{{fmtMSS(scope.row.duration.toFixed(0))}}</span>
<span v-if="scope.row.time && scope.row.duration">{{fmtMSS(scope.row.time.toFixed(0))}}/{{fmtMSS(scope.row.duration.toFixed(0))}} ({{progress(scope.row)}}%)</span>
<span v-if="scope.row.onlinePlay">在线解析</span>
</template>
</el-table-column>
@@ -134,7 +136,7 @@
<div class="name" @click="detailEvent(props.data)">{{props.data.name}}</div>
<div class="info">
<span v-if="props.data.time && props.data.duration">
{{fmtMSS(props.data.time.toFixed(0))}}/{{fmtMSS(props.data.duration.toFixed(0))}}
{{fmtMSS(props.data.time.toFixed(0))}}/{{fmtMSS(props.data.duration.toFixed(0))}} ({{progress(props.data)}}%)
</span>
<span v-if="props.data.onlinePlay">在线解析</span>
<span v-if="props.data.detail && props.data.detail.fullList[0].list !== undefined && props.data.detail.fullList[0].list.length > 1">
@@ -152,9 +154,9 @@
import { mapMutations } from 'vuex'
import { history, sites, setting } from '../lib/dexie'
import zy from '../lib/site/tools'
import { remote } from 'electron'
import fs from 'fs'
import Waterfall from 'vue-waterfall-plugin'
const remote = require('@electron/remote')
const { clipboard } = require('electron')
export default {
@@ -169,6 +171,7 @@ export default {
multipleSelection: [],
areas: [],
types: [],
filteredList: [],
// Update
numNoUpdate: 0,
// Toolbar
@@ -176,7 +179,7 @@ export default {
selectedAreas: [],
selectedTypes: [],
sortKeyword: '',
sortKeywords: ['按片名', '按上映年份', '按更新时间'],
sortKeywords: ['按片名', '按上映年份', '按更新时间', '按完成度'],
selectedYears: { start: 0, end: new Date().getFullYear() },
onlyShowItemsHasUpdate: false
}
@@ -292,7 +295,6 @@ export default {
toggleViewMode () {
this.setting.historyViewMode = this.setting.historyViewMode === 'picture' ? 'table' : 'picture'
if (this.setting.historyViewMode === 'table') {
setTimeout(() => { this.rowDrop() }, 100)
this.showShiftPrompt()
} else {
setTimeout(() => { if (this.$refs.historyWaterfall) this.$refs.historyWaterfall.refresh() }, 700)
@@ -339,6 +341,9 @@ export default {
return new Date(b.detail.last) - new Date(a.detail.last)
})
break
case '按完成度':
filteredData.sort(this.sortByProgress)
break
default:
break
}
@@ -348,6 +353,16 @@ export default {
this.filteredList = this.filteredList.filter(x => x.hasUpdate)
}
},
progress (e) {
return e.duration > 0 ? ((e.time / e.duration) * 100).toFixed(0) : 0
},
sortByProgress (a, b) {
if (this.progress(a) < this.progress(b)) {
return -1
} else {
return 1
}
},
fmtMSS (s) {
return (s - (s %= 60)) / 60 + (s > 9 ? ':' : ':0') + s
},

View File

@@ -110,10 +110,10 @@ import { mapMutations } from 'vuex'
import { iptv, channelList, setting } from '../lib/dexie'
import { iptv as defaultChannels } from '../lib/dexie/initData'
import zy from '../lib/site/tools'
import { remote } from 'electron'
import fs from 'fs'
import Sortable from 'sortablejs'
import axios from 'axios'
const remote = require('@electron/remote')
export default {
name: 'iptv',
data () {

View File

@@ -252,6 +252,7 @@ import { mapMutations } from 'vuex'
import { star, history, setting, shortcut, mini, channelList, sites } from '../lib/dexie'
import zy from '../lib/site/tools'
import Player from 'xgplayer'
import 'xgplayer-mp4'
import HlsJsPlayer from 'xgplayer-hls.js'
import FlvJsPlayer from 'xgplayer-flv.js'
import mt from 'mousetrap'
@@ -259,7 +260,8 @@ import Clickoutside from 'element-ui/src/utils/clickoutside'
import { exec, execFile } from 'child_process'
import PinyinMatch from 'pinyin-match'
const { remote, clipboard } = require('electron')
const { clipboard } = require('electron')
const remote = require('@electron/remote')
const win = remote.getCurrentWindow()
const URL = require('url')
const VIDEO_DETAIL_CACHE = {}
@@ -342,7 +344,7 @@ export default {
videoTitle: true,
airplay: true,
closeVideoTouch: true,
ignores: ['cssFullscreen', 'replay', 'error'], // 为了切换播放器类型时避免显示错误刷新,暂时忽略错误
ignores: ['replay', 'error'], // 为了切换播放器类型时避免显示错误刷新,暂时忽略错误
preloadTime: 300
},
state: {
@@ -445,6 +447,9 @@ export default {
set (val) {
this.SET_DetailCache(val)
}
},
VideoEssentialInfo () {
return this.video.key + '@' + this.video.info.id + '@' + this.video.info.index
}
},
watch: {
@@ -458,12 +463,11 @@ export default {
}
}
},
video: {
VideoEssentialInfo: {
handler () {
if (this.changingIPTV) return
this.getUrls()
},
deep: true
}
},
setting: {
handler () {
@@ -655,9 +659,9 @@ export default {
const currentSite = await sites.find({ key: this.video.key })
this.$message.info('即将调用解析接口播放,请等待...')
if (currentSite.jiexiUrl) {
this.onlineUrl = /^\s*(default|默认)\s*$/i.test(currentSite.jiexiUrl) ? this.setting.defaultParseURL + url : currentSite.jiexiUrl + url
this.onlineUrl = currentSite.jiexiUrl + url
} else {
this.onlineUrl = url
this.onlineUrl = this.setting.defaultParseURL + url
}
this.videoPlaying('online')
return
@@ -1568,7 +1572,7 @@ export default {
},
minMaxEvent () {
win.on('minimize', () => {
if (this.xg && this.xg.hasStart) {
if (this.xg && this.xg.hasStart && this.setting.pauseWhenMinimize) {
this.xg.pause()
}
})
@@ -1664,6 +1668,9 @@ export default {
cursor: pointer;
margin-left: 3px;
}
.xgplayer-skin-default .xg-btn-playPrev {
margin-left: 50px;
}
.xgplayer-skin-default .xg-btn-quitMiniMode {
display: none;
}

View File

@@ -1,6 +1,18 @@
<template>
<div class="listpage" id="recommendations">
<div class="listpage-header" id="recommendations-header">
<el-select v-model="selectedRecommendationType" size="small" slot="prepend"
:popper-append-to-body="false"
popper-class="popper"
default-first-option placeholder="请选择"
@change="changeRecommendationTypeEvent">
<el-option
v-for="item in recommendationTypes"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-button type="text">视频数{{ recommendations.length }}</el-button>
<el-button :loading="loading" @click.stop="updateEvent" icon="el-icon-refresh">更新推荐</el-button>
</div>
@@ -86,7 +98,6 @@
<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>
@@ -118,7 +129,6 @@
<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>
@@ -138,10 +148,12 @@
</template>
<script>
import { mapMutations } from 'vuex'
import { history, recommendation, setting } from '../lib/dexie'
import { history, recommendation, setting, sites, cachedMovies } from '../lib/dexie'
import zy from '../lib/site/tools'
import Waterfall from 'vue-waterfall-plugin'
import axios from 'axios'
const { clipboard } = require('electron')
export default {
name: 'recommendations',
data () {
@@ -152,13 +164,47 @@ export default {
types: [],
areas: [],
filteredList: [],
// 不同推荐
recommendationsDefault: [],
recommendationTypes: ['豆瓣热门电影', '豆瓣高分电影', '豆瓣华语电影', '豆瓣冷门佳片', '豆瓣热门剧集', '豆瓣热门美剧', '豆瓣热门英剧', '豆瓣热门国产剧', '豆瓣热门综艺', '豆瓣热门动漫', '豆瓣热门纪录片', '豆瓣热门动画电影'],
selectedRecommendationType: '豆瓣热门电影',
// Toolbar
showToolbar: false,
selectedAreas: [],
selectedTypes: [],
sortKeyword: '',
sortKeywords: ['按片名', '按上映年份', '按更新时间'],
selectedYears: { start: 0, end: new Date().getFullYear() }
sortKeywords: ['按片名', '按上映年份', '按更新时间', '按评分'],
selectedYears: { start: 0, end: new Date().getFullYear() },
// 缓存数据
localCachedMovies: [],
// 豆瓣
douban: {
page_limit: 50,
hotMoviePageStart: 0,
hotmovie: [],
hotTVPageStart: 0,
hotTV: [],
highRateMoviePageStart: 0,
highRateMovie: [],
hotAnimePageStart: 0,
hotAnime: [],
hotDocumentaryPageStart: 0,
hotDocumentary: [],
hotTVShowPageStart: 0,
hotTVShow: [],
hotCartonMoviePageStart: 0,
hotCartonMovie: [],
hotAmericanTVSeriesPageStart: 0,
hotAmericanTVSeries: [],
hotBritishTVSeriesPageStart: 0,
hotBritishTVSeries: [],
hotChineseTVSeriesPageStart: 0,
hotChineseTVSeries: [],
goodButNotHotMoviesPageStart: 0,
goodButNotHotMovies: [],
chineseMoviesPageStart: 0,
chineseMovies: []
}
}
},
components: {
@@ -223,6 +269,199 @@ export default {
},
methods: {
...mapMutations(['SET_VIEW', 'SET_DETAIL', 'SET_VIDEO', 'SET_SHARE', 'SET_SETTING']),
changeRecommendationTypeEvent () {
if (this.selectedRecommendationType === '作者推荐') {
this.recommendations = this.recommendationsDefault
} else {
if (this.selectedRecommendationType === '豆瓣热门电影') {
this.recommendations = [...this.douban.hotmovie]
}
if (this.selectedRecommendationType === '豆瓣高分电影') {
this.recommendations = [...this.douban.highRateMovie]
}
if (this.selectedRecommendationType === '豆瓣热门剧集') {
this.recommendations = [...this.douban.hotTV]
}
if (this.selectedRecommendationType === '豆瓣热门美剧') {
this.recommendations = [...this.douban.hotAmericanTVSeries]
}
if (this.selectedRecommendationType === '豆瓣热门英剧') {
this.recommendations = [...this.douban.hotBritishTVSeries]
}
if (this.selectedRecommendationType === '豆瓣热门国产剧') {
this.recommendations = [...this.douban.hotChineseTVSeries]
}
if (this.selectedRecommendationType === '豆瓣热门动漫') {
this.recommendations = [...this.douban.hotAnime]
}
if (this.selectedRecommendationType === '豆瓣热门纪录片') {
this.recommendations = [...this.douban.hotDocumentary]
}
if (this.selectedRecommendationType === '豆瓣热门综艺') {
this.recommendations = [...this.douban.hotTVShow]
}
if (this.selectedRecommendationType === '豆瓣热门动画电影') {
this.recommendations = [...this.douban.hotCartonMovie]
}
if (this.selectedRecommendationType === '豆瓣冷门佳片') {
this.recommendations = [...this.douban.goodButNotHotMovies]
}
if (this.selectedRecommendationType === '豆瓣华语电影') {
this.recommendations = [...this.douban.chineseMovies]
}
if (this.recommendations.length === 0) {
this.updateDoubanRecommendationsEvent()
}
}
},
getRecommendationsDoubanMovieOrTV (doubanUrl) {
axios.get(doubanUrl).then(res => {
if (res.data) {
res.data.subjects.forEach(element => {
const localCachedMovie = this.localCachedMovies.find(e => e.key === this.sites[0].key && e.name === element.title)
if (localCachedMovie) {
this.updateDoubanRecommendataions(localCachedMovie)
} else {
this.searchAndCacheMovie(element)
}
})
}
})
},
updateDoubanRecommendataions (movie) {
this.recommendations.push(movie)
if (this.selectedRecommendationType === '豆瓣热门电影') {
this.douban.hotmovie.push(movie)
}
if (this.selectedRecommendationType === '豆瓣高分电影') {
this.douban.highRateMovie.push(movie)
}
if (this.selectedRecommendationType === '豆瓣热门剧集') {
this.douban.hotTV.push(movie)
}
if (this.selectedRecommendationType === '豆瓣热门美剧') {
this.douban.hotAmericanTVSeries.push(movie)
}
if (this.selectedRecommendationType === '豆瓣热门英剧') {
this.douban.hotBritishTVSeries.push(movie)
}
if (this.selectedRecommendationType === '豆瓣热门国产剧') {
this.douban.hotChineseTVSeries.push(movie)
}
if (this.selectedRecommendationType === '豆瓣热门动漫') {
this.douban.hotAnime.push(movie)
}
if (this.selectedRecommendationType === '豆瓣热门纪录片') {
this.douban.hotDocumentary.push(movie)
}
if (this.selectedRecommendationType === '豆瓣热门综艺') {
this.douban.hotTVShow.push(movie)
}
if (this.selectedRecommendationType === '豆瓣热门动画电影') {
this.douban.hotCartonMovie.push(movie)
}
if (this.selectedRecommendationType === '豆瓣冷门佳片') {
this.douban.goodButNotHotMovies.push(movie)
}
if (this.selectedRecommendationType === '豆瓣华语电影') {
this.douban.chineseMovies.push(movie)
}
},
searchAndCacheMovie (element) {
zy.searchFirstDetail(this.sites[0].key, element.title).then(detailRes => {
if (detailRes) {
const doc = {
key: this.sites[0].key,
ids: detailRes.id,
site: this.sites[0],
name: detailRes.name,
detail: detailRes,
rate: element.rate
}
this.updateDoubanRecommendataions(doc)
this.localCachedMovies.push(doc)
cachedMovies.add(doc)
}
})
},
updateEvent () {
if (this.selectedRecommendationType === '作者推荐') {
this.updateAuthorRecommendataions()
} else {
this.updateDoubanRecommendationsEvent()
}
},
updateAuthorRecommendataions () {
const url = 'https://raw.githubusercontent.com/cuiocean/ZY-Player-Resources/main/Recommendations/Recommendations.json'
this.loading = true
axios.get(url).then(res => {
if (res.status === 200) {
if (res.data.length > 0) {
this.recommendations = res.data
recommendation.clear().then(recommendation.bulkAdd(this.recommendations))
this.getFilterData()
this.$message.success('更新推荐成功. 仅根据作者cuiocean个人喜好推荐,不喜请无视.')
}
}
this.loading = false
}).catch(error => {
this.loading = false
this.$message.error('更新推荐失败. ' + error)
this.$message.warning('最新的推荐数据保存在Github上,请考虑使用代理或者等待下一版本内置数据更新.')
})
},
updateDoubanRecommendationsEvent () {
let doubanUrl = ''
if (this.selectedRecommendationType === '豆瓣热门电影') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=热门&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotMoviePageStart}`
this.douban.hotMoviePageStart = this.douban.hotMoviePageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣热门剧集') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=热门&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotTVPageStart}`
this.douban.hotTVPageStart = this.douban.hotTVPageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣热门美剧') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=美剧&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotCartonMoviePageStart}`
this.douban.hotCartonMoviePageStart = this.douban.hotCartonMoviePageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣热门英剧') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=英剧&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotBritishTVSeriesPageStart}`
this.douban.hotBritishTVSeriesPageStart = this.douban.hotBritishTVSeriesPageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣热门国产剧') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=国产剧&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotChineseTVSeriesPageStart}`
this.douban.hotChineseTVSeriesPageStart = this.douban.hotChineseTVSeriesPageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣高分电影') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=豆瓣高分&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.highRateMoviePageStart}`
this.douban.highRateMoviePageStart = this.douban.highRateMoviePageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣热门动漫') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=日本动画&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotAnimePageStart}`
this.douban.hotAnimePageStart = this.douban.hotAnimePageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣热门纪录片') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=纪录片&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotDocumentaryPageStart}`
this.douban.hotDocumentaryPageStart = this.douban.hotDocumentaryPageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣热门综艺') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=tv&tag=综艺&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotTVShowPageStart}`
this.douban.hotTVShowPageStart = this.douban.hotTVShowPageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣热门动画电影') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=动画&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.hotCartonMoviePageStart}`
this.douban.hotCartonMoviePageStart = this.douban.hotCartonMoviePageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣冷门佳片') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=冷门佳片&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.goodButNotHotMoviesPageStart}`
this.douban.goodButNotHotMoviesPageStart = this.douban.goodButNotHotMoviesPageStart + this.douban.page_limit
}
if (this.selectedRecommendationType === '豆瓣华语电影') {
doubanUrl = `https://movie.douban.com/j/search_subjects?type=movie&tag=华语&sort=recommend&page_limit=${this.douban.page_limit}&page_start=${this.douban.chineseMoviesPageStart}`
this.douban.chineseMoviesPageStart = this.douban.chineseMoviesPageStart + this.douban.page_limit
}
this.getRecommendationsDoubanMovieOrTV(doubanUrl)
},
toggleViewMode () {
this.setting.recommendationViewMode = this.setting.recommendationViewMode === 'picture' ? 'table' : 'picture'
if (this.setting.recommendationViewMode === 'table') {
@@ -259,7 +498,7 @@ export default {
switch (this.sortKeyword) {
case '按上映年份':
filteredData.sort(function (a, b) {
return a.detail.year - b.detail.year
return b.detail.year - a.detail.year
})
break
case '按片名':
@@ -272,6 +511,11 @@ export default {
return new Date(b.detail.last) - new Date(a.detail.last)
})
break
case '按评分':
filteredData.sort(function (a, b) {
return b.rate - a.rate
})
break
default:
break
}
@@ -288,26 +532,6 @@ export default {
}
}
},
updateEvent () {
const url = 'https://raw.githubusercontent.com/cuiocean/ZY-Player-Resources/main/Recommendations/Recommendations.json'
this.loading = true
const axios = require('axios')
axios.get(url).then(res => {
if (res.status === 200) {
if (res.data.length > 0) {
this.recommendations = res.data
recommendation.clear().then(recommendation.bulkAdd(this.recommendations))
this.getFilterData()
this.$message.success('更新推荐成功. 仅根据作者cuiocean个人喜好推荐,不喜请无视.')
}
}
this.loading = false
}).catch(error => {
this.loading = false
this.$message.error('更新推荐失败. ' + error)
this.$message.warning('最新的推荐数据保存在Github上,请考虑使用代理或者等待下一版本内置数据更新.')
})
},
async playEvent (e) {
const db = await history.find({ site: e.key, ids: e.ids })
if (db) {
@@ -317,14 +541,6 @@ export default {
}
this.view = 'Play'
},
deleteEvent (e) {
recommendation.remove(e.id).then(res => {
if (res) {
this.$message.warning('删除失败')
}
this.getRecommendations()
})
},
shareEvent (e) {
this.share = {
show: true,
@@ -344,12 +560,9 @@ export default {
})
},
getRecommendations () {
recommendation.all().then(res => {
this.recommendations = res.sort(function (a, b) {
return b.id - a.id
})
this.getFilterData()
})
this.recommendationsDefault = []
this.changeRecommendationTypeEvent()
this.getFilterData()
},
getFilterData () {
this.types = [...new Set(this.recommendations.map(ele => ele.detail.type))].filter(x => x)
@@ -361,10 +574,24 @@ export default {
res.recommendationViewMode = this.setting.recommendationViewMode
setting.update(res)
})
},
getAllSites () {
sites.all().then(res => {
if (res.length > 0) {
this.sites = res.filter(item => item.isActive)
}
})
},
getCachedMovies () {
cachedMovies.all().then(res => {
this.localCachedMovies = res
})
}
},
created () {
this.getAllSites()
this.getRecommendations()
this.getCachedMovies()
},
mounted () {
addEventListener('resize', () => {

View File

@@ -5,9 +5,7 @@
<div class="info">
<a @click="linkOpen('http://zyplayer.fun/')">官网</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player')">Github</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/releases/tag/v' + pkg.version)">v{{pkg.version}}更新日志</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/issues/80')">常见问题</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/issues')">反馈建议</a>
<a @click="linkOpen('https://github.com/Hunlongyu/ZY-Player/discussions/776')">软件完全免费如遇收费请立即给差评并退费</a>
<a style="color:#38dd77" @click="openUpdate()" v-show="update.find" >最新版本v{{update.version}}</a>
</div>
<div class="shortcut">
@@ -93,9 +91,6 @@
<div class="zy-select">
<div class="vs-placeholder vs-noAfter" @click="show.configSitesDataUrlDialog = true">设置源站接口文件</div>
</div>
<div class="zy-input" @click="toggleExcludeRootClasses">
<input type="checkbox" v-model = "d.excludeRootClasses" @change="updateSettingEvent"> 屏蔽主分类
</div>
</div>
</div>
<div class="site">
@@ -113,6 +108,15 @@
</div>
</div>
</div>
<div class="site">
<div class="title">窗口及播放</div>
<div class="site-box">
<div class="zy-input">
<input type="checkbox" v-model = "d.restoreWindowPositionAndSize" @change="updateSettingEvent"> 记录并恢复窗口位置和大小
<input type="checkbox" v-model = "d.pauseWhenMinimize" @change="updateSettingEvent"> 最小化时暂停播放
</div>
</div>
</div>
<div class="theme">
<div class="title">主题</div>
<div class="theme-box">
@@ -142,13 +146,6 @@
</div>
</div>
</div>
<div class="qrcode">
<div class="title">请作者吃辣条</div>
<div class="qrcode-box">
<img class="qrcode-item" src="../assets/image/wepay-hunlongyu.png">
<img class="qrcode-item" src="../assets/image/wepay_cuiocean.jpg">
</div>
</div>
<div class="clearDB">
<span @click="clearDBEvent" class="clearBtn">软件重置</span>
<span @click="changePasswordEvent" class="clearBtn">设置密码</span>
@@ -167,7 +164,7 @@
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取消</el-button>
<el-button type="danger" @click="get7kParseURL">重置</el-button>
<el-button type="danger" @click="resetDefaultParseURL">重置</el-button>
<el-button type="primary" @click="configDefaultParseURL">确定</el-button>
</span>
</el-dialog>
@@ -181,7 +178,7 @@
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取消</el-button>
<el-button type="danger" @click="getDefaultdeSitesDataURL">重置</el-button>
<el-button type="danger" @click="resetDefaultSitesDataURL">重置</el-button>
<el-button type="primary" @click="configSitesDataURL">确定</el-button>
</span>
</el-dialog>
@@ -259,7 +256,8 @@ import { mapMutations } from 'vuex'
import pkg from '../../package.json'
import { setting, sites, shortcut } from '../lib/dexie'
import { localKey as defaultShortcuts } from '../lib/dexie/initData'
import { shell, clipboard, remote, ipcRenderer } from 'electron'
import { shell, clipboard, ipcRenderer } from 'electron'
const remote = require('@electron/remote')
import db from '../lib/dexie/dexie'
import zy from '../lib/site/tools'
export default {
@@ -328,12 +326,17 @@ export default {
this.d = res
this.setting = this.d
if (!this.setting.defaultParseURL) this.configDefaultParseURL()
if (!this.setting.sitesDataURL) this.getDefaultdeSitesDataURL()
if (!this.setting.sitesDataURL) this.resetDefaultSitesDataURL()
})
},
getDefaultSites () {
zy.getDefaultSites(this.setting.sitesDataURL).then(res => {
if (res.length > 0) {
async getDefaultSites () {
const s = await setting.find()
zy.getDefaultSites(s.sitesDataURL).then(res => {
if (res && typeof res === 'string') {
const json = JSON.parse(res)
sites.clear().then(sites.bulkAdd(json))
}
if (res && typeof res === 'object') {
sites.clear().then(sites.bulkAdd(res))
}
}).catch(error => {
@@ -374,25 +377,20 @@ export default {
this.d.excludeRootClasses = !this.d.excludeRootClasses
this.updateSettingEvent()
},
async get7kParseURL () {
this.$message.info('正在获取7K源解析地址...')
const parseURL = await zy.get7kParseURL()
if (parseURL.startsWith('http')) {
this.$message.success('获取成功,更新应用默认解析接口地址...')
this.setting.defaultParseURL = parseURL
}
async resetDefaultParseURL () {
this.setting.defaultParseURL = 'https://jx.bpba.cc/?v='
},
async configDefaultParseURL () {
if (!this.setting.defaultParseURL) await this.get7kParseURL()
if (!this.setting.defaultParseURL) await this.resetDefaultParseURL()
this.d.defaultParseURL = this.setting.defaultParseURL?.trim()
this.show.configDefaultParseUrlDialog = false
this.updateSettingEvent()
},
getDefaultdeSitesDataURL () {
this.setting.sitesDataURL = 'https://gitee.com/cuiocean/ZY-Player-Resources/raw/main/Sites/Sites.json'
resetDefaultSitesDataURL () {
this.setting.sitesDataURL = 'https://raw.iqiq.io/Hunlongyu/ZY-Player-Resources/main/Sites/20220713.json'
},
configSitesDataURL () {
if (!this.setting.sitesDataURL) this.getDefaultdeSitesDataURL()
if (!this.setting.sitesDataURL) this.resetDefaultSitesDataURL()
this.d.sitesDataURL = this.setting.sitesDataURL
this.show.configSitesDataUrlDialog = false
this.updateSettingEvent()
@@ -597,7 +595,7 @@ export default {
}
},
created () {
this.getSites()
// this.getSites()
this.getSetting()
this.getShortcut()
this.checkUpdate()

View File

@@ -4,10 +4,12 @@
<el-button @click.stop="exportFavoritesEvent" icon="el-icon-upload2" title="导出全部,自动添加扩展名">导出</el-button>
<el-button @click.stop="importFavoritesEvent" icon="el-icon-download" title="支持同时导入多个文件">导入</el-button>
<el-button @click.stop="removeSelectedItems" icon="el-icon-delete-solid">{{ multipleSelection.length === 0 ? "清空" : "删除所选" }}</el-button>
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">检查更新</el-button>
<b-button-group>
<el-switch v-model="onlyShowItemsHasUpdate" active-text="有更新" inactive-text="全部" @change="refreshFilteredList"></el-switch>
<el-button @click.stop="updateAllEvent" icon="el-icon-refresh">检查更新</el-button>
</b-button-group>
</div>
<div class="toolbar" v-show="showToolbar">
<el-switch v-model="onlyShowItemsHasUpdate" active-text="有更新" inactive-text="全部" @change="refreshFilteredList"></el-switch>
<el-select v-model="selectedAreas" size="small" multiple placeholder="地区" popper-class="popper" :popper-append-to-body="false" @remove-tag="refreshFilteredList" @change="refreshFilteredList">
<el-option
v-for="item in areas"
@@ -177,7 +179,7 @@
import { mapMutations } from 'vuex'
import { history, star, sites, setting } from '../lib/dexie'
import zy from '../lib/site/tools'
import { remote } from 'electron'
const remote = require('@electron/remote')
import fs from 'fs'
import Sortable from 'sortablejs'
import Waterfall from 'vue-waterfall-plugin'
@@ -322,7 +324,7 @@ export default {
switch (this.sortKeyword) {
case '按上映年份':
filteredData.sort(function (a, b) {
return a.detail.year - b.detail.year
return b.detail.year - a.detail.year
})
break
case '按片名':

View File

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

View File

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

View File

@@ -1,8 +1,7 @@
import Dexie from 'dexie'
import { setting, sites, localKey, iptv, recommendations } from './initData'
import { sites, localKey, iptv, recommendations, iniSetting } from './initData'
const db = new Dexie('zy')
db.version(4).stores({
search: '++id, keywords',
setting: 'id, theme, site, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode, searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck',
@@ -63,8 +62,55 @@ db.version(9).stores({
history: '++id, [site+ids], name, type, year, index, time, duration, detail, onlinePlay, hasUpdate'
})
db.version(10).stores({
setting: 'id, theme, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode, searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck, rootClassFilter, r18ClassFilter, classFilter'
}).upgrade(trans => {
trans.setting.toCollection().modify(setting => {
delete setting.site
setting.rootClassFilter = ['电影', '电影片', '电视剧', '连续剧', '综艺', '动漫']
setting.r18ClassFilter = ['伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番', 'VIP']
setting.classFilter = ['电影', '电影片', '电视剧', '连续剧', '综艺', '动漫', '伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番', 'VIP']
})
})
db.version(11).stores({
setting: 'id, theme, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode,' +
'searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck, rootClassFilter, r18ClassFilter, classFilter, restoreWindowPositionAndSize, windowPositionAndSize, pauseWhenMinimize',
cachedMovies: '++id, [key+ids], site, name, detail, index, rate, hasUpdate'
}).upgrade(trans => {
trans.setting.toCollection().modify(setting => {
setting.restoreWindowPositionAndSize = false
setting.windowPositionAndSize = {
x: 0,
y: 0,
width: 1080,
height: 720
}
setting.pauseWhenMinimize = false
})
})
db.version(11).stores({
setting: 'id, theme, shortcut, view, volume, externalPlayer, searchGroup, excludeRootClasses, excludeR18Films, forwardTimeInSec, starViewMode, recommandationViewMode,' +
'searchViewMode, password, proxy, allowPassWhenIptvCheck, autocleanWhenIptvCheck, rootClassFilter, r18ClassFilter, classFilter, restoreWindowPositionAndSize,' +
'windowPositionAndSize, pauseWhenMinimize, sitesDataURL, defaultParseURL'
}).upgrade(trans => {
trans.setting.toCollection().modify(setting => {
setting.sitesDataURL = 'https://raw.iqiq.io/Hunlongyu/ZY-Player-Resources/main/Sites/20220713.json'
setting.defaultParseURL = 'https://jx.bpba.cc/?v='
})
})
db.version(12).stores({
sites: '++id, key, name, api, download, jiexiUrl, isActive, group, reverseOrder'
}).upgrade(trans => {
trans.sites.toCollection().modify(site => {
site.reverseOrder = false
})
})
db.on('populate', () => {
db.setting.bulkAdd(setting)
db.setting.bulkAdd(iniSetting)
db.sites.bulkAdd(sites)
db.shortcut.bulkAdd(localKey)
db.iptv.bulkAdd(iptv)

View File

@@ -8,6 +8,7 @@ import search from './search'
import iptv from './iptv'
import channelList from './channelList'
import recommendation from './recommendation'
import cachedMovies from './cachedMovies'
export {
history,
@@ -19,5 +20,6 @@ export {
iptv,
channelList,
search,
recommendation
recommendation,
cachedMovies
}

View File

@@ -0,0 +1,82 @@
[{
"id": 0,
"theme": "light",
"shortcut": true,
"view": "picture",
"externalPlayer": "",
"searchGroup": "全站",
"excludeRootClasses": true,
"excludeR18Films": true,
"forwardTimeInSec": 5,
"waitingTimeInSec": 15,
"starViewMode": "picture",
"recommendationViewMode": "picture",
"historyViewMode": "picture",
"searchViewMode": "picture",
"password": "",
"proxy": {
"type": "none",
"scheme": "",
"url": "",
"port": ""
},
"allowPassWhenIptvCheck": true,
"autocleanWhenIptvCheck": false,
"autoChangeSourceWhenIptvStalling": true,
"shortcutModified": false,
"sitesDataURL": "https://raw.iqiq.io/Hunlongyu/ZY-Player-Resources/main/Sites/20220713.json",
"rootClassFilter": [
"电影",
"电影片",
"电视剧",
"连续剧",
"综艺",
"动漫"
],
"r18ClassFilter": [
"伦理",
"论理",
"倫理",
"福利",
"激情",
"理论",
"写真",
"情色",
"美女",
"街拍",
"赤足",
"性感",
"里番",
"VIP"
],
"classFilter": [
"电影",
"电影片",
"电视剧",
"连续剧",
"综艺",
"动漫",
"伦理",
"论理",
"倫理",
"福利",
"激情",
"理论",
"写真",
"情色",
"美女",
"街拍",
"赤足",
"性感",
"里番",
"VIP"
],
"restoreWindowPositionAndSize": false,
"windowPositionAndSize": {
"x": 0,
"y": 0,
"width": 1080,
"height": 720
},
"pauseWhenMinimize": false
}]

View File

@@ -0,0 +1,112 @@
[
{
"name": "playAndPause",
"desc": "播放或暂停",
"key": "space"
},
{
"name": "forward",
"desc": "快进",
"key": "right"
},
{
"name": "back",
"desc": "快退",
"key": "left"
},
{
"name": "volumeUp",
"desc": "音量调高",
"key": "up"
},
{
"name": "volumeDown",
"desc": "音量调低",
"key": "down"
},
{
"name": "mute",
"desc": "静音",
"key": "m"
},
{
"name": "top",
"desc": "置顶或退出置顶",
"key": "t"
},
{
"name": "fullscreen",
"desc": "进入或退出全屏",
"key": "f"
},
{
"name": "escape",
"desc": "退出全屏/精简模式",
"key": "esc"
},
{
"name": "next",
"desc": "下一集",
"key": "alt+right"
},
{
"name": "prev",
"desc": "上一集",
"key": "alt+left"
},
{
"name": "home",
"desc": "跳到视频开始位置",
"key": "home"
},
{
"name": "end",
"desc": "跳到视频结束位置",
"key": "end"
},
{
"name": "startPosition",
"desc": "标记片头",
"key": "ctrl+home"
},
{
"name": "endPosition",
"desc": "标记片尾",
"key": "ctrl+end"
},
{
"name": "clearPosition",
"desc": "清除标记",
"key": "ctrl+del"
},
{
"name": "opacityUp",
"desc": "透明度调高",
"key": "alt+up"
},
{
"name": "opacityDown",
"desc": "透明度调低",
"key": "alt+down"
},
{
"name": "playbackRateUp",
"desc": "播放倍速加快",
"key": "pageup"
},
{
"name": "playbackRateDown",
"desc": "播放倍速减慢",
"key": "pagedown"
},
{
"name": "mini",
"desc": "进入或退出mini模式",
"key": "alt+m"
},
{
"name": "resetMini",
"desc": "重置mini窗口",
"key": "ctrl+0"
}
]

View File

@@ -1,145 +1,3 @@
const setting = [
{
id: 0,
theme: 'light',
shortcut: true,
view: 'picture',
externalPlayer: '',
searchGroup: '全站',
excludeRootClasses: true,
excludeR18Films: true,
forwardTimeInSec: 5,
waitingTimeInSec: 15,
starViewMode: 'picture',
recommendationViewMode: 'picture',
historyViewMode: 'picture',
searchViewMode: 'picture',
password: '',
proxy: {
type: 'none',
scheme: '',
url: '',
port: ''
},
allowPassWhenIptvCheck: true,
autocleanWhenIptvCheck: false,
autoChangeSourceWhenIptvStalling: true
}
]
const localKey = [
{
name: 'playAndPause',
desc: '播放或暂停',
key: 'space'
},
{
name: 'forward',
desc: '快进',
key: 'right'
},
{
name: 'back',
desc: '快退',
key: 'left'
},
{
name: 'volumeUp',
desc: '音量调高',
key: 'up'
},
{
name: 'volumeDown',
desc: '音量调低',
key: 'down'
},
{
name: 'mute',
desc: '静音',
key: 'm'
},
{
name: 'top',
desc: '置顶或退出置顶',
key: 't'
},
{
name: 'fullscreen',
desc: '进入或退出全屏',
key: 'f'
},
{
name: 'escape',
desc: '退出全屏/精简模式',
key: 'esc'
},
{
name: 'next',
desc: '下一集',
key: 'alt+right'
},
{
name: 'prev',
desc: '上一集',
key: 'alt+left'
},
{
name: 'home',
desc: '跳到视频开始位置',
key: 'home'
},
{
name: 'end',
desc: '跳到视频结束位置',
key: 'end'
},
{
name: 'startPosition',
desc: '标记片头',
key: 'ctrl+home'
},
{
name: 'endPosition',
desc: '标记片尾',
key: 'ctrl+end'
},
{
name: 'clearPosition',
desc: '清除标记',
key: 'ctrl+del'
},
{
name: 'opacityUp',
desc: '透明度调高',
key: 'alt+up'
},
{
name: 'opacityDown',
desc: '透明度调低',
key: 'alt+down'
},
{
name: 'playbackRateUp',
desc: '播放倍速加快',
key: 'pageup'
},
{
name: 'playbackRateDown',
desc: '播放倍速减慢',
key: 'pagedown'
},
{
name: 'mini',
desc: '进入或退出mini模式',
key: 'alt+m'
},
{
name: 'resetMini',
desc: '重置mini窗口',
key: 'ctrl+0'
}
]
const getSite = (key) => {
for (const i of sites) {
if (key === i.key) {
@@ -151,11 +9,13 @@ const getSite = (key) => {
const sites = require('./iniData/Sites.json')
const iptv = require('./iniData/Iptv.json')
const recommendations = require('./iniData/Recommendations.json')
const iniSetting = require('./iniData/iniSetting.json')
const localKey = require('./iniData/localKey.json')
export {
setting,
sites,
iptv,
recommendations,
iniSetting,
localKey,
getSite
}

View File

@@ -5,6 +5,12 @@ export default {
async find () {
return await setting.get({ id: 0 })
},
async bulkAdd (doc) {
return await setting.bulkAdd(doc)
},
async add (doc) {
return await setting.add(doc)
},
async update (docs) {
return await setting.update(0, docs)
}

View File

@@ -1,6 +1,8 @@
import Vue from 'vue'
import { Message, Button, Table, TableColumn, Tag, Input, InputNumber, Dialog, Form, FormItem, Switch, Select, Option, Checkbox, Autocomplete, Col, Tree, Divider, Progress, MessageBox } from 'element-ui'
import Plugin from 'v-fit-columns'
import { ButtonGroupPlugin } from 'bootstrap-vue'
Vue.use(ButtonGroupPlugin)
Vue.use(Button)
Vue.use(Col)
Vue.use(Table)

View File

@@ -11,7 +11,7 @@ import SocksProxyAgent from 'socks-proxy-agent'
// 要在设置中添加代理设置可参考https://stackoverflow.com/questions/37393248/how-connect-to-proxy-in-electron-webview
const http = require('http')
const https = require('http')
const { remote } = require('electron')
const remote = require('@electron/remote')
const win = remote.getCurrentWindow()
const session = win.webContents.session
const ElectronProxyAgent = require('electron-proxy-agent')
@@ -111,13 +111,16 @@ const zy = {
axios.get(url).then(res => {
const data = res.data
const json = parser.parse(data, this.xmlConfig)
const jsondata = json.rss === undefined ? json : json.rss
const jsondata = json?.rss === undefined ? json : json.rss
if (!jsondata?.class || !jsondata?.list) resolve()
const arr = []
if (jsondata.class) {
// 有些网站返回的分类名里会含有一串包含在{}内的字符串,移除掉
const regex = /\{.*\}/i
for (const i of jsondata.class.ty) {
const j = {
tid: i._id,
name: i._t
name: i._t.replace(regex, '')
}
arr.push(j)
}
@@ -216,16 +219,55 @@ const zy = {
axios.get(url, { timeout: 3000 }).then(res => {
const data = res.data
const json = parser.parse(data, this.xmlConfig)
const jsondata = json.rss === undefined ? json : json.rss
const jsondata = json?.rss === undefined ? json : json.rss
if (json && jsondata && jsondata.list) {
let videoList = jsondata.list.video
if (Object.prototype.toString.call(videoList) === '[object Object]') videoList = [].concat(videoList)
videoList = videoList.filter(e => e.name.toLowerCase().includes(wd.toLowerCase()))
if (videoList.length) {
videoList = videoList?.filter(e => e.name.toLowerCase().includes(wd.toLowerCase()))
if (videoList?.length) {
resolve(videoList)
} else {
resolve()
}
} else {
resolve()
}
}).catch(err => {
reject(err)
})
}).catch(err => {
reject(err)
})
})
},
/**
* 搜索资源详情
* @param {*} key 资源网 key
* @param {*} wd 搜索关键字
* @returns
*/
searchFirstDetail (key, wd) {
return new Promise((resolve, reject) => {
this.getSite(key).then(res => {
const site = res
const url = `${site.api}?wd=${encodeURI(wd)}`
axios.get(url, { timeout: 3000 }).then(res => {
const data = res.data
const json = parser.parse(data, this.xmlConfig)
const jsondata = json?.rss === undefined ? json : json.rss
if (json && jsondata && jsondata.list) {
let videoList = jsondata.list.video
if (Object.prototype.toString.call(videoList) === '[object Object]') videoList = [].concat(videoList)
videoList = videoList?.filter(e => e.name.toLowerCase().includes(wd.toLowerCase()))
if (videoList?.length) {
this.detail(key, videoList[0].id).then(detailRes => {
resolve(detailRes)
})
} else {
resolve()
}
} else {
resolve()
}
}).catch(err => {
reject(err)
@@ -248,8 +290,9 @@ const zy = {
axios.get(url).then(res => {
const data = res.data
const json = parser.parse(data, this.xmlConfig)
const jsondata = json.rss === undefined ? json : json.rss
const videoList = jsondata.list.video
const jsondata = json?.rss === undefined ? json : json.rss
const videoList = jsondata?.list?.video
if (!videoList) resolve()
// Parse video lists
let fullList = []
let index = 0
@@ -491,17 +534,36 @@ const zy = {
})
})
},
get7kParseURL () {
/**
* 获取豆瓣相关视频推荐列表
* @param {*} name 视频名称
* @param {*} year 视频年份
* @returns 豆瓣相关视频推荐列表
*/
doubanRecommendations (name, year) {
return new Promise((resolve, reject) => {
axios.get('https://zy.7kjx.com/').then(res => {
const $ = cheerio.load(res.data)
const parseURL = $('body > div.container > div > div.stui-pannel > div.col-pd > p:contains("解析接口:")').first().find('a').text()
resolve(parseURL)
}).catch(err => { reject(err) })
const nameToSearch = name.replace(/\s/g, '')
const recommendations = []
this.doubanLink(nameToSearch, year).then(link => {
if (link.includes('https://www.douban.com/search')) {
resolve(recommendations)
} else {
axios.get(link).then(response => {
const $ = cheerio.load(response.data)
$('div.recommendations-bd').find('div>dl>dd>a').each(function (index, element) {
recommendations.push($(element).text())
})
resolve(recommendations)
}).catch(err => {
reject(err)
})
}
}).catch(err => {
reject(err)
})
})
},
getDefaultSites () {
const url = 'https://gitee.com/cuiocean/ZY-Player-Resources/raw/main/Sites/Sites.json'
getDefaultSites (url) {
return new Promise((resolve, reject) => {
axios.get(url).then(res => {
resolve(res.data)

2060
yarn.lock

File diff suppressed because it is too large Load Diff