mirror of
https://github.com/cuiocean/ZY-Player.git
synced 2026-02-14 16:06:48 +08:00
Compare commits
105 Commits
v2.7.1-Hap
...
v2.7.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17601935f1 | ||
|
|
0f85e65e26 | ||
|
|
8eb4ca5090 | ||
|
|
d6fcd151e3 | ||
|
|
711637ac8b | ||
|
|
1769fb9780 | ||
|
|
40de0337be | ||
|
|
b0fbdeef15 | ||
|
|
f75d961aaa | ||
|
|
12e2500fd7 | ||
|
|
e405225e02 | ||
|
|
dcba96a773 | ||
|
|
9910daa7c0 | ||
|
|
5ac57092db | ||
|
|
8864a624f5 | ||
|
|
72ae7494d2 | ||
|
|
877b564322 | ||
|
|
52562a1a12 | ||
|
|
43ff812b21 | ||
|
|
a3a26e0deb | ||
|
|
289f3c3c2d | ||
|
|
b580ae4329 | ||
|
|
8ae32f0f55 | ||
|
|
713ffa6b3e | ||
|
|
a3bc8f1f31 | ||
|
|
22318f601b | ||
|
|
aa8b4b527d | ||
|
|
7bee4df2f9 | ||
|
|
7a72b352c0 | ||
|
|
f924c6979b | ||
|
|
ef2740d801 | ||
|
|
0650e868eb | ||
|
|
573f026207 | ||
|
|
f9504439b0 | ||
|
|
59f9772e82 | ||
|
|
2db48c9220 | ||
|
|
63c6ad0ec0 | ||
|
|
730181e4bc | ||
|
|
7d61bb04a7 | ||
|
|
613901872e | ||
|
|
ef3264f3db | ||
|
|
56d5858e0f | ||
|
|
3f4da7806a | ||
|
|
00aa3c8408 | ||
|
|
a9d889f7d8 | ||
|
|
b946423b65 | ||
|
|
f2c87a7880 | ||
|
|
0ecc1367d2 | ||
|
|
9673303fe9 | ||
|
|
88582b45b3 | ||
|
|
443cdc59fc | ||
|
|
c08ae7666c | ||
|
|
6bfeb9fcd8 | ||
|
|
e3bc519128 | ||
|
|
504b11ceec | ||
|
|
d281a2adab | ||
|
|
4e0f73a3de | ||
|
|
3a78425e7f | ||
|
|
a38b9aed56 | ||
|
|
ec4980a4b6 | ||
|
|
2c93d755c7 | ||
|
|
fe75084dd0 | ||
|
|
047224ce80 | ||
|
|
31ea52e267 | ||
|
|
088cd70e41 | ||
|
|
6bfd96942d | ||
|
|
5d6579326c | ||
|
|
334933ce82 | ||
|
|
9fd3e5e2ed | ||
|
|
1e7199af3f | ||
|
|
0d83b277e7 | ||
|
|
08cbd1a73c | ||
|
|
01e58f458b | ||
|
|
3ac21ad1e5 | ||
|
|
f098198448 | ||
|
|
35e6e59b73 | ||
|
|
bf9eaf09eb | ||
|
|
03ec267c1f | ||
|
|
97aea7b98d | ||
|
|
f8ad00b4d0 | ||
|
|
607d4baae4 | ||
|
|
51208892d0 | ||
|
|
516b791719 | ||
|
|
8032645c25 | ||
|
|
9d0536a3f8 | ||
|
|
85a39d67ce | ||
|
|
3184147910 | ||
|
|
9d8a09e90d | ||
|
|
5d08e715aa | ||
|
|
98378788fd | ||
|
|
af768d527a | ||
|
|
4e5aab6f66 | ||
|
|
fda305a8ae | ||
|
|
21487e2754 | ||
|
|
0f934413d0 | ||
|
|
96c68da8b7 | ||
|
|
0e0de8f23c | ||
|
|
2b24ac7d0c | ||
|
|
458144a6ea | ||
|
|
69113c7a9a | ||
|
|
d247a2fa23 | ||
|
|
c14c78b68f | ||
|
|
cc4e2c5cad | ||
|
|
073e111af8 | ||
|
|
f134785696 |
@@ -42,9 +42,8 @@
|
||||
|
||||
### 🌴 下载
|
||||
|
||||
- 🍓 [Github -- 官方下载](https://github.com/Hunlongyu/ZY-Player/releases)
|
||||
- 🍉 [蓝奏云 -- 快速下载](https://www.lanzoux.com/b04s6a3re) 密码:95px
|
||||
- 🍒 适用于32位操作系统的x86软件,在蓝奏云网盘里, 后缀名: ZY Player * 32位.exe
|
||||
- 🎃 软件暂时关闭下载通道. 请大家支持正版.
|
||||
- 🎭 所有资源来自网上, 该软件不参与任何制作, 上传, 储存等内容, 禁止传播违法资源. 该软件仅供学习参考, 请于安装后24小时内删除.
|
||||
|
||||
### 🎠 平台
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@
|
||||
<li><i class="bx bx-chevron-right"></i> <a target="_blank" href="#">Home</a></li>
|
||||
<li><i class="bx bx-chevron-right"></i> <a target="_blank" href="https://www.ghpym.com/zyplayer.html">果核剥壳</a></li>
|
||||
<li><i class="bx bx-chevron-right"></i> <a target="_blank" href="https://www.iplaysoft.com/zy-player.html">异次元软件世界</a></li>
|
||||
<li><i class="bx bx-chevron-right"></i> <a target="_blank" href="https://xydh.fun/">炫辕</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
42
package.json
42
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zy",
|
||||
"version": "2.7.1-HappyNewYear",
|
||||
"version": "2.7.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
@@ -19,54 +19,54 @@
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"cheerio": "^1.0.0-rc.5",
|
||||
"core-js": "^3.8.1",
|
||||
"core-js": "^3.9.0",
|
||||
"dexie": "^3.0.3",
|
||||
"electron-localshortcut": "^3.2.1",
|
||||
"electron-proxy-agent": "^1.2.0",
|
||||
"electron-updater": "^4.3.5",
|
||||
"element-ui": "^2.14.1",
|
||||
"fast-xml-parser": "^3.17.5",
|
||||
"element-ui": "^2.15.0",
|
||||
"fast-xml-parser": "^3.18.0",
|
||||
"html2canvas": "^1.0.0-rc.7",
|
||||
"iptv-playlist-parser": "^0.5.1",
|
||||
"iptv-playlist-parser": "^0.6.0",
|
||||
"m3u": "0.0.2",
|
||||
"m3u8-parser": "^4.5.0",
|
||||
"m3u8-parser": "^4.5.2",
|
||||
"memcached": "^2.2.2",
|
||||
"modern-normalize": "^1.0.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"pinyin-match": "^1.1.7",
|
||||
"pinyin-match": "^1.2.0",
|
||||
"qrcode.vue": "^1.7.0",
|
||||
"randomstring": "^1.1.5",
|
||||
"session": "^0.1.0",
|
||||
"sortablejs": "^1.12.0",
|
||||
"sortablejs": "^1.13.0",
|
||||
"v-fit-columns": "^0.2.0",
|
||||
"vue": "^2.6.12",
|
||||
"vue-infinite-loading": "^2.4.5",
|
||||
"vue-waterfall-plugin": "^1.1.0",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuex": "^3.6.0",
|
||||
"xgplayer": "^2.16.0",
|
||||
"xgplayer-flv.js": "^2.1.2",
|
||||
"xgplayer-hls.js": "^2.3.0"
|
||||
"vuex": "^3.6.2",
|
||||
"xgplayer": "2.17.13",
|
||||
"xgplayer-flv.js": "^2.2.0",
|
||||
"xgplayer-hls.js": "^2.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.9",
|
||||
"@vue/cli-plugin-eslint": "~4.5.9",
|
||||
"@vue/cli-plugin-vuex": "~4.5.9",
|
||||
"@vue/cli-service": "~4.5.9",
|
||||
"@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.1.1",
|
||||
"electron": "^11.3.0",
|
||||
"electron-devtools-installer": "^3.1.1",
|
||||
"eslint": "^7.16.0",
|
||||
"eslint": "^7.20.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-promise": "^4.3.1",
|
||||
"eslint-plugin-standard": "^4.1.0",
|
||||
"eslint-plugin-vue": "^7.3.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.5",
|
||||
"vue-cli-plugin-electron-builder": "2.0.0-rc.6",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,6 @@
|
||||
.el-select-dropdown__item.selected.hover{ //是上游的bug吗?临时性修补
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.el-select-dropdown__wrap{
|
||||
max-height: 574px
|
||||
}
|
||||
@@ -284,7 +283,8 @@
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
&:hover {
|
||||
top: -3px;
|
||||
width: 102%;
|
||||
height: 102%
|
||||
}
|
||||
.img{
|
||||
position: relative;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
--l-fc-3: #823aa0;
|
||||
--l-bgc-1: #ffffff;
|
||||
--l-bgc-2: #f2f6f9;
|
||||
--l-bgc-3: #F9FBFC;
|
||||
--l-bsc: 0 1px 3px #8e8da233, 0 1px 2px #8e8da244;
|
||||
--l-bsc-hover: 0 14px 28px #8e8da255, 0 10px 10px #8e8da244;
|
||||
--l-bsc-2: 0 -4px 23px 0 #8e8da233;
|
||||
@@ -32,6 +33,7 @@
|
||||
--d-fc-3: #38dd77;
|
||||
--d-bgc-1: #222222;
|
||||
--d-bgc-2: #2f2f2f;
|
||||
--d-bgc-3: #292929;
|
||||
--d-bsc: 0 1px 3px #38dd7733, 0 1px 2px #38dd7744;
|
||||
--d-bsc-hover: 0 14px 28px #38dd7755, 0 10px 10px #38dd7744;
|
||||
--d-bsc-2: 0 -4px 23px 0 #38dd7733;
|
||||
@@ -50,6 +52,7 @@
|
||||
--g-fc-3: #C1D95C;
|
||||
--g-bgc-1: #4baea0;
|
||||
--g-bgc-2: #74b4ac;
|
||||
--g-bgc-3: #60B1A6;
|
||||
--g-bsc: 0 1px 3px #e1ebe033, 0 1px 2px #e1ebe044;
|
||||
--g-bsc-hover: 0 14px 28px #e1ebe055, 0 10px 10px #e1ebe044;
|
||||
--g-bsc-2: 0 -4px 23px 0 #e1ebe033;
|
||||
@@ -68,6 +71,7 @@
|
||||
--p-fc-3: #177ea7;
|
||||
--p-bgc-1: #ff8499;
|
||||
--p-bgc-2: #fea1b2;
|
||||
--p-bgc-3: #FF93A6;
|
||||
--p-bsc: 0 1px 3px #ef528533, 0 1px 2px #ef528544;
|
||||
--p-bsc-hover: 0 14px 28px #ef528555, 0 10px 10px #ef528544;
|
||||
--p-bsc-2: 0 -4px 23px 0 #ef528533;
|
||||
|
||||
@@ -167,6 +167,21 @@
|
||||
}
|
||||
}
|
||||
.box{
|
||||
.title{
|
||||
.right{
|
||||
color: var(--d-fc-2);
|
||||
svg{
|
||||
stroke: var(--d-c-5);
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
&:hover{
|
||||
stroke: var(--d-c-8);
|
||||
stroke-width: 1.5;
|
||||
fill: var(--d-c-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.more{
|
||||
span{
|
||||
svg{
|
||||
@@ -446,7 +461,7 @@
|
||||
.show-picture{
|
||||
color: var(--d-fc-1);
|
||||
.card{
|
||||
background-color: var(--d-bgc-1);
|
||||
background-color: var(--d-bgc-3);
|
||||
box-shadow: var(--d-bsc);
|
||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||
&:hover{
|
||||
|
||||
@@ -161,6 +161,21 @@
|
||||
}
|
||||
}
|
||||
.box{
|
||||
.title{
|
||||
.right{
|
||||
color: var(--g-fc-2);
|
||||
svg{
|
||||
stroke: var(--g-c-5);
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
&:hover{
|
||||
stroke: var(--g-c-8);
|
||||
stroke-width: 1.5;
|
||||
fill: var(--g-c-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.more{
|
||||
span{
|
||||
svg{
|
||||
@@ -434,7 +449,7 @@
|
||||
.show-picture{
|
||||
color: var(--g-fc-1);
|
||||
.card{
|
||||
background-color: var(--g-bgc-1);
|
||||
background-color: var(--g-bgc-3);
|
||||
box-shadow: var(--g-bsc);
|
||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||
&:hover{
|
||||
|
||||
@@ -161,6 +161,21 @@
|
||||
}
|
||||
}
|
||||
.box{
|
||||
.title{
|
||||
.right{
|
||||
color: var(--l-fc-2);
|
||||
svg{
|
||||
stroke: var(--l-c-5);
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
&:hover{
|
||||
stroke: var(--l-c-8);
|
||||
stroke-width: 1.5;
|
||||
fill: var(--l-c-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.more{
|
||||
span{
|
||||
svg{
|
||||
@@ -434,7 +449,7 @@
|
||||
.show-picture{
|
||||
color: var(--l-fc-1);
|
||||
.card{
|
||||
background-color: var(--l-bgc-1);
|
||||
background-color: var(--l-bgc-3);
|
||||
box-shadow: var(--l-bsc);
|
||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||
&:hover{
|
||||
|
||||
@@ -161,6 +161,21 @@
|
||||
}
|
||||
}
|
||||
.box{
|
||||
.title{
|
||||
.right{
|
||||
color: var(--p-fc-2);
|
||||
svg{
|
||||
stroke: var(--p-c-5);
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
&:hover{
|
||||
stroke: var(--p-c-8);
|
||||
stroke-width: 1.5;
|
||||
fill: var(--p-c-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.more{
|
||||
span{
|
||||
svg{
|
||||
@@ -434,7 +449,7 @@
|
||||
.show-picture{
|
||||
color: var(--p-fc-1);
|
||||
.card{
|
||||
background-color: var(--p-bgc-1);
|
||||
background-color: var(--p-bgc-3);
|
||||
box-shadow: var(--p-bsc);
|
||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||
&:hover{
|
||||
|
||||
@@ -39,7 +39,7 @@ function createWindow () {
|
||||
// 修改request headers
|
||||
// Sec-Fetch下禁止修改,浏览器自动加上请求头 https://www.cnblogs.com/fulu/p/13879080.html 暂时先用index.html的meta referer policy替代
|
||||
const filter = {
|
||||
urls: ['http://*/*', 'http://*/*']
|
||||
urls: ['http://*/*', 'http://*/*']
|
||||
}
|
||||
win.webContents.session.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
|
||||
const url = new URL(details.url)
|
||||
@@ -47,7 +47,7 @@ function createWindow () {
|
||||
if (!details.url.includes('//localhost') && details.requestHeaders.Referer && details.requestHeaders.Referer.includes('//localhost')) {
|
||||
details.requestHeaders.Referer = url.origin
|
||||
}
|
||||
callback({
|
||||
callback({ // https://github.com/electron/electron/issues/23988 回调似乎无法修改headers,暂时先用index.html的meta referer policy替代
|
||||
cancel: false,
|
||||
requestHeaders: details.requestHeaders
|
||||
})
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
import { mapMutations } from 'vuex'
|
||||
export default {
|
||||
name: 'Aside',
|
||||
data () {
|
||||
return {
|
||||
lastViewOpenDetail: '',
|
||||
savedDetail: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
view: {
|
||||
get () {
|
||||
@@ -74,9 +80,19 @@ export default {
|
||||
methods: {
|
||||
...mapMutations(['SET_VIEW', 'SET_DETAIL']),
|
||||
changeView (e) {
|
||||
// 记录打开detail的view
|
||||
if (this.detail.show === true) {
|
||||
this.lastViewOpenDetail = this.view
|
||||
this.savedDetail = this.detail
|
||||
}
|
||||
this.view = e
|
||||
this.detail = {
|
||||
show: false
|
||||
// 如果回到上一次打开detail的试图页面,恢复detail页面
|
||||
if (e === this.lastViewOpenDetail) {
|
||||
this.detail = this.savedDetail
|
||||
} else {
|
||||
this.detail = {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="operate">
|
||||
<span @click="playEvent(0)">播放</span>
|
||||
<span @click="playEvent(selectedEpisode)">播放</span>
|
||||
<span @click="starEvent">收藏</span>
|
||||
<span @click="downloadEvent">下载</span>
|
||||
<span @click="shareEvent">分享</span>
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
<div class="m3u8">
|
||||
<div class="box">
|
||||
<span v-for="(i, j) in videoList" :key="j" @click="playEvent(j)">{{i | ftName}}</span>
|
||||
<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>
|
||||
@@ -87,14 +87,19 @@ export default {
|
||||
videoFullList: [],
|
||||
info: {},
|
||||
playOnline: false,
|
||||
selectedEpisode: 0, // 选定集数
|
||||
selectedOnlineSite: '哔嘀',
|
||||
onlineSites: ['哔嘀', '素白白', '简影', '极品', '喜欢看', '1080影视']
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
ftName (e) {
|
||||
const name = e.split('$')[0]
|
||||
return name
|
||||
ftName (e, n) {
|
||||
const num = e.split('$')
|
||||
if (num.length > 1) {
|
||||
return e.split('$')[0]
|
||||
} else {
|
||||
return `第${(n + 1)}集`
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -256,7 +261,8 @@ export default {
|
||||
this.share = {
|
||||
show: true,
|
||||
key: this.detail.key,
|
||||
info: this.info
|
||||
info: this.info,
|
||||
index: this.selectedEpisode
|
||||
}
|
||||
},
|
||||
doubanLinkEvent () {
|
||||
@@ -276,7 +282,10 @@ export default {
|
||||
const id = this.detail.info.ids || this.detail.info.id
|
||||
const cacheKey = this.detail.key + '@' + id
|
||||
const db = await history.find({ site: this.detail.key, ids: id })
|
||||
if (db) this.videoFlag = db.videoFlag
|
||||
if (db) {
|
||||
this.videoFlag = db.videoFlag
|
||||
this.selectedEpisode = db.index
|
||||
}
|
||||
if (!this.DetailCache[cacheKey]) {
|
||||
this.DetailCache[cacheKey] = await zy.detail(this.detail.key, id)
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
<el-input v-model="siteInfo.download" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入Download接口地址,可以空着"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="解析接口" prop='jiexiUrl'>
|
||||
<el-input v-model="siteInfo.jiexiUrl" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入解析接口地址,可以空着"/>
|
||||
<el-input v-model="siteInfo.jiexiUrl" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入解析接口地址,默认源自带解析,若要调用应用默认解析接口请输入默认或default"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组" prop='group'>
|
||||
<el-select v-model="siteInfo.group" allow-create filterable default-first-option placeholder="请输入分组">
|
||||
@@ -119,7 +119,6 @@ import { mapMutations } from 'vuex'
|
||||
import { sites, setting } 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'
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
@@ -173,9 +172,9 @@ export default {
|
||||
},
|
||||
getFilters () {
|
||||
const groups = [...new Set(this.sites.map(site => site.group))]
|
||||
var filters = []
|
||||
const filters = []
|
||||
groups.forEach(g => {
|
||||
var doc = {
|
||||
const doc = {
|
||||
text: g,
|
||||
value: g
|
||||
}
|
||||
@@ -327,8 +326,8 @@ export default {
|
||||
if (!this.checkSiteKey()) {
|
||||
return false
|
||||
}
|
||||
var randomstring = require('randomstring')
|
||||
var doc = {
|
||||
const randomstring = require('randomstring')
|
||||
const doc = {
|
||||
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.length ? this.sites[this.sites.length - 1].id + 1 : 1,
|
||||
name: this.siteInfo.name,
|
||||
@@ -380,43 +379,76 @@ export default {
|
||||
}
|
||||
const options = {
|
||||
filters: [
|
||||
{ name: 'JSON file', extensions: ['json'] }
|
||||
{ name: '支持的文件格式', extensions: ['json', 'txt'] }
|
||||
],
|
||||
properties: ['openFile', 'multiSelections']
|
||||
}
|
||||
remote.dialog.showOpenDialog(options).then(result => {
|
||||
if (!result.canceled) {
|
||||
result.filePaths.forEach(file => {
|
||||
var str = fs.readFileSync(file)
|
||||
const json = JSON.parse(str)
|
||||
json.forEach(ele => {
|
||||
if (ele.api && this.sites.filter(x => x.key === ele.key).length === 0 && this.sites.filter(x => x.name === ele.name && x.api === ele.api).length === 0) {
|
||||
// 不含该key 同时也不含名字和url一样的
|
||||
if (ele.isActive === undefined) {
|
||||
ele.isActive = true
|
||||
if (file.endsWith('json')) {
|
||||
const str = fs.readFileSync(file)
|
||||
const json = JSON.parse(str)
|
||||
json.forEach(ele => {
|
||||
if (ele.api && this.sites.filter(x => x.key === ele.key).length === 0 && this.sites.filter(x => x.name === ele.name && x.api === ele.api).length === 0) {
|
||||
// 不含该key 同时也不含名字和url一样的
|
||||
if (ele.isActive === undefined) {
|
||||
ele.isActive = true
|
||||
}
|
||||
if (ele.group === undefined) {
|
||||
ele.group = '导入'
|
||||
}
|
||||
this.sites.push(ele)
|
||||
}
|
||||
if (ele.group === undefined) {
|
||||
ele.group = '导入'
|
||||
}
|
||||
this.sites.push(ele)
|
||||
})
|
||||
this.resetId(this.sites)
|
||||
sites.clear().then(sites.bulkAdd(this.sites))
|
||||
this.$message.success('导入成功')
|
||||
this.getSites()
|
||||
}
|
||||
if (file.endsWith('txt')) {
|
||||
try {
|
||||
const txt = fs.readFileSync(file, 'utf8')
|
||||
const json = JSON.parse(txt)
|
||||
json.forEach(ele => {
|
||||
if (ele.api && this.sites.filter(x => x.key === ele.key).length === 0 && this.sites.filter(x => x.name === ele.name && x.api === ele.api).length === 0) {
|
||||
// 不含该key 同时也不含名字和url一样的
|
||||
if (ele.isActive === undefined) {
|
||||
ele.isActive = true
|
||||
}
|
||||
if (ele.group === undefined) {
|
||||
ele.group = '导入'
|
||||
}
|
||||
this.sites.push(ele)
|
||||
}
|
||||
})
|
||||
this.resetId(this.sites)
|
||||
sites.clear().then(sites.bulkAdd(this.sites))
|
||||
this.$message.success('导入成功')
|
||||
this.getSites()
|
||||
} catch (error) {
|
||||
this.$message.warning('导入失败')
|
||||
}
|
||||
})
|
||||
this.resetId(this.sites)
|
||||
sites.clear().then(sites.bulkAdd(this.sites))
|
||||
this.$message.success('导入成功')
|
||||
this.getSites()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
resetSitesEvent () {
|
||||
this.stopFlag = true
|
||||
if (this.checkAllSitesLoading) {
|
||||
this.$message.info('部分检测还未完全终止, 请稍等...')
|
||||
return
|
||||
let url = this.setting.sitesDataURL
|
||||
if (!url) {
|
||||
// 如果没有设置源站文件链接,使用默认的gitee源
|
||||
url = 'https://gitee.com/cuiocean/ZY-Player-Resources/raw/main/Sites/Sites.json'
|
||||
}
|
||||
sites.clear().then(sites.bulkAdd(defaultSites).then(this.getSites()))
|
||||
this.$message.success('重置源成功')
|
||||
zy.getDefaultSites(url).then(res => {
|
||||
if (res.length > 0) {
|
||||
sites.clear().then(sites.bulkAdd(res))
|
||||
this.$message.success('重置源成功')
|
||||
this.getSites()
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$message.error('导入云端源站失败. ' + error)
|
||||
})
|
||||
},
|
||||
moveToTopEvent (i) {
|
||||
if (this.checkAllSitesLoading) {
|
||||
@@ -436,7 +468,7 @@ export default {
|
||||
sites.add(row)
|
||||
},
|
||||
resetId (inArray) {
|
||||
var id = 1
|
||||
let id = 1
|
||||
inArray.forEach(ele => {
|
||||
ele.id = id
|
||||
id += 1
|
||||
@@ -446,7 +478,7 @@ export default {
|
||||
// 因为el-table的数据是单向绑定,我们先同步el-table里的数据和其绑定的数据
|
||||
this.syncTableData()
|
||||
sites.clear().then(res => {
|
||||
var id = 1
|
||||
let id = 1
|
||||
this.sites.forEach(ele => {
|
||||
ele.id = id
|
||||
id += 1
|
||||
@@ -467,7 +499,7 @@ export default {
|
||||
return false
|
||||
}
|
||||
const tbody = document.getElementById('sites-table').querySelector('.el-table__body-wrapper tbody')
|
||||
var _this = this
|
||||
const _this = this
|
||||
Sortable.create(tbody, {
|
||||
onEnd ({ newIndex, oldIndex }) {
|
||||
const currRow = _this.sites.splice(oldIndex, 1)[0]
|
||||
|
||||
@@ -347,7 +347,7 @@ export default {
|
||||
searchGroup: '',
|
||||
searchGroups: [],
|
||||
// 福利片关键词
|
||||
r18KeyWords: ['伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番'],
|
||||
r18KeyWords: ['伦理', '论理', '倫理', '福利', '激情', '理论', '写真', '情色', '美女', '街拍', '赤足', '性感', '里番', 'VIP'],
|
||||
filteredList: [],
|
||||
areas: [],
|
||||
selectedAreas: [],
|
||||
@@ -457,6 +457,7 @@ export default {
|
||||
},
|
||||
searchContents: {
|
||||
handler (list) {
|
||||
list = list.filter(res => !this.setting.excludeR18Films || !this.containsR18Keywords(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()
|
||||
@@ -544,7 +545,7 @@ export default {
|
||||
return a.localeCompare(b, 'zh')
|
||||
},
|
||||
dateFormat (row, column) {
|
||||
var date = row[column.property]
|
||||
const date = row[column.property]
|
||||
if (date === undefined) {
|
||||
return ''
|
||||
}
|
||||
@@ -620,7 +621,7 @@ export default {
|
||||
// 屏蔽主分类
|
||||
const classToHide = ['电影', '电影片', '电视剧', '连续剧', '综艺', '动漫']
|
||||
zy.class(key).then(res => {
|
||||
var allClass = [{ name: '最新', tid: 0 }]
|
||||
const allClass = [{ name: '最新', tid: 0 }]
|
||||
res.class.forEach(element => {
|
||||
if (!this.setting.excludeRootClasses || !classToHide.includes(element.name)) {
|
||||
if (this.setting.excludeR18Films) {
|
||||
@@ -640,7 +641,7 @@ export default {
|
||||
})
|
||||
},
|
||||
containsR18Keywords (name) {
|
||||
var containKeyWord = false
|
||||
const containKeyWord = false
|
||||
if (!name) {
|
||||
return containKeyWord
|
||||
}
|
||||
@@ -656,12 +657,12 @@ export default {
|
||||
infiniteHandler ($state) {
|
||||
const key = this.site.key
|
||||
const typeTid = this.type.tid
|
||||
var page = this.pagecount
|
||||
let page = this.pagecount
|
||||
if (this.toFlipPagecount()) {
|
||||
page = this.totalpagecount - this.pagecount + 1
|
||||
}
|
||||
this.statusText = ' '
|
||||
if (key === undefined || page < 1 || typeTid === undefined) {
|
||||
if (key === undefined || page < 1 || page > this.totalpagecount || typeTid === undefined) {
|
||||
$state.complete()
|
||||
this.statusText = '暂无数据'
|
||||
return false
|
||||
@@ -675,10 +676,9 @@ export default {
|
||||
if (res) {
|
||||
this.pagecount -= 1
|
||||
const type = Object.prototype.toString.call(res)
|
||||
if (type === '[object Undefined]') {
|
||||
$state.complete()
|
||||
}
|
||||
if (type === '[object Array]') {
|
||||
// 过滤掉无链接的项
|
||||
res = res.filter(e => e.dl.dd && (e.dl.dd._t || (Object.prototype.toString.call(e.dl.dd) === '[object Array]' && e.dl.dd.some(i => i._t))))
|
||||
if (!this.toFlipPagecount()) {
|
||||
// zy.list 返回的是按时间从旧到新排列, 我门需要翻转为从新到旧
|
||||
this.list.push(...res.reverse())
|
||||
@@ -686,9 +686,10 @@ export default {
|
||||
// 如果是需要解析的视频网站,zy.list已经是按从新到旧排列
|
||||
this.list.push(...res)
|
||||
}
|
||||
}
|
||||
if (type === '[object Object]') {
|
||||
this.list.push(res)
|
||||
} else if (type === '[object Object]') {
|
||||
if (res.dl.dd && (res.dl.dd._t || (Object.prototype.toString.call(res.dl.dd) === '[object Array]' && res.dl.dd.some(e => e._t)))) {
|
||||
this.list.push(res)
|
||||
}
|
||||
}
|
||||
$state.loaded()
|
||||
// 更新缓存数据
|
||||
@@ -759,8 +760,8 @@ export default {
|
||||
})
|
||||
},
|
||||
querySearch (queryString, cb) {
|
||||
var searchList = this.searchList.slice(0, -1)
|
||||
var results = queryString ? searchList.filter(this.createFilter(queryString)) : this.searchList
|
||||
const searchList = this.searchList.slice(0, -1)
|
||||
const results = queryString ? searchList.filter(this.createFilter(queryString)) : this.searchList
|
||||
// 调用 callback 返回建议列表的数据
|
||||
cb(results)
|
||||
},
|
||||
@@ -818,20 +819,24 @@ export default {
|
||||
zy.detail(site.key, element.id).then(detailRes => {
|
||||
if (id !== this.searchID || !this.searchRunning) return
|
||||
detailRes.site = site
|
||||
this.searchContents.push(detailRes)
|
||||
this.searchContents.sort(function (a, b) {
|
||||
return a.site.id - b.site.id
|
||||
})
|
||||
if (this.isValidSearchResult(detailRes)) {
|
||||
this.searchContents.push(detailRes)
|
||||
this.searchContents.sort(function (a, b) {
|
||||
return a.site.id - b.site.id
|
||||
})
|
||||
}
|
||||
}).finally(() => { count++; if (count === res.length) { this.siteSearchCount++; this.statusText = '暂无数据' } })
|
||||
})
|
||||
} else if (type === '[object Object]') {
|
||||
zy.detail(site.key, res.id).then(detailRes => {
|
||||
if (id !== this.searchID || !this.searchRunning) return
|
||||
detailRes.site = site
|
||||
this.searchContents.push(detailRes)
|
||||
this.searchContents.sort(function (a, b) {
|
||||
return a.site.id - b.site.id
|
||||
})
|
||||
if (this.isValidSearchResult(detailRes)) {
|
||||
this.searchContents.push(detailRes)
|
||||
this.searchContents.sort(function (a, b) {
|
||||
return a.site.id - b.site.id
|
||||
})
|
||||
}
|
||||
}).finally(() => { this.siteSearchCount++; this.statusText = '暂无数据' })
|
||||
} else if (res === undefined) {
|
||||
this.siteSearchCount++
|
||||
@@ -841,6 +846,10 @@ export default {
|
||||
}).catch(() => { this.siteSearchCount++; if (this.searchGroup === '站内') this.$message.error('本次查询状态异常,未获取到数据!') })
|
||||
})
|
||||
},
|
||||
isValidSearchResult (detailRes) {
|
||||
return detailRes.dl.dd && (detailRes.dl.dd._t || (Object.prototype.toString.call(detailRes.dl.dd) === '[object Array]' &&
|
||||
detailRes.dl.dd.some(i => i._t)))
|
||||
},
|
||||
searchAndRecord () {
|
||||
this.addSearchRecord()
|
||||
this.searchEvent()
|
||||
|
||||
@@ -111,7 +111,6 @@
|
||||
import { mapMutations } from 'vuex'
|
||||
import { history, sites, setting } from '../lib/dexie'
|
||||
import zy from '../lib/site/tools'
|
||||
import Sortable from 'sortablejs'
|
||||
import { remote } from 'electron'
|
||||
import fs from 'fs'
|
||||
import Waterfall from 'vue-waterfall-plugin'
|
||||
@@ -212,6 +211,7 @@ export default {
|
||||
removeSelectedItems () {
|
||||
if (!this.multipleSelection.length) this.multipleSelection = this.history
|
||||
this.multipleSelection.forEach(e => history.remove(e.id))
|
||||
this.multipleSelection = []
|
||||
this.getAllhistory()
|
||||
this.updateDatabase()
|
||||
},
|
||||
@@ -278,8 +278,14 @@ export default {
|
||||
remote.dialog.showOpenDialog(options).then(result => {
|
||||
if (!result.canceled) {
|
||||
result.filePaths.forEach(file => {
|
||||
var str = fs.readFileSync(file)
|
||||
const str = fs.readFileSync(file)
|
||||
const json = JSON.parse(str)
|
||||
json.forEach(record => {
|
||||
if (record.detail && record.detail.m3u8List) {
|
||||
record.detail.fullList = [].concat({ flag: 'm3u8', list: record.detail.m3u8List })
|
||||
delete record.detail.m3u8List
|
||||
}
|
||||
})
|
||||
history.bulkAdd(json).then(res => {
|
||||
this.$message.success('导入成功')
|
||||
this.getAllhistory()
|
||||
@@ -299,7 +305,7 @@ export default {
|
||||
})
|
||||
},
|
||||
getSiteName (key) {
|
||||
var site = this.sites.find(e => e.key === key)
|
||||
const site = this.sites.find(e => e.key === key)
|
||||
if (site) {
|
||||
return site.name
|
||||
}
|
||||
@@ -313,7 +319,7 @@ export default {
|
||||
},
|
||||
updateDatabase () {
|
||||
history.clear().then(res => {
|
||||
var id = length
|
||||
let id = length
|
||||
this.history.forEach(ele => {
|
||||
ele.id = id
|
||||
id -= 1
|
||||
@@ -321,21 +327,8 @@ export default {
|
||||
})
|
||||
})
|
||||
},
|
||||
rowDrop () {
|
||||
if (!document.getElementById('history-table')) return
|
||||
const tbody = document.getElementById('history-table').querySelector('.el-table__body-wrapper tbody')
|
||||
const _this = this
|
||||
Sortable.create(tbody, {
|
||||
onEnd ({ newIndex, oldIndex }) {
|
||||
const currRow = _this.history.splice(oldIndex, 1)[0]
|
||||
_this.history.splice(newIndex, 0, currRow)
|
||||
_this.updateDatabase()
|
||||
}
|
||||
})
|
||||
},
|
||||
updateViewMode () {
|
||||
if (this.setting.historyViewMode === 'table') {
|
||||
setTimeout(() => { this.rowDrop() }, 100)
|
||||
this.showShiftPrompt()
|
||||
} else {
|
||||
setTimeout(() => { if (this.$refs.historyWaterfall) this.$refs.historyWaterfall.refresh() }, 700)
|
||||
@@ -358,7 +351,6 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.setting.historyViewMode === 'table') setTimeout(() => { this.rowDrop() }, 100)
|
||||
addEventListener('keydown', code => { if (code.keyCode === 16) this.shiftDown = true })
|
||||
addEventListener('keyup', code => { if (code.keyCode === 16) this.shiftDown = false })
|
||||
addEventListener('resize', () => {
|
||||
|
||||
@@ -113,6 +113,7 @@ import zy from '../lib/site/tools'
|
||||
import { remote } from 'electron'
|
||||
import fs from 'fs'
|
||||
import Sortable from 'sortablejs'
|
||||
import axios from 'axios'
|
||||
export default {
|
||||
name: 'iptv',
|
||||
data () {
|
||||
@@ -168,9 +169,9 @@ export default {
|
||||
},
|
||||
getFilters () {
|
||||
const groups = [...new Set(this.channelList.map(iptv => iptv.group))]
|
||||
var filters = []
|
||||
const filters = []
|
||||
groups.forEach(g => {
|
||||
var doc = {
|
||||
const doc = {
|
||||
text: g,
|
||||
value: g
|
||||
}
|
||||
@@ -257,7 +258,7 @@ export default {
|
||||
},
|
||||
mergeChannel () {
|
||||
if (this.inputContent && this.multipleSelection.length) {
|
||||
var channels = []
|
||||
let channels = []
|
||||
const id = this.multipleSelection[0].id
|
||||
this.multipleSelection.forEach(ele => {
|
||||
channels = channels.concat(ele.channels)
|
||||
@@ -274,8 +275,13 @@ export default {
|
||||
if (e.url) {
|
||||
this.video = { iptv: e }
|
||||
} else {
|
||||
const prefer = e.prefer ? e.channels.find(c => c.id === e.prefer) : e.channels.filter(c => c.isActive)[0]
|
||||
if (!prefer) return
|
||||
let prefer
|
||||
if (e.prefer) prefer = e.channels.find(c => c.id === e.prefer)
|
||||
if (!prefer) prefer = e.channels.filter(c => c.isActive)[0]
|
||||
if (!prefer) {
|
||||
this.$message.error('当前频道所有源已全部停用,不可播放!')
|
||||
return
|
||||
}
|
||||
this.video = { iptv: prefer }
|
||||
}
|
||||
this.view = 'Play'
|
||||
@@ -298,6 +304,7 @@ export default {
|
||||
ele.channels.splice(ele.channels.findIndex(e => e.id === row.id), 1)
|
||||
channelList.remove(row.channelID)
|
||||
if (ele.channels.length) {
|
||||
if (ele.prefer === row.id) delete ele.prefer
|
||||
if (ele.channels.length === 1) ele.hasChildren = false
|
||||
channelList.add(ele)
|
||||
this.$set(this.$refs.iptvTable.store.states.lazyTreeNodeMap, ele.id, ele.channels)
|
||||
@@ -320,7 +327,7 @@ export default {
|
||||
remote.dialog.showSaveDialog(options).then(result => {
|
||||
if (!result.canceled) {
|
||||
if (result.filePath.endsWith('m3u')) {
|
||||
var writer = require('m3u').extendedWriter()
|
||||
const writer = require('m3u').extendedWriter()
|
||||
this.iptvList.forEach(e => {
|
||||
writer.file(e.url, -1, e.name)
|
||||
})
|
||||
@@ -343,10 +350,55 @@ export default {
|
||||
this.$message.info('正在检测, 请勿操作.')
|
||||
return false
|
||||
}
|
||||
this.$msgbox.prompt('请输入网址', '提示', {
|
||||
distinguishCancelAndClose: true,
|
||||
inputValue: 'http://y.qibaobaike.com/nzy.txt',
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '本地文件'
|
||||
}).then(({ value }) => {
|
||||
this.importOnlineChannels(value)
|
||||
}).catch(action => {
|
||||
if (action === 'cancel') {
|
||||
this.importLocalChannels()
|
||||
}
|
||||
})
|
||||
},
|
||||
async importOnlineChannels (url) {
|
||||
try {
|
||||
const docs = []
|
||||
let id = this.channelList.length ? this.channelList.slice(-1)[0].id + 1 : 1
|
||||
const res = await axios.get(url)
|
||||
const result = res.data.split('\n')
|
||||
const supportFormats = /\.(m3u8|flv)$/
|
||||
for (const i of result) {
|
||||
if (i.includes('http') && supportFormats.test(i)) {
|
||||
const j = i.split(',')
|
||||
const doc = {
|
||||
id: id,
|
||||
name: j[0],
|
||||
url: j[1],
|
||||
isActive: true
|
||||
}
|
||||
id += 1
|
||||
docs.push(doc)
|
||||
}
|
||||
}
|
||||
// 获取url不重复的列表
|
||||
const uniqueList = [...new Map(docs.map(item => [item.url, item])).values()]
|
||||
iptv.clear().then(res => {
|
||||
iptv.bulkAdd(uniqueList).then(e => { // 支持导入同名频道,群里反馈
|
||||
this.updateChannelList()
|
||||
})
|
||||
})
|
||||
this.$message.success('导入成功')
|
||||
} catch (error) {
|
||||
this.$message.warning('导入失败')
|
||||
}
|
||||
},
|
||||
importLocalChannels () {
|
||||
const options = {
|
||||
filters: [
|
||||
{ name: 'm3u file', extensions: ['m3u', 'm3u8'] },
|
||||
{ name: 'JSON file', extensions: ['json'] }
|
||||
{ name: '支持的文件格式', extensions: ['m3u', 'm3u8', 'json', 'txt'] }
|
||||
],
|
||||
properties: ['openFile', 'multiSelections']
|
||||
}
|
||||
@@ -360,11 +412,12 @@ export default {
|
||||
const parser = require('iptv-playlist-parser')
|
||||
const playlist = fs.readFileSync(file, { encoding: 'utf-8' })
|
||||
const result = parser.parse(playlist)
|
||||
const supportFormats = /\.(m3u8|flv)$/
|
||||
result.items.forEach(ele => {
|
||||
const urls = ele.url.split('#').filter(e => e.startsWith('http')) // 网址带#时自动分割
|
||||
urls.forEach(url => {
|
||||
if (ele.name && url && new URL.URL(url).pathname.endsWith('.m3u8')) { // 网址可能带参数
|
||||
var doc = {
|
||||
if (ele.name && url && (supportFormats.test(url) || supportFormats.test(new URL.URL(url).pathname))) { // 网址可能带参数
|
||||
const doc = {
|
||||
id: id,
|
||||
name: ele.name,
|
||||
url: url,
|
||||
@@ -382,8 +435,9 @@ export default {
|
||||
this.updateChannelList()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// Import Json file
|
||||
this.$message.success('导入成功')
|
||||
}
|
||||
if (file.endsWith('json')) {
|
||||
const importedList = JSON.parse(fs.readFileSync(file))
|
||||
importedList.forEach(ele => {
|
||||
const commonEle = this.channelList.find(e => e.name === ele.name)
|
||||
@@ -397,9 +451,41 @@ export default {
|
||||
}
|
||||
})
|
||||
this.updateDatabase()
|
||||
this.$message.success('导入成功')
|
||||
}
|
||||
if (file.endsWith('txt')) {
|
||||
try {
|
||||
const docs = []
|
||||
let id = this.channelList.length ? this.channelList.slice(-1)[0].id + 1 : 1
|
||||
const playlist = fs.readFileSync(file, 'utf8')
|
||||
const result = playlist.split('\n')
|
||||
const supportFormats = /\.(m3u8|flv)$/
|
||||
for (const i of result) {
|
||||
if (i.includes('http') && supportFormats.test(i)) {
|
||||
const j = i.split(',')
|
||||
const doc = {
|
||||
id: id,
|
||||
name: j[0],
|
||||
url: j[1],
|
||||
isActive: true
|
||||
}
|
||||
id += 1
|
||||
docs.push(doc)
|
||||
}
|
||||
}
|
||||
// 获取url不重复的列表
|
||||
const uniqueList = [...new Map(docs.map(item => [item.url, item])).values()]
|
||||
iptv.clear().then(res => {
|
||||
iptv.bulkAdd(uniqueList).then(e => { // 支持导入同名频道,群里反馈
|
||||
this.updateChannelList()
|
||||
})
|
||||
})
|
||||
this.$message.success('导入成功')
|
||||
} catch (error) {
|
||||
this.$message.warning('导入失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
this.$message.success('导入成功')
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -438,12 +524,12 @@ export default {
|
||||
res = res.filter(o => !this.iptvList.find(e => o.url === e.url))
|
||||
const resClone = JSON.parse(JSON.stringify(res))
|
||||
const uniqueChannelName = {}
|
||||
for (var i = 0; i < resClone.length; i++) {
|
||||
var channelName = resClone[i].name.trim().replace(/[- ]?(1080p|蓝光|超清|高清|标清|hd|cq|4k)(\d{1,2})?$/i, '')
|
||||
for (let i = 0; i < resClone.length; i++) {
|
||||
let channelName = resClone[i].name.trim().replace(/[- ]?(1080p|蓝光|超清|高清|标清|hd|cq|4k)(\d{1,2})?$/i, '')
|
||||
if (channelName.match(/cctv/i)) channelName = channelName.replace('-', '')
|
||||
if (Object.keys(uniqueChannelName).some(name => channelName.match(new RegExp(`${name}(1080p|4k|(?!\\d))`, 'i')))) continue // 避免重复
|
||||
const matchRule = new RegExp(`${channelName}(1080p|4k|(?!\\d))`, 'i')
|
||||
for (var j = i; j < resClone.length; j++) {
|
||||
for (let j = i; j < resClone.length; j++) {
|
||||
if (resClone[j].name.match(/cctv/i)) {
|
||||
resClone[j].name = resClone[j].name.replace('-', '')
|
||||
}
|
||||
@@ -513,7 +599,7 @@ export default {
|
||||
})
|
||||
},
|
||||
resetId (channelList) {
|
||||
var id = 1
|
||||
let id = 1
|
||||
channelList.forEach(ele => {
|
||||
ele.id = id
|
||||
id += 1
|
||||
@@ -575,7 +661,7 @@ export default {
|
||||
})
|
||||
},
|
||||
async checkChannelsBySite (channels) {
|
||||
var siteList = {}
|
||||
const siteList = {}
|
||||
channels.forEach(channel => {
|
||||
const site = channel.url.split('/')[2]
|
||||
if (siteList[site]) {
|
||||
@@ -592,44 +678,50 @@ export default {
|
||||
await this.checkSingleChannel(c)
|
||||
}
|
||||
},
|
||||
async checkSingleChannel (channel) {
|
||||
if (this.setting.allowPassWhenIptvCheck && !channel.isActive) {
|
||||
async checkSingleChannel (channel, force = false) {
|
||||
if (this.stopFlag) {
|
||||
this.checkProgress += 1
|
||||
return
|
||||
}
|
||||
channel.status = ' '
|
||||
const ele = this.channelList.find(e => e.id === channel.channelID)
|
||||
if (this.stopFlag) {
|
||||
this.checkProgress += 1
|
||||
return channel.status
|
||||
}
|
||||
const flag = await zy.checkChannel(channel.url)
|
||||
this.checkProgress += 1
|
||||
ele.hasCheckedNum++
|
||||
if (flag) {
|
||||
channel.status = '可用'
|
||||
if (!force && this.setting.allowPassWhenIptvCheck && (!channel.isActive || !ele.isActive)) {
|
||||
if (!ele.isActive) {
|
||||
ele.status = '跳过'
|
||||
} else if (!channel.isActive) {
|
||||
channel.status = '跳过'
|
||||
}
|
||||
} else {
|
||||
channel.status = '失效'
|
||||
channel.isActive = false
|
||||
if (this.setting.autocleanWhenIptvCheck) {
|
||||
ele.channels.splice(ele.channels.findIndex(e => e.id === channel.id), 1)
|
||||
ele.hasCheckedNum--
|
||||
channel.status = ' '
|
||||
const flag = await zy.checkChannel(channel.url)
|
||||
if (flag) {
|
||||
channel.status = '可用'
|
||||
} else {
|
||||
channel.status = '失效'
|
||||
channel.isActive = false
|
||||
if (this.setting.autocleanWhenIptvCheck) {
|
||||
if (ele.prefer === channel.id) delete ele.prefer
|
||||
ele.channels.splice(ele.channels.findIndex(e => e.id === channel.id), 1)
|
||||
ele.hasCheckedNum--
|
||||
}
|
||||
}
|
||||
}
|
||||
this.checkProgress += 1
|
||||
ele.hasCheckedNum++
|
||||
if (ele.hasCheckedNum === ele.channels.length) {
|
||||
ele.status = ele.channels.some(channel => channel.status === '可用') ? '可用' : '失效'
|
||||
if (ele.status === '失效') ele.isActive = false
|
||||
if (ele.status === ' ') {
|
||||
ele.status = ele.channels.some(channel => channel.status === '可用') ? '可用' : '失效'
|
||||
if (ele.status === '失效') ele.isActive = false
|
||||
}
|
||||
channelList.remove(channel.channelID)
|
||||
if (ele.channels.length === 1) ele.hasChildren = false
|
||||
if (ele.channels.length) channelList.add(ele)
|
||||
}
|
||||
return channel.status
|
||||
},
|
||||
async checkChannel (row) {
|
||||
if (row.channels) {
|
||||
row.status = ' '
|
||||
row.hasCheckedNum = 0
|
||||
row.channels.forEach(e => this.checkSingleChannel(e))
|
||||
row.channels.forEach(e => this.checkSingleChannel(e, true))
|
||||
} else {
|
||||
this.checkSingleChannel(row)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
<div class="box">
|
||||
<div class="title">
|
||||
<span v-if="this.right.list.length > 1">『第 {{(video.info.index + 1)}} 集』</span>{{name}}
|
||||
<span class="right" @click="() => { onlineUrl = ''; videoStop(); }">
|
||||
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="closeIconTitle">
|
||||
<title id="closeIconTitle">关闭</title>
|
||||
<path d="M6.34314575 6.34314575L17.6568542 17.6568542M6.34314575 17.6568542L17.6568542 6.34314575"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="player" v-show="!onlineUrl">
|
||||
<div id="xgplayer"></div>
|
||||
@@ -44,7 +50,7 @@
|
||||
<polyline points="12 5 12 12 16 16"></polyline>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="zy-svg" @click="starEvent" :class="isStar ? 'active' : ''" v-show="right.list.length > 0">
|
||||
<span class="zy-svg" @click="starEvent" :class="isStar ? 'active' : ''" v-show="right.list.length > 0 || isStar">
|
||||
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-labelledby="favouriteIconTitle">
|
||||
<title id="favouriteIconTitle">收藏</title>
|
||||
<path d="M12,21 L10.55,19.7051771 C5.4,15.1242507 2,12.1029973 2,8.39509537 C2,5.37384196 4.42,3 7.5,3 C9.24,3 10.91,3.79455041 12,5.05013624 C13.09,3.79455041 14.76,3 16.5,3 C19.58,3 22,5.37384196 22,8.39509537 C22,12.1029973 18.6,15.1242507 13.45,19.7149864 L12,21 Z"></path>
|
||||
@@ -59,13 +65,13 @@
|
||||
<path d="M5 2h14a3 3 0 0 1 3 3v17H2V5a3 3 0 0 1 3-3z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="zy-svg" @click="miniEvent" v-show="right.list.length > 0">
|
||||
<span class="zy-svg" @click="miniEvent" v-show="!onlineUrl && 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">
|
||||
<span class="zy-svg" @click="playWithExternalPalyerEvent" v-show="!onlineUrl && 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>
|
||||
<polygon points="20 8 20 20 4 20 4 8"></polygon>
|
||||
@@ -89,7 +95,7 @@
|
||||
<rect x="17" y="6" width="1" height="1"></rect>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="zy-svg" @click="showShortcutEvent" :class="right.type === 'shortcut' ? 'active' : ''" v-show="right.list.length > 0">
|
||||
<span class="zy-svg" @click="showShortcutEvent" :class="right.type === 'shortcut' ? 'active' : ''" v-show="!onlineUrl && 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>
|
||||
@@ -211,7 +217,7 @@
|
||||
</div>
|
||||
</transition>
|
||||
<transition name="slideX">
|
||||
<div v-if="state.showChannelList" class="list" v-clickoutside="closeListEvent">
|
||||
<div v-if="state.showChannelList && channelList && channelList.length > 0" class="list" v-clickoutside="closeListEvent">
|
||||
<div class="list-top">
|
||||
<span class="list-top-title">频道列表</span>
|
||||
<span class="list-top-close zy-svg" @click="state.showChannelList = false">
|
||||
@@ -251,11 +257,11 @@ import FlvJsPlayer from 'xgplayer-flv.js'
|
||||
import mt from 'mousetrap'
|
||||
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 win = remote.getCurrentWindow()
|
||||
const PinyinMatch = require('pinyin-match')
|
||||
|
||||
const URL = require('url')
|
||||
const VIDEO_DETAIL_CACHE = {}
|
||||
|
||||
const addPlayerBtn = function (event, svg, attrs) {
|
||||
@@ -366,7 +372,8 @@ export default {
|
||||
currentShortcutList: [],
|
||||
onlineUrl: '',
|
||||
playerType: 'hls',
|
||||
exportablePlaylist: false
|
||||
exportablePlaylist: false,
|
||||
changingIPTV: false
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
@@ -453,6 +460,7 @@ export default {
|
||||
},
|
||||
video: {
|
||||
handler () {
|
||||
if (this.changingIPTV) return
|
||||
this.getUrls()
|
||||
},
|
||||
deep: true
|
||||
@@ -538,7 +546,7 @@ export default {
|
||||
const index = this.video.info.index || 0
|
||||
const db = await history.find({ site: this.video.key, ids: this.video.info.id })
|
||||
const key = this.video.key + '@' + this.video.info.id
|
||||
var time = this.video.info.time
|
||||
let time = this.video.info.time
|
||||
this.xg.removeAllProgressDot()
|
||||
this.startPosition = { min: '00', sec: '00' }
|
||||
this.endPosition = { min: '00', sec: '00' }
|
||||
@@ -572,10 +580,12 @@ export default {
|
||||
},
|
||||
playChannel (channel) {
|
||||
this.isLive = true
|
||||
this.getPlayer('hls')
|
||||
if (channel.channels) {
|
||||
let prefer
|
||||
this.right.sources = channel.channels.filter(e => e.isActive)
|
||||
channel = channel.prefer ? channel.channels.find(e => e.id === channel.prefer) : channel.channels.filter(e => e.isActive)[0]
|
||||
if (channel.prefer) prefer = channel.channels.find(e => e.id === channel.prefer)
|
||||
if (!prefer) prefer = channel.channels.filter(e => e.isActive)[0]
|
||||
channel = prefer
|
||||
} else {
|
||||
const ele = this.channelList.find(e => e.id === channel.channelID)
|
||||
ele.prefer = channel.id
|
||||
@@ -583,18 +593,34 @@ export default {
|
||||
channelList.add(ele)
|
||||
this.right.sources = ele.channels.filter(e => e.isActive)
|
||||
}
|
||||
this.changingIPTV = true // 避免二次执行playChannel
|
||||
this.video.iptv = channel
|
||||
this.name = channel.name
|
||||
const supportFormats = /\.(m3u8|flv)$/
|
||||
const extRE = channel.url.match(supportFormats) || new URL.URL(channel.url).pathname.match(supportFormats)
|
||||
this.getPlayer(extRE[1])
|
||||
if (extRE[1] === 'flv') this.xg.config.isLive = true
|
||||
this.xg.src = channel.url
|
||||
this.xg.play()
|
||||
document.querySelector('xg-btn-showhistory').style.display = 'none'
|
||||
document.querySelector('.xgplayer-playbackrate').style.display = 'none'
|
||||
this.changingIPTV = false
|
||||
setTimeout(() => {
|
||||
if (!document.getElementById('xgplayer').querySelector('video')) {
|
||||
this.getPlayer(this.playerType, true)
|
||||
this.playChannel(channel)
|
||||
}
|
||||
}, 1000)
|
||||
if (document.querySelector('xg-btn-showhistory')) document.querySelector('xg-btn-showhistory').style.display = 'none'
|
||||
if (document.querySelector('.xgplayer-playbackrate')) document.querySelector('.xgplayer-playbackrate').style.display = 'none'
|
||||
},
|
||||
getPlayer (playerType, force = false) {
|
||||
async getPlayer (playerType, force = false) {
|
||||
if (!force && this.playerType === playerType) return
|
||||
this.xg.src = ''
|
||||
this.config.url = ''
|
||||
this.xg.destroy()
|
||||
if (this.playerType !== 'flv') {
|
||||
this.xg.src = '' // https://developers.google.com/web/updates/2017/06/play-request-was-interrupted#danger-zone
|
||||
this.config.url = ''
|
||||
}
|
||||
try {
|
||||
if (this.xg) this.xg.destroy()
|
||||
} catch (err) { }
|
||||
this.xg = null
|
||||
switch (playerType) {
|
||||
case 'mp4':
|
||||
@@ -609,27 +635,29 @@ export default {
|
||||
this.playerInstall()
|
||||
this.bindEvent()
|
||||
this.playerType = playerType
|
||||
if (this.miniMode) { await this.saveMiniWindowState(); this.miniEvent() }
|
||||
},
|
||||
playVideo (index = 0, time = 0) {
|
||||
this.isLive = false
|
||||
this.isStar = false
|
||||
this.exportablePlaylist = false
|
||||
this.fetchPlaylist().then(async (fullList) => {
|
||||
var playlist = fullList[0].list // ZY支持的已移到首位
|
||||
let playlist = fullList[0].list // ZY支持的已移到首位
|
||||
// 如果设定了特定的video flag, 获取该flag下的视频列表
|
||||
const videoFlag = this.video.info.videoFlag
|
||||
if (videoFlag) {
|
||||
playlist = fullList.find(x => x.flag === videoFlag).list
|
||||
}
|
||||
this.right.list = playlist
|
||||
var url = playlist[index].split('$')[1]
|
||||
if (playlist.every(e => e.split('$')[1].endsWith('.m3u8'))) this.exportablePlaylist = true
|
||||
const url = playlist[index].includes('$') ? playlist[index].split('$')[1] : playlist[index]
|
||||
if (playlist.every(e => e.includes('$') ? e.split('$')[1].endsWith('.m3u8') : e.endsWith('.m3u8'))) this.exportablePlaylist = true
|
||||
if (!url.endsWith('.m3u8') && !url.endsWith('.mp4')) {
|
||||
const currentSite = await sites.find({ key: this.video.key })
|
||||
this.$message.info('即将调用解析接口播放,请等待...')
|
||||
if (currentSite.jiexiUrl) {
|
||||
this.onlineUrl = currentSite.jiexiUrl + url
|
||||
this.onlineUrl = /^\s*(default|默认)\s*$/i.test(currentSite.jiexiUrl) ? this.setting.defaultParseURL + url : currentSite.jiexiUrl + url
|
||||
} else {
|
||||
this.onlineUrl = 'https://jx.7kjx.com/?url=' + url
|
||||
this.onlineUrl = url
|
||||
}
|
||||
this.videoPlaying('online')
|
||||
return
|
||||
@@ -641,6 +669,12 @@ export default {
|
||||
const key = this.video.key + '@' + this.video.info.id
|
||||
const startTime = VIDEO_DETAIL_CACHE[key].startPosition || 0
|
||||
this.xg.play()
|
||||
setTimeout(() => {
|
||||
if (!document.getElementById('xgplayer').querySelector('video')) {
|
||||
this.getPlayer(this.playerType, true)
|
||||
this.getUrls()
|
||||
}
|
||||
}, 1000)
|
||||
if (document.querySelector('xg-btn-showhistory')) document.querySelector('xg-btn-showhistory').style.display = 'block'
|
||||
if (document.querySelector('.xgplayer-playbackrate')) document.querySelector('.xgplayer-playbackrate').style.display = 'inline-block'
|
||||
this.xg.once('playing', () => {
|
||||
@@ -677,7 +711,7 @@ export default {
|
||||
name: res.name
|
||||
})
|
||||
resolve(res.fullList)
|
||||
})
|
||||
}).catch(err => { this.$message.error('播放地址可能已失效,请换源并调整收藏', err); this.name = this.video.info.name; this.updateStar(); this.otherEvent() })
|
||||
} else {
|
||||
res = this.DetailCache[cacheKey]
|
||||
this.name = res.name
|
||||
@@ -770,13 +804,10 @@ export default {
|
||||
},
|
||||
prevEvent () {
|
||||
if (this.video.iptv) {
|
||||
var index = this.channelList.findIndex(obj => obj.id === this.video.iptv.channelID)
|
||||
if (index >= 1) {
|
||||
var channel = this.channelList[index - 1]
|
||||
this.playChannel(channel)
|
||||
} else {
|
||||
this.$message.warning('这已经是第一个频道了。')
|
||||
}
|
||||
let index = this.channelList.findIndex(obj => obj.id === this.video.iptv.channelID)
|
||||
index = index === 0 ? this.channelList.length - 1 : index - 1
|
||||
const channel = this.channelList[index]
|
||||
this.playChannel(channel)
|
||||
} else {
|
||||
if (this.video.info.index >= 1) {
|
||||
this.video.info.index--
|
||||
@@ -788,13 +819,10 @@ export default {
|
||||
},
|
||||
nextEvent () {
|
||||
if (this.video.iptv) {
|
||||
var index = this.channelList.findIndex(obj => obj.id === this.video.iptv.channelID)
|
||||
if (index < (this.channelList.length - 1)) {
|
||||
var channel = this.channelList[index + 1]
|
||||
this.playChannel(channel)
|
||||
} else {
|
||||
this.$message.warning('这已经是最后一个频道了。')
|
||||
}
|
||||
let index = this.channelList.findIndex(obj => obj.id === this.video.iptv.channelID)
|
||||
index = index === this.channelList.length - 1 ? 0 : index + 1
|
||||
const channel = this.channelList[index]
|
||||
this.playChannel(channel)
|
||||
} else {
|
||||
if (this.video.info.index < (this.right.list.length - 1)) {
|
||||
this.video.info.index++
|
||||
@@ -873,7 +901,7 @@ export default {
|
||||
}
|
||||
},
|
||||
async miniEvent () {
|
||||
this.mainWindowBounds = JSON.parse(JSON.stringify(win.getBounds()))
|
||||
if (!this.miniMode) this.mainWindowBounds = JSON.parse(JSON.stringify(win.getBounds()))
|
||||
let miniWindowBounds
|
||||
await mini.find().then(res => { if (res) miniWindowBounds = res.bounds })
|
||||
if (!miniWindowBounds) miniWindowBounds = { x: win.getPosition()[0], y: win.getPosition()[1], width: 550, height: 340 }
|
||||
@@ -882,7 +910,7 @@ export default {
|
||||
document.querySelector('xg-btn-quitMiniMode').style.display = 'block'
|
||||
this.miniMode = true
|
||||
},
|
||||
async exitMiniEvent () {
|
||||
async saveMiniWindowState () {
|
||||
await mini.find().then(res => {
|
||||
let doc = {}
|
||||
doc = {
|
||||
@@ -895,6 +923,9 @@ export default {
|
||||
mini.add(doc)
|
||||
}
|
||||
})
|
||||
},
|
||||
async exitMiniEvent () {
|
||||
await this.saveMiniWindowState()
|
||||
win.setBounds(this.mainWindowBounds)
|
||||
this.xg.exitCssFullscreen()
|
||||
document.querySelector('xg-btn-quitMiniMode').style.display = 'none'
|
||||
@@ -904,14 +935,16 @@ export default {
|
||||
this.share = {
|
||||
show: true,
|
||||
key: this.video.key,
|
||||
info: this.DetailCache[this.video.key + '@' + this.video.info.id]
|
||||
info: this.DetailCache[this.video.key + '@' + this.video.info.id],
|
||||
index: this.video.info.index
|
||||
}
|
||||
},
|
||||
issueEvent () {
|
||||
async issueEvent () {
|
||||
const currentSite = await sites.find({ key: this.video.key })
|
||||
const info = {
|
||||
video: this.video,
|
||||
list: this.right.list,
|
||||
playlist: VIDEO_DETAIL_CACHE[this.video.key + '@' + this.video.info.id] || [],
|
||||
video: Object.assign({ site: currentSite, detail: this.DetailCache[this.video.key + '@' + this.video.info.id] }, this.video.info),
|
||||
playlist: this.right.list.map(e => e.split('$')[1]),
|
||||
playerType: this.onlineUrl ? '在线解析' : this.playerType,
|
||||
playerError: this.xg.error || '',
|
||||
playerState: this.xg.readyState || '',
|
||||
networkState: this.xg.networkState || ''
|
||||
@@ -938,7 +971,7 @@ export default {
|
||||
this.$message.error('请设置第三方播放器路径')
|
||||
// 在线播放该视频
|
||||
if (playlistUrls[this.video.info.index].endsWith('.m3u8')) {
|
||||
var link = 'https://www.m3u8play.com/?play=' + playlistUrls[this.video.info.index]
|
||||
const link = 'http://hunlongyu.gitee.io/zy-player-web?url=' + playlistUrls[this.video.info.index] + '&name=' + this.video.info.name
|
||||
const open = require('open')
|
||||
open(link)
|
||||
}
|
||||
@@ -961,11 +994,11 @@ export default {
|
||||
const path = require('path')
|
||||
const os = require('os')
|
||||
const fs = require('fs')
|
||||
var filePath = path.join(os.tmpdir(), fileName + '.m3u')
|
||||
const filePath = path.join(os.tmpdir(), fileName + '.m3u')
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.unlinkSync(filePath)
|
||||
}
|
||||
var str = '#EXTM3U' + os.EOL
|
||||
let str = '#EXTM3U' + os.EOL
|
||||
for (let ind = startIndex; ind < m3u8Arr.length; ind++) {
|
||||
str += `#EXTINF: -1, 第${ind + 1}集` + os.EOL
|
||||
str += m3u8Arr[ind] + os.EOL
|
||||
@@ -1030,7 +1063,7 @@ export default {
|
||||
},
|
||||
listItemEvent (n) {
|
||||
if (this.video.iptv) {
|
||||
var channel = this.channelList[n]
|
||||
const channel = this.channelList[n]
|
||||
// 是直播源,直接播放
|
||||
this.playChannel(channel)
|
||||
} else {
|
||||
@@ -1392,7 +1425,7 @@ export default {
|
||||
this.channelTree = []
|
||||
const groups = [...new Set(this.channelList.map(iptv => iptv.group))]
|
||||
groups.forEach(g => {
|
||||
var doc = {
|
||||
const doc = {
|
||||
label: g,
|
||||
children: this.channelList.filter(x => x.group === g).map(i => { return { label: i.name, channel: i } })
|
||||
}
|
||||
@@ -1401,16 +1434,45 @@ export default {
|
||||
})
|
||||
},
|
||||
bindEvent () {
|
||||
// 直播卡顿时换源换台
|
||||
let stallIptvTimeout
|
||||
let stallCount = 0
|
||||
this.xg.on('waiting', () => {
|
||||
if (this.isLive && this.setting.autoChangeSourceWhenIptvStalling) {
|
||||
stallIptvTimeout = setTimeout(() => {
|
||||
let index = this.right.sources.indexOf(this.video.iptv) + 1
|
||||
if (index === this.right.sources.length) index = 0
|
||||
stallCount++
|
||||
if (stallCount >= this.right.sources.length) {
|
||||
stallCount = 0
|
||||
this.nextEvent()
|
||||
} else {
|
||||
this.playChannel(this.right.sources[index])
|
||||
}
|
||||
}, this.setting.waitingTimeInSec * 1000)
|
||||
}
|
||||
})
|
||||
this.xg.on('canplay', () => {
|
||||
stallCount = 0
|
||||
clearTimeout(stallIptvTimeout)
|
||||
})
|
||||
this.xg.on('destroy', () => {
|
||||
stallCount = 0
|
||||
clearTimeout(stallIptvTimeout)
|
||||
})
|
||||
|
||||
this.xg.on('exitFullscreen', () => {
|
||||
if (this.miniMode) this.xg.getCssFullscreen()
|
||||
})
|
||||
|
||||
this.xg.on('volumechange', () => {
|
||||
this.config.volume = this.xg.volume.toFixed(2)
|
||||
setting.find().then(res => { res.volume = this.config.volume; setting.update(res) })
|
||||
const volume = this.config.volume
|
||||
setTimeout(() => { if (volume === this.config.volume) setting.find().then(res => { res.volume = this.config.volume; setting.update(res) }) }, 500)
|
||||
})
|
||||
|
||||
this.xg.on('timeupdate', () => {
|
||||
if (this.isLive) return
|
||||
const key = this.video.key + '@' + this.video.info.id
|
||||
if (VIDEO_DETAIL_CACHE[key] && VIDEO_DETAIL_CACHE[key].endPosition) {
|
||||
const time = this.xg.duration - VIDEO_DETAIL_CACHE[key].endPosition - this.xg.currentTime
|
||||
@@ -1439,8 +1501,8 @@ export default {
|
||||
this.toggleHistory()
|
||||
})
|
||||
|
||||
this.xg.on('videoStop', () => {
|
||||
if (this.miniMode) this.exitMiniEvent()
|
||||
this.xg.on('videoStop', async () => {
|
||||
if (this.miniMode) await this.exitMiniEvent()
|
||||
this.videoStop()
|
||||
})
|
||||
|
||||
@@ -1471,6 +1533,7 @@ export default {
|
||||
})
|
||||
|
||||
this.xg.on('play', () => {
|
||||
clearTimeout(stallIptvTimeout)
|
||||
if (!this.video.key) {
|
||||
if (!this.video.iptv && !this.video.info.ids) {
|
||||
// 如果当前播放页面的播放信息没有被赋值,播放历史记录
|
||||
@@ -1479,7 +1542,7 @@ export default {
|
||||
this.videoStop()
|
||||
return
|
||||
}
|
||||
var historyItem = this.right.history[0]
|
||||
const historyItem = this.right.history[0]
|
||||
this.video = { key: historyItem.site, info: { id: historyItem.ids, name: historyItem.name, index: historyItem.index } }
|
||||
} else if (this.video.iptv && !this.isLive) {
|
||||
this.playChannel(this.video.iptv)
|
||||
@@ -1500,7 +1563,8 @@ export default {
|
||||
this.state.showTimespanSetting = false
|
||||
this.right.list = []
|
||||
this.getAllhistory()
|
||||
this.getPlayer(this.playerType, true)
|
||||
if (this.playerType === 'flv') this.xg.destroy()
|
||||
this.getPlayer('hls', true)
|
||||
},
|
||||
minMaxEvent () {
|
||||
win.on('minimize', () => {
|
||||
@@ -1677,12 +1741,6 @@ export default {
|
||||
.xgplayer-skin-default .xgplayer-playbackrate {
|
||||
width: 40px !important;
|
||||
}
|
||||
.xgplayer-skin-default .xgplayer-playbackrate .name {
|
||||
top: 10px !important;
|
||||
}
|
||||
.xgplayer-skin-default .xgplayer-playbackrate ul {
|
||||
bottom: 25px;
|
||||
}
|
||||
.xgplayer-skin-default .xgplayer-playbackrate ul li {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
@@ -1731,6 +1789,7 @@ export default {
|
||||
.right {
|
||||
float: right;
|
||||
svg {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ export default {
|
||||
}
|
||||
},
|
||||
filteredRecommendations () {
|
||||
var filteredData = this.recommendations.filter(x => (this.selectedAreas.length === 0) || this.selectedAreas.includes(x.detail.area))
|
||||
let filteredData = this.recommendations.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
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcut">
|
||||
<div class="shortcut" title="清理缓存后图片资源需重新下载,不建议清理,软件会根据磁盘空间动态管理缓存大小">
|
||||
<div class="title">缓存</div>
|
||||
<div class="shortcut-box">
|
||||
<div class="zy-select">
|
||||
<div class="vs-placeholder vs-noAfter" @click="clearCache">清理视频缓存</div>
|
||||
<div class="vs-placeholder vs-noAfter" @click="clearCache">清理缓存</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,6 +75,10 @@
|
||||
<div class="zy-input">
|
||||
<input type="checkbox" v-model = "d.autocleanWhenIptvCheck" @change="updateSettingEvent"> 检测时自动清理无效源
|
||||
</div>
|
||||
<div class="zy-input">
|
||||
<input type="checkbox" v-model = "d.autoChangeSourceWhenIptvStalling" @change="updateSettingEvent">
|
||||
卡顿时自动换源换台:<input style="width:50px" type="number" min=0 v-model.number = "d.waitingTimeInSec" @change="updateSettingEvent">秒
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="site">
|
||||
@@ -83,6 +87,12 @@
|
||||
<div class="zy-select">
|
||||
<div class="vs-placeholder vs-noAfter" @click="editSitesEvent">编辑源</div>
|
||||
</div>
|
||||
<div class="zy-select">
|
||||
<div class="vs-placeholder vs-noAfter" @click="show.configDefaultParseUrlDialog = true">设置默认解析接口</div>
|
||||
</div>
|
||||
<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>
|
||||
@@ -148,6 +158,34 @@
|
||||
<span>所有资源来自网上, 该软件不参与任何制作, 上传, 储存等内容, 禁止传播违法资源. 该软件仅供学习参考, 请于安装后24小时内删除.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div> <!-- 设置默认解析接口 -->
|
||||
<el-dialog :visible.sync="show.configDefaultParseUrlDialog" v-if='show.configDefaultParseUrlDialog' title="设置默认解析接口" :append-to-body="true" @close="closeDialog">
|
||||
<el-form label-width="45px" label-position="left">
|
||||
<el-form-item label="URL:">
|
||||
<el-input v-model="setting.defaultParseURL" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入解析接口地址,为空时会自动设置,重置时会自动更新默认接口地址"/>
|
||||
</el-form-item>
|
||||
</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="primary" @click="configDefaultParseURL">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div> <!-- 设置源站接口文件 -->
|
||||
<el-dialog :visible="show.configSitesDataUrlDialog" v-if='show.configSitesDataUrlDialog' title="设置源站接口文件" :append-to-body="true" @close="closeDialog">
|
||||
<el-form label-width="45px" label-position="left">
|
||||
<el-form-item label="URL:">
|
||||
<el-input v-model="setting.sitesDataURL" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入解析接口地址,为空时会自动设置,重置时会自动更新默认接口地址"/>
|
||||
</el-form-item>
|
||||
</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="primary" @click="configSitesDataURL">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div> <!-- 输入密码页面 -->
|
||||
<el-dialog :visible.sync="show.checkPasswordDialog" v-if='show.checkPasswordDialog' :append-to-body="true" @close="closeDialog" width="300px">
|
||||
<el-form label-width="75px" label-position="left">
|
||||
@@ -220,7 +258,7 @@
|
||||
import { mapMutations } from 'vuex'
|
||||
import pkg from '../../package.json'
|
||||
import { setting, sites, shortcut } from '../lib/dexie'
|
||||
import { sites as defaultSites, localKey as defaultShortcuts } from '../lib/dexie/initData'
|
||||
import { localKey as defaultShortcuts } from '../lib/dexie/initData'
|
||||
import { shell, clipboard, remote, ipcRenderer } from 'electron'
|
||||
import db from '../lib/dexie/dexie'
|
||||
import zy from '../lib/site/tools'
|
||||
@@ -238,7 +276,9 @@ export default {
|
||||
checkPasswordDialog: false,
|
||||
changePasswordDialog: false,
|
||||
proxy: false,
|
||||
proxyDialog: false
|
||||
proxyDialog: false,
|
||||
configDefaultParseUrlDialog: false,
|
||||
configSitesDataUrlDialog: false
|
||||
},
|
||||
d: { },
|
||||
latestVersion: pkg.version,
|
||||
@@ -287,13 +327,24 @@ export default {
|
||||
setting.find().then(res => {
|
||||
this.d = res
|
||||
this.setting = this.d
|
||||
if (!this.setting.defaultParseURL) this.configDefaultParseURL()
|
||||
if (!this.setting.sitesDataURL) this.getDefaultdeSitesDataURL()
|
||||
})
|
||||
},
|
||||
getDefaultSites () {
|
||||
zy.getDefaultSites(this.setting.sitesDataURL).then(res => {
|
||||
if (res.length > 0) {
|
||||
sites.clear().then(sites.bulkAdd(res))
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$message.error('获取云端源站失败. ' + error)
|
||||
})
|
||||
},
|
||||
getSites () {
|
||||
sites.all().then(res => {
|
||||
if (res.length <= 0) {
|
||||
this.$message.warning('检测到视频源未能正常加载, 即将重置源.')
|
||||
sites.clear().then(sites.bulkAdd(defaultSites).then(this.getSites()))
|
||||
this.getDefaultSites()
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -323,6 +374,29 @@ 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 configDefaultParseURL () {
|
||||
if (!this.setting.defaultParseURL) await this.get7kParseURL()
|
||||
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'
|
||||
},
|
||||
configSitesDataURL () {
|
||||
if (!this.setting.sitesDataURL) this.getDefaultdeSitesDataURL()
|
||||
this.d.sitesDataURL = this.setting.sitesDataURL
|
||||
this.show.configSitesDataUrlDialog = false
|
||||
this.updateSettingEvent()
|
||||
},
|
||||
selectLocalPlayer () {
|
||||
const options = {
|
||||
filters: [
|
||||
@@ -333,7 +407,7 @@ export default {
|
||||
}
|
||||
remote.dialog.showOpenDialog(options).then(result => {
|
||||
if (!result.canceled) {
|
||||
var playerPath = result.filePaths[0].replace(/\\/g, '/')
|
||||
const playerPath = result.filePaths[0].replace(/\\/g, '/')
|
||||
this.$message.success('设定第三方播放器路径为:' + result.filePaths[0])
|
||||
this.d.externalPlayer = playerPath
|
||||
this.updateSettingEvent()
|
||||
@@ -365,6 +439,8 @@ export default {
|
||||
async closeDialog () {
|
||||
this.show.checkPasswordDialog = false
|
||||
this.show.changePasswordDialog = false
|
||||
this.show.configDefaultParseUrlDialog = false
|
||||
this.show.configSitesDataUrlDialog = false
|
||||
if (this.show.proxyDialog) {
|
||||
this.show.proxyDialog = false
|
||||
this.setting.proxy.type = 'none'
|
||||
@@ -502,6 +578,7 @@ export default {
|
||||
ipcRenderer.send('downloadUpdate')
|
||||
ipcRenderer.on('update-downloaded', () => {
|
||||
this.update.downloaded = true
|
||||
this.$message.success('更新已下载完成!Mac用户须手动点击“安装”,其它系统会在退出后自动安装')
|
||||
})
|
||||
},
|
||||
installUpdate () {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="share" id="share" @click="shareClickEvent" v-clickoutside="shareClickEvent">
|
||||
<div class="left">
|
||||
<img :src="pic" alt="" @load="picLoadEvent">
|
||||
<img :src="share.info.pic" alt="">
|
||||
</div>
|
||||
<div class="right" id="right">
|
||||
<div class="title">{{ share.info.name }}</div>
|
||||
@@ -76,37 +76,33 @@ export default {
|
||||
info: {}
|
||||
}
|
||||
},
|
||||
async getUrl (dl) {
|
||||
const t = dl.dd._t
|
||||
if (t) {
|
||||
return t.split('#')[0].split('$')[1]
|
||||
} else {
|
||||
const id = this.share.info.ids || this.share.info.id
|
||||
const cacheKey = this.share.key + '@' + id
|
||||
let res = this.DetailCache[cacheKey]
|
||||
if (!this.DetailCache[cacheKey]) {
|
||||
res = await zy.detail(this.share.key, id)
|
||||
this.DetailCache[cacheKey] = res
|
||||
}
|
||||
if (res) {
|
||||
return res.fullList[0].list[0]
|
||||
}
|
||||
async getUrl (index) {
|
||||
const id = this.share.info.ids || this.share.info.id
|
||||
const cacheKey = this.share.key + '@' + id
|
||||
let res = this.DetailCache[cacheKey]
|
||||
if (!this.DetailCache[cacheKey]) {
|
||||
res = await zy.detail(this.share.key, id)
|
||||
this.DetailCache[cacheKey] = res
|
||||
}
|
||||
if (res) {
|
||||
const url = res.fullList[0].list[index]
|
||||
return url.includes('$') ? url.split('$')[1] : url
|
||||
}
|
||||
},
|
||||
async getDetail () {
|
||||
this.loading = true
|
||||
this.pic = this.share.info.pic
|
||||
const url = await this.getUrl(this.share.info.dl)
|
||||
const index = this.share.index || 0
|
||||
const url = await this.getUrl(index)
|
||||
this.link = 'http://hunlongyu.gitee.io/zy-player-web?url=' + url + '&name=' + this.share.info.name
|
||||
this.loading = false
|
||||
},
|
||||
picLoadEvent () {
|
||||
const dom = document.getElementById('share')
|
||||
html2canvas(dom).then(res => {
|
||||
const png = res.toDataURL('image/png')
|
||||
const p = nativeImage.createFromDataURL(png)
|
||||
clipboard.writeImage(p)
|
||||
this.$message.success('已复制到剪贴板,快去分享吧~ 严禁传播违法资源!!!')
|
||||
this.$nextTick(() => {
|
||||
const dom = document.getElementById('share')
|
||||
html2canvas(dom, { useCORS: true }).then(res => {
|
||||
const png = res.toDataURL('image/png')
|
||||
const p = nativeImage.createFromDataURL(png)
|
||||
clipboard.writeImage(p)
|
||||
this.$message.success('已复制到剪贴板,快去分享吧~ 严禁传播违法资源!!!')
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -201,6 +201,14 @@ export default {
|
||||
set (val) {
|
||||
this.SET_SETTING(val)
|
||||
}
|
||||
},
|
||||
DetailCache: {
|
||||
get () {
|
||||
return this.$store.getters.getDetailCache
|
||||
},
|
||||
set (val) {
|
||||
this.SET_DetailCache(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -269,9 +277,9 @@ export default {
|
||||
},
|
||||
async playEvent (e) {
|
||||
if (e.index) {
|
||||
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: e.index }, detail: e.detail }
|
||||
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: e.index } }
|
||||
} else {
|
||||
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 }, detail: e.detail }
|
||||
this.video = { key: e.key, info: { id: e.ids, name: e.name, index: 0 } }
|
||||
}
|
||||
if (e.hasUpdate) {
|
||||
this.clearHasUpdateFlag(e)
|
||||
@@ -306,21 +314,24 @@ export default {
|
||||
this.getFavorites()
|
||||
}
|
||||
},
|
||||
updateEvent (e) {
|
||||
zy.detail(e.key, e.ids).then(detailRes => {
|
||||
var doc = {
|
||||
async updateEvent (e) {
|
||||
try {
|
||||
if (!this.DetailCache[e.key + '@' + e.ids]) {
|
||||
this.DetailCache[e.key + '@' + e.ids] = await zy.detail(e.key, e.ids)
|
||||
}
|
||||
const doc = {
|
||||
id: e.id,
|
||||
key: e.key,
|
||||
ids: e.ids,
|
||||
site: e.site,
|
||||
name: e.name,
|
||||
detail: detailRes,
|
||||
detail: this.DetailCache[e.key + '@' + e.ids],
|
||||
index: e.index
|
||||
}
|
||||
star.get(e.id).then(resStar => {
|
||||
if (!e.hasUpdate && e.detail.last !== detailRes.last) {
|
||||
if (!e.hasUpdate && e.detail.last !== doc.detail.last) {
|
||||
doc.hasUpdate = true
|
||||
var msg = `同步"${e.name}"成功, 检查到更新。`
|
||||
const msg = `同步"${e.name}"成功, 检查到更新。`
|
||||
this.$message.success(msg)
|
||||
} else {
|
||||
this.numNoUpdate += 1
|
||||
@@ -328,10 +339,10 @@ export default {
|
||||
star.update(e.id, doc)
|
||||
this.getFavorites()
|
||||
})
|
||||
}).catch(err => {
|
||||
var msg = `同步"${e.name}"失败, 请重试。`
|
||||
} catch (err) {
|
||||
const msg = `同步"${e.name}"失败, 请重试。`
|
||||
this.$message.warning(msg, err)
|
||||
})
|
||||
}
|
||||
},
|
||||
updateAllEvent () {
|
||||
this.numNoUpdate = 0
|
||||
@@ -354,7 +365,7 @@ export default {
|
||||
if (row.site) {
|
||||
return row.site.name
|
||||
} else {
|
||||
var site = this.sites.find(e => e.key === row.key)
|
||||
const site = this.sites.find(e => e.key === row.key)
|
||||
if (site) {
|
||||
return site.name
|
||||
}
|
||||
@@ -406,10 +417,10 @@ export default {
|
||||
}
|
||||
remote.dialog.showOpenDialog(options).then(result => {
|
||||
if (!result.canceled) {
|
||||
var starList = Array.from(this.list)
|
||||
var id = this.list.length + 1
|
||||
const starList = Array.from(this.list)
|
||||
let id = this.list.length + 1
|
||||
result.filePaths.forEach(file => {
|
||||
var str = fs.readFileSync(file)
|
||||
const str = fs.readFileSync(file)
|
||||
const json = JSON.parse(str)
|
||||
json.reverse().forEach(ele => {
|
||||
const starExists = starList.some(x => x.key === ele.key && x.ids === ele.ids)
|
||||
@@ -424,7 +435,7 @@ export default {
|
||||
last: ele.last,
|
||||
note: ele.note
|
||||
}
|
||||
var doc = {
|
||||
const doc = {
|
||||
id: id,
|
||||
key: ele.key,
|
||||
ids: ele.ids,
|
||||
@@ -457,7 +468,7 @@ export default {
|
||||
updateDatabase () {
|
||||
this.syncTableData()
|
||||
star.clear().then(res => {
|
||||
var id = this.list.length
|
||||
let id = this.list.length
|
||||
this.list.forEach(ele => {
|
||||
ele.id = id
|
||||
id -= 1
|
||||
|
||||
@@ -48,6 +48,17 @@ db.version(7).stores({
|
||||
})
|
||||
})
|
||||
|
||||
db.version(8).stores({
|
||||
}).upgrade(trans => {
|
||||
trans.sites.toCollection().modify(site => {
|
||||
if (site.api.includes('7kjx.com')) site.jiexiUrl = 'default'
|
||||
})
|
||||
trans.setting.toCollection().modify(setting => {
|
||||
setting.waitingTimeInSec = 15
|
||||
setting.autoChangeSourceWhenIptvStalling = true
|
||||
})
|
||||
})
|
||||
|
||||
db.on('populate', () => {
|
||||
db.setting.bulkAdd(setting)
|
||||
db.sites.bulkAdd(sites)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,182 +1 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"key": "mahuazy",
|
||||
"name": "麻花资源",
|
||||
"api": "http://www.mhapi123.com/inc/ldg_api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"key": "1886zy",
|
||||
"name": "1886 资源",
|
||||
"api": "http://cj.1886zy.co/inc/api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"key": "123ku",
|
||||
"name": "123 资源",
|
||||
"api": "http://cj.123ku2.com:12315/inc/api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"key": "7K资源",
|
||||
"id": 4,
|
||||
"name": "7K资源",
|
||||
"api": "https://zy.7kjx.com/cjapi.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "https://jx.7kjx.com/?url=",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"key": "1717云资源网",
|
||||
"id": 5,
|
||||
"name": "1717云资源网",
|
||||
"api": "http://zy.itono.cn/inc/api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "https://www.1717yun.com/jiexi/?url=",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"key": "subo988",
|
||||
"name": "速播资源站",
|
||||
"api": "https://www.subo988.com/inc/api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"key": "88zyw",
|
||||
"name": "88 影视资源站",
|
||||
"api": "http://www.88zyw.net/inc/api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"key": "zuidazy",
|
||||
"id": 8,
|
||||
"name": "最大资源网",
|
||||
"api": "http://www.zdziyuan.com/inc/ldg_sea.php",
|
||||
"download": "http://www.zdziyuan.com/inc/apidown.php",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"key": "mbo",
|
||||
"id": 9,
|
||||
"name": "秒播资源",
|
||||
"api": "http://caiji.mb77.vip/inc/seacmsapi.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"key": "apibdzy",
|
||||
"name": "百度云资源",
|
||||
"api": "https://api.apibdzy.com/api.php/provide/vod/at/xml",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"key": "okzy",
|
||||
"name": "OK 资源网",
|
||||
"api": "http://cj.okzy.tv/inc/api.php",
|
||||
"download": "http://cj.okzy.tv/inc/apidown.php",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"key": "kuyunzy",
|
||||
"name": "酷云资源",
|
||||
"api": "http://caiji.kuyun98.com/inc/ldg_api.php",
|
||||
"download": "http://caiji.kuyun98.com/inc/apidown.php",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"key": "kubozy",
|
||||
"name": "酷播资源",
|
||||
"api": "http://api.kbzyapi.com/inc/api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"key": "yongjiuzy",
|
||||
"name": "永久资源",
|
||||
"api": "http://cj.yongjiuzyw.com/inc/api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"key": "rrzy",
|
||||
"name": "人人资源",
|
||||
"api": "https://www.rrzyw.cc/api.php/provide/vod/from/rrm3u8/at/xml/",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"key": "bbkdj",
|
||||
"name": "步步高顶尖资源网",
|
||||
"api": "http://api.bbkdj.com/api",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"key": "zuixinzy",
|
||||
"name": "最新资源",
|
||||
"api": "http://api.zuixinapi.com/inc/api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"key": "209zy",
|
||||
"name": "209 资源",
|
||||
"api": "http://cj.1156zy.com/inc/api.php",
|
||||
"download": "",
|
||||
"jiexiUrl": "",
|
||||
"group": "默认",
|
||||
"isActive": true
|
||||
}
|
||||
]
|
||||
[]
|
||||
|
||||
@@ -9,6 +9,7 @@ const setting = [
|
||||
excludeRootClasses: true,
|
||||
excludeR18Films: true,
|
||||
forwardTimeInSec: 5,
|
||||
waitingTimeInSec: 15,
|
||||
starViewMode: 'picture',
|
||||
recommendationViewMode: 'picture',
|
||||
historyViewMode: 'picture',
|
||||
@@ -21,7 +22,8 @@ const setting = [
|
||||
port: ''
|
||||
},
|
||||
allowPassWhenIptvCheck: true,
|
||||
autocleanWhenIptvCheck: false
|
||||
autocleanWhenIptvCheck: false,
|
||||
autoChangeSourceWhenIptvStalling: true
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import { Message, Button, Table, TableColumn, Tag, Input, InputNumber, Dialog, Form, FormItem, Switch, Select, Option, Checkbox, Autocomplete, Col, Tree, Divider, Progress } from 'element-ui'
|
||||
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'
|
||||
Vue.use(Button)
|
||||
Vue.use(Col)
|
||||
@@ -21,3 +21,4 @@ Vue.use(Tree)
|
||||
Vue.use(Divider)
|
||||
Vue.use(Progress)
|
||||
Vue.prototype.$message = Message
|
||||
Vue.prototype.$msgbox = MessageBox
|
||||
|
||||
@@ -35,29 +35,29 @@ const onlineVideo = {
|
||||
},
|
||||
playVideoOnBde4 (videoName, videoIndex) {
|
||||
videoName = videoName.replace(/\s/g, '')
|
||||
var url = `https://bde4.com/search/${videoName}`
|
||||
const url = `https://bde4.com/search/${videoName}`
|
||||
axios.get(url).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
var e = $('div.search-list')
|
||||
var searchResult = $(e).find('div>div>div>div>a').toArray()
|
||||
const e = $('div.search-list')
|
||||
const searchResult = $(e).find('div>div>div>div>a').toArray()
|
||||
// 获取第一个搜索结果的视频链接
|
||||
var detailPageLink = $(searchResult[0]).attr('href')
|
||||
const detailPageLink = $(searchResult[0]).attr('href')
|
||||
// 获取第一个搜索结果的title
|
||||
var title = $(searchResult[0]).attr('title')
|
||||
const title = $(searchResult[0]).attr('title')
|
||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||
// 如果第一个搜索结果不符合,打开搜索页面
|
||||
open(url)
|
||||
} else {
|
||||
var detailPageFullLink = 'https://bde4.com/' + detailPageLink
|
||||
const detailPageFullLink = 'https://bde4.com/' + detailPageLink
|
||||
// 解析详情页面
|
||||
axios.get(detailPageFullLink).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
var e = $('div.info1')
|
||||
var videoList = $(e).find('a').toArray()
|
||||
var videoFullLink = detailPageFullLink
|
||||
const d = $('div.info1')
|
||||
const videoList = $(d).find('a').toArray()
|
||||
let videoFullLink = detailPageFullLink
|
||||
// 获取index视频链接
|
||||
if (videoIndex < videoList.length) {
|
||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
if (indexVideoLink.includes('.htm')) {
|
||||
videoFullLink = 'https://bde4.com' + indexVideoLink
|
||||
}
|
||||
@@ -69,31 +69,31 @@ const onlineVideo = {
|
||||
},
|
||||
playVideoOnK1080 (videoName, videoIndex) {
|
||||
videoName = videoName.replace(/\s/g, '')
|
||||
var url = `https://k1080.net/vodsearch123/-------------.html?wd=${videoName}&submit=`
|
||||
const url = `https://k1080.net/vodsearch123/-------------.html?wd=${videoName}&submit=`
|
||||
axios.get(url).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
var e = $('#searchList')
|
||||
var searchResult = $(e).find('li>div>a').toArray()
|
||||
const e = $('#searchList')
|
||||
const searchResult = $(e).find('li>div>a').toArray()
|
||||
// 获取第一个搜索结果的视频链接
|
||||
var detailPageLink = $(searchResult[0]).attr('href')
|
||||
const detailPageLink = $(searchResult[0]).attr('href')
|
||||
// 获取第一个搜索结果的title
|
||||
var title = $(searchResult[0]).attr('title')
|
||||
const title = $(searchResult[0]).attr('title')
|
||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||
// 如果第一个搜索结果不符合,打开搜索页面
|
||||
open(url)
|
||||
} else {
|
||||
// 解析详情页面
|
||||
var detailPageFullLink = 'https://k1080.net' + detailPageLink
|
||||
const detailPageFullLink = 'https://k1080.net' + detailPageLink
|
||||
axios.get(detailPageFullLink).then(res2 => {
|
||||
const $ = cheerio.load(res2.data)
|
||||
// 获取playlist1
|
||||
var e = $('#playlist1')
|
||||
const d = $('#playlist1')
|
||||
// 获取所有视频链接
|
||||
var videoList = $(e).find('div>ul>li>a').toArray()
|
||||
var videoFullLink = detailPageFullLink
|
||||
const videoList = $(d).find('div>ul>li>a').toArray()
|
||||
let videoFullLink = detailPageFullLink
|
||||
// 获取index视频链接
|
||||
if (videoIndex < videoList.length) {
|
||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
if (indexVideoLink.includes('.htm')) {
|
||||
videoFullLink = 'https://k1080.net' + indexVideoLink
|
||||
}
|
||||
@@ -105,31 +105,31 @@ const onlineVideo = {
|
||||
},
|
||||
playVideoOnSubaibai (videoName, videoIndex) {
|
||||
videoName = videoName.replace(/\s/g, '')
|
||||
var url = `https://www.subaibai.com/xssearch?q=${videoName}`
|
||||
const url = `https://www.subaibai.com/xssearch?q=${videoName}`
|
||||
axios.get(url).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
var e = $('div.search_list')
|
||||
var searchResult = $(e).find('div>ul>li>h3>a').toArray()
|
||||
const e = $('div.search_list')
|
||||
const searchResult = $(e).find('div>ul>li>h3>a').toArray()
|
||||
// 获取第一个搜索结果的视频链接
|
||||
var detailPageLink = $(searchResult[0]).attr('href')
|
||||
const detailPageLink = $(searchResult[0]).attr('href')
|
||||
// 获取第一个搜索结果的title
|
||||
var title = $(searchResult[0]).text()
|
||||
const title = $(searchResult[0]).text()
|
||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||
// 如果第一个搜索结果不符合,打开搜索页面
|
||||
open(url)
|
||||
} else {
|
||||
// 解析详情页面
|
||||
var detailPageFullLink = detailPageLink
|
||||
const detailPageFullLink = detailPageLink
|
||||
axios.get(detailPageFullLink).then(res2 => {
|
||||
const $ = cheerio.load(res2.data)
|
||||
// 获取playlist1
|
||||
var e = $('div.paly_list_btn')
|
||||
const d = $('div.paly_list_btn')
|
||||
// 获取所有视频链接
|
||||
var videoList = $(e).find('a').toArray()
|
||||
const videoList = $(d).find('a').toArray()
|
||||
// 获取index视频链接
|
||||
var videoFullLink = detailPageFullLink
|
||||
let videoFullLink = detailPageFullLink
|
||||
if (videoIndex < videoList.length) {
|
||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
if (indexVideoLink.includes('.htm')) {
|
||||
videoFullLink = indexVideoLink
|
||||
}
|
||||
@@ -141,31 +141,31 @@ const onlineVideo = {
|
||||
},
|
||||
playVideoOnYhdm (videoName, videoIndex) {
|
||||
videoName = videoName.replace(/\s/g, '')
|
||||
var url = `http://www.yhdm.tv/search/${videoName}`
|
||||
const url = `http://www.yhdm.tv/search/${videoName}`
|
||||
axios.get(url).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
var e = $('div.lpic')
|
||||
var searchResult = $(e).find('div>ul>li>h2>a').toArray()
|
||||
const e = $('div.lpic')
|
||||
const searchResult = $(e).find('div>ul>li>h2>a').toArray()
|
||||
// 获取第一个搜索结果的视频链接
|
||||
var detailPageLink = $(searchResult[0]).attr('href')
|
||||
const detailPageLink = $(searchResult[0]).attr('href')
|
||||
// 获取第一个搜索结果的title
|
||||
var title = $(searchResult[0]).attr('title')
|
||||
const title = $(searchResult[0]).attr('title')
|
||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||
// 如果第一个搜索结果不符合,打开搜索页面
|
||||
open(url)
|
||||
} else {
|
||||
// 解析详情页面
|
||||
var detailPageFullLink = 'http://www.yhdm.tv/' + detailPageLink
|
||||
const detailPageFullLink = 'http://www.yhdm.tv/' + detailPageLink
|
||||
axios.get(detailPageFullLink).then(res2 => {
|
||||
const $ = cheerio.load(res2.data)
|
||||
// 获取playlist1
|
||||
var e = $('div.movurl')
|
||||
const d = $('div.movurl')
|
||||
// 获取所有视频链接
|
||||
var videoList = $(e).find('div>ul>li>a').toArray()
|
||||
const videoList = $(d).find('div>ul>li>a').toArray()
|
||||
// 获取index视频链接
|
||||
var videoFullLink = detailPageFullLink
|
||||
let videoFullLink = detailPageFullLink
|
||||
if (videoIndex < videoList.length) {
|
||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
if (indexVideoLink.includes('.htm')) {
|
||||
videoFullLink = 'http://www.yhdm.tv/' + indexVideoLink
|
||||
}
|
||||
@@ -177,31 +177,31 @@ const onlineVideo = {
|
||||
},
|
||||
playVideoOndmdm2020 (videoName, videoIndex) {
|
||||
videoName = videoName.replace(/\s/g, '')
|
||||
var url = `http://www.dmdm2020.com/dongmansearch.html?wd=${videoName}&submit=`
|
||||
const url = `http://www.dmdm2020.com/dongmansearch.html?wd=${videoName}&submit=`
|
||||
axios.get(url).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
var e = $('#searchList')
|
||||
var searchResult = $(e).find('ul>li>div>h4>a').toArray()
|
||||
const e = $('#searchList')
|
||||
const searchResult = $(e).find('ul>li>div>h4>a').toArray()
|
||||
// 获取第一个搜索结果的视频链接
|
||||
var detailPageLink = $(searchResult[0]).attr('href')
|
||||
const detailPageLink = $(searchResult[0]).attr('href')
|
||||
// 获取第一个搜索结果的title
|
||||
var title = $(searchResult[0]).text()
|
||||
const title = $(searchResult[0]).text()
|
||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||
// 如果第一个搜索结果不符合,打开搜索页面
|
||||
open(url)
|
||||
} else {
|
||||
// 解析详情页面
|
||||
var detailPageFullLink = 'http://www.dmdm2020.com' + detailPageLink
|
||||
const detailPageFullLink = 'http://www.dmdm2020.com' + detailPageLink
|
||||
axios.get(detailPageFullLink).then(res2 => {
|
||||
const $ = cheerio.load(res2.data)
|
||||
// 获取playlist1
|
||||
var e = $('#playlist1')
|
||||
const d = $('#playlist1')
|
||||
// 获取所有视频链接
|
||||
var videoList = $(e).find('div>ul>li>a').toArray()
|
||||
const videoList = $(d).find('div>ul>li>a').toArray()
|
||||
// 获取index视频链接
|
||||
var videoFullLink = detailPageFullLink
|
||||
let videoFullLink = detailPageFullLink
|
||||
if (videoIndex < videoList.length) {
|
||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
if (indexVideoLink.includes('.htm')) {
|
||||
videoFullLink = 'http://www.dmdm2020.com' + indexVideoLink
|
||||
}
|
||||
@@ -213,31 +213,31 @@ const onlineVideo = {
|
||||
},
|
||||
playVideoOnSyrme (videoName, videoIndex) {
|
||||
videoName = videoName.replace(/\s/g, '')
|
||||
var url = `https://syrme.top/searchs?q=${videoName}`
|
||||
const url = `https://syrme.top/searchs?q=${videoName}`
|
||||
axios.get(url).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
var e = $('ul.MovieList')
|
||||
var searchResult = $(e).find('ul>li>article>a').toArray()
|
||||
const e = $('ul.MovieList')
|
||||
const searchResult = $(e).find('ul>li>article>a').toArray()
|
||||
// 获取第一个搜索结果的视频链接
|
||||
var detailPageLink = $(searchResult[0]).attr('href')
|
||||
const detailPageLink = $(searchResult[0]).attr('href')
|
||||
// 获取第一个搜索结果的title
|
||||
var title = $(searchResult[0]).find('a>h2').text()
|
||||
const title = $(searchResult[0]).find('a>h2').text()
|
||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||
// 如果第一个搜索结果不符合,打开搜索页面
|
||||
open(url)
|
||||
} else {
|
||||
// 解析详情页面
|
||||
var detailPageFullLink = 'https://syrme.top' + detailPageLink
|
||||
const detailPageFullLink = 'https://syrme.top' + detailPageLink
|
||||
axios.get(detailPageFullLink).then(res2 => {
|
||||
const $ = cheerio.load(res2.data)
|
||||
// 获取playlist1
|
||||
var e = $('#categories-2')
|
||||
const d = $('#categories-2')
|
||||
// 获取所有视频链接
|
||||
var videoList = $(e).find('div>ul>li>a').toArray()
|
||||
const videoList = $(d).find('div>ul>li>a').toArray()
|
||||
// 获取index视频链接
|
||||
var videoFullLink = detailPageFullLink
|
||||
let videoFullLink = detailPageFullLink
|
||||
if (videoIndex < videoList.length) {
|
||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
videoFullLink = 'https://syrme.top' + indexVideoLink
|
||||
}
|
||||
open(videoFullLink)
|
||||
@@ -247,31 +247,31 @@ const onlineVideo = {
|
||||
},
|
||||
playVideoOnJpysvip (videoName, videoIndex) {
|
||||
videoName = videoName.replace(/\s/g, '')
|
||||
var url = `https://www.jpysvip.net/vodsearch/-------------.html?wd=${videoName}&submit=`
|
||||
const url = `https://www.jpysvip.net/vodsearch/-------------.html?wd=${videoName}&submit=`
|
||||
axios.get(url).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
var e = $('#searchList')
|
||||
var searchResult = $(e).find('ul>li>div>a').toArray()
|
||||
const e = $('#searchList')
|
||||
const searchResult = $(e).find('ul>li>div>a').toArray()
|
||||
// 获取第一个搜索结果的视频链接
|
||||
var detailPageLink = $(searchResult[0]).attr('href')
|
||||
const detailPageLink = $(searchResult[0]).attr('href')
|
||||
// 获取第一个搜索结果的title
|
||||
var title = $(searchResult[0]).attr('title')
|
||||
const title = $(searchResult[0]).attr('title')
|
||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||
// 如果第一个搜索结果不符合,打开搜索页面
|
||||
open(url)
|
||||
} else {
|
||||
// 解析详情页面
|
||||
var detailPageFullLink = 'https://www.jpysvip.net' + detailPageLink
|
||||
const detailPageFullLink = 'https://www.jpysvip.net' + detailPageLink
|
||||
axios.get(detailPageFullLink).then(res2 => {
|
||||
const $ = cheerio.load(res2.data)
|
||||
// 获取playlist1
|
||||
var e = $('#playlist1')
|
||||
const d = $('#playlist1')
|
||||
// 获取所有视频链接
|
||||
var videoList = $(e).find('div>ul>li>a').toArray()
|
||||
const videoList = $(d).find('div>ul>li>a').toArray()
|
||||
// 获取index视频链接
|
||||
var videoFullLink = detailPageFullLink
|
||||
let videoFullLink = detailPageFullLink
|
||||
if (videoIndex < videoList.length) {
|
||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
videoFullLink = 'https://www.jpysvip.net/' + indexVideoLink
|
||||
}
|
||||
open(videoFullLink)
|
||||
@@ -281,31 +281,31 @@ const onlineVideo = {
|
||||
},
|
||||
playVideoOnXhkan (videoName, videoIndex) {
|
||||
videoName = videoName.replace(/\s/g, '')
|
||||
var url = `https://www.xhkan.com/vodsearch.html?wd=${videoName}&submit=`
|
||||
const url = `https://www.xhkan.com/vodsearch.html?wd=${videoName}&submit=`
|
||||
axios.get(url).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
var e = $('#searchList')
|
||||
var searchResult = $(e).find('ul>li>div>a').toArray()
|
||||
const e = $('#searchList')
|
||||
const searchResult = $(e).find('ul>li>div>a').toArray()
|
||||
// 获取第一个搜索结果的视频链接
|
||||
var detailPageLink = $(searchResult[0]).attr('href')
|
||||
const detailPageLink = $(searchResult[0]).attr('href')
|
||||
// 获取第一个搜索结果的title
|
||||
var title = $(searchResult[0]).attr('title')
|
||||
const title = $(searchResult[0]).attr('title')
|
||||
if (title === null || title === undefined || !title.replace(/\s/g, '').includes(videoName)) {
|
||||
// 如果第一个搜索结果不符合,打开搜索页面
|
||||
open(url)
|
||||
} else {
|
||||
// 解析详情页面
|
||||
var detailPageFullLink = detailPageLink
|
||||
const detailPageFullLink = detailPageLink
|
||||
axios.get(detailPageFullLink).then(res2 => {
|
||||
const $ = cheerio.load(res2.data)
|
||||
// 获取playlist1
|
||||
var e = $('#playlist1')
|
||||
const d = $('#playlist1')
|
||||
// 获取所有视频链接
|
||||
var videoList = $(e).find('div>ul>li>a').toArray()
|
||||
const videoList = $(d).find('div>ul>li>a').toArray()
|
||||
// 获取index视频链接
|
||||
var videoFullLink = detailPageFullLink
|
||||
let videoFullLink = detailPageFullLink
|
||||
if (videoIndex < videoList.length) {
|
||||
var indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
const indexVideoLink = $(videoList[videoIndex]).attr('href')
|
||||
videoFullLink = indexVideoLink
|
||||
}
|
||||
open(videoFullLink)
|
||||
|
||||
@@ -3,23 +3,32 @@ import axios from 'axios'
|
||||
import parser from 'fast-xml-parser'
|
||||
import cheerio from 'cheerio'
|
||||
import { Parser as M3u8Parser } from 'm3u8-parser'
|
||||
// import FLVDemuxer from 'xgplayer-flv.js/src/flv/demux/flv-demuxer.js'
|
||||
import SocksProxyAgent from 'socks-proxy-agent'
|
||||
|
||||
// axios使用系统代理 https://evandontje.com/2020/04/02/automatic-system-proxy-configuration-for-electron-applications/
|
||||
// xgplayer使用chromium代理设置,浏览器又默认使用系统代理 https://www.chromium.org/developers/design-documents/network-settings
|
||||
// 要在设置中添加代理设置,可参考https://stackoverflow.com/questions/37393248/how-connect-to-proxy-in-electron-webview
|
||||
var http = require('http')
|
||||
var https = require('http')
|
||||
const http = require('http')
|
||||
const https = require('http')
|
||||
const { remote } = require('electron')
|
||||
var win = remote.getCurrentWindow()
|
||||
var session = win.webContents.session
|
||||
var ElectronProxyAgent = require('electron-proxy-agent')
|
||||
const win = remote.getCurrentWindow()
|
||||
const session = win.webContents.session
|
||||
const ElectronProxyAgent = require('electron-proxy-agent')
|
||||
const URL = require('url')
|
||||
const request = require('request')
|
||||
let proxyURL
|
||||
|
||||
// 取消axios请求 浅析cancelToken https://juejin.cn/post/6844904168277147661 https://masteringjs.io/tutorials/axios/cancel
|
||||
// const source = axios.CancelToken.source()
|
||||
// const cancelToken = source.token
|
||||
|
||||
// 请求超时时限
|
||||
// axios.defaults.timeout = 10000 // 可能使用代理,增长超时
|
||||
const TIMEOUT = 6000
|
||||
const TIMEOUT = 20000
|
||||
|
||||
// 重试次数,共请求3次
|
||||
axios.defaults.retry = 2
|
||||
// 重试次数,共请求2次
|
||||
axios.defaults.retry = 1
|
||||
|
||||
// 请求的间隙
|
||||
axios.defaults.retryDelay = 1000
|
||||
@@ -42,7 +51,7 @@ axios.interceptors.response.use(function (response) {
|
||||
}, function (err) { // 请求错误时做些事
|
||||
// 请求超时的之后,抛出 err.code = ECONNABORTED的错误..错误信息是 timeout of xxx ms exceeded
|
||||
if (err.code === 'ECONNABORTED' && err.message.indexOf('timeout') !== -1) {
|
||||
var config = err.config
|
||||
const config = err.config
|
||||
config.__retryCount = config.__retryCount || 0
|
||||
|
||||
if (config.__retryCount >= config.retry) {
|
||||
@@ -52,7 +61,7 @@ axios.interceptors.response.use(function (response) {
|
||||
|
||||
config.__retryCount += 1
|
||||
|
||||
var backoff = new Promise(function (resolve) {
|
||||
const backoff = new Promise(function (resolve) {
|
||||
setTimeout(function () {
|
||||
resolve()
|
||||
}, config.retryDelay || 1)
|
||||
@@ -99,7 +108,7 @@ const zy = {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.getSite(key).then(res => {
|
||||
const url = res.api
|
||||
axios.post(url).then(res => {
|
||||
axios.get(url).then(res => {
|
||||
const data = res.data
|
||||
const json = parser.parse(data, this.xmlConfig)
|
||||
const jsondata = json.rss === undefined ? json : json.rss
|
||||
@@ -144,7 +153,7 @@ const zy = {
|
||||
} else {
|
||||
url = `${site.api}?ac=videolist&pg=${pg}`
|
||||
}
|
||||
axios.post(url).then(async res => {
|
||||
axios.get(url).then(async res => {
|
||||
const data = res.data
|
||||
const json = parser.parse(data, this.xmlConfig)
|
||||
const jsondata = json.rss === undefined ? json : json.rss
|
||||
@@ -176,7 +185,7 @@ const zy = {
|
||||
} else {
|
||||
url = `${site.api}?ac=videolist`
|
||||
}
|
||||
axios.post(url).then(async res => {
|
||||
axios.get(url).then(async res => {
|
||||
const data = res.data.match(/<list [^>]*>/)[0] + '</list>' // 某些源站不含页码时获取到的数据parser无法解析
|
||||
const json = parser.parse(data, this.xmlConfig)
|
||||
const jsondata = json.rss === undefined ? json : json.rss
|
||||
@@ -203,15 +212,20 @@ const zy = {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.getSite(key).then(res => {
|
||||
const site = res
|
||||
wd = encodeURI(wd)
|
||||
var url = `${site.api}?wd=${wd}`
|
||||
axios.post(url, { timeout: 3000 }).then(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) {
|
||||
const videoList = jsondata.list.video
|
||||
resolve(videoList)
|
||||
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) {
|
||||
resolve(videoList)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
reject(err)
|
||||
@@ -231,20 +245,21 @@ const zy = {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.getSite(key).then(res => {
|
||||
const url = `${res.api}?ac=videolist&ids=${id}`
|
||||
axios.post(url).then(res => {
|
||||
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
|
||||
// Parse video lists
|
||||
var fullList = []
|
||||
let fullList = []
|
||||
let index = 0
|
||||
const supportedFormats = ['m3u8', 'mp4']
|
||||
const dd = videoList.dl.dd
|
||||
const type = Object.prototype.toString.call(dd)
|
||||
if (type === '[object Array]') {
|
||||
for (const i of dd) {
|
||||
const ext = Array.from(new Set(...i._t.split('#').map(e => e.split('$')[1].match(/\.\w+?$/)))).map(e => e.slice(1))
|
||||
i._t = i._t.replace(/\$+/g, '$')
|
||||
const ext = Array.from(new Set(...i._t.split('#').map(e => e.includes('$') ? e.split('$')[1].match(/\.\w+?$/) : e.match(/\.\w+?$/)))).map(e => e.slice(1))
|
||||
if (ext.length && ext.length <= supportedFormats.length && ext.every(e => supportedFormats.includes(e))) {
|
||||
if (ext.length === 1) {
|
||||
i._flag = ext[0]
|
||||
@@ -256,7 +271,7 @@ const zy = {
|
||||
fullList.push(
|
||||
{
|
||||
flag: i._flag,
|
||||
list: i._t.split('#').filter(e => e && e.split('$')[1] && e.split('$')[1].startsWith('http'))
|
||||
list: i._t.split('#').filter(e => e && (e.startsWith('http') || (e.split('$')[1] && e.split('$')[1].startsWith('http'))))
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -264,10 +279,13 @@ const zy = {
|
||||
fullList.push(
|
||||
{
|
||||
flag: dd._flag,
|
||||
list: dd._t.split('#').filter(e => e && e.split('$')[1] && e.split('$')[1].startsWith('http'))
|
||||
list: dd._t.replace(/\$+/g, '$').split('#').filter(e => e && (e.startsWith('http') || (e.split('$')[1] && e.split('$')[1].startsWith('http'))))
|
||||
}
|
||||
)
|
||||
}
|
||||
fullList.forEach(item => {
|
||||
if (item.list.every(e => e.includes('$') && /^\s*\d+\s*$/.test(e.split('$')[0]))) item.list.sort((a, b) => { return a.split('$')[0] - b.split('$')[0] })
|
||||
})
|
||||
if (fullList.length > 1) { // 将ZY支持的播放列表前置
|
||||
index = fullList.findIndex(e => supportedFormats.includes(e.flag) || e.flag.startsWith('ZY支持'))
|
||||
if (index !== -1) {
|
||||
@@ -299,7 +317,7 @@ const zy = {
|
||||
const site = res
|
||||
if (site.download) {
|
||||
const url = `${site.download}?ac=videolist&ids=${id}&ct=1`
|
||||
axios.post(url).then(res => {
|
||||
axios.get(url).then(res => {
|
||||
const data = res.data
|
||||
const json = parser.parse(data, this.xmlConfig)
|
||||
const jsondata = json.rss === undefined ? json : json.rss
|
||||
@@ -308,10 +326,10 @@ const zy = {
|
||||
const type = Object.prototype.toString.call(dd)
|
||||
if (type === '[object Array]') {
|
||||
for (const i of dd) {
|
||||
downloadUrls = i._t.split('#').map(e => encodeURI(e.split('$')[1])).join('\n')
|
||||
downloadUrls = i._t.replace(/\$+/g, '$').split('#').map(e => encodeURI(e.includes('$') ? e.split('$')[1] : e)).join('\n')
|
||||
}
|
||||
} else {
|
||||
downloadUrls = dd._t.split('#').map(e => encodeURI(e.split('$')[1])).join('\n')
|
||||
downloadUrls = dd._t.replace(/\$+/g, '$').split('#').map(e => encodeURI(e.includes('$') ? e.split('$')[1] : e)).join('\n')
|
||||
}
|
||||
if (downloadUrls) {
|
||||
info = '调用下载接口获取到的链接已复制, 快去下载吧!'
|
||||
@@ -327,7 +345,7 @@ const zy = {
|
||||
zy.detail(key, id).then(res => {
|
||||
const dl = res.fullList.find(e => e.flag === videoFlag) || res.fullList[0]
|
||||
for (const i of dl.list) {
|
||||
const url = encodeURI(i.split('$')[1])
|
||||
const url = encodeURI(i.includes('$') ? i.split('$')[1] : i)
|
||||
downloadUrls += (url + '\n')
|
||||
}
|
||||
if (downloadUrls) {
|
||||
@@ -366,22 +384,47 @@ const zy = {
|
||||
* @param {*} channel 直播频道 url
|
||||
* @returns boolean
|
||||
*/
|
||||
async checkChannel (channel) {
|
||||
checkChannel (url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios.get(channel).then(res => {
|
||||
const manifest = res.data
|
||||
var parser = new M3u8Parser()
|
||||
parser.push(manifest)
|
||||
parser.end()
|
||||
var parsedManifest = parser.manifest
|
||||
if (parsedManifest.segments.length) {
|
||||
resolve(true)
|
||||
} else {
|
||||
resolve(false)
|
||||
const supportFormats = /\.(m3u8|flv)$/
|
||||
const extRE = url.match(supportFormats) || new URL.URL(url).pathname.match(supportFormats)
|
||||
if (extRE[1] === 'flv') {
|
||||
const MAX_CONTENT_LENGTH = 2000 // axios配置maxContentLength不生效,先用request凑合
|
||||
let receivedLength = 0
|
||||
let options = { uri: url, gzip: true, timeout: 10000 }
|
||||
if (proxyURL) {
|
||||
if (proxyURL.startsWith('http')) options = Object.assign({ proxy: proxyURL }, options)
|
||||
if (proxyURL.startsWith('socks5')) options = Object.assign({ agent: new SocksProxyAgent(proxyURL) }, options)
|
||||
}
|
||||
}).catch(e => {
|
||||
resolve(false)
|
||||
})
|
||||
const req = request.get(options)
|
||||
.on('data', (str) => {
|
||||
receivedLength += str.length
|
||||
if (receivedLength > MAX_CONTENT_LENGTH) {
|
||||
resolve(true) // 应该用FLVDemuxer.probe来检测,先凑合
|
||||
req.abort()
|
||||
}
|
||||
})
|
||||
.on('error', function (err) {
|
||||
resolve(false)
|
||||
console.log(err)
|
||||
})
|
||||
.on('end', () => { resolve(false) })
|
||||
} else if (extRE[1] === 'm3u8') {
|
||||
axios.get(url).then(res => {
|
||||
const manifest = res.data
|
||||
const parser = new M3u8Parser()
|
||||
parser.push(manifest)
|
||||
parser.end()
|
||||
const parsedManifest = parser.manifest
|
||||
if (parsedManifest.segments.length) {
|
||||
resolve(true)
|
||||
} else {
|
||||
resolve(false)
|
||||
}
|
||||
}).catch(e => {
|
||||
resolve(false)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
@@ -393,16 +436,16 @@ const zy = {
|
||||
doubanLink (name, year) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 豆瓣搜索链接
|
||||
var nameToSearch = name.replace(/\s/g, '')
|
||||
var doubanSearchLink = 'https://www.douban.com/search?q=' + nameToSearch
|
||||
const nameToSearch = name.replace(/\s/g, '')
|
||||
const doubanSearchLink = 'https://www.douban.com/search?q=' + nameToSearch
|
||||
axios.get(doubanSearchLink).then(res => {
|
||||
const $ = cheerio.load(res.data)
|
||||
// 查询所有搜索结果, 看名字和年代是否相符
|
||||
var link = ''
|
||||
let link = ''
|
||||
$('div.result').each(function () {
|
||||
var linkInDouban = $(this).find('div>div>h3>a').first()
|
||||
var nameInDouban = linkInDouban.text().replace(/\s/g, '')
|
||||
var subjectCast = $(this).find('span.subject-cast').text()
|
||||
const linkInDouban = $(this).find('div>div>h3>a').first()
|
||||
const nameInDouban = linkInDouban.text().replace(/\s/g, '')
|
||||
const subjectCast = $(this).find('span.subject-cast').text()
|
||||
if (nameToSearch === nameInDouban && subjectCast && subjectCast.includes(year)) {
|
||||
link = linkInDouban.attr('href')
|
||||
}
|
||||
@@ -426,14 +469,14 @@ const zy = {
|
||||
*/
|
||||
doubanRate (name, year) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var nameToSearch = name.replace(/\s/g, '')
|
||||
const nameToSearch = name.replace(/\s/g, '')
|
||||
this.doubanLink(nameToSearch, year).then(link => {
|
||||
if (link.includes('https://www.douban.com/search')) {
|
||||
resolve('暂无评分')
|
||||
} else {
|
||||
axios.get(link).then(response => {
|
||||
const parsedHtml = cheerio.load(response.data)
|
||||
var rating = parsedHtml('body').find('#interest_sectl').first().find('strong').first()
|
||||
const rating = parsedHtml('body').find('#interest_sectl').first().find('strong').first()
|
||||
if (rating.text()) {
|
||||
resolve(rating.text().replace(/\s/g, ''))
|
||||
} else {
|
||||
@@ -448,16 +491,34 @@ const zy = {
|
||||
})
|
||||
})
|
||||
},
|
||||
async proxy () {
|
||||
get7kParseURL () {
|
||||
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) })
|
||||
})
|
||||
},
|
||||
getDefaultSites () {
|
||||
const url = 'https://gitee.com/cuiocean/ZY-Player-Resources/raw/main/Sites/Sites.json'
|
||||
return new Promise((resolve, reject) => {
|
||||
axios.get(url).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(err => { reject(err) })
|
||||
})
|
||||
},
|
||||
proxy () {
|
||||
return new Promise((resolve, reject) => {
|
||||
setting.find().then(db => {
|
||||
if (db && db.proxy && db.proxy.type === 'manual') {
|
||||
if (db.proxy.scheme && db.proxy.url && db.proxy.port) {
|
||||
const proxyURL = db.proxy.scheme + '://' + db.proxy.url.trim() + ':' + db.proxy.port.trim()
|
||||
proxyURL = db.proxy.scheme + '://' + db.proxy.url.trim() + ':' + db.proxy.port.trim()
|
||||
session.setProxy({ proxyRules: proxyURL })
|
||||
http.globalAgent = https.globalAgent = new ElectronProxyAgent(session)
|
||||
}
|
||||
} else {
|
||||
proxyURL = ''
|
||||
session.setProxy({ proxyRules: 'direct://' })
|
||||
http.globalAgent = https.globalAgent = new ElectronProxyAgent(session)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user